【CGGC 2024】Preview Site Write-up

Suifeng0214 Lv3

Web

Preview Site

Problem source code:

1
2
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# app.py
from flask import Flask, request, redirect, render_template, session, url_for, flash
import urllib.request
import urllib.error
import urllib.parse
import os

app = Flask(__name__)
app.secret_key = os.urandom(24)

users = {'guest': 'guest'}

def send_request(url, follow=True):
try:
response = urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
response = e
redirect_url = response.geturl()
if redirect_url != url and follow:
return send_request(redirect_url, follow=False)
return response.read().decode('utf-8')


@app.route('/login', methods=['GET', 'POST'])
def login():
next_url = request.args.get('next', url_for('index'))
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if users.get(username) == password:
session['username'] = username
flash('login success')
return redirect(next_url)
else:
error = 'login failed'
return render_template('login.html', error=error, next=next_url)
return render_template('login.html', next=next_url)

@app.route('/logout')
def logout():
session.pop('username', None)
next_url = request.args.get('next', url_for('index'))
return redirect(next_url)

@app.route('/fetch', methods=['GET', 'POST'])
def fetch():
if 'username' not in session:
return redirect(url_for('login'))

if request.method == 'POST':
url = request.form.get('url')
if not url:
flash('Please provide a URL.')
return render_template('fetch.html')
try:
if not url.startswith(os.getenv("DOMAIN", "http://previewsite/")):
raise ValueError('badhacker')
resp = send_request(url)
return render_template('fetch.html', content=resp)
except Exception as e:
error = f'error:{e}'
return render_template('fetch.html', error=error)
return render_template('fetch.html')

@app.route('/')
def index():
username = session.get('username')
return render_template('index.html', username=username)

在逛了一番前端網頁後,會發現 /fetch 頁面可以 send request

解題思路

觀察 send_request() 可以發現:

  • response = urllib.request.urlopen(url) 默認情況下會自動跟隨 HTTP redirect。
  • 所以 redirect_url = response.geturl() 可以獲取 redirect 到最後的 url。

這樣一來目標就是讓網頁最後重導向至內網資源獲取 FLAG (SSRF)
再來,觀察有什麼可以利用的 redirect function
loginlogout 都有
但是 logout 比較好利用,因為 login 還要吃 POST 的帳密,logout 直接 GET 就好
接著構造出來就好了:http://previewsite/logout?next=file:///etc/passwd
然後開始猜 flag path
Payload: http://previewsite/logout?next=file:///flag

過程犯蠢

(還是留在自己的 hackmd 就好了..)

On this page
【CGGC 2024】Preview Site Write-up