ランサムウェアに感染したことを想定した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;
}