droids4 (Reverse Engineering)
Reverse the pass, patch the file, get the flag. Check out this file.
添付ファイル
・four.apk
"you got this"とだけ書かれており、一切のヒントがないため、apktoolでファイルをデコンパイルした後、FlagstaffHill.smaliを確認する。
$ apktool d four.apk
I: Using Apktool 2.7.0-dirty on four.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /home/colza/.local/share/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
FlaggstaffHill.smaliは以下のようになっていた。
.class public Lcom/hellocmu/picoctf/FlagstaffHill;
.super Ljava/lang/Object;
.source "FlagstaffHill.java"
# direct methods
.method public constructor <init>()V
.locals 0
.line 6
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public static native cardamom(Ljava/lang/String;)Ljava/lang/String;
.end method
.method public static getFlag(Ljava/lang/String;Landroid/content/Context;)Ljava/lang/String;
.locals 8
.param p0, "input" # Ljava/lang/String;
.param p1, "ctx" # Landroid/content/Context;
.line 12
new-instance v0, Ljava/lang/StringBuilder;
const-string v1, "aaa"
invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
.line 13
.local v0, "ace":Ljava/lang/StringBuilder;
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
.line 14
.local v2, "jack":Ljava/lang/StringBuilder;
new-instance v3, Ljava/lang/StringBuilder;
invoke-direct {v3, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
.line 15
.local v3, "queen":Ljava/lang/StringBuilder;
new-instance v4, Ljava/lang/StringBuilder;
invoke-direct {v4, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
move-object v1, v4
.line 17
.local v1, "king":Ljava/lang/StringBuilder;
const/4 v4, 0x0
invoke-virtual {v0, v4}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v5
add-int/lit8 v5, v5, 0x4
int-to-char v5, v5
invoke-virtual {v0, v4, v5}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 18
const/4 v5, 0x1
invoke-virtual {v0, v5}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v6
add-int/lit8 v6, v6, 0x13
int-to-char v6, v6
invoke-virtual {v0, v5, v6}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 19
const/4 v6, 0x2
invoke-virtual {v0, v6}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v7
add-int/lit8 v7, v7, 0x12
int-to-char v7, v7
invoke-virtual {v0, v6, v7}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 21
invoke-virtual {v2, v4}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v7
add-int/lit8 v7, v7, 0x7
int-to-char v7, v7
invoke-virtual {v2, v4, v7}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 22
invoke-virtual {v2, v5}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v7
add-int/2addr v7, v4
int-to-char v7, v7
invoke-virtual {v2, v5, v7}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 23
invoke-virtual {v2, v6}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v7
add-int/2addr v7, v5
int-to-char v7, v7
invoke-virtual {v2, v6, v7}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 25
invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v7
add-int/2addr v7, v4
int-to-char v7, v7
invoke-virtual {v3, v4, v7}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 26
invoke-virtual {v3, v5}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v7
add-int/lit8 v7, v7, 0xb
int-to-char v7, v7
invoke-virtual {v3, v5, v7}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 27
invoke-virtual {v3, v6}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v7
add-int/lit8 v7, v7, 0xf
int-to-char v7, v7
invoke-virtual {v3, v6, v7}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 29
invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v7
add-int/lit8 v7, v7, 0xe
int-to-char v7, v7
invoke-virtual {v1, v4, v7}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 30
invoke-virtual {v1, v5}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v4
add-int/lit8 v4, v4, 0x14
int-to-char v4, v4
invoke-virtual {v1, v5, v4}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 31
invoke-virtual {v1, v6}, Ljava/lang/StringBuilder;->charAt(I)C
move-result v4
add-int/lit8 v4, v4, 0xf
int-to-char v4, v4
invoke-virtual {v1, v6, v4}, Ljava/lang/StringBuilder;->setCharAt(IC)V
.line 33
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v4
const-string v5, ""
invoke-virtual {v5, v4}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
move-result-object v4
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v5
invoke-virtual {v4, v5}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
move-result-object v4
.line 34
invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v5
invoke-virtual {v4, v5}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
move-result-object v4
invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v5
invoke-virtual {v4, v5}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
move-result-object v4
.line 36
.local v4, "password":Ljava/lang/String;
invoke-virtual {p0, v4}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v5
if-eqz v5, :cond_0
const-string v5, "call it"
return-object v5
.line 37
:cond_0
const-string v5, "NOPE"
return-object v5
.end method
getFlag関数は入力文字とpasswordが一致するかを確認している。
line12~line17の部分で、まずStringBuilderを用いて"aaa"という文字を、ace, jack, queen, kingの4つの変数に代入している。
以降、line19までの部分で以下の処理をし、ace = etsとなる。
ace[0] = ace[0] + 0x4
ace[1] = ace[1] + 0x13
ace[2] = ace[2] + 0x12
line21~line23の部分で以下の処理をし、jack = habとなる。
jack[0] = jack[0] + 0x7
jack[1] = jack[1] + 0x0
jack[2] = jack[2] + 0x1
line25~line27の部分で以下の処理をし、queen = alpとなる。
queen[0] = queen[0] + 0x0
queen[1] = queen[1] + 0xb
queen[2] = queen[2] + 0xf
line29~line31の部分で以下の処理をし、king = oupとなる。
king[0] = king[0] + 0xe
king[1] = king[1] + 0x14
king[2] = king[2] + 0xf
line33~line36の部分で、concatでqueen+jack+ace+kingとなる文字列を作成し、これをpasswordと置いている。
よってpassword = alphabetsoupである。
エミュレータで起動したアプリにalphabetsoupを入力して、ボタンを押す。

"call it"と表示されたので、passwordがあっていることが確認できた。
call itと表示する部分のコードをフラグ出力関数を呼び出すように書き換える必要がある。
getFlag関数以外の謎関数はcardamomしかないので、次はcardamom関数を解析する。
cardamomが実装されているファイルを探す。
$ grep -R "cardamom" four/
grep: four/lib/arm64-v8a/libhellojni.so: binary file matches
grep: four/lib/armeabi-v7a/libhellojni.so: binary file matches
grep: four/lib/x86/libhellojni.so: binary file matches
grep: four/lib/x86_64/libhellojni.so: binary file matches
four/smali/com/hellocmu/picoctf/FlagstaffHill.smali:.method public static native cardamom(Ljava/lang/String;)Ljava/lang/String;
libhellojni.soというファイルに記述されていそうなので、これをghidraで確認する。
今回は、four/lib/x86_64/libhellojni.soを使った。
Symbol Treeからcardamomという文字列を含む関数名を探す。
undefined8
Java_com_hellocmu_picoctf_FlagstaffHill_cardamom
(long *param_1,undefined8 param_2,undefined8 param_3)
{
byte bVar1;
undefined8 uVar2;
char *local_50;
uVar2 = (**(code **)(*param_1 + 0x548))(param_1,param_3,0);
bVar1 = chervil(uVar2);
(**(code **)(*param_1 + 0x550))(param_1,param_3,uVar2);
if ((bVar1 & 1) == 0) {
local_50 = "try again";
}
else {
local_50 = (char *)pepper(uVar2);
}
uVar2 = (**(code **)(*param_1 + 0x538))(param_1,local_50);
free(local_50);
return uVar2;
}
param3がJavaから渡されたString引数だと考えられ、以下の部分でparam3の文字列を取得してポインタがuVar2に代入されていると考えられる。
uVar2 = (**(code **)(*param_1 + 0x548))(param_1,param_3,0);
chervil関数は入力文字が基準にあっているかを判断している。
その後、pepper関数を呼び出していることが分かる。
void pepper(char *param_1)
{
char *pcVar1;
size_t sVar2;
pcVar1 = strdup(param_1);
sVar2 = strlen(param_1);
unscramble(&DAT_00101aa0,0x1f,pcVar1,sVar2 & 0xffffffff);
return;
}
入力文字を使用して何らかの演算をしていることがわかる。おそらくDAT_00101aa0を入力文字を利用して復号している。
unscramble関数はdroids3のときと同じだったので、復号コードをそのまま利用しても良いが、今回はFlagstaffHill.smaliを書き換えることでフラグを出力させる。
書き換え内容は以下の通り。
# 書き換え前
# const-string v5, "call it"
# return-object v5
# 書き換え後
invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->cardamom(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
これを再ビルドしてエミュレータで起動する。
ビルドするときにエラーが出たので、$ic_launcher_foreground__0.xmlとその参照元コードの削除をした。
$ apktool b four_edited --use-aapt2 -o four_edited.apk
I: Using Apktool 2.7.0-dirty
I: Checking whether sources has changed...
I: Checking whether resources has changed...
I: Building resources...
W: aapt: brut.common.BrutException: brut.common.BrutException: Could not extract resource: /prebuilt/linux/aapt2_64 (defaulting to $PATH binary)
I: Copying libs... (/lib)
I: Building apk file...
I: Copying unknown files/dir...
I: Built apk into: four_edited.apk
apktoolでビルドしたファイルには署名がないので、署名する。
$ wget https://github.com/patrickfav/uber-apk-signer/releases/download/v1.3.0/uber-apk-signer-1.3.0.jar
$ java -jar uber-apk-signer-1.3.0.jar --apk four_edited.apk
エミュレータに再ビルドしたapkファイルをインストールし、アプリを起動する。
alphabetsoupと入力して、ボタンを押す。

フラグが得られた。
picoCTF{not.particularly.silly}
