Fundamentos linux 1
Tema: Fundamentos linux 1
Entradas:
Dec 01, 2025 19:15
Imaginemos que te dan la siguiente tarea. Una mezcla de ejercicio de The Bandit con el penúltimo proyecto en Hack4u.
Tu jefe te deja su usuario y contraseña, y te dice: tienes que buscar un archivo con datos sobre las máquinas de HackTheBox, es el archivo que utilizaremos para crear un script que sirva como buscador en local. Con funciones de filtrado por máquina, dificultad y sistema operativo. Me suena que era un archivo javascript y que lo descargamos hace un par de días.
¿Cómo lo planteamos? Lo primero sería encontrar ese archivo, para ello utilizaremos la función find y sus opciones. Accedemos con su usuario hacksak.
hacksak@fedora: find / -type f -user jefe -atime -2 -name \*.js 2>/dev/null
Es decir: búscame a partir del directorio raíz / los archivos cuyo propietario sea "jefe", -atime o -mtime, aquellos archivos que han sido accedidos o modificados en los últimos dos días, y que terminan en la extensión .js, los Stderr envíalos a /dev/null.
drwxr-xr-x. jefe jefe 42 B Wed Nov 26 22:05:55 2025
drwxr-xr-x jefe jefe 42 B Mon Dec 1 07:19:28 2025
Vemos que pertenece al user jefe con grupo jefe y el directorio también. Eso podría ser un problema porque no vamos a poder crear un archivo aquí con nuestro user. Pero pudiendo acceder como jefe podemos hacer varias cosas, una de ellas es unir hacksak a grupo jefe (o un grupo de la tarea) y darle permisos a ese grupo.
usermod -aG jefe hacksak
chmod 775 o chmod g+rwx /htbmachines
Otra opción sería usar ACL:
setfacl -m u:hacksak:rwx /htbmachines
Le estaría dando permisos específicamente para ese directorio.
Con find, accesos de usuarios y cambios de permisos puedes hacer los primeros niveles de The Bandit.
Ahora analicemos algunos fragmentos del script de búsqueda para entender las funciones grep, awk, pipes, argumentos y más.
declare -i parameter_counter=0
while getopts "m:dls:D:o:h" arg;do
case $arg in
m) machine=$OPTARG; let parameter_counter+=1;;
d)let parameter_counter+=2;;
D)
dificultad=$OPTARG
let parameter_counter+=5
;;
o)
os=$OPTARG
let parameter_counter+=6
;;
h) #salta el panel de ayuda
esac
done
if [ $parameter_counter -eq 1 ]; then
searchMachine $machine
elif ...
else
helpPanel
fi
Si pongo por la terminal:
./htbscript.sh -m Unicode
¿Que hace nuestro código? En el bucle while le asignamos un valor al contador en función de qué comando activa el usuario. El bloque de los condicionales se encarga de comprobar el contador y llamar a la función correspondiente.
Entramos en el while y getopts comprueba que nuestro argumento m coincide con alguno de los posibles y además necesita un argumento obligatoriamente, eso significa los dos puntos (:). Va al case y comprueba que m está en una de las opciones, le asigna machine y sube el contador a 1. Como el while no encuentra nada más sale. Vamos al if y cumplimos la condición de contador = 1, entonces ejecutamos la función y le pasamos el argumento.
¿Qué ocurre si le ponemos ./htbscript -m? Bueno aquí el getopts nos controla los errores, no entraría en el while, le asigna el arg ?, y la forma correcta de controlarlo sería esta:
\?) # Si es h O si es error (?)
helpPanel
;;
Pero en este caso nos funciona porque el counter se queda a 0 y en el bloque if pasa al else que es el panel de ayuda.
¿Por qué algunos tienen un ; y otros ;;?
El simple es para identificar que aquí termina un comando y empieza el siguiente. El doble es equivalente a un break, aquí salgo del case, ve al esac y no compruebes más casos.
Ahora veamos la función searchMachine en específico y analicemos sus elementos.
function searchMachine(){
machineName="$1"
echo -e "\n${yellowColour}# buscando ${machineName} ${endColour}"
sleep 1
checkMachine=$(cat bundle.js | awk "/name: \"$machineName\"/, /resuelta:/" | grep -vE "id:|sku:|resuelta" | tr -d '"' | sed -e 's/^\s*//')
if [ "$checkMachine" ]; then
echo -e "\n$checkMachine"
else
echo -e "\n${redColour}# no existe esa maquina ${endColour}"
fi
}
Vale la primera línea cogemos el argumento 1 en la terminal, que ya le hemos asignado el nombre de la máquina que haya escrito el usuario y se lo pasamos a la variable machineName.
Hacemos un echo para que salga por pantalla con colores que estamos buscando la máquina, el -e es para que no tenga en cuenta los caracteres especiales como el \n. Un sleep solo para dar la impresión de que estamos buscando.
Luego viene la búsqueda principal. Vayamos por partes: - Tenemos todo el contenido de bundle.js como Stdout, entra por la pipe --> es el Stdin de awk.
- ¿Qué es awk? Es un lenguaje de programación y su manual tiene 600 páginas [...] así que nos daría para varios post hablar solo de él. De momento nos centramos en esta línea awk:
"/name: \"$machineName\"/, /resuelta:/"
awk es un line-oriented language. Es decir el patrón va primero y luego la acción. En este caso tenemos patron1, patron2. Hay varias formas de poner los patrones, de esta forma la coma fuera del / / diferencia un patrón y otro. Además le estamos diciendo identifica patron 1 y coge todo lo que encuentres hasta patron 2.
\" \"
es para escapar las comillas que engloban toda la expresión, es para que no tome literalmente $machinename, si no que se de cuenta que es una variable, se traduzca y así awk recibe:
Coge todo lo que esté entre name: Unicode --hasta -- resuelta:
¿Y dónde está la acción? Pues awk por defecto si no especificas ninguna acción hace un print del resultado, habríamos tenido el mismo resultado poniendo esto:
awk "/name: \"$machineName\"/, /resuelta:/ {print}"
El Stdin de grep sería el siguiente:
name: "Unicode",
id: sf(),
sku: crypto.randomUUID(),
ip: "10.10.11.126",
so: "Linux",
dificultad: "Media",
skills: "JWT Enumeration JWT - Claim Misuse Vulnerability JSON Web Key Generator (Playing with mkjwk) Forge JWT Open Redirect Vulnerability Creating a JWT for the admin user LFI (Local File Inclusion) - Unicode Normalization Vulnerability Abusing Sudoers Privilege Playing with pyinstxtractor and pycdc Bypassing badchars and creating a new passwd archive (Privilege Escalation)",
like: "eWPT eWPTXv2 OSWE",
youtube: "https://www.youtube.com/watch?v=ofz_1ncuCm4",
resuelta: !0
Queremos limpiar y quedarnos con lo que nos interesa. En este caso grep -vE, la v de invert, si normalmente grep muestra lo que coincide con el patrón de búsqueda ahora no muestra lo que coincida; y E es para que no interprete literalmente | sino como un OR. Su Stdout se lo pasamos a los comandos tr -d para eliminar las comillas y sed -e para el espaciado.
Otro ejemplo de la función que busca por sistema operativo:
checkOS=$(cat bundle.js | grep "so: \"$osName\"" -B 5 | grep name: | awk 'NF{print $NF}' | tr -d '"' | tr -d ',' | column)
En este caso el grep tiene un comando -B 5, coge las 5 líneas anteriores a la coincidencia (b de before), el awk esta vez utiliza la variable $NF (number of fields) para imprimir la última palabra de la línea coincidente a name. Es decir, name: Unicode. Podríamos haberle dicho print $2, pero imagina que cambian la descripción por: The name: Unicode entonces estaríamos imprimiendo name:. NF guarda el número total de campos, así que al utilizarlo siempre nos quedamos con el último, aquí utilizamos directamente la acción de awk sobre el patrón que le entra como stdin. El NF{print $NF} es una forma de comprobar que no viene vacío y no dé error.
Hemos visto como implementar los comandos con funciones. Y si queremos que nuestro script busque por sistema operativo y dificultad. Pues una solución sencilla es hacer la suma de las opciones con parameter counter, en este caso 11, luego en nuestro bloque condicional ejecutamos una función para las dos.