droids3 (Reverse Engineering)
Find the pass, get the flag. Check out this file.
添付ファイル
・three.apk
"make this app your own"と書かれており、ヒントがほぼゼロである。
今までと同様の手順でFlagstaffHill.smaliファイルを探して、中身を見る。
$ apktool d three.apk
I: Using Apktool 2.7.0-dirty on three.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...
three/smali/com/hellocmu/FlagstaffHill.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 cilantro(Ljava/lang/String;)Ljava/lang/String;
.end method
.method public static getFlag(Ljava/lang/String;Landroid/content/Context;)Ljava/lang/String;
.locals 1
.param p0, "input" # Ljava/lang/String;
.param p1, "ctx" # Landroid/content/Context;
.line 19
invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->nope(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
.line 20
.local v0, "flag":Ljava/lang/String;
return-object v0
.end method
.method public static nope(Ljava/lang/String;)Ljava/lang/String;
.locals 1
.param p0, "input" # Ljava/lang/String;
.line 11
const-string v0, "don\'t wanna"
return-object v0
.end method
.method public static yep(Ljava/lang/String;)Ljava/lang/String;
.locals 1
.param p0, "input" # Ljava/lang/String;
.line 15
invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->cilantro(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
.end method
呼び出されている中で気になる関数はcilantroであり、ネイティブコードで実装された外部ライブラリの関数であることが分かる。
.method public static native cilantro(Ljava/lang/String;)Ljava/lang/String;
.end method
cilantro関数が実装されているファイルを探す。
$ grep -R "cilantro" three/
grep: three/lib/arm64-v8a/libhellojni.so: binary file matches
grep: three/lib/armeabi-v7a/libhellojni.so: binary file matches
grep: three/lib/x86/libhellojni.so: binary file matches
grep: three/lib/x86_64/libhellojni.so: binary file matches
three/smali/com/hellocmu/picoctf/FlagstaffHill.smali:.method public static native cilantro(Ljava/lang/String;)Ljava/lang/String;
three/smali/com/hellocmu/picoctf/FlagstaffHill.smali: invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->cilantro(Ljava/lang/String;)Ljava/lang/String;
libhellojni.soというファイルに記述されていそうなので、これをghidraで確認する。
今回は、three/lib/x86_64/libhellojni.soを使った。
Symbol Treeからcilantroという文字列を含む関数名を探す。
undefined8
Java_com_hellocmu_picoctf_FlagstaffHill_cilantro
(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 = dill(uVar2);
(**(code **)(*param_1 + 0x550))(param_1,param_3,uVar2);
if ((bVar1 & 1) == 0) {
local_50 = "try again";
}
else {
local_50 = (char *)sumac();
}
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);
dill関数は入力文字が基準にあっているかを判断している。
その後、sumac関数を呼び出していることが分かる。
void sumac(void)
{
char *pcVar1;
size_t sVar2;
pcVar1 = strdup("againmissing");
sVar2 = strlen("againmissing");
unscramble(&DAT_00101a85,0x1a,pcVar1,sVar2 & 0xffffffff);
return;
}
DAT_00101a85は以下のようになっていた。
00101a85 11 ?? 11h
00101a86 0e ?? 0Eh
00101a87 02 ?? 02h
00101a88 06 ?? 06h
00101a89 2d ?? 2Dh -
00101a8a 39 ?? 39h 9
00101a8b 2f ?? 2Fh /
00101a8c 08 ?? 08h
00101a8d 07 ?? 07h
00101a8e 00 ?? 00h
00101a8f 1d ?? 1Dh
00101a90 49 ?? 49h I
00101a91 03 ?? 03h
00101a92 12 ?? 12h
00101a93 15 ?? 15h
00101a94 47 ?? 47h G
00101a95 0f ?? 0Fh
00101a96 43 ?? 43h C
00101a97 1a ?? 1Ah
00101a98 10 ?? 10h
00101a99 01 ?? 01h
00101a9a 08 ?? 08h
00101a9b 1a ?? 1Ah
00101a9c 04 ?? 04h
00101a9d 09 ?? 09h
00101a9e 1a ?? 1Ah
00101a9f 00 ?? 00h
unscramble関数に主な演算が書かれていそう。
void * unscramble(long param_1,int param_2,long param_3,int param_4)
{
void *pvVar1;
int local_38;
int local_34;
pvVar1 = calloc((long)param_2,1);
local_38 = 0;
for (local_34 = 0; local_34 < param_2; local_34 = local_34 + 1) {
*(byte *)((long)pvVar1 + (long)local_34) =
*(byte *)(param_1 + local_34) ^ *(byte *)(param_3 + local_38 % param_4);
local_38 = local_38 + 1;
}
return pvVar1;
}
0x1a分の領域をcallocして、for文を利用して0x1aの領域を0番目から計算結果を格納している。
計算内容は、param1の0番目をparam3の0番目を"againmissing"の12文字で割った余りでXORしている。
つまり、DAT_00101a85の値を順に"againmissing"でXORしている。
同じ値で2度XORすると、元の値に戻るので、DAT_00101a85の26バイトのデータをunscramble関数でXORする。
以下、実行コード。
from pwn import *
data = "110e02062d392f0807001d49031215470f431a1001081a04091a"
key = "againmissing"
data = bytes.fromhex(data)
flag = xor(data, key)
print(flag)
実行する。
$ python3 solve.py
/home/colza/.local/lib/python3.12/site-packages/pwnlib/util/fiddling.py:340: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
strs = [packing.flat(s, word_size = 8, sign = False, endianness = 'little') for s in args]
b'picoCTF{tis.but.a.scratch}'
フラグが得られた。
picoCTF{tis.but.a.scratch}
