🧩 Explicación de las funciones del script

🔄 Actualizar (-u)

Al usar el parámetro -u:
./htbmachines.sh -u

el script actualiza automáticamente el archivo bundle.js, que contiene toda la base de datos de máquinas de Hack The Box.

⚙️ Función updateFiles()

function updateFiles() {
    tput civis  # Oculta el cursor

    if [ ! -f bundle.js ]; then
        echo -e "${yellowColour}[+] Descargando archivo por primera vez...${endColour}"
        curl -s $main_url > bundle.js
        js-beautify bundle.js | sponge bundle.js
        echo -e "${greenColour}[✓] Archivo descargado correctamente.${endColour}\n"

    else
        echo -e "${yellowColour}[+] Verificando actualizaciones...${endColour}"
        curl -s $main_url > bundle_tmp.js
        js-beautify bundle_tmp.js | sponge bundle_tmp.js

        md5_original_value=$(md5sum bundle.js | awk '{print $1}')
        md5_tmp_value=$(md5sum bundle_tmp.js | awk '{print $1}')

        if [ "$md5_tmp_value" == "$md5_original_value" ]; then
            echo -e "${blueColour}[=] No hay actualizaciones disponibles.${endColour}"
            rm bundle_tmp.js
        else
            echo -e "${greenColour}[↑] Se han encontrado actualizaciones.${endColour}"
            rm bundle.js && mv bundle_tmp.js bundle.js
            echo -e "${greenColour}[✓] Base de datos actualizada correctamente.${endColour}"
        fi
    fi

    tput cnorm  # Vuelve a mostrar el cursor
}

🔍 Buscar máquina

# Búsqueda de máquinas (placeholder)
function searchMachine() {
    machineName="$1"
    echo -e "\n${yellowColour}[+] Listando las propiedades de la máquina ${blueColour}$machineName${endColour}\n" 
    checker_maquina=$(grep "$machineName" bundle.js)
 
    if [ -n "$checker_maquina" ]; then
    	cat bundle.js | awk "/name: \"$machineName\"/,/resuelta:/" |
    	grep -vE "id:|sku|resuelta" |
    	tr -d '"' | tr -d ',' |
    	sed 's/^ *//' |
    	while read line; do
        	key=$(echo "$line" | cut -d':' -f1 | xargs)
        	value=$(echo "$line" | cut -d':' -f2- | xargs)
        	echo -e "${purpleColour}$key:${endColour} ${greenColour}$value${endColour}"
    	done	
    	echo
    else 
        echo -e "\n${redColour}[!] No se encontró ninguna máquina con el nombre ${blueColour}$machineName${endColour}\n"
        return 1
    fi
}

🧠 **Explicación del funcionamiento

  1. Entrada:

    • La función searchMachine recibe un parámetro, el nombre de la máquina ($1), que se almacena en la variable machineName.
  2. Comprobación de existencia:

    • Utiliza grep para verificar si el nombre de la máquina ($machineName) se encuentra dentro del archivo bundle.js.

    • La salida del comando grep se almacena en la variable checker_maquina.

    • Si checker_maquina contiene algo (es decir, si la máquina existe), se procede a procesar el archivo. Si no, se muestra un mensaje de error en color rojo ($redColour).

  3. Extracción de datos:

    • Si la máquina existe, la función extrae las propiedades de la máquina con una serie de comandos:

      • awk "/name: \"$machineName\"/,/resuelta:/": Extrae desde el bloque que contiene el nombre de la máquina hasta la sección que contiene el campo resuelta:.

      • grep -vE "id:|sku|resuelta": Filtra las líneas para eliminar los campos que no son relevantes, como id, sku y resuelta.

      • tr -d '"' | tr -d ',': Elimina las comillas y las comas, que no son necesarias para la presentación.

      • sed 's/^ *//': Elimina los espacios en blanco al inicio de cada línea.

      • while read line; do ... done: Lee cada línea del bloque procesado y extrae la clave y el valor de cada propiedad de la máquina.

  4. Visualización de datos:

    • Utiliza cut -d':' -f1 para obtener la clave (como Nombre, IP, Dificultad, etc.) y cut -d':' -f2- para obtener el valor asociado.

    • Los colores se utilizan para hacer que la salida sea más legible y atractiva:

      • Las claves (por ejemplo, Nombre, IP, Dificultad, etc.) se muestran en morado ($purpleColour).

      • Los valores (por ejemplo, el nombre de la máquina, la dirección IP, la dificultad) se muestran en verde ($greenColour).

  5. Manejo de errores:

    • Si la máquina no se encuentra en el archivo bundle.js, la función muestra un mensaje de error utilizando el color rojo ($redColour) para la advertencia [!] y azul ($blueColour) para el nombre de la máquina.

🔍 Buscar máquina por IP

function searchIP() {
    ipAddress="$1"
    machineName="$(cat bundle.js | grep "ip: \"$ipAddress\"" -B 3 | grep "name: " | awk 'NF{print $NF}' | tr -d '"' | tr -d ',')"

    if [ -n "$machineName" ]; then
        echo -e "\n${yellowColour}[+] La IP ${blueColour}$ipAddress${yellowColour} corresponde a la máquina ${blueColour}$machineName${endColour}\n"
        echo -e "${yellowColour}[+] Estas son sus propiedades:${endColour}\n"
        searchMachine "$machineName"
    else
        echo -e "\n${redColour}[!] No se encontró ninguna máquina con la IP ${blueColour}$ipAddress${endColour}\n"
    fi
}

🧠 Explicación del funcionamiento

  1. Entrada:

    • La función searchIP recibe una dirección IP ($1) como argumento.

    • Esta IP se guarda en la variable ipAddress.

  2. Búsqueda del nombre de máquina:

    • Se usa grep "ip: \"$ipAddress\"" para encontrar la línea exacta con esa IP dentro de bundle.js.

    • La opción -B 3 indica que se muestren también las 3 líneas anteriores (donde probablemente esté el campo "name").

    • Luego, se filtra con grep "name: " y se extrae el último campo con awk 'NF{print $NF}'.

    • Se eliminan las comillas y comas con tr -d.

  3. Verificación:

    • Si se encontró un nombre de máquina asociado a la IP, se muestra un mensaje informativo y se llama a la función searchMachine para mostrar todos sus datos.
  4. Colores utilizados:

    • La IP y el nombre de la máquina se muestran en azul ($blueColour).

    • Los mensajes informativos en amarillo ($yellowColour).

    • En caso de error (si no se encuentra la IP), el mensaje se muestra en rojo ($redColour).

🔗 Obtener link de resolución en YouTube

Una manera de que nos envié el video de la resolución de la máquina

function getYoutubeLink() {
    machineName="$1"
    youtubeLink=$(awk '/name: "'"$machineName"'"/,/resuelta:/' bundle.js | grep -vE "id:|sku|resuelta" | grep youtube | tr -d '"' | tr -d ',' | sed 's/^ *//')

    if [ -n "$youtubeLink" ]; then
        echo -e "\n${yellowColour}📺 [LINK ENCONTRADO]${endColour} 🔍"
        echo -e "${blueColour}Máquina:${endColour} ${purpleColour}$machineName${endColour}"
        echo -e "${greenColour}▶️ YouTube:${endColour} ${blueColour}$youtubeLink${endColour}\n"
    else
        echo -e "\n${redColour}❌ No se encontró un enlace de YouTube para la máquina ${purpleColour}$machineName${endColour}\n"
    fi
}

🧠 ¿Qué hace esta función?

  1. Entrada:

    • Recibe como argumento el nombre de una máquina ($1), almacenado en machineName.
  2. Verificación del nombre:

    • El uso de awk asegura que el nombre buscado exista dentro del archivo bundle.js.

    • Si el bloque no existe, el grep youtube posterior no devolverá nada, lo que equivale a decir que la máquina no tiene un vídeo o directamente no existe.

  3. Extracción del enlace de YouTube:

    • Se localiza el bloque de propiedades de la máquina usando awk (name: "..." hasta resuelta:).

    • Luego se filtra la línea con grep youtube para detectar si hay un enlace.

    • Se limpian los caracteres innecesarios con tr y sed.

  4. Salida:

    • ✅ Si se encuentra un enlace:

      • Se muestra el nombre de la máquina y el enlace con formato visual destacado (iconos y colores).
    • ❌ Si no se encuentra el nombre o no tiene un enlace de YouTube:

      • Se muestra un mensaje indicando que no se encontró ningún vídeo.

🎯 Filtrar máquinas por dificultad

function filtrar_nivel() {
    nivel="$1"
 results_check=$(cat bundle.js | grep "dificultad: \"$nivel\"" -B 5 | grep name | awk 'NF{print $NF}' | tr -d '"' | tr -d ',')
    if [ -n "$results_check" ]; then
        echo -e "\n${yellowColour}[+]${endColour} ${purpleColour}Estas son las máquinas con dificultad ${greenColour}$nivel${purpleColour}:${endColour}"
        echo "$results_check" | column
    
    else
    	# Obtener los niveles disponibles si la dificultad no existe
    	categoria_niveles=$(cat bundle.js | grep "dificultad: " | awk -F': ' '{print $2}' | sort | uniq | tr -d '"' | tr -d ',')

    	# Mostrar el mensaje de error con niveles disponibles
    	echo -e "\n${redColour}[!]${endColour} ${yellowColour}La dificultad '${purpleColour}$nivel${endColour} ${yellowColour}no existe.${endColour}\n"
    
    	# Usar 'column' para mostrar los niveles en columnas
    	echo -e "\n${yellowColour}[+] Estos son los niveles disponibles:${endColour}\n"
    	echo "$categoria_niveles" | column
    fi

    
}

🧠 ¿Cómo funciona?

📥 Entrada:

🔍 Búsqueda de máquinas:

Si encuentra resultados:

Si no encuentra resultados:

🐧🪟 Buscar máquinas por sistema operativo

function searchSo() {

	so="$1"
	os_results=$(cat bundle.js | grep "so: \"$so\"" -B 5 | grep "name: " | awk 'NF{print $NF}' | tr -d '"' | tr -d ',' )

	if [ -n "$os_results" ]; then
		echo -e "\n${yellowColour}[+]${endColour} ${purpleColour}Estas son las máquinas del sistema operativo ${greenColour}$so ${purpleColour}:${endColour}"
        	echo "$os_results" | column
	else
		
        	echo -e "\n${redColour}❌ No se encontró máquinas del sistema operativo $so ${purpleColour} $so ${endColour}\n"
	fi

}

🧠 ¿Cómo funciona?

📥 Entrada:

🔍 Proceso:

  1. Busca en bundle.js las entradas que contengan so: "$so".

  2. Retrocede 5 líneas (-B 5) para capturar también el bloque donde se define el nombre de la máquina.

  3. Extrae y limpia los nombres de las máquinas encontradas.

Si hay coincidencias:

Si no hay coincidencias:

🎯🖥️ Buscar por Sistema operativo y por nivel

Esta funcionalidad permite buscar máquinas que coincidan tanto por sistema operativo como por nivel de dificultad. Se deben usar dos parámetros.

⚙️ Preparación: Chivatos y Flags

Antes de ejecutar la función, se usan chivatos (banderas internas) para saber si el usuario ha pasado correctamente ambos argumentos:

# Chivatos
declare -i chivato_difficulty=0
declare -i chivato_os=0

Y en el case de argumentos:

d) nivel=$OPTARG; chivato_difficulty=1; let parameter_counter+=5;;
	o) so=$OPTARG;  chivato_os=1; let parameter_counter+=6;;

Se evalúan así:

elif [ $chivato_difficulty -eq 1 ] && [ $chivato_os -eq 1 ] ; then
	getOsDifficultyMachines $nivel $so

🔍 Función: getOsDifficultyMachines

function getOsDifficultyMachines() {
    difficulty="$1"
    os="$2"

    check_results="$(cat bundle.js | grep "so: \"$os\"" -C 4 | grep "dificultad: \"$difficulty\"" -B 5 | grep "name: " | awk 'NF{print $NF}' | tr -d '"' | tr -d ',')"

    if [ -n "$check_results" ]; then
        echo -e "\n${yellowColour}[+]${endColour} ${purpleColour}Máquinas con SO ${greenColour}$os${endColour} ${purpleColour}y dificultad ${greenColour}$difficulty${endColour}${purpleColour}:${endColour}\n"
        echo "$check_results" | column
    else
        echo -e "\n${redColour}❌ No se encontraron máquinas con SO ${purpleColour}$os${endColour} ${redColour}y dificultad ${purpleColour}$difficulty${endColour}\n"

        # Mostrar opciones válidas como sugerencia (opcional)
        echo -e "${yellowColour}[+] Niveles disponibles:${endColour}"
        cat bundle.js | grep "dificultad: " | awk -F': ' '{print $2}' | tr -d '",' | sort -u | column
        echo -e "\n${yellowColour}[+] Sistemas operativos disponibles:${endColour}"
        cat bundle.js | grep "so: " | awk -F': ' '{print $2}' | tr -d '",' | sort -u | column
    fi
}

🧠 ¿Cómo funciona?

📥 Entrada:
difficulty: Nivel de dificultad (Ej: Fácil, Media, Difícil).

os: Sistema operativo (Ej: Linux, Windows).

🔎 Proceso:
Busca en bundle.js las líneas que coincidan con el sistema operativo usando grep "so: "$os"" -C 4 para incluir contexto.

De ese bloque, filtra las líneas que además incluyan la dificultad.

Extrae los nombres de las máquinas que cumplan ambas condiciones.

✅ Si hay coincidencias:
Se muestra la lista de máquinas que cumplen ambos criterios (SO y dificultad), organizadas con column.

❌ Si no hay coincidencias:
Se muestra un mensaje de error.

Además, como ayuda, se listan:

Todos los niveles de dificultad disponibles.

Todos los sistemas operativos disponibles.

🎯 **Buscar por Habilidades (Skills)

Esta función permite buscar máquinas que requieran ciertas skills técnicas específicas, como SQL Injection, SSRF

function searchSkills() {
    skills="$1"
    check_results="$(cat bundle.js | grep -i "skills:.*$skills" -B 6 | grep "name: " | awk 'NF{print $NF}' | tr -d '"' | tr -d ',' )"
    
    if [ -n "$check_results" ]; then
        echo -e "\n${yellowColour}[+]${endColour} ${purpleColour}Estas son las máquinas con la skill ${greenColour}$skills ${purpleColour}:${endColour}"
        echo "$check_results" | column
    else
        # Mostrar mensaje de error más útil
        echo -e "\n${redColour}[!]${endColour} ${yellowColour}No se encontraron máquinas con la skill '${purpleColour}$skills${endColour}${yellowColour}'.${endColour}"
    fi
}

📥 Entrada:

  1. Búsqueda insensible a mayúsculas/minúsculas (-i) en las líneas con skills:.

  2. -B 6: Incluye 6 líneas anteriores para obtener el bloque que contiene también el nombre de la máquina.

  3. grep "name: ": Filtra la línea exacta donde aparece el nombre.

  4. awk + tr: Limpia el texto para mostrar solo el nombre de la máquina.