Este artículo trata sobre el uso de patrones de codificación seguros y la corrección de código vulnerable. Las aplicaciones necesitan bibliotecas de terceros para proporcionar instalaciones comunes para tareas repetitivas como registro, análisis, etc. Cuando un desarrollador utiliza una biblioteca de código abierto, debe comprender que su código hereda también posibles problemas de seguridad. Por esta razón, las bibliotecas de código abierto deben auditarse en busca de riesgos para evitar problemas como la vulnerabilidad log4shell .
Incluso si la biblioteca es segura...
Como parte de nuestro trabajo diurno, auditamos el código fuente de la CLI de Apache Commons para ver si hay vulnerabilidades explotables presentes.
Apache Commons CLI es una biblioteca de código abierto que proporciona una API para analizar argumentos de línea de comandos.
Esos argumentos son utilizados por las utilidades de línea de comandos escritas en Java. Proporcionar entrada segura a los programas es un tema candente hoy en día y esta biblioteca hace muy bien su trabajo.
La postura de seguridad de esta biblioteca es buena y, en el momento en que se escribe esta publicación, no hay vulnerabilidades notables.
… se puede utilizar de forma no segura
Incluso si el análisis de la línea de comandos se realiza correctamente, es posible que un desarrollador use patrones de programación incorrectos en su código, lo que presenta un grave problema de seguridad como la inyección de comandos arbitrarios.
La clase DefaultParser no filtra los caracteres que se pueden usar para agregar otro comando, como: ' ; | &
. Esta es una elección de diseño y los desarrolladores deben ser conscientes de ello. Si necesitan usar argumentos analizados en operaciones delicadas, como generar un nuevo proceso, deben ocuparse de la parte de desinfección.
La clase OptionValidator contiene una rutina nombrada isValidChar
para desinfectar la entrada. Este método se utiliza Character.isJavaIdentifierPart
como rutina de saneamiento y comprueba si el valor de la opción puede ser un identificador de Java válido. Sin embargo, es posible eludir esta validación inyectando comandos arbitrarios si el parámetro se usa como un comando que se ejecutará más adelante.
Considere el siguiente código:
cmd = parser.parse(options, args);
if (cmd.hasOption("t")) {
String value = cmd.getOptionValue("t");
System.out.println("Value is = " + value);
try {
Process process = Runtime.getRuntime().exec("sh -c " + value);
printResults(process);
} catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
}
Ejecutando el código, pasando un argumento de línea de comando que contiene un ';', podemos verificar que la inyección de comando es posible:
$ java -classpath commons-cli-1.4.jar:. Application -t "ls;id"
Value is = ls;id
Application.class
Application.java
commons-cli
commons-cli-1.4.jar
dotfiles
uid=1000(paolo) gid=1000(paolo) groups=1000(paolo),470(wheel)
La aplicación confía en la entrada del usuario, por lo que el ";" El carácter se pasa a la llamada exec() por lo que la cadena resultante es: sh -c ls; id
. Como resultado, el id
comando se ejecuta después de sh -c ls
.
Es importante comprender que el problema es cómo se escribe la aplicación cliente. Cambiando la línea exec en lo siguiente, la inyección de comando es imposible:
Process process = Runtime.getRuntime().exec(value);
Con este estilo de codificación, el mismo
intento de ataque resulta en intentar ejecutar el comando "ls;id" si se usa exec(). Esto condujo a una excepción de "comando no encontrado".
Como práctica recomendada para los desarrolladores, podemos sugerir no agregar un valor analizado a "sh -c", sino usarlo directamente en exec() o una llamada similar.
Conclusiones
En el próximo artículo, diseñaremos una versión mejorada del analizador que agrega una API de desinfección diseñada para consumir entradas para uso confidencial.
Esto permitirá que incluso las aplicaciones con patrones inseguros estén protegidas contra vulnerabilidades de inyección de comandos.