2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SECCON Beginners CTF 2025 ~reversing編~

Posted at

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!!!}
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?