Find Your MySQL Username/Password in WordPress

If you need to manually manage your MySQL database associated with a WordPress installation, you’ll need to get the proper credentials first. Database connection information usually consists of:

  • Username (DB_USER)
  • Password (DB_PASSWORD)
  • Database name (DB_NAME)
  • Database host (DB_HOST)
  • Database port (WordPress assumes MySQL’s default port of 3306)

This information can be found in your wp-config.php. To show all lines of wp-config.php that have “DB_” in them, run the following command from the terminal:

grep -r 'DB_' wp-config.php
define('DB_NAME', 'wordpress');
define('DB_USER', 'username');
define('DB_PASSWORD', '********');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

This information can now be used to log in to MySQL’s command-line interface:

mysql -u username -p

Leaving the “-p” parameter empty will trigger MySQL to prompt you for a password. On a *NIX server, it will look like you’re not typing anything — this is by design. While you may specify the password in the same line, this can leave your plaintext password in your command history, which is easily readable. If you want to use this format anyway (i.e., in a script), note that you cannot put a space between the “-p” flag and your password:

mysql -u username -ppassword

Once you’ve logged in, you can view available databases with the show databases; command. To use your wordpress database, take the value from DB_NAME (above) and use the use command: use wordpress;. To see available tables in the selected database, run show tables;.

Find Your MySQL Username/Password in WordPress

CryptoPHP – A WordPress backdoor in social.png

Summary

This is a series of posts on CryptoPHP, a PHP backdoor used for spamming and blackhat SEO. It seems to come bundled with certain copies of WordPress themes from unofficial sites and resides in a file named “social.png”. It comes installed with a list of email addresses and domains to contact and communicates with a C2 server using cURL and OpenSSL for encryption. Its main purpose appears to be to facilitate the display of links and other content, sent from the C2 server. When the script determines that a web crawler (e.g., GoogleBot), and not a real user, is viewing the site, it injects links to third-party sites in hopes of being indexed.

Symptoms

CryptoPHP communicates with external servers, requiring multiple external requests. You may see the following symptoms:

  • WordPress is slow to load, especially during the first pageview
  • Error messages in your server log, possibly due to failed requests.
  • Error messages from IDS/IPS or other security software (e.g., Suhosin) indicating that someone is making calls to exec and eval.

Discovery

A few days ago, I noticed that a WordPress installation was running extremely slowly. After enabling xhprof and profiling the index page, I noticed that a single method (RoQfzgyhgTpMgdUIktgNdYvKE) was taking around 160 seconds to run. The method name (others in the stack were similarly named) and the 23 calls to curl_exec came off as immediately suspicious. I used grep to search for the file and found it under the themes folder as images/social.png.

This file was included at the bottom of a theme file, causing it to be executed on each page load.

<?php include_once(‘images/social.png’); ?>

Opening social.png in a text editor reveals obfuscated and minified code. While it looks like a mess, it’s simply renamed variables and functions with whitespace removed, and can be undone rather easily with the “Find/Replace All” feature of your favorite text editor.

Obfuscated CryptoPHP

 

How to Remove CryptoPHP or social.png

In the limited tests that I’ve done, the offending file – social.png – is the only file that is malicious. It seems to be added to the images/ directory in themes downloaded from unofficial sources. Another line in the main theme files (index.php, header.php or footer.php) includes the file.

While nothing in the file itself indicates that personal or sensitive data is being transmitted back to the server, the file allows its controllers to send commands to it. These commands are then executed by the eval and exec commands in PHP. It is theoretically possible for content, account information, etc. to be transmitted back to the controlling server.

Since the WordPress instance I was using was running on localhost, it would have been unreachable by the controlling servers. It could still phone home and download commands, but could not be controlled directly.  However, due to the possibility of sensitive data being stolen, and the evidence of storing information in the database, I’d recommend a complete re-install of WordPress and changing your admin password(s).

Coming Soon

  • Encryption methods (including a script to decrypt database contents)
  • Detailed/technical review
CryptoPHP – A WordPress backdoor in social.png

Extract one table from a mysqldump file

I recently had to restore a MySQL table from a nightly database backup. Given the size of the dumpfile and the fact that only one table needed modified, I ended up using sed to extract the table:

sed -n '/CREATE TABLE.*table/,/UNLOCK TABLES/p' full_database_backup.sql > table.sql

The -n flag is an alias for –quiet, which suppresses output other than what sed is told to print.  The p at the end of the expression tells sed to print the matches to the screen.
I’ve created a bash script to handle this, and placed it in /bin/dbextract. It’s intended to be used the same way  as the actual command, in that output is directed to stdout. (You’ll want to redirect it with “> outfile”)

Extract one table from a mysqldump file

MySQL datadir on different partition

This writeup will walk you through installing MySQL with the data directory on a separate partition. Although a new install is pretty straightforward, we ran into some quirks when trying to move the data directory on an existing installation. For this tutorial, I’ll be using an otherwise-fresh Ubuntu 14.04 install with MySQL already installed.

The default MySQL data directory (where the database files are stored) is in /var/lib/mysql. I’ll be moving this to a disk mounted at /mnt/SAN for the purpose of freeing up disk space on the VM. (I’m not going to discuss the benefits and drawbacks of doing so, as that’s beyond the scope of this article. I assume that if you’re here, you’ve already determined a need to mount the data directory on another filesystem.)

There are a couple of steps involved in this:

  1. Create the new directory
  2. Stopping the MySQL service
  3. Copying the files to the new location
  4. Editing /etc/mysql/my.cnf
  5. Editing the AppArmor profile
  6. Reloading the AppArmor profile and restarting MySQL

The new data directory will be located at /mnt/SAN/mysql, which will have to be created. When creating this directory, ensure it’s owned by the mysql group and user, and set permissions to 700.

sudo mkdir -p /mnt/SAN/mysql
sudo chown mysql:mysql /mnt/SAN/mysql
sudo chmod 700 /mnt/SAN/mysql

Next, stop the MySQL service:

sudo service mysql stop

or

sudo /etc/init.d/mysql stop

Once you’ve set up the new data directory on your mounted partition, copy the files over:

cp -dpR /var/lib/mysql/* /mnt/SAN/mysql/

The -dpR flags do the following:

-d prevents symlinks from being followed
-p preserves ownership, timestamps and permissions
-R copies recursively

Once the files have copied, ensure the permissions match those of the original data directory (/var/lib/mysql/). Make sure the new mysql directory has the correct ownership and permissions as well!

At this point, a directory listing of /mnt/SAN/mysql should match /var/lib/mysql exactly.

Now, we’ll edit the MySQL config file, located at /etc/mysql/my.cnf. I recommend backing this file up first!

sudo cp /etc/mysql/my.cnf /etc/mysql/my.cnf.bak
sudo emacs /etc/mysql/my.cnf

Look for the “datadir” param, which should be set to the default value of “/var/lib/mysql”

[mysqld]
#
# * Basic Settings
#
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking

Change this value to your new mysql data directory (/mnt/SAN/mysql) and save the file.

If you try to start the MySQL service now, it’ll likely fail because AppArmor sees it accessing a directory it’s not supposed to. Dmesg will show errors like this:

init: mysql main process ended, respawning
 init: mysql post-start process (14005) terminated with status 1
 apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/sbin/mysqld" pid=14020 comm="apparmor_parser"
 init: mysql main process (14032) terminated with status 1
 init: mysql respawning too fast, stopped

In order to correct this, we’ll have to tell AppArmor to allow mysql to read/write to the new data directory. Open up the MySQL AppArmor profile:

sudo emacs /etc/apparmor.d/usr.sbin.mysql

Comment out the lines pertaining to the old data directory, and add the new data directory to the AppArmor profile:

...
#/var/lib/mysql/ r,
#/var/lib/mysql/** rwk,
/mnt/SAN/mysql/ r,
/mnt/SAN/mysql/** rwk,
...

Once this is done, reload the AppArmor profile:

sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysql

If all the permissions are correct, the mysql service should now start:
sudo service mysql start

or

sudo /etc/init.d/mysql start

If you’re still running into issues, make sure that:

  • The new data directory has the correct permissions
  • The AppArmor profile is correct
  • You’ve started the mysql service (mysqld)
MySQL datadir on different partition

IP Address Validation Without Regular Expressions

Validating an IP address is pretty simple, but requires an obnoxious regular expression in order to account for the possible values. Most examples I’ve seen resort to a regular expression to solve the task, but using a simple [0-9]{1,3} pattern isn’t enough. For example, it won’t prevent an IP like 444.555.666.777 from getting past the filter, so it has to be a little more complex:

((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(?![\\d])

Expressions like this suck, so I decided to go about writing my own function to validate an IPv4 address. In Python, it can be done in one line:

  1. is_valid = (ip.count('.') == 3 and False if False in [int(i) in range(0,256) for i in ip.split('.')] else True)

This statement starts with counting the occurrences of the ‘.’ character  in the `ip` variable (string). The interpreter will halt here if the number of octets is incorrect, preventing iteration over clearly-invalid IPv4 addresses like “192.168.1” or “192.168..1.1”. The second half of the statement uses a generator to determine if each octet is a number between 0 and 255 (inclusive), resulting in a list of boolean values. If False is in this list, the statement will evaluate to False.

Timeit shows this function is ever-so-slightly slower than compiling the above regular expression. The regular expression takes between 0.000015 – 0.000025s while the function has been consistently around 0.000025s.

Another variant of the function (using a similar methodology) in PHP looks like this:

  1. function validate_ip($ip) {
  2.     $i = 0;
  3.     foreach(explode('.', $ip) as $part) {
  4.         if ((int)$part >= 0 && (int)$part < 256) {
  5.             $i++;
  6.         }
  7.     }
  8.     return ($i === 4);
  9. }

While newer versions of PHP support generators, this function was written for an older version, hence the difference in formatting. Unfortunately, I don’t have any benchmarks for this function.

 

IP Address Validation Without Regular Expressions

MySQL Database Backup With mysqldump + netcat

I ran into a situation recently where I had to copy a database, but didn’t have the disk space for a full dump. Instead of rsync or scp (which can be done over netcat as well), I opted to pipe the output of mysqldump to netcat and transfer the data directly to the other server.

My setup was Ubuntu server 12.04 and Linux Mint 16 (client). First, start netcat on the client machine on an available port (e.g., 1234) and redirect the output to the desired .sql file:

nc -l 1234 > backup.sql.gz

On the server, we’ll route the mysqldump output through a gzip wrapper and into netcat. In this example, the destination machine (above) is 172.21.1.2, and should hopefully be listening on port 1234 already. (It is worthwhile to note that you should supply the MySQL password in the command itself, rather than just using the “-p” option. The password prompt will be transmitted to the listening machine,  which will end the netcat session. Security-conscious users can enter a space before the command to keep it from being stored in bash history.)

mysqldump -u root -pP@$$w0rd db_name | gzip | nc -w1 172.21.1.2 1234

MySQL Database Backup With mysqldump + netcat

Free Windows Test Virtual Machines

For Linux users looking to run Windows in a virtual machine, but not looking to pay for a copy of Windows, you can download free VM images directly from Microsoft. (Also featured on this page is a link to a game by Microsoft titled “Escape from XP“)

Windows 8.1 (IE 11) Batch Download
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20140402/VirtualBox/IE11_Win8.1/Linux/IE11.Win8.1.For.LinuxVirtualBox.txt

Windows 8 (IE 10) Batch Download
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20131127/VirtualBox/IE10_Win8/Linux/IE10.Win8.For.LinuxVirtualBox.txt

Windows 7 (IE 11)
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20131127/VirtualBox/IE11_Win7/Linux/IE11.Win7.ForLinuxVirtualBox.txt

Windows 7 (IE 10)
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20131127/VirtualBox/IE10_Win7/Linux/IE10.Win7.For.LinuxVirtualBox.txt

Windows 7 (IE 9)
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20131127/VirtualBox/IE9_Win7/Linux/IE9.Win7.For.LinuxVirtualBox.txt

Windows 7 (IE 8)
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20131127/VirtualBox/IE8_Win7/Linux/IE8.Win7.For.LinuxVirtualBox.txt

Windows Vista (IE 7)
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20131127/VirtualBox/IE7_Vista/Linux/IE7.Vista.For.LinuxVirtualBox.txt

Windows XP (IE 8)
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20131127/VirtualBox/IE8_WinXP/Linux/IE8.WinXP.For.LinuxVirtualBox.txt

Windows XP (IE 6)
wget -i https://az412801.vo.msecnd.net/vhd/VMBuild_20131127/VirtualBox/IE6_WinXP/Linux/IE6.WinXP.For.LinuxVirtualBox.txt

Free Windows Test Virtual Machines