Analysis
Para este reto se otorga un archivo zip que contiene tanto el binario como todo el código fuente del proyecto lo que hace que sea mas sencillo analizarlo, también se añaden los archivos de docker que definen la configuración cuando se lanza el reto.
❯ tree
.
├── Dockerfile
├── banner_fail
├── build.sh
├── chal
│ ├── Makefile
│ ├── chal
│ ├── chal.cpp
│ ├── check_functions.cpp
│ ├── check_functions.h
│ ├── hackdef.cpp
│ ├── hackdef.h
│ ├── libc.so.6 -> libchecks.so
│ ├── libchecks.so
│ ├── libhackdef.so
│ ├── print_flag.cpp
│ └── print_flag_docker
├── service.conf
├── start.sh
├── stop.sh
└── wrapper
2 directories, 19 files
El Dockerfile nos muestra que se copian varios archivos, los binarios, las librerías necesarias y las flags, tanto las individuales así como las específicas del equipo.
❯ cat Dockerfile
FROM ubuntu:latest
RUN apt-get -qq update && apt-get install -qq --no-install-recommends xinetd libssl3
RUN groupadd -g 1001 ctf
RUN useradd -m -u 1001 -g 1001 -s /bin/bash ctf
COPY service.conf /service.conf
COPY banner_fail /banner_fail
COPY wrapper /wrapper
COPY chal/chal /home/ctf/chal
RUN chown ctf:ctf /home/ctf/chal
COPY chal/libchecks.so /usr/lib/x86_64-linux-gnu/
COPY chal/libhackdef.so /usr/lib/x86_64-linux-gnu/
COPY chal/print_flag_docker /print_flag
RUN chmod 755 /print_flag
RUN chmod u+s /print_flag
COPY flags/flag_00.txt /
COPY flags/flag_01.txt /
COPY flags/flag_02.txt /
COPY flags/flag_03.txt /
COPY flags/flag_04.txt /
COPY flags/flag_05.txt /
RUN chmod 444 /flag_0*.txt
COPY flags/flag_06.txt /root/
COPY flags/flag_ahaumx.txt /root/
COPY flags/flag_bluetm.txt /root/
COPY flags/flag_hackfc.txt /root/
COPY flags/flag_hawkss.txt /root/
COPY flags/flag_mylpwn.txt /root/
COPY flags/flag_pwndir.txt /root/
COPY flags/flag_snoopy.txt /root/
COPY flags/flag_takzac.txt /root/
COPY flags/flag_yaquic.txt /root/
RUN chmod 400 /root/flag_*.txt
EXPOSE 1380
USER ctf
CMD ["/usr/sbin/xinetd", "-filelog", "/dev/stderr", "-dontfork", "-f", "/service.conf"]
Ya que no correremos el proyecto de docker para hacer el exploit será necesario mover los archivos manualmente, iniciando por mover el archivo print_flag a / asignando a root como propietario y privilegios suid, luego movemos las librerías.
❯ ls -l /print_flag
-rwsr-xr-x 1 root root 50912 Oct 19 15:18 /print_flag
❯ ls -l /usr/lib/x86_64-linux-gnu/libchecks.so
-rwxr-xr-x 1 root root 900168 Oct 19 15:17 /usr/lib/x86_64-linux-gnu/libchecks.so
❯ ls -l /usr/lib/x86_64-linux-gnu/libhackdef.so
-rwxr-xr-x 1 root root 267520 Oct 19 15:17 /usr/lib/x86_64-linux-gnu/libhackdef.so
Luego movemos todas las flags al directorio /, excepto la flag 06 y las flags de los equipos ya que esas se mueven al directorio /root, en este caso como solo nos interesa la del equipo MyLittlePwny solo movemos la flag con nomnre mylpwn.
❯ ls -l /flag_0*.txt
.rw-r--r-- root root 31 B Sun Oct 19 16:38:10 2025 /flag_00.txt
.rw-r--r-- root root 31 B Sun Oct 19 15:54:24 2025 /flag_01.txt
.rw-r--r-- root root 31 B Sun Oct 19 15:54:24 2025 /flag_02.txt
.rw-r--r-- root root 31 B Sun Oct 19 15:54:24 2025 /flag_03.txt
.rw-r--r-- root root 31 B Sun Oct 19 15:54:24 2025 /flag_04.txt
.rw-r--r-- root root 31 B Mon Oct 20 11:53:27 2025 /flag_05.txt
❯ cat /flag_0*.txt
hackdef{0_f4k3_fl4g_4_t35t1ng}
hackdef{1_f4k3_fl4g_4_t35t1ng}
hackdef{2_f4k3_fl4g_4_t35t1ng}
hackdef{3_f4k3_fl4g_4_t35t1ng}
hackdef{4_f4k3_fl4g_4_t35t1ng}
hackdef{5_f4k3_fl4g_4_t35t1ng}
❯ sudo ls -l /root/flag_*.txt
.rw-r--r-- root root 31 B Sat Nov 1 15:58:43 2025 /root/flag_06.txt
.rw-r--r-- root root 37 B Sun Nov 2 10:58:22 2025 /root/flag_mylpwn.txt
❯ sudo cat /root/flag_*.txt
hackdef{6_f4k3_fl4g_4_t35t1ng}
hackdef{mylpwn_f4k3_fl4g_4_t35t1ng}
❯ ./chal
Servicio interno de tarjetas de credito
** Menu:
1. Registrar tarjeta
2. Registrar NIP
3. Consultar NIP
4. Borrar NIP
5. Salir
> cnt_checker = 1
Checking... 550
OK
int main() {
init_buffers();
init_flagprocess();
init_seccomp();
init_handlers();
init_flags();
ignorar_signals();
std::future<void> fut_flag_check = std::async(std::launch::async, service_flag_checker);
std::future<void> fut = std::async(std::launch::async, service_checker);
std::future<void> fut2 = std::async(std::launch::async, main_menu);
fut.wait_for(std::chrono::seconds(30));
return 0;
}
#define TIMEOUT_CHECK_FUNC 100
CheckFunc handlers[NUM_CHECK_FUNCS];
void init_handlers() {
handlers[0] = fn_000;
handlers[1] = fn_001;
handlers[2] = fn_002;
handlers[3] = fn_003;
.........................
handlers[1157] = fn_1157;
handlers[1158] = fn_1158;
handlers[1159] = fn_1159;
}
#define NUM_CHECK_FUNCS 290 * 4
#define TIMEOUT_CHECK_FUNC 100
void service_checker() {
int cnt_checks = 0;
while (cnt_checks++ < 4) {
std::cout << "cnt_checker = " << cnt_checks << std::endl;
int op = randint(NUM_CHECK_FUNCS - 1);
std::future<bool> fut = std::async(std::launch::async, execute_check, op);
if (fut.wait_for(std::chrono::milliseconds(TIMEOUT_CHECK_FUNC)) == std::future_status::ready) {
bool result = fut.get();
if (result) {
std::cout << "OK" << std::endl;
} else {
std::cout << "WA - Algo no anda bien. Te estare vigilando..." << std::endl;
salir();
}
} else {
std::cout << "Te descubri hacker!" << std::endl;
salir();
}
std::this_thread::sleep_for(std::chrono::seconds(10));
}
puts("Bye!");
salir();
}
bool execute_check(int op) {
std::cout << "Checking... " << op << std::endl;
switch (op) {
case 0: return check_000(handlers[0]);
case 1: return check_001(handlers[1]);
case 2: return check_002(handlers[2]);
.............................................
case 1157: return check_1157(handlers[1157]);
case 1158: return check_1158(handlers[1158]);
case 1159: return check_1159(handlers[1159]);
default:
std::cout << "NO" << std::endl;
salir();
}
return true;
}
#define TIMEOUT_CHECK_FUNC 160
bool check_000(CheckFunc check) {
int a=397540806, b=808190108, c=948268558, d=403076592, e=488643755;
std::future<void> fut = std::async(std::launch::async, check, &a, &b, &c, &d, &e);
if (fut.wait_for(std::chrono::microseconds(TIMEOUT_CHECK_FUNC)) == std::future_status::ready) {
if (b != 152045996) return false;
if (c != 727562417) return false;
if (d != 153545182) return false;
return true;
} else {
return false;
}
}
bool check_1159(CheckFunc check) {
int a=852726792, b=398756715, c=1025465896, d=783751113, e=391710928;
std::future<void> fut = std::async(std::launch::async, check, &a, &b, &c, &d, &e);
if (fut.wait_for(std::chrono::microseconds(TIMEOUT_CHECK_FUNC)) == std::future_status::ready) {
if (e != 898176514) return false;
if (a != 117814948) return false;
if (d != 87295856) return false;
return true;
} else {
return false;
}
}
void fn_000(int *a, int *b, int *c, int *d, int *e) {
*b = 152045996;
*c = 727562417;
*a = 333394009;
*d = 153545182;
*e = 460636857;
}
void fn_1159(int *a, int *b, int *c, int *d, int *e) {
*b = 76210336;
*c = 778987935;
*d = 87295856;
*a = 117814948;
*e = 898176514;
}





