CreateProcessA参数型Shellcode的编码问题钻研
颁布功夫 2021-12-22单一来讲,,,,,,该节造系统的缝隙由两个法式组成:主题法式CoreProcess和辅助法式HelpProcess,,,,,,主题法式CoreProcess通过系统函数CreateProcessA来启动HelpProcess(同时传递了有关参数)。。。。。。。其中,,,,,,CoreProcess的简化代码如下:

显然,,,,,,HelpProcess的WinMain函数存在一个经典的栈溢露马脚。。。。。。。当lpCmdLine的数据长度超过400字节时,,,,,,对buff的strcpy操作就会产生溢出;;;;;;当长度超过404字节时,,,,,,就会覆盖到eipCallerNext,,,,,,从而劫持HelpProcess的法式节造流。。。。。。。
回溯代码可知,,,,,,lpCmdLine的数据起源是CoreProcess的CreateProcessA挪用,,,,,,且是用户可控的。。。。。。。因而,,,,,,该缝隙的利用看起来是单一的,,,,,,只必要推算好eipCallerNext的偏移量并利用shellcode填充buff即可。。。。。。。该缝隙的利用链和仓库布局如下所示:

在利用过程中,,,,,,选取测试填充字符进行溢出时,,,,,,eipCallerNext的覆盖总是正确的;;;;;;但选取metasploit的shellcode来溢出时,,,,,,eipCallerNext的覆盖就变得不正确。。。。。。。对数据进行比力后发现,,,,,,shellcode在CoreProcess和HelpProcess是不一样的,,,,,,即shellcode传递到HelpProcess后产生了扭转。。。。。。。此表,,,,,,通过尝试metasploit的分歧shellcode,,,,,,发现这种扭转没有显著的法规可循。。。。。。。
针对这个问题,,,,,,ADLab的安全钻研员进行了深刻的分析,,,,,,弄清了CreateProcessA参数传递的shellcode的编码问题,,,,,,并开发了自动化处置步骤,,,,,,从而兼容肆意shellcode。。。。。。。
CreateProcessA的参数处置
Windows操作系统的内核是支持全球各类说话的,,,,,,其提供统一的Unicode编码型内核态API;;;;;;针对具体的国度或地域,,,,,,Windows通过区域编码来实现本地说话支持,,,,,,即Ansi字符串型的用户态API。。。。。。。这些用户态API在内部先把Ansi字符串转换为Unicode字符串,,,,,,而后再挪用内核态API;;;;;;这个转换过程是通明的,,,,,,用户编写的法式对此无感知。。。。。。。
在Window操作系统上,,,,,,1个Unicode字符由2个字节组成,,,,,,1个Ansi字符由1个字节或2个字节组成。。。。。。。当首字节的值是0到127时,,,,,,它是1个ASCII字符,,,,,,对应Unicode字符的2字节的内容就是该ASCII字符加1个填充字符0;;;;;;例如,,,,,,Ansi字符”A”,,,,,,其对应的Unicode字符是”A\x00”。。。。。。。当首字节的值大于127时,,,,,,则当前字节和下个字节组合起来是一个区域说话的字符,,,,,,区域说话字符存在对应的Unicode字符映射表;;;;;;例如,,,,,,”\xce\xd2”的“\xce”不是1个合法的ASCII字符,,,,,,它只能和“\xd2”结合作为1个中文字符“我”,,,,,,对应的Unicode字符是”\x11\x62”。。。。。。。
如下所示,,,,,,CreateProcessA就是一个Ansi编码型的用户态API,,,,,,字符串”AAAA”会被自动转换为Unicode字符串并传递给HelpProcess,,,,,,而后在挪用WinMain之前又被自动还原为Ansi字符串。。。。。。。因而,,,,,,对于Ansi字符串”AAAA”,,,,,,CoreProcess和HelpProcess在法式开发上都无需做任何额表的处置。。。。。。。

通常情况下,,,,,,CreateProcessA参数lpCmdline的起源是靠得住的,,,,,,好比编译时预约义的字符串和API的返回值,,,,,,此时lpCmdline都是正确的Ansi字符串。。。。。。。因而,,,,,,CreateProcess险些总能在Unicode和Ansi之间自由地正确转换。。。。。。。
现实上,,,,,,对于任何一门区域说话,,,,,,其Ansi字符和Unicode字符的映射都不是逐一映射关系;;;;;;即在2字节的全数取值空间中,,,,,,Ansi字符表的有效项数总是幼于Unicode字符表的有效项数。。。。。。。这意味着,,,,,,针对无法确认是区域说话的2个字节,,,,,,若是强造视作Ansi字符则转换成Unicode字符后不愿定能还原为初始的Ansi字符。。。。。。。例如:”\xeb\x2a”是一条通例的jmp offset指令,,,,,,它不是1个合法的中文字符;;;;;;若是视作Ansi字符强造转换为Unicode字符则是”\x3f\x00”,,,,,,再次转换为Ansi字符即是”?”,,,,,,迷失了jmp offset指令的语义。。。。。。。
因而,,,,,,通过CreateProcessA的cmdline参数进行shellcode传递,,,,,,必必要思考区域说话的Ansi字符和Unicode字符相互转换的问题。。。。。。。
在本文的缝隙利用案例中,,,,,,本地域域的说话是中文简体,,,,,,对应Ansi编码表是GBK。。。。。。。因而,,,,,,必必要对metasploit的shellcode进行GBK编码,,,,,,确保其是正确的Ansi字符串。。。。。。。
GBK表的编码在2字节取值空间的领域是8140-FEFE,,,,,,即第1字节的取值领域是0x81到0xFE,,,,,,第2字节的取值是0x40到0xFE,,,,,,如下所示:

此表,,,,,,第2字节的现实有效取值还有更多约束。。。。。。。好比,,,,,,第2字节不能为0X7F。。。。。。。针对某些取值的字节,,,,,,第2字节的取值比[0x40, 0xFE]的空间更幼。。。。。。。如下图所示,,,,,,有的只能取该空间的后半部门,,,,,,有的则只能取前半部门。。。。。。。
对于shellcode来讲,,,,,,其每个字节的取值在0到255之间都是齐全合法的。。。。。。。因而,,,,,,本文的缝隙利用要实现shellcode的轻易代替,,,,,,必必要有一种步骤来对shellcode中违背GBK编码的字节进行处置,,,,,,从而预防Ansi字符和Unicode字符间转换导致的shellcode字符被扭转的问题。。。。。。。一个根基的步骤是依照如下的流程对shellcode进行处置,,,,,,其关键是对GBK表进行查表并建改汇编指令。。。。。。。

以如下的shellcode为例,,,,,,在扫描到字节0xEB时,,,,,,发现长短ASCII字符且查表GBK了局是不存在,,,,,,必要进行转换;;;;;;查问GBK表后发现,,,,,,在0xEB之前插入0x90能够使得90 EB是一个合法的GBK字符,,,,,,同时90EB 38又不扭转原来的汇编语义,,,,,,转换成功。。。。。。。同理,,,,,,持续扫描到下一个字节0XEB时,,,,,,再做同样的转换就能够。。。。。。。但是,,,,,,第2次的转换插入了新的字节0x90,,,,,,导致了原始lab1对应的偏移量产生了扭转;;;;;;原始lab的指令现实位于转后的lab+1地位,,,,,,使得第一个0XEB的语义犯法了。。。。。。。因而,,,,,,转换过程还要求跟踪指令区块的长度变动。。。。。。。

除了指令区块的长度扭转表,,,,,,还有其它兼容性问题。。。。。。。好比,,,,,,shellcode中特殊取值(典型有0)的字节处置问题,,,,,,对shellcode的内嵌参数批改问题等。。。。。。。因而,,,,,,只管查表转换是最底子的法子,,,,,,但全表查问的空间大,,,,,,限度了shellcode的矫捷性。。。。。。。为相识决该问题,,,,,,ADLab的安全钻研员提出了一种基于推算的shellcode编码步骤。。。。。。。
Shellcode推算转换
首先,,,,,,我们把shellcode分为两部门:头部的固定decoder和尾部的多变payload。。。。。。。而后,,,,,,选取查表方式进行手工编写切合GBK编码的汇编代码。。。。。。。其中,,,,,,decoder的长度很有限,,,,,,决定了这个编写的价值不大;;;;;;同时,,,,,,多变payload是没有额表限度的,,,,,,通过编写对应的encoder来编码payload使其不违反GBK编码,,,,,,又能够被decoder还原。。。。。。。通过这种方式,,,,,,对原始shellcode的选择和扭转就齐全不用关切GBK编码问题,,,,,,使得该缝隙的利用越发丰硕。。。。。。。
为了削减decoder的体积,,,,,,我们设计了一种推算步骤来编码和解码,,,,,,这样就不必要存储GBK字符表或者复杂的规定。。。。。。。原始shellcode编码时的推算规定如下:
遇到字节是ASCII、0x80和0xff,,,,,,直接保留。。。。。。。
遇到字节是\x00,,,,,,转换成加法运算符\x90和2个推算数符\x80和\x80。。。。。。。
遇到字节是\x90,,,,,,转换成加法运算符\x90和2个推算数符\x48和\x48。。。。。。。
遇到2个字节能够转换为unicode字符,,,,,,直接保留这2个字节。。。。。。。
遇到前面都不能处置的字节,,,,,,直接转换成加法运算符\x90和2个推算数符,,,,,,第1个是\x80,,,,,,第2个是差值。。。。。。。
选取上述的编码步骤后,,,,,,任何shellcode都能够被转换为合法GBK字符串,,,,,,并且decoder对payload的解码推算也极度单一,,,,,,只必要如下的1条文则:
遇到字符是\x90,,,,,,直接对后2个字符进行加法推算,,,,,,并用了局代替字符\x90。。。。。。。
至此,,,,,,CreateProcessA参数传递的shellcode的编码问题就全数被约束在了只有一条文则的decoder代码中,,,,,,很显然这是一个天堑极度明确的部门问题,,,,,,因而很容易就解决了。。。。。。。选取这种步骤,,,,,,本文的缝隙利用能够轻易挪用metasploit中的shellcode,,,,,,无需再不安它们的指令内部细节。。。。。。。
在多说话环境下,,,,,,shellcode若是不是直接的内存传递,,,,,,则可能会被系统API函数所转换,,,,,,从而导致其因在获得执行权之前产生内容扭转而无效。。。。。。。因而,,,,,,在缝隙利用过程中,,,,,,必要把稳shellcode是否受到多说话版本的API影响。。。。。。。


京公网安备11010802024551号