Table of Contents
- My Proposal
- Community-Bonding period
- Results of my contribution
- My Google Summer of Code journey
Being interested in computer security, and being an opensource lover, I wanted to
participate in Google Summer of Code this year, after checking out the list of organizations,
I applied for Metasploit, because
Ruby is my main programming language, and because I
was very interested in contributing to a framework of this popularity.
You can find my proposal here.
Google Summer of Code project description here.
The idea I worked on was enhancing SQL injection support on the Metasploit Framework, SQL injections are vulnerabilities that have been around for a very long time, they are due to the lack of input sanitization, and can allow attackers to get access to sensitive data, some metasploit modules already implement SQL injection attacks, the problem for module writers is that many types of SQL injections require effort implementing, take for example time-based SQL injections, the module writer has to do some kind of binary search to leak bytes of data, or for example, cases where the results of the query are truncated of a given length, the module writer has to leak substrings, and concatenate them.
The aim of my project was to add a library that takes care of all these issues, making module writing easier in the case of SQL injections, I wanted the library to:
- Support not only HTTP, allow the user to handle connection with the server.
- Have module writers run SQL directly, whether the SQL injection is blind or not.
- Have methods for enumerating table names, column names, whatever is useful and commonly done when doing SQL injection.
On May 4th, I got the announcement of being accepted to take part on the program, during the month of May, I did my best to get familiar with the codebase, I tried fixing issues, understanding the structure of the libraries that had a similar usage to what I was willing to develop.
Some issues I fixed in the Community-Bonding period:
a module I also contributed during that period:
Thanks to h00die for helping out on this module.
Results of my contribution
After working three months on implementing the project on my proposal, here are the Pull-Requests I sent during the GSoC period:
- Support for MySQL/MariaDB injection, and rewrite of some module
- Specs/Unit tests for the library
- Support for SQLite injection, and module for a peplink balance vulnerability (CVE-2017-8835)
- Also was able to discover and fix a bug on cookie parsing, while testing my library
- Not merged yet:
- PR not sent yet
- To-do work
- Specs for the other implementations, should be using the shared examples, because of the similar structure between DBMS-specific classes.
- Perhaps modules exploiting Oracle Database/MSSQL injection vulnerabilities, and using the library (only testing modules have been provided).
Summary of the work done:
|DBMS||Regular SQLi||Time Blind||Boolean blind||Reading files||Writing to files|
||Yes (select .. into dumpfile)|
|SQLite||Yes||Yes (heavy queries)||Yes||No||Yes (attach database ..)|
|PostgreSQL||Yes||Yes||Yes||No||Yes (copy … to …)|
|Microsoft SQL Server||Yes||Yes (stacked queries)||Yes||No||No|
|Oracle Database||Yes, one row at a time||Yes (
- Methods for enumerating databases, table names, column names, users also were provided for each DBMS.
My Google Summer of Code journey
During the first month of the program, I started working on support for
MySQL/MariaDB, the design of the library is really something I am proud of,
from the beginning, I decided to let users provide a block that would query the server (so that the library does not have to handle networking stuff),
and to make use of inheritance, so that SQL Injection objects behave the same, even if technically, their exploitation is done differently, take for example
regular SQL injections, running:
# @sqli being a MySQLi::Common object @sqli.run_sql('select group_concat(table_name) from information_schema.tables')
would yield the given SQL query to the block, but if the object was a
MySQLi::BooleanBasedBlind, the same call would yield:
ascii(mid(cast((select group_concat(table_name) from information_schema.tables) as binary), 1, 1))&1<>0 ascii(mid(cast((select group_concat(table_name) from information_schema.tables) as binary), 1, 1))&2<>0 ascii(mid(cast((select group_concat(table_name) from information_schema.tables) as binary), 1, 1))&4<>0 ... ascii(mid(cast((select group_concat(table_name) from information_schema.tables) as binary), 1, 1))&128<>0 ascii(mid(cast((select group_concat(table_name) from information_schema.tables) as binary), 2, 1))&1<>0 ...
Each of these is sent to the block to be evaluated as a condition, and leak one bit of information, recall that in boolean-based blind SQLi, we only know whether the query returned a result or not, so we can only leak one bit with one query, and this is exactly what the library does.
Time-based blind support for
MySQL/MariaDB was done in a similar manner, except that these conditions were wrapped inside
if(..., sleep(delay), 1), so that
a delay is produced if the condition is true, the library also takes care of checking if the target slept more than sleepdelay or not, and all of this is transparent
to the user.
The library had to be refactored multiple times, to support more options, to avoid having repetitive code and make unit-testing possible, also, for printing verbose messages that
could be useful to the user, it was necessary to access the module
datastore (because printing methods had to access the
VERBOSE option which is set by the user),
accessing it wasn’t an easy task, because the whole library was a mixin (a module the user should include inside their metasploit module class), the mixin methods could access it,
but not the classes that belong to it, a factory pattern was necessary to get this working, basically, a
create_sqli method that the user calls, that instanciates one of the classes
and injects the datastore through its constructor.
My list of features to add kept growing during this period, and I implemented many of them (support for truncated queries, hex encoding strings, encoders and so on).
Also, during the first month, I re-wrote some SQL injection modules to make them use my library, it reduced a lot of their complexity, and made them more efficient:
- eyesofnetwork_autodiscovery_rce :
460LoC, running time divided by 2.
- openemr_sqli_dump :
151LoC, improved injection (now skips builtin tables and only retrieves userdata).
SQLite support and specs for the library
SQLite support was the second on my priority list, mostly because I always thought
SQLite was different because of its minimalism, different in a way that could make it hard for module writers to implement SQL injection attacks
when it is in-use, the part that was a bit more complex was time-based blind SQL injection, because
SQLite does not have a function like sleep, that causes a delay in the response, I had to use heavy queries, which are queries that
take time to be computed, luckily, there exists a function called
randomblob, which takes a size parameter, and returns random data of that size, the user specifies a delay in seconds, and I do some benchmarking in the library to
find the perfect randomblob parameter that would cause that delay, once found,
1000000 for example, my SQL injection would work like this:
# @sqli being a SQLitei::TimeBasedBlind object @sqli.run_sql("select group_concat(tbl_name,';') from sqlite_master where type='table'")
The library would yield conditions like this to the block, to be evaluated, and would measure timings, and return the data.
unicode(substr(cast((select group_concat(tbl_name,';') from sqlite_master where type='table') as blob), 1, 1))&1<>0 and randomblob(1000000) unicode(substr(cast((select group_concat(tbl_name,';') from sqlite_master where type='table') as blob), 1, 1))&2<>0 and randomblob(1000000) unicode(substr(cast((select group_concat(tbl_name,';') from sqlite_master where type='table') as blob), 1, 1))&4<>0 and randomblob(1000000) ... unicode(substr(cast((select group_concat(tbl_name,';') from sqlite_master where type='table') as blob), 1, 1))&128<>0 and randomblob(1000000) unicode(substr(cast((select group_concat(tbl_name,';') from sqlite_master where type='table') as blob), 2, 1))&1<>0 and randomblob(1000000) ...
If a bit is 1, it should get to the second part of the
and, which should take some time to run, this is how the library knows the outcome of the condition.
I also had to refactor code when adding support for
SQLite, because I noticed there was a lot of code shared between DBMS-specific implementations, on blind queries mostly, so I added a
Utils mixin that acts like an interface that
is implemented in all the classes that make use of the shared code.
For testing I wrote:
PostgreSQL, and other database-management systems
PostgreSQL support was easy to add, because of how popular the DBMS is, it was easy to find vulnerable software to test it, to get it running in a testing environment and so on, I wrote a module for CVE-2019-13373, which is a vulnerability
found on some versions of D-Link Central WiFi Manager CWM(100), and a test module for vulnerable code I wrote myself.
Oracle Database was added in the last month of the program, I provided test modules for each, but no exploit on real vulnerabilities, might implement some in the future.
Working with rapid7 members on the Metasploit Framework was really a great experience for me, I learned a lot of things, would like to thank my mentor, Jeffrey Martin (
Op3n4M3), as well as other contributors who helped me out,
dwelch-r7 to name a few.
I learned a lot, on advanced git topics, on software testing and programming/software engineering best practices.
I will keep contributing to the project, hopefully getting all of the library merged, and getting some other issues I had noticed in the codebase fixed.