← Back to Writeups
CUSTOMEasyReversing

Classdump

XESXOR9/10/20262 min read
#reversing#custom#easy#ipa_extraction#class_dump_analysis#radare2_disassembly#swift_string_initialization_tr#video_frame_analysis

Classdump

Platform: spbctf | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-07-05 | Status: Solved Techniques: ipa_extraction, class_dump_analysis, radare2_disassembly, swift_string_initialization_tracing, video_frame_analysis

Summary

Task: iOS app (GimmeFlag.ipa) with class dump showing FirstFlagViewController with a 'secret' property; demo video shows correct secret entered but flag hidden by simulator detection. Solution: disassembled the init method with radare2 to find the hardcoded 19-byte secret string 'REDACTED' at address 0x100011f40, which is concatenated into the flag format.

Classdump — spbctf

Description

Now look at the classes and watch the demo.

An iOS application GimmeFlag.ipa is provided along with a class dump (classdump.txt), a preview screenshot (gimme_flag_preview.png), and a demo video (first_task_demo.mov). The class dump reveals three view controllers: FirstFlagViewController, SecondFlagViewController, ThirdFlagViewController. This writeup covers the first flag (the one demonstrated in the video).

Analysis

Binary identification: Extracted the IPA (which is just a ZIP) to get Payload/GimmeFlag.app/GimmeFlag — a Mach-O 64-bit arm64 Swift binary. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Class dump analysis: The classdump.txt (generated by class-dump 3.5) revealed the key class:

  • _TtC9GimmeFlag23FirstFlagViewController — has properties input (UITextField), flag_1, and secret (both String). Has method firstFlagWithFirstFlagBtn:.

The preview screenshot showed the First task UI: a text field labeled "Input secret here" and a "Give me first flag" button.

Strings output: Found key strings including "flag is spbctf{", "secret: ", "Wrong secret, try again", "Success", and a URL http://jay-from-future.xyz/get_flag.html (for the third task). ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Demo video analysis: Extracted frames with ffmpeg. The demo showed:

  • A secret being pasted into the password field (dots visible, 8+ characters)
  • Banner: "GimmeFlag pasted from CoreSimulatorBridge" (clipboard paste)
  • Alert: "Success — Secret is correct, but simulator detected" — the correct secret was entered but the flag is hidden on the simulator due to an anti-simulator check.

Disassembly of firstFlag function (radare2): The main logic function at 0x10000a8f4 (1908 bytes):

  • Reads self.input.text (user input from UITextField)
  • Compares it against self.secret using Swift String equality at 0x10000acfc
  • On match (success branch at 0x10000ad1c): concatenates "flag is spbctf{" (at 0x100012010, len 15) + the secret value + "}" (at 0x100012020, len 1), displays result in alert
  • On mismatch (failure branch at 0x10000ae70): displays alert with title "Error" and message "Wrong secret, try again" ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

The key insight is that the secret is hardcoded in the FirstFlagViewController.init method.

Finding the hardcoded secret in the initializer: Disassembled FirstFlagViewController.init at 0x10000bbb4:

  • At 0x10000bc68: loads a string literal from address 0x100011f40 with length 0x13 (19 bytes)
  • Stores it into self.secret (via the Swift property offset table at 0x10001a000+0x6b8)
  • The 19-byte string at 0x100011f40: REDACTED (leetspeak for "catch decoded value") ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Since the success branch builds "flag is spbctf{" + secret + "}", and the secret is REDACTED:

python
#!/usr/bin/env python3
# Reconstruct the flag from the hardcoded secret
secret = "REDACTED"  # 19 bytes at 0x100011f40
flag = f"spbctf{{{secret}}}"
print(flag)
# spbctf{REDACTED}

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍