前回 から、setodaNote CTF Exhibition を開始しました。CTF には複数のカテゴリがありますが、リバースエンジニアリング、Pwn を優先的に取り組みたいと思っています。これらがだいたい終わったので、次は、ksnctf も並行して進めようと思います。
ksnctf も、国内の常設の CTF です。
それでは、やっていきます。
はじめに
「セキュリティ」の記事一覧です。良かったら参考にしてください。
・第2回:Ghidraで始めるリバースエンジニアリング(使い方編)
・第3回:VirtualBoxにParrotOS(OVA)をインストールする
・第4回:tcpdumpを理解して出力を正しく見れるようにする
・第5回:nginx(エンジンエックス)を理解する
・第6回:Python+Flask(WSGI+Werkzeug+Jinja2)を動かしてみる
・第7回:Python+FlaskのファイルをCython化してみる
・第8回:shadowファイルを理解してパスワードを解読してみる
・第9回:安全なWebアプリケーションの作り方(徳丸本)の環境構築
・第10回:Vue.jsの2.xと3.xをVue CLIを使って動かしてみる(ビルドも行う)
・第11回:Vue.jsのソースコードを確認する(ビルド後のソースも見てみる)
・第12回:徳丸本:OWASP ZAPの自動脆弱性スキャンをやってみる
・第13回:徳丸本:セッション管理を理解してセッションID漏洩で成りすましを試す
・第14回:OWASP ZAPの自動スキャン結果の分析と対策:パストラバーサル
・第15回:OWASP ZAPの自動スキャン結果の分析と対策:クロスサイトスクリプティング(XSS)
・第16回:OWASP ZAPの自動スキャン結果の分析と対策:SQLインジェクション
・第17回:OWASP ZAPの自動スキャン結果の分析と対策:オープンリダイレクト
・第18回:OWASP ZAPの自動スキャン結果の分析と対策:リスク中すべて
・第19回:CTF初心者向けのCpawCTFをやってみた
・第20回:hashcatの使い方とGPUで実行したときの時間を見積もってみる
・第21回:Scapyの環境構築とネットワークプログラミング
・第22回:CpawCTF2にチャレンジします(クリア状況は随時更新します)
・第23回:K&Rのmalloc関数とfree関数を理解する
・第24回:C言語、アセンブラでシェルを起動するプログラムを作る(ARM64)
・第25回:機械語でシェルを起動するプログラムを作る(ARM64)
・第26回:入門セキュリhttps://github.com/SECCON/SECCON2017_online_CTF.gitティコンテスト(CTFを解きながら学ぶ実践技術)を読んだ
・第27回:x86-64 ELF(Linux)のアセンブラをGDBでデバッグしながら理解する(GDBコマンド、関連ツールもまとめておく)
・第28回:入門セキュリティコンテスト(CTFを解きながら学ぶ実践技術)のPwnable問題をやってみる
・第29回:実行ファイルのセキュリティ機構を調べるツール「checksec」のまとめ
・第30回:setodaNote CTF Exhibitionにチャレンジします(クリア状況は随時更新します)
・第31回:常設CTFのksnctfにチャレンジします(クリア状況は随時更新します) ← 今回
ksnctf の公式サイトは以下です。
それでは、やっていきます。
ksnctfに登録する
上の URL にアクセスして、右上の Login をクリックすると、with Twitter と出るので、クリックします。

アクセスを許可するかを聞かれるので、「連携アプリを認証」をクリックします。

ログインできました。登録の手順は以上となります。
問題一覧
問題数は 41問で、全てのポイントを獲得すると 5161ポイントになると思います。Ranking を見ると、10,232名中、6名が 5161ポイントでした。参加人数が多いですね。
この CTF は、Writeup の記事を書くことは問題ないとのことですが、フラグの文字列は公開してはいけないとのことです。
公式サイトでは、各問題について、カテゴリは書かれていませんが、参考までに、私が勝手に想像したカテゴリを書いておきます。
| No. | カテゴリ | 問題名 | ポイント | 状況 |
|---|---|---|---|---|
| 1 | Misc | Test Problem | 1 | Complete |
| 2 | Crypto | Easy Cipher | 50 | Complete |
| 3 | Crypto | Crawling Chaos | 100 | Complete |
| 4 | Pwn | Villager A | 300 | 0% |
| 5 | Crypto | Onion | 70 | 0% |
| 6 | Login | 120 | 0% | |
| 7 | Programming | 110 | 0% | |
| 8 | Basic is secure? | 50 | 0% | |
| 9 | Digest is secure! | 150 | 0% | |
| 10 | Misc | #! | 20 | Complete |
| 11 | Riddle | 200 | 0% | |
| 12 | Hypertext Preprocessor | 70 | 0% | |
| 13 | Proverb | 70 | 0% | |
| 14 | John | 60 | 0% | |
| 15 | Jewel | 250 | 0% | |
| 16 | Math I | 150 | 0% | |
| 17 | Math II | 50 | 0% | |
| 18 | USB flash drive | 80 | 0% | |
| 19 | ZIP de kure | 150 | 0% | |
| 20 | G00913 | 30 | 0% | |
| 21 | Perfect Cipher | 200 | 0% | |
| 22 | Square Cipher | 60 | 0% | |
| 23 | Villager B | 450 | 0% | |
| 24 | Rights out | 100 | 0% | |
| 25 | Reserved | 30 | 0% | |
| 26 | Sherlock Holmes | 70 | 0% | |
| 27 | Lives out | 150 | 0% | |
| 28 | Lo-Tech Cipher | 60 | 0% | |
| 29 | Double Blind | 50 | 0% | |
| 30 | Alpha Mixed Cipher | 80 | 0% | |
| 31 | KanGacha | 130 | 0% | |
| 32 | Web | Simple Auth | 50 | Complete |
| 33 | HTTPS is secure. | 180 | 0% | |
| 34 | Are you human? | 220 | 0% | |
| 35 | Web | Simple Auth II | 20 | 0% |
| 36 | Are you ESPer? | 120 | 0% | |
| 37 | Competitive Programming | 600 | 0% | |
| 38 | Canned Can Opener | 80 | 0% | |
| 39 | Unknown Plaintext Attack | 250 | 0% | |
| 40 | Deep Flag Network | 100 | 0% | |
| 41 | Private BBS | 80 | 0% |
Misc
最初の問題は、Test Problem ということなので、おそらく Misc(ミスク)です。
Test Problem
Problems から、Test Problem をクリックします。

1ポイント獲得して、10,232名中、7,679位になりました!
0ポイントのままの方もいましたし、同じポイントの場合は、後からそのポイントになった人が、上の順位になるようです。
#!
問題名が記号だけです。

20ポイント獲得して、計21ポイントで、10,232名中、7,550位になりました!
Crypto
Easy Cipher
シーザー暗号っぽいです。

わりと高い頻度で出てくるので、Python で書いておきます。
def str2int( ss, offset=0 ): lst = [] for cc in ss: if cc == ' ': lst.append( ord(cc) ) elif '0' <= cc <= '9': mm = (ord(cc) - ord('0') + offset) % (ord('9') - ord('0') + 1) lst.append( mm + ord('0') ) elif 'A' <= cc <= 'Z': mm = (ord(cc) - ord('A') + offset) % (ord('Z') - ord('A') + 1) lst.append( mm + ord('A') ) elif 'a' <= cc <= 'z': mm = (ord(cc) - ord('a') + offset) % (ord('z') - ord('a') + 1) lst.append( mm + ord('a') ) else: lst.append( ord(cc) + offset ) print( f"str2int={lst}" ) return lst def int2chr( lst ): ss = "" lst_ret = [] for ii in lst: ss += chr(ii) lst_ret.append( chr(ii) ) #print( lst_ret ) print( f"int2chr={ss}" ) return lst_ret ss = "EBG KVVV vf n fvzcyr yrggre fhofgvghgvba pvcure gung ercynprf n yrggre jvgu gur yrggre KVVV yrggref nsgre vg va gur nycunorg. EBG KVVV vf na rknzcyr bs gur Pnrfne pvcure, qrirybcrq va napvrag Ebzr. Synt vf SYNTFjmtkOWFNZdjkkNH. Vafreg na haqrefpber vzzrqvngryl nsgre SYNT" lst = str2int( ss, -13 ) lst_ret = int2chr( lst )
実行してみます。デバッグ的なところは省略して貼ります。
$ python tmp.py int2chr=ROT XIII is a simple letter substitution cipher that replaces a letter with the letter XIII letters after it in the alphabet! ROT XIII is an example of the Caesar cipher developed in ancient Rome! Flag is FLAGSwzgxBJSAMqwxxAU! Insert an underscore immediately after FLAG
空白だけではなく、記号なども、そのまま出力するようにした方が良かったですね。
50ポイント獲得して、計71ポイントで、10,232名中、5,561位になりました!
Crawling Chaos
リンクが 1つあるだけです。このリンクをクリックすると、入力フォームが 1つと、送信ボタンがあるだけです。最初は Web かな、と思いましたが、Crypto にしておきます。

試しに、aaa と送ると、何もなかったかのように、フォームに入力した文字列が消えるだけです。問題のタイトルの Crawling Chaos とは、「カオスを這う」という意味のようです。
こういう問題の場合、攻撃になりそうな、よくある文字列をひたすら入力していくのが正しいやり方なんでしょうか。普通のブラウザで試すと、いろいろ見えないものが多いので、Burp Suite などを使って、HTTP のヘッダも見ていく方がいいと思います。
Burp Suite を使って見てみると、レスポンスに、大量の変な文字が返ってきています。以下のような文字列で、まだまだ大量に続きます。
(ᒧᆞωᆞ)=(/ᆞωᆞ/),(ᒧᆞωᆞ).ᒧうー=-!!(/ᆞωᆞ/).にゃー,(〳ᆞωᆞ)=(ᒧᆞωᆞ),(〳ᆞωᆞ).〳にゃー=- -!(ᒧᆞωᆞ).ᒧうー,(ᒧᆞωᆞ).ᒧうーー=(〳ᆞωᆞ).〳にゃー- -!(ᒧᆞωᆞ).ᒧうー,(〳ᆞωᆞ).〳にゃーー=(ᒧᆞωᆞ).ᒧうーー- -(〳ᆞωᆞ).〳にゃー,(ᒧᆞωᆞ).ᒧうーー=(〳ᆞωᆞ).〳にゃーー- -!(ᒧᆞωᆞ)
この大量の文字列の中に、フラグが隠れてると推測されます。URL が unya.html ということで、「う」と「にゃ」が関係しそうです。さすがに顔文字は関係ないかな、と思います。
「う」と「にゃ」に続く、全角のハイフンと半角のハイフンは、モールス信号のように見えます。モールス信号と言えば、「う」と「にゃ」で、モールス信号のようにも見えます(笑)。明確な区切り文字が分かりません。
見方を変えて、「う」が 0 で、「にゃ」が 1 で、2進数と見て、ASCIIコードになるとかは、どうでしょうか。うーん、違う気がする。
「うー」「にゃー」で検索したら、デコードとか出てきました。なるほど、そういうことですね。
本来なら、入力フォームに何か入力すると、JavaScript のポップアップが出る仕様みたいです。現在は、jquery がうまく読めないようです。試しに、jquery をローカルに置いた状態で、unya.html を動かしてみました。
以下のように、何か文字を入れると、ポップアップが出ます。しかし、ソースを見ても、こんなポップアップが出るようなソースは無く、「うー」と「にゃー」だけに見えます。

JavaScript は、変数名に全角文字が使えるということで、これは正しいソースコードということです。このままでは読みにくいので、とりあえず、console.log で出力してみればいいらしい。それを整形したのが以下です。
変数 f を、false にしなければ良さそうですね。そのためには、if(t.charCodeAt(i) * (i + 1) != p[i]) が常に成り立つように、p に合わせて入力してやればいいです。
$(function(){ $("form").submit(function(){ var t = $('input[type="text"]').val(); var p = Array(70,152,195,284,475,612,791,896,810,850,737,1332,1469,1120,1470,832,1785,2196,1520,1480,1449); var f = false; if(p.length == t.length){ f = true; for(var i = 0; i < p.length; i++) if(t.charCodeAt(i) * (i + 1) != p[i]) f = false; if(f) alert("(」・ω・)」うー!(/・ω・)/にゃー!"); } if(!f) alert("No"); return false; }); });
では、Python で計算させます。
lst = [70,152,195,284,475,612,791,896,810,850,737,1332,1469,1120,1470,832,1785,2196,1520,1480,1449] for ii, pp in enumerate(lst): ret = int( pp / (ii + 1) ) assert ret * (ii + 1) == pp, "fatal" print( chr(ret), end="" ) print()
実行すると、フラグが出力されます。
100ポイント獲得して、計171ポイントで、10,232名中、4,150位になりました!
Onion
大量の文字があります。base64 でしょうか。76文字ごとに改行が入っています。

base64 でデコードしてみましたが、文字列ではなさそうです。
ちょっと分からないので、後回しです。
これも、Crypto にしておきます。
Web
Simple Auth
URL が 2つあります(http と https)が、実際に行ってみると、見た目は同じに見えます。

ソースがあるので、見てみます。FLAG_???????????????? を入れると、良さそうですが、これが、そのままフラグになるということでしょうか。
<!DOCTYPE html> <html> <head> <title>Simple Auth</title> </head> <body> <div> <?php $password = 'FLAG_????????????????'; if (isset($_POST['password'])) if (strcasecmp($_POST['password'], $password) == 0) echo "Congratulations! The flag is $password"; else echo "incorrect..."; ?> </div> <form method="POST"> <input type="password" name="password"> <input type="submit"> </form> </body> </html>
このソースを、Webサーバにアップして、やってみます。やっぱりこれでいいですよね。ということは、このソースは、実際に使われてるソースとは違うってことですかね。???? を考える問題ということでしょうか。

うーん、後回しにします。
Simple Auth II
Simple Auth に続いて、Simple Auth II です。なぜか Ⅱ の方がポイントが低いです。

こちらも、URL が 2つあります(http と https)が、実際に行ってみると、見た目は同じに見えます。ソースがあるので、見てみます。
こちらも後回しにします。
Pwn
Villager A
300ポイントの問題です。SSH でログインできるということでしょうか。

試しに、ログインしてみます。入れました。フラグがあります!しかし、権限がないです。q4 というプログラムが置いてありますね。あ、SGID のビットが立ってますね、このプログラムを実行してるときにフラグが読めるということでしょうか。
$ ssh q4@ctfq.u1tramarine.blue -p 10004 The authenticity of host '[ctfq.u1tramarine.blue]:10004 ([160.16.127.224]:10004)' can't be established. RSA key fingerprint is SHA256:LBqdPUUa6DGkF6+BSQfNrILUDplXcgxzAUIiW/DeFQ8. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '[ctfq.u1tramarine.blue]:10004' (RSA) to the list of known hosts. q4@ctfq.u1tramarine.blue's password: [q4@eceec62b961b ~]$ ls flag.txt q4 [q4@eceec62b961b ~]$ cat flag.txt cat: flag.txt: Permission denied [q4@eceec62b961b ~]$ ls -alF total 32 dr-xr-xr-x 1 root root 4096 Feb 27 2021 ./ drwxr-xr-x 1 root root 4096 Feb 27 2021 ../ -rw-r--r-- 1 root root 18 Jul 21 2020 .bash_logout -rw-r--r-- 1 root root 141 Jul 21 2020 .bash_profile -rw-r--r-- 1 root root 456 Feb 27 2021 .bashrc -r--r----- 1 root q4a 22 Feb 26 2021 flag.txt -r-xr-sr-x 1 root q4a 5857 Feb 26 2021 q4*
32bit のプログラムのようです。とりあえず、実行してみます。
[q4@eceec62b961b ~]$ file q4 q4: setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.18, BuildID[sha1]=526c75e7f0f34744808eb1b09a5a91880562efc8, not stripped [q4@eceec62b961b ~]$ ./q4 What's your name? aa Hi, aa Do you want the flag? bb Do you want the flag? ^C [q4@eceec62b961b ~]$
300ポイントなので、簡単ではなさそうです。後回しにします。
おわりに
今回は、ksnctf のチャレンジを開始しました。
少しやってみた感想ですが、カテゴリが分からないのと、問題のレベルが分からないのは、初心者には少し厳しかったです。どんなカテゴリでも、ある程度進められる CTF中級者以上の方が対象な気がします。
あと、メンテナンスがされていないようなので、初心者は、変なところにハマるところもありました。別の問題で、レベルを上げてから、再挑戦したいと思います。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。