Ransomware for CTF

ランサムウェアに感染したことを想定したCTF問題を考える。

■問題
 ランサムウェアに感染し、重要なファイル「secret.dat」が暗号化されてしまった。復号するには正しいパスワードを入力する必要がある。ランサムウェアプログラムを解析し「secret.dat」を復号せよ。復号されると「secret.dat」にFlagが保存される。
 ※ransamware.outとsecret.datが用意されている。

■解き方
 ransomware.outを実行するとパスワードを求められる。正しいパスワードを入力すると 「secret.dat」 は復号されるが、当然パスワードはわからない。そこで、gdbで解析を行いパスワードを比較する部分を見つける。比較結果が格納されるレジスタの値を変更することでパスワードがわからなくとも復号処理に進めることができる。Binary for Linuxと同じ解き方である。

secret.datの中身を見ると暗号化されていることがわかる。ransomware.outを実行すると入力を求められるが、正しくないと復号化されない。

gdbで解析を行う。disas mainコマンドでmain関数内の処理を確認する。

strcmp関数が入力した文字を比較している関数であると目星をつける。strcmp関数の比較結果はeaxレジスタに格納される。比較結果が等しい場合は0に、等しくない場合は0以外がeaxレジスタに格納され、そのあとのtest演算でraxが0かどうかを確認している。このtest演算直前にBreakポイントを設定する。
※一度、gdbでrunを実行しないと、Breakポイントの設定ができなかった。

runコマンドでプログラムを実行する。Inputには適当な文字を入力する。その後、Breakポイントで停止するので、その時点のeaxレジスタの値を確認する。当然、比較結果は正しくないので0以外が設定されている。このままでは比較NGとなるのでeaxレジスタを0にSetし、Continueコマンドでプログラムを再開する。その結果、復号化が成功する。

secret.datを確認すると復号化されてFlagが表示される。



ソースコード(gcc ransomware.c -o ransomware.out -lcrypt)

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(){

    char input[50];
    char output[50];
    int i=0;

    FILE *fp;
    char ch[80];
    char temp;
    int j;

    printf("input:");
    scanf("%s", input);

    /* password is "1234pass" */

    if(strcmp( crypt(input,"$1$seed"),"$1$seed$9Pv9DYRW3TuJN8QETcvoE1")==0){
       
        fp = fopen("secret.dat","rb+");
        while(!feof(fp)) {
         temp = fgetc(fp);
         if(!feof(fp)) 
          ch[i] = temp ^ '1';
         i++;
        }

        rewind(fp);
        j = i - 1;

        for (i = 0; i < j; i++)
        fputc(ch[i], fp);

        fclose(fp);

        printf("Congraturation! File is decrypted!\n");
    }
    else
        printf("Not Correct.\n");

    return 0;
}

コメントをどうぞ