CrazyLazyProgram1 [100pt]
改行が面倒だったのでワンライナーにしてみました。
using System;class Program {static void Main() {int len=0x23;Console.Write("INPUT > ");string flag=Console.ReadLine();if((flag.Length)!=len){Console.WriteLine("WRONG!");}else{if(flag[0]==0x63&&flag[1]==0x74&&flag[2]==0x66&&flag[3]==0x34&&flag[4]==0x62&&flag[5]==0x7b&&flag[6]==0x31&&flag[7]==0x5f&&flag[8]==0x31&&flag[9]==0x69&&flag[10]==0x6e&&flag[11]==0x33&&flag[12]==0x72&&flag[13]==0x35&&flag[14]==0x5f&&flag[15]==0x6d&&flag[16]==0x61&&flag[17]==0x6b&&flag[18]==0x33&&flag[19]==0x5f&&flag[20]==0x50&&flag[21]==0x47&&flag[22]==0x5f&&flag[23]==0x68&&flag[24]==0x61&&flag[25]==0x72&&flag[26]==0x64&&flag[27]==0x5f&&flag[28]==0x32&&flag[29]==0x5f&&flag[30]==0x72&&flag[31]==0x33&&flag[32]==0x61&&flag[33]==0x64&&flag[34]==0x7d){Console.WriteLine("YES!!!\nThis is Flag :)");}else{Console.WriteLine("WRONG!");}}}}
コードを見るとflag変数にASCIIコードが代入されていることがわかります.
codes = [
0x63,0x74,0x66,0x34,0x62,0x7b,0x31,0x5f,0x31,0x69,0x6e,0x33,0x72,0x35,0x5f,0x6d,0x61,0x6b,0x33,
0x5f,0x50,0x47,0x5f,0x68,0x61,0x72,0x64,0x5f,0x32,0x5f,0x72,0x33,0x61,0x64,0x7d
]
flag = ''.join(chr(x) for x in codes)
print(flag)
でデコードすると
% python clipt.py
ctf4b{1_1in3r5_mak3_PG_hard_2_r3ad}
CrazyLazyProgram2 [100pt]
コーディングが面倒だったので機械語で作ってみました
とりあえず,渡されたファイルがオブジェクトファイルのためGhidraで逆コンパイルして中身を見てみます.
void main(void)
{
char local_38;
char cStack_37;
char cStack_36;
char cStack_35;
char cStack_34;
char cStack_33;
char cStack_32;
char cStack_31;
char cStack_30;
char cStack_2f;
char cStack_2e;
char cStack_2d;
char cStack_2c;
char cStack_2b;
char cStack_2a;
char cStack_29;
char cStack_28;
char cStack_27;
char cStack_26;
char cStack_25;
char cStack_24;
char cStack_23;
char cStack_22;
char cStack_21;
char cStack_20;
char cStack_1f;
char cStack_1e;
char cStack_1d;
char cStack_1c;
char cStack_1b;
char cStack_1a;
char cStack_19;
char cStack_18;
undefined4 local_c;
printf("Enter the flag: ");
__isoc99_scanf(&DAT_001003c6,&local_38);
local_c = 0;
if (((((((((local_38 == 'c') && (local_c = 1, cStack_37 == 't')) &&
(local_c = 2, cStack_36 == 'f')) &&
(((local_c = 3, cStack_35 == '4' && (local_c = 4, cStack_34 == 'b')) &&
((local_c = 5, cStack_33 == '{' &&
((local_c = 6, cStack_32 == 'G' && (local_c = 7, cStack_31 == 'O')))))))) &&
(local_c = 8, cStack_30 == 'T')) &&
(((((local_c = 9, cStack_2f == 'O' && (local_c = 10, cStack_2e == '_')) &&
(local_c = 0xb, cStack_2d == 'G')) &&
((local_c = 0xc, cStack_2c == '0' && (local_c = 0xd, cStack_2b == 'T')))) &&
(local_c = 0xe, cStack_2a == '0')))) &&
(((local_c = 0xf, cStack_29 == '_' && (local_c = 0x10, cStack_28 == '9')) &&
(((local_c = 0x11, cStack_27 == '0' &&
(((local_c = 0x12, cStack_26 == 't' && (local_c = 0x13, cStack_25 == '0')) &&
(local_c = 0x14, cStack_24 == '_')))) &&
(((local_c = 0x15, cStack_23 == 'N' && (local_c = 0x16, cStack_22 == '0')) &&
(local_c = 0x17, cStack_21 == 'm')))))))) &&
(((local_c = 0x18, cStack_20 == '0' && (local_c = 0x19, cStack_1f == 'r')) &&
((local_c = 0x1a, cStack_1e == '3' &&
(((local_c = 0x1b, cStack_1d == '_' && (local_c = 0x1c, cStack_1c == '9')) &&
(local_c = 0x1d, cStack_1b == '0')))))))) &&
(((local_c = 0x1e, cStack_1a == 't' && (local_c = 0x1f, cStack_19 == '0')) &&
(local_c = 0x20, cStack_18 == '}')))) {
puts("Flag is correct!");
}
return;
}
コード内のif条件をみると以下のフラグが見つかります.
ctf4b{GOTO_G0T0_90t0_N0m0r3_90t0}
wasm_S_exp [100pt]
フラグをチェックしてくれるプログラム
.watのファイルが渡される..watファイルを調べてみるとWebassemblyテキストらしいです.
Mmdn web docs:https://developer.mozilla.org/ja/docs/WebAssembly
ファイルの中身を見ると
(module
(memory (export "memory") 1 )
(func (export "check_flag") (result i32)
i32.const 0x7b
i32.const 38
call $stir
i32.load8_u
i32.ne
if
i32.const 0
return
end
i32.const 0x67
i32.const 20
call $stir
i32.load8_u
i32.ne
if
i32.const 0
return
end
...
i32.consthはスタックに代入する命令であり,最初は0x7bと38が与えられています.
call $stirはstir関数の呼び出しを行っています.stirの定義箇所を見ると
(func $stir (param $x i32) (result i32)
i32.const 1024
i32.const 23
i32.const 37
local.get $x
i32.const 0x5a5a
i32.xor
i32.mul
i32.add
i32.const 101
i32.rem_u
i32.add
return
)
呼び出しが行われると入力の値(38)を0x5a5aとXORをとりその後,37で乗算,23加算,101で剰余算を行い1024で加算を行い戻り値として返しています.
戻り値 = ((((入力値 ^ 0x5a5a) * 37) + 23) % 101) + 1024
i32.load8_uで戻り値をアドレスとして文字を読み込み,i32.neで最初に行ったi32.const 0x7bと比較を行い異なる場合はプログラムを終了します.以上の流れが理解できれば,アドレスを求める計算と比較する値の対応関係がわかるため以下のプログラムを作成しました.
def solve_flag():
checks = [
(0x7b, 38), (0x67, 20), (0x5f, 46), (0x21, 3), (0x63, 18),
(0x6e, 119), (0x5f, 51), (0x79, 59), (0x34, 9), (0x57, 4),
(0x35, 37), (0x33, 12), (0x62, 111), (0x63, 45), (0x7d, 97),
(0x30, 54), (0x74, 112), (0x31, 106), (0x66, 43), (0x34, 17),
(0x34, 98), (0x54, 120), (0x5f, 25), (0x6c, 127), (0x41, 26)
]
flag_array = [None] * 101
for char_code, index in checks:
mangled = index ^ 0x5a5a
mangled = mangled * 37
mangled = mangled + 23
offset = mangled % 101
flag_array[offset] = chr(char_code)
reconstructed_flag = "".join(char for char in flag_array if char is not None)
return reconstructed_flag
if __name__ == "__main__":
flag = solve_flag()
print("フラグ:")
print(flag)
上記のプログラムは,先ほどのアドレス計算処理(加算はオフセットなので無視)行いそれらを順番に並び替えて保持している値を表示させるプログラムです.
% python decode.py
フラグ:
ctf4b{WAT_4n_345y_l0g1c!}
MAFC [339pt]
flagが欲しいかい?ならこのマルウェアを解析してみな。
Wanna get flag? if so, Reversing this Malware if you can
MalwareAnalysis-FirstChallenge.exeとflag.encryptedをそれぞれ解析します.flag.encryptedを開いてみるとflag.txtを暗号化したファイルであろうと考えられます.
MalwareAnalysis-FirstChallenge.exeをGhidraで解析すると
- アルゴリズム: AES-256-CBC(PKCS#7)
- 鍵: SHA-256("ThisIsTheEncryptKey")
adafd798c69ffaef2b2bbb44364f0952b988cdd37bb66bb2cb19b5827a8a2465 - IV(hex): 49 00 56 00 43 00 61 00 6E 00 4F 00 62 00 66 00
がわかります.
from pathlib import Path
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import hashlib
ct = Path("flag.encrypted").read_bytes()
key = hashlib.sha256(b"ThisIsTheEncryptKey").digest()
iv = "IVCanObfuscation".encode("utf-16le")[:16]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
pt_padded = cipher.decryptor().update(ct) + cipher.decryptor().finalize()
unpadder = padding.PKCS7(128).unpadder()
pt = unpadder.update(pt_padded) + unpadder.finalize()
print(pt.rstrip(b"\x00").decode())
実行した結果
% python decode.py
ctf4b{way_2_90!_y0u_suc3553d_2_ana1yz3_Ma1war3!!!}