1
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?

[rev] aeppel (SECCON 14 Quals) writeup

Last updated at Posted at 2025-12-24

  • Source: SECCON 14 Quals
  • Author: rand0m

AppleScriptのバイナリが渡される。Linux環境では実行できないのでとりあえず逆アセンブルしたい。
applescript-disassemblerというツールを見つけたので、これを使う。

とりあえず雑に実行してみると数万文字のbyte列と僅かな命令が出力された。これでは何も分からないので困った。(SECCON大会中はこのようなbyte列でdisassemblerがエラーを吐いていたためLLMに言われるがまま適当に自分でパッチして実行していたが、今はbyte列を扱えるようにするPRが取り込まれていた)

$ ./applescript-disassembler/disassembler.py 1.scpt
=== data offset 2 ===
Function name : <Value type=object value=<Value type=event_identifier value=b'aevt'-b'oapp'-b'null'-b'\x00\x00\x80\x00'-b'****'-b'\x00\x00\x90\x00'>>
Function arguments:  <empty or unknown>
 00000 PushLiteral 0 # <Value type=rawdata value=b'scptFasdUAS 1.101.10\x0e\x00\x00\x00\x04\x0f\xff\xff\xff\xfe\x00\x01...(略)...ascr\x00\x01\x00\r\xfa\xde\xde\xad'>
 00001 Push0
 00002 MessageSend 1 # <Value type=object value=<Value type=event_identifier value=b'syso'-b'dsct'-b'****'-b'\x00\x00\x00\x00'-b'scpt'-b'\x00\x00\x00\x00'>>
 00005 GetData
 00006 PopGlobal b'res'
 00007 StoreResult
 00008 PushGlobal b'res'
 00009 Return
 0000a Return

とりあえずスクリプトを書いてこの謎のbyte列を取り出す。

#!/usr/bin/env python3
import ast
import subprocess

p = subprocess.run(
   ["./applescript-disassembler/disassembler.py", "1.scpt"],
   capture_output=True,
   text=True,
)

s = p.stdout
k = s.find("Value type=rawdata value=")
a = s.find("b'", k)
q = "'"
if a == -1:
   a = s.find('b"', k)
   q = '"'

i = a + 2
esc = False
while True:
   c = s[i]
   if esc:
       esc = False
   elif c == "\\":
       esc = True
   elif c == q:
       break
   i += 1

raw = ast.literal_eval(s[a : i + 1])

with open("raw.bin", "wb") as f:
   f.write(raw)

このraw.binをfileコマンドで読んだが何も分からず。LLMに投げるとAppleScriptのバイナリだと教えてくれたが、disassemblerが認識してくれない。
どうやらヘッダが本来Fasdであるところ、scptFasdになっている。単純にraw.binの先頭4byteを切り捨てるとdisassmblerが認識してくれるようになった。

すると、長いのでここには載せないが1600行ほどのそれっぽい出力が得られた。この結果をLLMに投げてflagを復元するスクリプトを書くよう頼むと、以下のスクリプトが得られた。

どうやらShimbashiという名前の関数で入力を処理し、var_11の値と一致するかを確認しているらしい。具体的な計算式まで教えてくれた。
暗号化された値 = 元の文字コード + 13 + ((13 * (インデックス % 3 + 1)) % 11) とのこと。

def solve_applescript_flag():
    # Source 16-17 から抽出したターゲット配列 (var_11)
    # 最後の 0x10 は MakeVector の要素数指定のため除外
    target_values = [
        0x72, 0x83, 0x7f, 0x7d, 0x78, 0x82, 0x74, 0x85, 
        0x78, 0x81, 0x87, 0x75, 0x86, 0x81, 0x4b, 0x44
    ]

    # Source 14 で定義されているプレフィックス
    # [0x53, 0x45, 0x43, 0x43, 0x4f, 0x4e, 0x7b] -> "SECCON{"
    prefix = "SECCON{"
    
    # Source 15 で定義されているサフィックス
    # 0x7d -> "}"
    suffix = "}"

    # Shimbashi 関数の逆算
    # 暗号化ロジック: target = char_code + 13 + ((13 * (i % 3 + 1)) % 11)
    # 復号ロジック: char_code = target - 13 - ((13 * (i % 3 + 1)) % 11)
    
    decrypted_content = ""
    
    for i, val in enumerate(target_values):
        # AppleScriptのループインデックスは通常1始まりとして扱われることが多い
        # ディスアセンブリの挙動的にも1-based indexとして計算すると意味の通る文字列になる
        index = i + 1
        
        # Source 68 の計算部分
        # PushVariable [var_5] (13) * index % 3 ... の部分
        # var_5 は (6856 - 6843) % 256 = 13
        noise = (13 * (index % 3 + 1)) % 11
        
        # 定数加算分 (Source 68: PushVariable [var_1] -> 13)
        constant_offset = 13
        
        original_char_code = val - constant_offset - noise
        decrypted_content += chr(original_char_code)

    flag = prefix + decrypted_content + suffix
    
    print(f"Decrypted Content: {decrypted_content}")
    print(f"Full Flag: {flag}")

    # 検算: Ginza関数のチェック (合計 % 256 == 95 ('_') かどうか)
    # Source 18: var_12 = 0x5f (95)
    total = sum(ord(c) for c in decrypted_content)
    if total % 256 == 95:
        print("Check: Ginza verification passed (Sum % 256 == 95).")
    else:
        print(f"Check: Ginza verification FAILED (Sum % 256 = {total % 256}).")

if __name__ == "__main__":
    solve_applescript_flag()

これを実行するとflagが得られた。
SECCON{applescriptfun<3}

1
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
1
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?