云舒 写道: > 使用安全的C#代码跳出CLR沙箱 > <http://www.icylife.net/yunshu/show.php?id=621> Submitted by *云舒* > <mailto:[EMAIL PROTECTED]> on 2008, September 20, 1:03 AM. *技术 > <http://www.icylife.net/yunshu/index.php?sortid=2>* > > 摘要:分析 介绍使用安全的C#代码跳出CLR沙箱的办法,原文是老外的《 > Writing a .NET Security Exploit POC》,我做了修改,并介绍了原理以及跟踪 > 过程。 > > Our Team:http://www.ph4nt0m.org > Author:云舒([EMAIL PROTECTED]) > > 本文可以任意转载,但请保持完整,并保留原始出处,谢谢。 > > *起因* > > 一个老外写了一篇文章<<Writing a .NET Security Exploit POC>>,他给出的 > 代码地址为http://www.frijters.net /TypeSafetyExploitPoC.cs.txt。KJ看到 > 后觉得可以用在SQL Server 2005里面,通过加载C#代码绕过沙箱的限制来获取 > shell。他测试之后代码无法运行,我就进行了一些初步调试,发现那段代码在 > 内存中暴力搜索 WinExec函数的地址时失败,返回为0,因此无法顺利执行。 > > 这时候刺也加了进来,我们分头同时想到不需要在C#里面做内存搜索,既然可以 > 写入机器码,那么直接写入一段shellcode就可以了。于是我在http: > //metasploit.com <http://metasploit.com/>找了一段shellcode,修改原始 > POC,刺也同时作出了可以成功运行的修改版POC代码。 > > 晚上KJ拿回家在SQL Server 2005中测试,确实可以,但是限制条件还是比较 > 多,所以基本上是鸡肋了,可以参看他的文章,地址为http://blog.csdn.net > /kj021320/archive/2008/09/17/2944371.aspx。但是事情没有完,这个代码不 > 复杂,但是原理是什么?他们都不太熟悉C#,因此责任就落在我身上了,开始了 > 痛苦的虚拟机中运行的代码的调试之旅。至于这段POC代码,我发到我们的邮件 > 列表了,地址是http: > //groups.google.com/group/ph4nt0m/browse_thread/thread/1ee957d07b33931f/6a0e158cb1deb078? > show_docid=6a0e158cb1deb078,在我的回复里面,这里就不写出来了。 > > *原理* > > 要了解这个POC的原理,首先需要知道一些CLR的基础。这些没找到文档资料,是 > 我跟踪分析出来的,不一定对,姑妄听之吧。在C# 中直接定义数组,是在栈中 > 保存元素的。但是如果数组是一个类中的成员,那么实例化这个类并且初始化这 > 个数组的时候,在栈中仅仅保存一个指针,这个指针指向真正存放元素的地方。 > 被指向的内存,第一个四字节保存着MethodTable,第二个四字节保存着元素的 > 个数,第三个四字节才开始是保存着真实的数组元素。 > > 在POC里,类Union2中有一个arr数组作为其成员,另一个o成员在后面被一个委 > 托赋值,其实就是一个函数指针,4字节。而拥有 u1和u2实例的UnsafeUnion结 > 构体,申明了[StructLayout(LayoutKind.Explicit)]和 [FieldOffset(0)],表 > 示u1和u2在内存中的偏移都是0字节。由于u1是两个int,均是4字节,而u2的o成 > 员和arr都是指针,也都是4字节。因此,u1的i元素和u2的o元素在内存中重合, > u1的j元素和u2的arr元素在内存中重合,u1和u2本身在内存中也完全融合。 > > POC中的del委托指向DummyMethod函数,在CLR语言中无法通过安全的代码来获取 > 函数指针,因此只能通过将del赋值给 object o来间接的传递函数地址。最终通 > 过修改int型j的值,间接的修改了arr指向的地址,将arr指向了DummyMethod函 > 数中。最终导致修改 arr,其实是修改了函数DummyMethod的代码,通过委托执 > 行函数的时候就执行了我们自定义的shellcode,跳出了sandbox了。 > > 那个POC的作者熟悉C#而不太熟悉安全,所以代码长而且不够可靠,修改了下就 > 好了。原理说得有些绕,下面我来用动态跟踪证实这一点。 > > *调试* > > C#代码是在CLR虚拟机里面运行的,因此基本无法用OllyDBG来做调试,如果有人 > 有办法,还请指点一下。经过在微软一番搜索,使用了Visual Studio 2008加载 > SOS.dll来做动态跟踪,具体的文档可以搜索SOS.dll就行了。下面简单介绍一下 > 我的调试过程,因为觉得这个很有意思。当然这个是摸索过后的情形,所以看起 > 来很顺利,其实我从学习SOS.dll到跟踪分析完成花了两天多时间。 > > 首先在加载我修改过的代码工程,在Main函数的第一行下断点,并打开Memory窗 > 口,再在右下角的Immediate Window窗口中输入命令.load sos加载sos.dll模 > 块,整个界面如下图所示,希望这是唯一的一个截图。 > > .load sos > extension C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded > > 可以看到,成功的加载了sos.dll,可以使用!help命令查看sos模块提供的调试 > 命令帮助信息,更多的请自己翻阅手册。 > 单步走一步,查看CLR虚拟机的栈信息,结果如下: > !clrstack -a > OS Thread Id: 0x8f4 (2292) > ESP EIP > 0012f418 00e500e4 TypeSafetyExploitPoC.Main(System.String[]) > PARAMETERS: > args = 0x01301628 > LOCALS: > 0x0012f440 = 0x00000000 > 0x0012f43c = 0x01301664 > 0x0012f438 = 0x00000000 > 0x0012f434 = 0x00000000 > 0x0012f430 = 0x00000000 > 0x0012f42c = 0x00000000 > 0x0012f428 = 0x00000000 > 0x0012f424 = 0x00000000 > 0x0012f420 = 0x00000000 > 0x0012f41c = 0x00000000 > 0x0012f418 = 0x00000000 > 0012f69c 79e7c74b [GCFrame: 0012f69c] > > 可以看到,变量u2已经被定义了,在栈里面的地址为0x01301664。再单步走到 > u2.o = del上面的语句,查看一下栈,会发现u1,u2,以及del都定义了,而且 > u1和u2指向的内存和我上面说的一样,是重合的,都是0x01301664 ----当然, > 具体的内存地址取决于机器。Del的值为0x01301674。 > > 在Memory窗口,查看0x01301664地址处的内存,单步走完u2.o = del这条语句, > 再查看内存,会发现0x01301664 + 4处的内存变成了del的值,即0x01301674, > 需要注意的是,u1和u2在内存上完全重合,因此u1.i的值就也变成了 > 0x01301674。为什么是0x01301664 + 4处的内存而不是0x01301664处?查看一下 > u2的结构,如下: > > !dumpobj 0x01301664 > Name: Union2 > MethodTable: 009830fc > EEClass: 00981344 > Size: 16(0x10) bytes > (E:\TestExp\bin\Debug\TestExp.exe) > Fields: > MT Field Offset Type VT Attr Value Name > 790fd0f0 4000003 4 System.Object 0 instance 01301674 o > 7912d7c0 4000004 8 System.Int32[] 0 instance 00000000 arr > > 可以看到,u2的第一个元素偏移量确实是4,偏移为0的地方,是存放着 > MethodTable的地址,记录了一些内部信息。 > 单步走过u1.j = u1.i,可以看到内存0x01301664 + 8处的值改变了。还是由于 > u1和u2内存重合的原因,u1.i,u1.j,以及u2.o,u2.arr都变成了相同的值,即 > del的值,也就是那个函数指针的值了。 > > 下面的一条指令u1.j = u2.arr[2] - 12是POC最关键的一条,还是由于内存重 > 合,这一条改变u1.j的值的指令,间接的修改了arr指向的地址,使arr指向了函 > 数指针指向的内存。因此在后面对arr进行修改的时候,其实是修改了函数指针 > 指向的内存,也就是修改了委托del指向的函数DummyMethod的指令。这条代码执 > 行后查看 arr的值,如下: > !dumpobj 0x01301664 > <Note: this object has an invalid CLASS field> > Name: Union2 > MethodTable: 009830fc > EEClass: 00981344 > Size: 16(0x10) bytes > (E:\TestExp\bin\Debug\TestExp.exe) > Fields: > MT Field Offset Type VT Attr Value Name > 790fd0f0 4000003 4 System.Object 0 instance 01301674 o > 7912d7c0 4000004 8 System.Int32[] 0 instance 0098c054 arr > > 可以看到,数组的数据已经是保存在地址0x0098c054处了。我们看一下函数指针 > 0x01301674的内容。 > > !dumpobj 0x01301674 > Name: System.Threading.ThreadStart > MethodTable: 791249e8 > EEClass: 790c57b8 > Size: 32(0x20) bytes > (C:\WINDOWS\assembly\GAC_32\mscorlib.0.0.0__b77a5c561934e089\mscorlib.dll) > Fields: > MT Field Offset Type VT Attr Value Name > 790fd0f0 40000ff 4 System.Object 0 instance 01301674 _target > 7910ebc8 4000100 8 ...ection.MethodBase 0 instance 0130182c _methodBase > 791016bc 4000101 c System.IntPtr 1 instance 003B20C4 _methodPtr > 791016bc 4000102 10 System.IntPtr 1 instance 0098C060 _methodPtrAux > 790fd0f0 400010c 14 System.Object 0 instance 00000000 _invocationList > 791016bc 400010d 18 System.IntPtr 1 instance 00000000 _invocationCount > > 注意其中的_methodPtrAux的值,对比一下上文的arr指向的内存,发现确实落在 > 其中了。其实上面的arr[2]取到的值就是这个_methodPtrAux的内容。后面的事 > 情就简单了,将shellcode拷贝到arr中,也就是拷贝到了DummyMethod函数的内 > 存中,改写了DummyMethod函数。最后调用del委托间接的调用DummyMethod,就 > 执行了我们的shellcode,突破sandbox 了。 > > 2008-09-20 > ------------------------------------------------------------------------ > 云舒 > > > 是不是不安装C#就没有CLR调试器了?我印象中VS里有带,可是重装了之后的却找 不到。感谢云舒牛牛如此精彩的文章
--~--~---------~--~----~------------~-------~--~----~ 要向邮件组发送邮件,请发到 [email protected] 要退订此邮件,请发邮件至 [EMAIL PROTECTED] -~----------~----~----~----~------~----~------~--~---

