🧩 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"
- Si
bundle.jsno existe, lo descarga por primera vez, lo formatea, y confirma con un mensaje verde ✅.
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}')
- Si ya existe, descarga una versión temporal (
bundle_tmp.js) y compara su hash MD5 con el archivo actual para ver si hay cambios.
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
}
-
Si no hay cambios, muestra un mensaje azul indicando que todo está actualizado.
-
Si hay una nueva versión, reemplaza el archivo y notifica que fue actualizado con éxito.
🔍 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
-
Entrada:
- La función
searchMachinerecibe un parámetro, el nombre de la máquina ($1), que se almacena en la variablemachineName.
- La función
-
Comprobación de existencia:
-
Utiliza
greppara verificar si el nombre de la máquina ($machineName) se encuentra dentro del archivobundle.js. -
La salida del comando
grepse almacena en la variablechecker_maquina. -
Si
checker_maquinacontiene 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).
-
-
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 camporesuelta:. -
grep -vE "id:|sku|resuelta": Filtra las líneas para eliminar los campos que no son relevantes, comoid,skuyresuelta. -
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.
-
-
-
Visualización de datos:
-
Utiliza
cut -d':' -f1para obtener la clave (comoNombre,IP,Dificultad, etc.) ycut -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).
-
-
-
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.
- Si la máquina no se encuentra en el archivo
🔍 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
-
Entrada:
-
La función
searchIPrecibe una dirección IP ($1) como argumento. -
Esta IP se guarda en la variable
ipAddress.
-
-
Búsqueda del nombre de máquina:
-
Se usa
grep "ip: \"$ipAddress\""para encontrar la línea exacta con esa IP dentro debundle.js. -
La opción
-B 3indica 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 conawk 'NF{print $NF}'. -
Se eliminan las comillas y comas con
tr -d.
-
-
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
searchMachinepara mostrar todos sus datos.
- Si se encontró un nombre de máquina asociado a la IP, se muestra un mensaje informativo y se llama a la función
-
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?
-
Entrada:
- Recibe como argumento el nombre de una máquina (
$1), almacenado enmachineName.
- Recibe como argumento el nombre de una máquina (
-
Verificación del nombre:
-
El uso de
awkasegura que el nombre buscado exista dentro del archivobundle.js. -
Si el bloque no existe, el
grep youtubeposterior no devolverá nada, lo que equivale a decir que la máquina no tiene un vídeo o directamente no existe.
-
-
Extracción del enlace de YouTube:
-
Se localiza el bloque de propiedades de la máquina usando
awk(name: "..."hastaresuelta:). -
Luego se filtra la línea con
grep youtubepara detectar si hay un enlace. -
Se limpian los caracteres innecesarios con
trysed.
-
-
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:
- La función recibe como argumento el nivel de dificultad (por ejemplo:
Fácil,Medio,Difícil,Insane).
🔍 Búsqueda de máquinas:
-
Busca en
bundle.jstodas las máquinas que tengan ese nivel de dificultad (grep "dificultad: \"$nivel\""). -
Luego retrocede 5 líneas (
-B 5) para capturar también la línea que contiene el nombre. -
Extrae los nombres con
grep namey los limpia para mostrar solo el texto necesario.
✅ Si encuentra resultados:
- Muestra una lista de las máquinas con ese nivel de dificultad, organizadas en columnas con
column.
❌ Si no encuentra resultados:
-
Informa al usuario de que el nivel introducido no existe.
-
A continuación, muestra una lista de niveles válidos extraída dinámicamente del propio archivo.
🐧🪟 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:
- La función recibe como argumento un sistema operativo (por ejemplo,
LinuxoWindows).
🔍 Proceso:
-
Busca en
bundle.jslas entradas que contenganso: "$so". -
Retrocede 5 líneas (
-B 5) para capturar también el bloque donde se define el nombre de la máquina. -
Extrae y limpia los nombres de las máquinas encontradas.
✅ Si hay coincidencias:
- Muestra los nombres de las máquinas que usan ese sistema operativo, organizados en columnas para facilitar la lectura.
❌ Si no hay coincidencias:
- Muestra un mensaje de error indicando que no se encontraron máquinas con ese sistema operativo.
🎯🖥️ 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:
- El usuario introduce como parámetro una skill, por ejemplo:
SQL Injection,SSRF,XSS
🔍 Lógica:
-
Búsqueda insensible a mayúsculas/minúsculas (
-i) en las líneas conskills:. -
-B 6: Incluye 6 líneas anteriores para obtener el bloque que contiene también el nombre de la máquina. -
grep "name: ": Filtra la línea exacta donde aparece el nombre. -
awk+tr: Limpia el texto para mostrar solo el nombre de la máquina.