Ghidraのデコンパイル誤りを訂正する訓練になるので記録に残しておく。
問題としては,ELFバイナリ「mystery」と PNG画像「mystery.png」が渡される。
mystery.png を確認すると,フッターの後ろにフラグっぽいのが見える。
フラグの長さは26バイト。
が, 0x80 など ASCII 外のコードがある。
Ghidraでデコンパイルしてみる
void main(void)
{
FILE *__stream;
FILE *__stream_00;
size_t sVar1;
long in_FS_OFFSET;
int local_54;
int local_50;
char local_38 [4];
char local_34;
char local_33;
char local_29;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
__stream = fopen("flag.txt","r");
__stream_00 = fopen("mystery.png","a");
if (__stream == (FILE *)0x0) {
puts("No flag found, please make sure this is run on the server");
}
if (__stream_00 == (FILE *)0x0) {
puts("mystery.png is missing, please run this on the server");
}
sVar1 = fread(local_38,0x1a,1,__stream);
if ((int)sVar1 < 1) {
/* WARNING: Subroutine does not return */
exit(0);
}
puts("at insert");
fputc((int)local_38[0],__stream_00);
fputc((int)local_38[1],__stream_00);
fputc((int)local_38[2],__stream_00);
fputc((int)local_38[3],__stream_00);
fputc((int)local_34,__stream_00);
fputc((int)local_33,__stream_00);
local_54 = 6;
while (local_54 < 0xf) {
fputc((int)(char)(local_38[local_54] + '\x05'),__stream_00);
local_54 = local_54 + 1;
}
fputc((int)(char)(local_29 + -3),__stream_00);
local_50 = 0x10;
while (local_50 < 0x1a) {
fputc((int)local_38[local_50],__stream_00);
local_50 = local_50 + 1;
}
fclose(__stream_00);
fclose(__stream);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
fputc をネットで調べるとファイルに追記する関数とわかる。
よって,この ELF は, flag.txt を読んで,フラグに何らかの細工(暗号化)をして, mystery.png の末尾に付加するものと予測する。
この処理を見ていく中で最大の問題は,変数 local_29 に対する代入が無く,値が不明ということだ。
ピンク先生は「アサインされていない変数」という言い方をされていた。
英語で書くと local_29 is not assigned という感じ。
こういうのはたいていFunction Signatureを変更するか,別の変数の型を変更すると自動的に直る場合が多い。
Ghidra のデコンパイル誤りを正していく。
local_38 は flag.txt の内容が入るバッファなので,変数名を flag に。
そして一番重要なのが,型。
前記 mystery.png をバイナリエディタで確認した時,フラグに長さが 26バイト だったので,
local_38 の型を char[26] に変更する。
void main(void)
{
long lVar1;
FILE *__stream;
FILE *__stream_00;
size_t sVar2;
long in_FS_OFFSET;
int local_54;
int local_50;
char flag [26];
lVar1 = *(long *)(in_FS_OFFSET + 0x28);
__stream = fopen("flag.txt","r");
__stream_00 = fopen("mystery.png","a");
if (__stream == (FILE *)0x0) {
puts("No flag found, please make sure this is run on the server");
}
if (__stream_00 == (FILE *)0x0) {
puts("mystery.png is missing, please run this on the server");
}
sVar2 = fread(flag,0x1a,1,__stream);
if ((int)sVar2 < 1) {
/* WARNING: Subroutine does not return */
exit(0);
}
puts("at insert");
fputc((int)flag[0],__stream_00);
fputc((int)flag[1],__stream_00);
fputc((int)flag[2],__stream_00);
fputc((int)flag[3],__stream_00);
fputc((int)flag[4],__stream_00);
fputc((int)flag[5],__stream_00);
local_54 = 6;
while (local_54 < 0xf) {
fputc((int)(char)(flag[local_54] + '\x05'),__stream_00);
local_54 = local_54 + 1;
}
fputc((int)(char)(flag[15] + -3),__stream_00);
local_50 = 0x10;
while (local_50 < 0x1a) {
fputc((int)flag[local_50],__stream_00);
local_50 = local_50 + 1;
}
fclose(__stream_00);
fclose(__stream);
if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
ビンゴ!
アサインされていない変数 local_29 が消えた。
さらに読みやすくする
local_54 の変数名を i に
__stream_00 の変数名を __stream_png に
local_50 の変数名を j に
void main(void)
{
long lVar1;
FILE *__stream;
FILE *__stream_png;
size_t sVar2;
long in_FS_OFFSET;
int i;
int j;
char flag [26];
lVar1 = *(long *)(in_FS_OFFSET + 0x28);
__stream = fopen("flag.txt","r");
__stream_png = fopen("mystery.png","a");
if (__stream == (FILE *)0x0) {
puts("No flag found, please make sure this is run on the server");
}
if (__stream_png == (FILE *)0x0) {
puts("mystery.png is missing, please run this on the server");
}
sVar2 = fread(flag,0x1a,1,__stream);
if ((int)sVar2 < 1) {
/* WARNING: Subroutine does not return */
exit(0);
}
puts("at insert");
fputc((int)flag[0],__stream_png);
fputc((int)flag[1],__stream_png);
fputc((int)flag[2],__stream_png);
fputc((int)flag[3],__stream_png);
fputc((int)flag[4],__stream_png);
fputc((int)flag[5],__stream_png);
i = 6;
while (i < 0xf) {
fputc((int)(char)(flag[i] + '\x05'),__stream_png);
i = i + 1;
}
fputc((int)(char)(flag[15] + -3),__stream_png);
j = 0x10;
while (j < 0x1a) {
fputc((int)flag[j],__stream_png);
j = j + 1;
}
fclose(__stream_png);
fclose(__stream);
if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
コードが読みやすくなったのであとはソルバーを書くだけ
# enc_string was extracted from behind the footer of mystery.png
enc_string = "picoCTK\x80k5zsid6q_3d659f57}"
enc = list(enc_string)
# print(enc)
ans = []
i = 0
while i < 0x6:
ans.append(ord(enc[i]))
i = i + 1
while i < 0xf:
ans.append(ord(enc[i]) - 0x5)
i = i + 1
ans.append(ord(enc[15]) + 0x3)
i = 0x10
while i < 0x1a:
ans.append(ord(enc[i]))
i = i + 1
print(''.join(map(chr,ans)))