TL;DR
Script embeds a second compiled AppleScript blob; the inner script starts at offset 190. The inner script validates a full flag string candidate
- | candidate | = 0x18 = 24
- prefix is
SECCON{ and suffix is } - The 16 char middle payload must satisfy Shimbashi, Ginza, Kanda and Sugamo
Shimbashi is an invertible per-character encoding with embedded target outputs
- for
Analysis
AppleScript compiled/run-only binaries commonly use the FasdUAS container

The interesting part in this challenge is that 1.scpt contains another compiled script inside it.
Extract the embedded inner compiled script Search for the second header by looking for scptFasdUAS
This file contains scptFasdUAS 1.101.10 The scpt prefix is 4 bytes, and the FasdUAS header begins immediately after it.
In this challenge the embedded header begins at offset 190, so carving from there yields a valid compiled script
dd if=1.scpt of=inner2.scpt bs=1 skip=190 status=nonefile inner2.scptDissassemble the AppleScript VM bytecode
Function name : b'Iidabashi'Function arguments: [b'candidate']This is the main predicate, it returns true/false and drives the Correct / Try Again High-level structure of Iidabashi (the checker) Reading Iidabashi top-to-bottom, you can reconstruct a clean high-level validator.
Normalize the input
The candidate is converted to an NSString and trimmed
- Jimbocho(t) is essentially NSString.stringWithString_(t).
- trimNSString(ns) uses NSCharacterSet.whitespaceAndNewlineCharacterSet and stringByTrimmingCharactersInset_
So we can treat the input as a normal trimmed string.
The script then checks
T via four independent mechanisms:
- Shimbashi(T, …) must exactly equal an embedded 16-element integer list.
- Ginza(T) must equal 0x5f.
- Kanda(T, …) must produce a record matching an embedded expected record.
- Sugamo(T, …) must produce another record matching another embedded expected record.
The crucial observation is that (1) is directly invertible, so the payload can be recovered without brute force. Shimbashi: recover the payload by inversion Shimbashi is where the challenge leaks the payload in an invertible way.
Constants inside Shimbashi
Iidabashi defines two constants (seen as globals in the disassembly) idaho = 0x1abb kansas = 0x1ac8 Inside Shimbashi, a value is computed:
- COLORADO=(kansas-idaho) mod 256
Numerically:
- COLORADO=(0x1ac8-0x1abb) mod256 = 0x0d =13 So COLORADO = 13.
The embedded target output list Iidabashi also embeds the 16 outputs that Shimbashi must produce. As hex
O = [0x72, 0x83, 0x7F, 0x7D, 0x78, 0x82, 0x74, 0x85, 0x78, 0x81, 0x87, 0x75, 0x86, 0x81, 0x4B, 0x44]Let be these values.
The per-index delta item
Encoding equation
So
For i=1
Applying this to all 16 positions yields applescriptfun<3
Ginza checksum mod 256
The expected result in bytecode is 0x5f.
Kanda and Sugamo are more complex stateful transforms that output a record (dictionary-like object) with emoji-codepoint keys such as U1F41D, U1F99C, etc.
The checker compares these fields against records embedded in the bytecode.
Both functions use Quotient and Remainder on values that can become negative.
Python’s // and % use floor division; many runtimes (including typical VM implementations) use truncation toward zero.
To match the bytecode behavior, implement
From the bytecode literals, the expected record for Kanda(T, …) is
U1F41D = 0x07C3U1F99C = 64104U1F11D = 0x1523U1F41C = 56473len = 0x10U1F41B = 0x4D16Likewise Sugamo(T, …) is
U1F41D = 62601U1F99C = 65475U1F11D = 65339len = 6Exploit
Reimplementing these transform is not required to recover T (cuz Shimbashi already gives it)
EXPECTED_SHIMBASHI = [ 0x72, 0x83, 0x7F, 0x7D, 0x78, 0x82, 0x74, 0x85, 0x78, 0x81, 0x87, 0x75, 0x86, 0x81, 0x4B, 0x44,]
COLORADO = 0x0DEXPECTED_GINZA = 0x5F
NEW_JERSEY = [0x37, 0xA9, 0x5D]U1F9E7 = 0x9DWESTCHESTER = 0x1ABB
EXPECTED_KANDA = { "U1F41D": 0x07C3, "U1F99C": 64104, "U1F11D": 0x1523, "U1F41C": 56473, "len": 0x10, "U1F41B": 0x4D16,}
EXPECTED_SUGAMO = { "U1F41D": 62601, "U1F99C": 65475, "U1F11D": 65339, "len": 6,}
def quot(a: int, b: int) -> int: return int(a / b)
def rem(a: int, b: int) -> int: return a - b * quot(a, b)
def norm16(x: int) -> int: return rem(rem(x, 65536) + 65536, 65536)
def invert_shimbashi(outputs: list[int]) -> str: chars: list[str] = [] for i, out in enumerate(outputs, start=1): v11 = rem(13 * (rem(i, 3) + 1), 11) c = out - COLORADO - v11 if not (0 <= c <= 0x10FFFF): raise ValueError("decoded codepoint out of range") chars.append(chr(c)) return "".join(chars)
def kanda(ns_payload: str) -> dict[str, int]: t = ns_payload n = len(t) v0, v1, v2 = NEW_JERSEY
rec_41d = 0 rec_99c = 0 rec_11d = WESTCHESTER rec_41c = 0 rec_41b = WESTCHESTER
for i in range(1, n + 1): code = ord(t[i - 1]) var11 = code - 0x80 var12 = rem(i * U1F9E7 + v0, 0x101) var13 = var11 * var12 + v1
var14 = v2 - i if var14 == 0: var14 = -1 var15 = quot(var13, var14)
var16 = var12 or 1 var17 = rem(var13, var16)
rec_99c = rem(rec_99c + var15 + var17, 65536) rec_41d = rec_41d + var13 rec_11d = rem(rec_11d + var15 + var17, 65536) rec_41c = rec_41c + v0 - v2
v0 = v0 + var17 v1 = v1 - var15 v2 = -v2 + rem(code, 5)
var18 = rem(var15 + var17 + U1F9E7, 65536) rec_41b = rem(rec_41b + var18 * (i + 3), 65536)
return { "U1F41D": norm16(rec_41d), "U1F99C": norm16(rec_99c), "U1F11D": norm16(rec_11d), "U1F41C": norm16(rec_41c), "len": norm16(n), "U1F41B": norm16(rec_41b), }
def sugamo(ns_payload: str) -> dict[str, int]: t = ns_payload n = min(len(t), 6) v0, v1, v2 = NEW_JERSEY rec_41d = 0 rec_99c = 0 rec_11d = 0
for i in range(1, n + 1): code = ord(t[i - 1]) var9 = (code - 0x80) * v0 + v1 var10 = v2 - i if var10 == 0: var10 = -1 var11 = quot(var9, var10)
var12 = v0 - rem(code, 9) if var12 == 0: var12 = 1 var13 = rem(var9, var12)
rec_99c = rem(rec_99c + var11 + var13, 65536) rec_41d = rem(rec_41d + var9, 65536) rec_11d = rem(rec_11d + var11 * var13, 65536)
v0 = v0 + var13 v1 = v1 - var11 v2 = -v2 + rem(code, 7)
return { "U1F41D": norm16(rec_41d), "U1F99C": norm16(rec_99c), "U1F11D": norm16(rec_11d), "len": n, }
def main() -> None: inner = invert_shimbashi(EXPECTED_SHIMBASHI) if rem(sum(ord(c) for c in inner), 256) != EXPECTED_GINZA: raise SystemExit("ginza check failed") if kanda(inner) != EXPECTED_KANDA: raise SystemExit("kanda check failed") if sugamo(inner) != EXPECTED_SUGAMO: raise SystemExit("sugamo check failed") print(f"SECCON{{{inner}}}")
if __name__ == "__main__": main()