ksnctf の Riddle 200 points の writeup
まず英語のなぞなぞに答える
1問目
Which creature in the morning goes on four feet, at noon on two, and in the evening upon three?
human --> Wrong...
HUMAN --> Wrong...
Human --> Correct!
先頭だけ大文字だ
2問目
What is the largest island in the world?
Greenland --> Correct!
3問目
Answer to the Ultimate Question of Life, The Universe, and Everything.
42 --> Correct!
最後の問題
What is the flag?
正解の長さは 0x15 = 21文字
でもstringsでは21文字のデータは発見できない
怪しい xor を発見
.text:00401963 sub_4017D0 xor [eax+esi], cl
デバッガで追ってみる
スタックの Human の H (0x48) と 0xF9 を xor してる
xorの結果スタックの H (0x48) が 0xB1 に変化してる
続けると
H=0x48 ^ 0xF9 --> 0xB1
u=0x75 ^ 0xB3 --> 0xC6
IDA で B1C6 を検索してみる
ない
IDA で C6B1 を検索してみる
あった(Ghidraだと検索時にエンディアンを指定できるのに!)
近くにファイナルアンサーもいる?
いた。ちょうど21文字
xor のかたわれはどこ?
IDA で F9B3 を検索してみる
ない
IDA で B3F9 を検索してみる
ない
ん? そう簡単にはいかないか?
Ghidra でデコンパイルしてみる
void __cdecl FUN_004017d0(void *p_input_str,int str_len)
{
char cVar1;
uint k;
int i;
byte x;
byte bVar2;
uint j;
undefined tmp;
i = 0;
do {
(&S)[i] = (char)i;
i = i + 1;
} while (i < 0x100);
x = 0;
k = 0;
do {
j = k & 0x8000000f;
if ((int)j < 0) {
j = (j - 1 | 0xfffffff0) + 1;
}
x = x + "M6eMYngDGFbYE9HQ"[j] + (&S)[k];
tmp = (&S)[x];
(&S)[x] = (&S)[k];
j = k + 1 & 0x8000000f;
(&S)[k] = tmp;
if ((int)j < 0) {
j = (j - 1 | 0xfffffff0) + 1;
}
x = x + "M6eMYngDGFbYE9HQ"[j] + (&DAT_00405579)[k];
tmp = (&S)[x];
(&S)[x] = (&DAT_00405579)[k];
j = k + 2 & 0x8000000f;
(&DAT_00405579)[k] = tmp;
if ((int)j < 0) {
j = (j - 1 | 0xfffffff0) + 1;
}
x = x + "M6eMYngDGFbYE9HQ"[j] + (&DAT_0040557a)[k];
tmp = (&S)[x];
(&S)[x] = (&DAT_0040557a)[k];
j = k + 3 & 0x8000000f;
(&DAT_0040557a)[k] = tmp;
if ((int)j < 0) {
j = (j - 1 | 0xfffffff0) + 1;
}
x = x + "M6eMYngDGFbYE9HQ"[j] + (&DAT_0040557b)[k];
tmp = (&S)[x];
(&S)[x] = (&DAT_0040557b)[k];
(&DAT_0040557b)[k] = tmp;
k = k + 4;
} while ((int)k < 0x100);
i = 0;
x = 0;
bVar2 = 0;
if (0 < str_len) {
do {
x = x + 1;
k = (uint)x;
cVar1 = (&S)[k];
bVar2 = bVar2 + cVar1;
(&S)[k] = (&S)[bVar2];
(&S)[bVar2] = cVar1;
*(byte *)(i + (int)p_input_str) =
*(byte *)(i + (int)p_input_str) ^ (&S)[(byte)((&S)[k] + cVar1)];
i = i + 1;
} while (i < str_len);
}
return;
}
RC4かな?
「見えるぞ、私にも敵が見える」
少し乱暴だけど Ghidra で RC4 のソルバー書いてみる
# Key-scheduling algorithm (KSA)
S = []
i = 0
while i < 0x100:
S.append(i & 0xff)
i = i + 1
# 00403818 4d 36 65 ... ds "M6eMYngDGFbYE9HQ"
key = getBytes(toAddr(0x00403818), 16)
i = 0
j = 0
temp = 0
while i < 0x100:
j = ord(key.tostring()[ i % len(key) ]) + S[i] + j & 0xff
temp = S[i]
S[i] = S[j]
S[j] = temp
i = i + 1
# Pseudo-random generation algorithm (PRGA)
# 0x004051f4 is the address of the encrypted string found by IDA
enc = getBytes(toAddr(0x004051f4),21)
ans = []
i = 0
j = 0
k = 0
while k < 21:
i = i + 1 & 0xff
j = j + S[i] & 0xff
temp = S[i]
S[i] = S[j]
S[j] = temp
ans.append(ord(enc.tostring()[k]) ^ S[ S[i] + S[j] & 0xff])
k = k + 1
print(''.join(map(chr,ans)))