0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

picoCTF 2019 writeup droids3

Last updated at Posted at 2025-12-16

droids3 (Reverse Engineering)

Find the pass, get the flag. Check out this file.

添付ファイル
・three.apk

three.apkをエミュレータで開く。
image.png

"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}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?