ksnctf Q.15 Jewel


apk(android アプリ)の問題。

とりあえず、ctf で apk を扱うときどういうツールを使うのか調べて、それらを使って色々調べてみることに。

最終的に使ったツールたち

  • apktool: apk を unzip するツール。AndroidManifest とか見れる
  • jadx: jar をデコンパイルするツール
  • dex2jar dex を jar に変換するツール

すると簡単に java ソースコードがでてきた。 何やらデバイス ID を SHA256 でほにゃほにゃしているっぽい。

public class JewelActivity extends Activity {
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(2130903040);
        String deviceId = ((TelephonyManager) getSystemService("phone")).getDeviceId();
        try {
            MessageDigest instance = MessageDigest.getInstance("SHA-256");
            instance.update(deviceId.getBytes("ASCII"));
            String bigInteger = new BigInteger(instance.digest()).toString(16);
            if (!deviceId.substring(0, 8).equals("99999991")) {
                new AlertDialog.Builder(this).setMessage("Your device is not supported").setCancelable(false).setPositiveButton("OK", new b(this)).show();
            } else if (!bigInteger.equals("356280a58d3c437a45268a0b226d8cccad7b5dd28f5d1b37abf1873cc426a8a5")) {
                new AlertDialog.Builder(this).setMessage("You are not a valid user").setCancelable(false).setPositiveButton("OK", new a(this)).show();
            } else {
                InputStream openRawResource = getResources().openRawResource(2130968576);
                byte[] bArr = new byte[openRawResource.available()];
                openRawResource.read(bArr);
                SecretKeySpec secretKeySpec = new SecretKeySpec(("!" + deviceId).getBytes("ASCII"), "AES");
                IvParameterSpec ivParameterSpec = new IvParameterSpec("kLwC29iMc4nRMuE5".getBytes());
                Cipher instance2 = Cipher.getInstance("AES/CBC/PKCS5Padding");
                instance2.init(2, secretKeySpec, ivParameterSpec);
                byte[] doFinal = instance2.doFinal(bArr);
                ImageView imageView = new ImageView(this);
                imageView.setImageBitmap(BitmapFactory.decodeByteArray(doFinal, 0, doFinal.length));
                setContentView(imageView);
            }
        } catch (Exception e) {
            Toast.makeText(this, e.toString(), 1).show();
        }
    }
}

何やらデバイス ID の比較により、行われているっぽい。

特定のデバイス ID でしか表示されない画像があるっぽい。

しかも画像はその場で生成してる。

デバイス ID の比較は、デバイス ID を SHA256 を通して行っているため直接デバイス ID を特定することはできない。

ただ、

if (!deviceId.substring(0, 8).equals("99999991"))

これをみると、デバイス ID の最初の数字が 99999991 にいなっているとわかる。

デバイス ID は 15 桁の数字なので、頑張って特定する。

import hashlib

imei = ""
for i in range(999999910000000, 999999999999999):
    imei = str(i)
    print(imei)
    m= hashlib.sha256()
    m.update(imei.encode())

    if m.hexdigest() == "356280a58d3c437a45268a0b226d8cccad7b5dd28f5d1b37abf1873cc426a8a5":
        print(imei)
        break

ちょっと時間かかる。

ソースコードメモ

getResources().openRawResource(2130968576)

これは、リソース ID「2130968576」を取得する関数。 このリソース ID は public.xml で定義されている。(今回は jewel_c.png)

ざっくり関数を調べると、暗号化に関わるものが多い。

上の関数で取得した画像に暗号化を施すと、ちゃんとした画像として見れるっぽい。

というわけで、java でソースコードをそのまま流用して画像の表示を試みる。

class Main {
    public static void main(String[] args) {

        String deviceId = "999999913371337";
        try {
            MessageDigest instance = MessageDigest.getInstance("SHA-256");
            instance.update(deviceId.getBytes("ASCII"));
            String bigInteger = new BigInteger(instance.digest()).toString(16);
            File file = new File("...\jewel_c.png");
            InputStream openRawResource = new FileInputStream(file);
            byte[] bArr = new byte[openRawResource.available()];
            openRawResource.read(bArr);
            SecretKeySpec secretKeySpec = new SecretKeySpec(("!" + deviceId).getBytes("ASCII"), "AES");
            IvParameterSpec ivParameterSpec = new IvParameterSpec("kLwC29iMc4nRMuE5".getBytes());
            Cipher instance2 = Cipher.getInstance("AES/CBC/PKCS5Padding");
            instance2.init(2, secretKeySpec, ivParameterSpec);
            byte[] doFinal = instance2.doFinal(bArr);
            FileOutputStream  outfile = new FileOutputStream("...\\out.png");
            outfile.write(doFinal);
            outfile.close();
        } catch (Exception e) {
        }
    }
}

こうして画像を保存すると、しっかりと表示可能な png ファイルができた。

どうやら png 画像にはコメントを埋め込むことができて、そこに flag が書いてあるらしい。

どうにか tEXt チャンクを覗けないか試したものの、最終的には string コマンドを使って、解決した。

感想

android 問は少ないらしい。