Esta CTF inició, pero por disponibilidad no pude empezar hasta el 03/06/2024, así que con ese handicap hice lo mejor que pude y me centré en los retos que más claro llevaba, se compone de los siguientes apartados:

Reversing

Passw0rd

Descripción:

Este archivo ELF requiere una contraseña. ¿Hay alguna forma de echar un ojo dentro sin conocerla?

Primero vamos a ver de que se trata el archivo

┌─[bicho@balam]─[~/Downloads]
└─[]=> file password
password: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=45fac682420d7c8916f247ae6df7db3f6fb6c280, for GNU/Linux 3.2.0, not stripped

Parece ser un archivo compilado, vamos a probar a usar strings para ver su contenido

┌─[bicho@balam]─[~/Downloads]
└─[]=> strings password
/lib64/ld-linux-x86-64.so.2
puts
strlen
__libc_start_main
__cxa_finalize
printf
__isoc99_scanf
libc.so.6
GLIBC_2.7
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
PTE1
u+UH
h5J07YnQH
Input password >
Incorrect
Correct!
FLAG is LetsCTF{y0u_kn0w_c0s1t4s_c0ngr4ts}

As easy as that!

Caja de secretos

Descripción:

He traido la Caja de Secretos de mi familia para venderla a una tienda de empeños, pero sin que la abran y comprueben su contenido no pueden tasarla, ayúdame.

Volvemos a observar el tipo de fichero

┌─[bicho@balam]─[~/Downloads]
└─[]=> file caja_de_secretos
caja_de_secretos: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fe28b4ebc7cef4d2a6e3b1fb18f9d87e0d5994aa, for GNU/Linux 3.2.0, not stripped

Vamos a usas strings otra vez

┌─[bicho@balam]─[~/Downloads]
└─[]=> strings caja_de_secretos
/lib64/ld-linux-x86-64.so.2
puts
strlen
__libc_start_main
__cxa_finalize
printf
__isoc99_scanf
strncmp
libc.so.6
GLIBC_2.7
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
PTE1
u+UH

...

Input secret key :
Incorrect
i_dont_know_rick_the_challenge_seems_fake
Correct! Flag is %s

Podría ser que otra vez hubiese funcionado? Vamos a verificar con ghidra

Efectivamente, era una trampa, para entender este ejercicio deberiamos saber C, como es el caso (más ez que una PR) voy a explicar que sucede

printf("Input secret key : ");
__isoc99_scanf(&DAT_001022cf,local_48);
sVar2 = strlen((char *)local_48);
 
## Las lineas anteriores imprimen el mensaje, leen la entrada del usuario y almacenan el tamaño de la entrada en sVar2
 
if (sVar2 == 0x29) {
 iVar1 = strncmp((char*)local_48,"i_dont_know_rick_the_challenge_seems_fake",0x29);
 
## Comprueba que la longitud de la entrada es 41 y almacena el resultado de la comparación de "i_dont_know_rick..." con la entrada
 
if (iVar1 == 0) {
   for (local_10 = 0; local_10 < 0x29; local_10 = local_10 + 1) {
	    local_78[local_10] = local_48[local_10] ^ (byte)local_128[local_10];
    }
    local_4f = 0;
    printf("Correct! Flag is %s\n",local_78);
    uVar3 = 0;
}
 
## Comprueba que los strings son iguales y decodifica la flag almacenada en el array local_128

Clicker bypass

Descripción:

Quieres conseguir el premio máximo de este juego. Lamentablemente, el premio máximo es tan caro que tienes que tomar métodos alternativos para ganarlo. Has conseguido hacerte con el archivo APK.

Parece que esta vez nos enfrentamos a un APK

┌─[bicho@balam]─[~/Downloads]
└─[]=> file clicker.apk
clicker.apk: Android package (APK), with classes.dex, with APK Signing Block

Vamos a intentar decompilarla para ver el funcionamiento

Parece que tenemos los archivos encargados de darnos la flag

Checksum checker

Descripción:

Se realiza una comprobación de la clave de alguna forma. ¿Podrás encontrar qué clave poner para sacar la flag?

Parece que volvemos a los binarios

┌─[bicho@balam]─[~/Downloads]
└─[]=> file checker
checker: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0696b9a8382f20eed53ba53c614088625a59a948, for GNU/Linux 4.4.0, not stripped

Vamos a ver que podemos sacar con strings

┌─[bicho@balam]─[~/Downloads]
└─[]=> strings checker
/lib64/ld-linux-x86-64.so.2
__cxa_finalize
malloc
__libc_start_main
free
strlen
sprintf
fread
__stack_chk_fail
libc.so.6
GLIBC_2.4
GLIBC_2.34
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
PTE1
u3UH
#EgH
es_esta_la_flag?
Usage:
./checker <clave>
%02x
Correcto, la flag es letsctf{%s}
Incorrecto, vuelve a intentarlo mas tarde.
;*3$"
QZ^&

Parece que va a ser parecido a Caja de secretos, asi que vamos a ghidra

En la funcion main parece que se realiza el md5String de una cadena de texto y luego se almacena en formato hexadecimal en un buffer, luego se comprueba que el argumento que damos sea mayor que 31 y se comprueba byte a byte que sea igual que md5sum, pues ya lo tenemos

┌─[bicho@balam]─[~/Downloads]
└─[]=> ./checker d286fdb1b2567940e8857d9a875764df
Correcto, la flag es letsctf{d286fdb1b2567940e8857d9a875764df}

¿Cuántos Pikachus?

Descripción:

Necesitamos saber cuántos Pikachus hay en este binario. Parece que tiene algún tipo de contraseña. ¿Puedes ayudarnos?

A estas alturas vamos a saltarnos el paso de verificar si contiene strings, vamos a ghidra directos y vamos a ir modificando las variables por su valor hexadecimal

Ahora que tenemos renombradas las variables vamos a ver como continua el programa

Parece que si iVar1 es verdadero nos da la flag, para que esto se cumpla input y cmp1 (comparacion1) tienen que ser iguales, pero podemos ver que cmp1 se va construyendo a lo largo del codigo, vamos a ver el valor

strcpy(cmp1, (char *)&ert);   // "ert"
strcat(cmp1, (char *)&tni);   // "ert" + "tni" = "erttni"
strcat(cmp1, (char *)&y_a);   // "erttni" + "y_a" = "erttniy_a"
strcat(cmp1, (char *)&onu_);  // "erttniy_a" + "onu_" = "erttniy_aonu_"

Parece un poco rara la constraseña, vamos a probar cambiando de big endian a little endian

strcpy(cmp1, (char *)&ert);   // "tre"
strcat(cmp1, (char *)&tni);   // "tre" + "int" = "treint"
strcat(cmp1, (char *)&y_a);   // "treint" + "a_y" = "treinta_y"
strcat(cmp1, (char *)&onu_);  // "treinta_y" + "_uno" = "treinta_y_uno"

Eso tiene bastante mas sentido, vamos a probar

Loco traceame

Descripción:

Ey conoces a mi amigo ltrace?

Todo apunta a que vamos a tener que usar ltrace con el fichero, vamos a ver

┌─[bicho@balam]─[~/Downloads]
└─[]=> ltrace ./tracer_l
printf("Input flag : ")                                = 13
__isoc99_scanf(0x63fb17579012, 0x7ffe90c99d10, 0, 0Input flag :

Parece que nos pregunta por una flag

printf("Input flag : ")                                = 13
__isoc99_scanf(0x63fb17579012, 0x7ffe90c99d10, 0, 0Input flag : prueba
)   = 1
strcmp("prueba", "LetsCTF{7r4c3d_dyn4m1c_l1br4ry_c"...) = 36
puts("Incorrect"Incorrect
)                                      = 10
+++ exited (status 1) +++

:0 Parece que tenemos la flag, pero incompleta, vamos a volver a ghidra

Despues de limpiar un poco la funcion no parece tan compleja, hace un XOR entre el caracter y 0x53 y lo almacena en flag, despues compara la entrada y la flag que ha compuesto y si no es igual da 0, parece simple, vamos a por ello

Crypto

Conversión sencilla

Descripción:

Ayúdame a devolverlo a su estado original, no sé cómo hacerlo...

Este reto nos da dos archivos, un archivo output.txt y otro easy_conversion.py, vamos a analizar este ultimo

from const import flag
 
def bytes_to_integer(x: bytes) -> int:
	x = int.from_bytes(x, byteorder="big")
	return x
 
print(bytes_to_integer(flag))

Parece ser que aplica la función bytes_to_integer al tipo de datos bytes con nombre flag, pero no tenemos la flag (obviamente), así que vamos a hacer los pasos a la inversa

output = 5931515605221015360233374431880296807621296221414030630676697270864438034431726887206259813910273514610615152253
 
print(output.to_bytes(len(str(output)), byteorder="big"))

Si lo ejecutamos obtendremos la flag

Droids?

Descripción:

AtihRIU{iwtht_pgtci_iwt_sgdxsh_ndjgt_addzxcv_udg}

Intgenerador

Descripción:

Este generador devuelve un integer de 16 dígitos si introduces cualquier integer entre 0 y 2^35. ¿Podrías decirnos cuáles son los valores de flag1, flag2 y flag3?

> Formato del flag:LetsCTF{flag1_flag2_flag3}

Este reto nos vuelve a dar dos archivos, uno llamado generador.py y otro llamado output.txt, vamos a intentar una técnica distinta a la usada en Conversión sencilla para intentar resolverlo

import random
 
k = 36
maxlength = 16
 
 
def f(x, cnt):
	cnt += 1
	r = 2**k
	if x == 0 or x == r:
		return -x, cnt
	if x * x % r != 0:
		return -x, cnt
	else:
		return -x * (x - r) // r, cnt
		
def g(x):
	ret = x * 2 + x // 3 * 10 - x // 5 * 10 + x // 7 * 10
	ret = ret - ret % 2 + 1
	return ret, x // 100 % 100
	
def digit(x):
	cnt = 0
	while x > 0:
		cnt += 1
		x //= 10
	return cnt
	
def pad(x, cnt):
	minus = False
	if x < 0:
		minus = True
		x, cnt = g(-x)
	sub = maxlength - digit(x)
	ret = x
	for i in range(sub - digit(cnt)):
		ret *= 10
		if minus:
			ret += pow(x % 10, x % 10 * i, 10)
		else:
			ret += pow(i % 10 - i % 2, i % 10 - i % 2 + 1, 10)
		ret += cnt * 10 ** (maxlength - digit(cnt))
		return ret
 
def int_generator(x):
	ret = -x
	x_, cnt = f(x, 0)
	while x_ > 0:
		ret = x_
		x_, cnt = f(x_, cnt)
	return pad(ret, cnt)
 
num1 = random.randint(0, 2 ** (k - 1))
num2 = random.randint(0, 2 ** (k - 1))
num3 = random.randint(0, 2 ** (k - 1))
 
print("int_generator(num1):{}".format(int_generator(num1)))
print("int_generator(num2):{}".format(int_generator(num2)))
print("int_generator(num3):{}".format(int_generator(num3)))

Una vez analizado el código, vamos esta vez a intentar hacer fuerza bruta, como sabemos los valores maximos y minimos para generar los numeros vamos a iterar a traves de ellos, pero si hiciesemos esto así tardariamos muchisimo tiempo, vamos a usar threads para atacar a pequeños rangos de numeros

from multiprocessing import Process 
 
...
 
def procFunc(start, finish):
	for num in range(start, finish):
		res = int_generator(num)
		if res == 1008844668800884:
			flag[0]=res
			logger.info("Flag 1: " + str(num))
		elif res == 2264663430088446:
			flag[1]=res
			logger.info("Flag 2: " + str(num))
		elif res == 6772814078400884:
			flag[2]=res
			logger.info("Flag 3: " + str(num))
		if flag[0] != -1 and flag[1] != -1 and flag[2] != -1:
			return
 
thn=32
 
logger = logging.getLogger(__name__)
logging.basicConfig(filename='output.log', level=logging.INFO)
 
flag=[-1,-1,-1]
prev=0
act=prev
section=2**k-1
procs=[]
 
for i in range(1, thn+1):
	prev=act
	act=section*i+1
	procs.append(Process(target=procFunc, args=(prev, act)))
	procs[i-1].start()
procs.append(Process(target=procFunc, args=(act, 2**k)))
procs[thn].start()  
 
for proc in procs:
	proc.join()
 
logger.info("Verifying flags:")
logger.info("int_generator(num1):{}".format(int_generator(flag[0])))
logger.info("Flag1 is: ", end="")
if flag[0] == 1008844668800884:
	logger.info("Correct")
 
logger.info("int_generator(num2):{}".format(int_generator(flag[1])))
logger.info("Flag1 is: ", end="")
if flag[0] == 2264663430088446:
	logger.info("Correct")
	
logger.info("int_generator(num3):{}".format(int_generator(flag[2])))
logger.info("Flag1 is: ", end="")
if flag[0] == 6772814078400884:
	logger.info("Correct")

Ejecutando el código deberíamos obtener la flag

Puzzle

Descripcion:

Estar familiarizado con diferentes encodings es una habilidad básica para competir en CTFs. ¿Podrás descifrar las piezas del puzzle para obtener el mensaje oculto?

letsctf{diff3r3nt_3NcOdINGS_F0r_

Web

Inspector Gadget

Descripción:

¿Tienes lo necesario para convertirte en el Inspector Web Gadget y encontrar lo que buscas en este desafío de ciberseguridad? Demuestra tus habilidades detectivescas y desentraña el misterio en línea!

Secure Ping Service

Descripción:

Un administrador de sistemas ha desarrollado un programa para hacer ping de forma segura, pero nosotros, como hackers, tenemos nuestras dudas sobre su verdadera seguridad. Estamos analizando detenidamente el programa en busca de posibles vulnerabilidades que puedan ser explotadas.

XSS1

Descripción:

Ejecuta un alert en la web con el mensaje ’XSS’.

Nota: Es posible que con algunos payloads el flag no se vea en el navegador, se recomienda Burp Suite.

hola

"hola

"/><div>This is a test</div><img src="

'/>

falla perrro' onmouseover='alert("XSS")

Tras hablar con compañeros, parece ser que el CTF analizaba el codigo en vez del evento, por lo que se necesitaba un formato exacto, modificando un poco el payload anterior conseguimos la flag

' onerror='alert("XSS")

Super secure login

Descripción:

He programado mi primer login en php. El profesor insistió en que lo probaría a fondo, así que he intentado hacerlo lo más seguro posible. ¿Puedes probarlo para asegurarte de que todo está bien?

Vamos a intentar entrar con “admin”

Vamos a revisar el codigo un poco a ver donde realiza la petición

Parece que hay un comentario que se ha dejado el developer

Genial, tenemos acceso al login

El código comprueba que la contraseña en md5 sea igual que una contraseña que existe en el codigo, vamos a intentar obtenerla

Ya tenemos la contraseña

Sqlazo

Descripción:

Me han pedido un programa sencillo para consultar la base de datos con los correos de los empleados de nuestra empresa a partir de su nombre de usuario. ¿Puedes echarle un vistazo?

Con este string podemos hacernos una idea del tipo de codificación que tiene la flag, vamos a comprobarlo

Las galletas de la abuelita

Descripción:

La abuelita ha dejado desatendido el tarro con las galletas. ¿Y si pruebas a coger una?

Si nos fijamos en las “cookies” de la peticion, veremos algo muy interesante

Vamos a forjar una petición con la cookie en 1 (true) con burpsuite

Sonic blazing

Descripción:

Tu objetivo es demostrar tu velocidad cibernética, como Sonic el Erizo, para superar este reto en tiempo récord. ¿Tienes lo necesario para cruzar la línea de meta antes que los villanos cibernáticos te alcancen?

Tan solo nos dice que seamos tan rápidos como podamos, pero no nos da más información, vamos a ver que más podemos encontrar en el codigo y los headers

Parece que tenemos que decodificar la contraseña que nos dan en base64 (probablemente)

Parece que el reto no es encontrar la información sino hacerlo rápido, vamos a analizar los pasos que tenemos que seguir:

peticion -> get chllngs.letsctf.com:<port>/index.php
password -> peticion.headers.Get-flag
decoded_password -> base64 decode password
post get chllngs.letsctf.com:<port>/index.php con contenido shellmates=<contraseña decodificada>

Ahora vamos a aplicarlo usando curl

#!/bin/bash
peticion=$(curl http://chllngs.letsctf.com:${1}/index.php -sIXHEAD)
cookie=$(echo "$peticion" | grep "Cookie:" | cut -f2 -d" ")
base64=$(echo "$peticion" | grep "Get-flag:" | cut -f2 -d" " | sed 's/\r$//' | base64 -d)

echo "Cookie: $cookie"
echo "Pass: $base64"

curl -XPOST --cookie "${cookie:0:-1}" http://chllngs.letsctf.com:${1}/index.php -d "shellmates=$base64"

Guardamos el script y le damos permisos de ejecución, el script cogerá un argumento, el cual será el número del puerto del reto

┌─[bicho@balam]─[~]
└─[]=> ./imfastafboi.sh 39248
Cookie: PHPSESSID=3ab3a20d03a291a54929f79da05e34a8;
Pass: a8gHORKVTKSgrroV
Wuau, perfecto! Aquí tienes el flag: LetsCTF{M4s_r4p1d0_qu3_uN_eR1z0}%

XSS 2

Tenemos una estructura muy similar al anterior reto de XSS así que vamos a empezar a probar payloads

hola

Vemos que el valor se almacena en una variable y luego se imprime en la página usando un concatenado de “Hola” + name, vamos a ver como se comporta

"<div>This is a test</div>

Parece que no podemos escapar las comillas de la variable, pero al poner una etiqueta html se interpreta, genial, vamos a inyectar un script que ejecute alert y ya (o no)

<script>alert("XSS")</script>

</body><a href="&#x6A;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3A;alert&lpar;&apos;XSS&apos;&rpar;">BiCH0</a>

Con este payload ya habríamos conseguido la inyección

Forensics

Mucho texto

Un presidente

Ez wireshark

Descripción:

¿Podrás encontrar la flag entre todos estos paquetes que hemos capturado?

En este reto se nos presenta una captura de wireshark, vamos a empezar filtrando los protocolos que veamos para minimizar los paquetes a revisar y posteriormente ordenaremos por tamaño de datos, de esta manera podemos ver si hay algun paquete anormalmente pequeño (o grande)

Y revisando un poco encontraremos los siguientes paquetes

Si revisamos su contenido veremos que uno de ellos contiene un string codificado en lo que parece ser codificación de césar

Lo pasamos por cyberchef

Y ya tenemos nuestra flag

PDF con mensaje

Descripción:

Sospechamos de un consultor externo de espionaje industrial. Durante la investigación, encontramos una unidad USB entre sus pertenencias que contiene un único archivo PDF. Ayúdanos a determinar si de alguna manera está utilizando este PDF para extraer el FLAG.

Nota: El formato del flag es LetsCTF{flag}

El primer paso será observar el contenido del pdf

Parece una imagen simplemente, vamos a mirar a ver si hay algún texto escondido en los colores de la imagen arrastrando el ratón mientras seleccionamos

No parece que haya nada, vamos a ver los metadatos del archivo

┌─[bicho@balam]─[~/Downloads]
└─[]=> exiftool wine.pdf
ExifTool Version Number         : 12.76
File Name                       : wine.pdf
Directory                       : .
File Size                       : 760 kB
File Modification Date/Time     : 2024:06:03 23:44:19+02:00
File Access Date/Time           : 2024:06:03 23:44:19+02:00
File Inode Change Date/Time     : 2024:06:03 23:44:20+02:00
File Permissions                : -rw-r--r--
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.3
Linearized                      : No
Create Date                     : 2017:09:26 21:03:34+01:00
Creator                         : pdftk 2.02 - www.pdftk.com
Modify Date                     : 2017:09:26 21:03:34+01:00
Producer                        : itext-paulo-155 (itextpdf.sf.net-lowagie.com)
Page Count                      : 1

No parece contener nada importante, más allá de la herramienta usada para su creación, vamos a buscar información acerca de ella

Parece que es una aplicación de creación de PDFs, vamos a comprobar que no haya datos ocultos usando strings junto con grep

┌─[bicho@balam]─[~/Downloads]
└─[]=> strings wine.pdf | grep -i flag
<</Ascent 1463 /CapHeight 674 /Descent -169 /Flags 32 /FontBBox
<</Ascent 946 /CapHeight 680 /Descent -232 /Flags 32 /FontBBox
┌─[bicho@balam]─[~/Downloads]
└─[]=> strings wine.pdf | grep -i ctf
┌─[bicho@balam]─[~/Downloads]
└─[]=> strings wine.pdf | grep -i pass

Parece que no hay nada, vamos a usar la herramienta origami para desempaquetar el pdf a ver si hay mas suerte

┌─[bicho@balam]─[/tmp/pdf]
└─[]=> pdfextract wine.pdf
[error] Breaking on: " /Creator\n..." at offset 0x40
[error] Last exception: [Origami::InvalidObjectError] Failed to parse object (no:1,gen:0)
	 -> [ArgumentError] wrong number of arguments (given 1, expected 0; required keyword: year)
Cannot decode stream 37 0 R: Origami::Filter::JPX is not yet supported
Cannot decode stream 38 0 R: Origami::Filter::JPX is not yet supported
Cannot decode stream 39 0 R: Origami::Filter::JPX is not yet supported
Cannot decode stream 65 0 R: DCT filter is not supported
Cannot decode stream 70 0 R: DCT filter is not supported
Cannot decode stream 88 0 R: DCT filter is not supported
Cannot decode stream 93 0 R: DCT filter is not supported
Cannot decode stream 98 0 R: DCT filter is not supported
Cannot decode stream 103 0 R: DCT filter is not supported
Cannot decode stream 108 0 R: DCT filter is not supported
Cannot decode stream 113 0 R: DCT filter is not supported
Extracted 38 PDF streams to 'wine.dump/streams'.
Extracted 0 scripts to 'wine.dump/scripts'.
Extracted 0 attachments to 'wine.dump/attachments'.
Extracted 2 fonts to 'wine.dump/fonts'.
Warning: file 'wine.dump/images/image_37.jp2' is intended to be viewed in CMYK color space.
Warning: file 'wine.dump/images/image_39.jp2' is intended to be viewed in CMYK color space.
Extracted 13 images to 'wine.dump/images'.

Una vez extraídos los datos vamos a usar ranger para explorar el resultado

En la subcarpeta images no encontramos nada, vamos a comprobar streams, para ello usaremos find junto con grep

┌─[bicho@balam]─[/tmp/pdf/wine.dump/streams]
└─[]=> find . -type f -exec grep flag {} \;
BT 1 0 0 1 0 0 Tm (This IS the flag/password/token you are looking for: b4rc4_v3lh4) Tj T* ET