SQLChop - SQL Injection Detection Engine

Tuesday, August 25, 2015


SQLChop is a novel SQL injection detection engine built on top of SQL tokenizing and syntax analysis. Web input (URLPath, body, cookie, etc.) will be first decoded to the raw payloads that web app accepts, then syntactical analysis will be performed on payload to classify result. The algorithm behind SQLChop is based on compiler knowledge and automata theory, and runs at a time complexity of O(N).

Documentation

http://sqlchop.chaitin.com/doc.html

Dependencies

The SQLChop alpha testing release includes the c++ header and shared object, a python library, and also some sample usages. The release has been tested on most linux distributions.
If using python, you need to install protobuf-python, e.g.:
$ sudo pip install protobuf
If using c++, you need to install protobuf, protobuf-compiler and protobuf-devel, e.g.:
$ sudo yum install protobuf protobuf-compiler protobuf-devel

Build

SQLChop Python API

The current alpha testing release is provided as a python library. C++ headers and examples will be released soon.
The following APIs are the main interfaces SQLChop export.

is_sqli

Given a raw payload, determine whether the payload is an SQL injection payload.
  • Parameter: string
  • Return value: bool, return True for SQLi payload, return False for normal case.
>>> from sqlchop import SQLChop
>>> detector = SQLChop()
>>> detector.is_sqli('SELECT 1 From users')
True
>>> detector.is_sqli("' or '1'='1")
True
>>> detector.is_sqli('select the best student from classes as the student union representative')
False
>>> detector.is_sqli('''(select(0)from(select(sleep(0)))v)/*'+(select(0)from(select(sleep(12)))v)+'"+(select(0)from(select(sleep(0)))v)+"*/''')
True

classify

Given a web application input, classify API will decode the input and find possible SQL injection payload inside. If SQLi payload found, payloads will be listed.
  • Parameter 1: object with following keys
    1. urlpath: string, the urlpath of web request
    2. body: string, the http body of POST/PUT request
    3. cookie: string, the cookie content of web request
    4. raw: string, other general field that needs general decoding.
  • Parameter 2: detail, if detail is True, detailed payload list will be returned, if False, only result will be returned, which runs faster.
  • Return: an object contains result and payloads
    1. result: int, positive value indicates the web request contains sql injection payload
    2. payloads: list of objects containing key, score, value and source
      • key: string, reserved
      • source: string, shows where this payload is embed in original web request and how the payload is decoded
      • value: decoded sqli payload
      • score: the score of the decoded sqli payload
Examples here:
>>> from sqlchop import SQLChop
>>> detector = SQLChop()
>>> detector.classify({'urlpath': '/tag/sr/news.asp?d=LTElMjBhbmQlMjAxPTIlMjB1bmlvbiUyMHNlbGVjdCUyMDEsMiwzLGNocigxMDYpLDUsNiw3LDgsOSwxMCwxMSwxMiUyMGZyb20lMjBhZG1pbg==' }, True)
>>> 
{
    'payloads': [{
        'key': '',
        'score': 4.070000171661377,
        'source': 'urlpath: querystring_decode b64decode url_decode ',
        'value': '-1 and 1=2 union select 1,2,3,chr(106),5,6,7,8,9,10,11,12 from admin'
    }],
    'result': 1
}

>>> detector.classify({'body': 'opt=saveedit&arrs1[]=83&arrs1[]=69&arrs1[]=76&arrs1[]=69&arrs1[]=67&arrs1[]=84&arrs1[]=32&arrs1[]=42&arrs1[]=32&arrs1[]=70&arrs1[]=114&arrs1[]=111&arrs1[]=109&arrs1[]=32&arrs1[]=84&arrs1[]=97&arrs1[]=98&arrs1[]=108&arrs1[]=101&arrs1[]=32&arrs1[]=87&arrs1[]=72&arrs1[]=69&arrs1[]=82&arrs1[]=69&arrs1[]=32&arrs1[]=78&arrs1[]=97&arrs1[]=109&arrs1[]=101&arrs1[]=61&arrs1[]=39&arrs1[]=83&arrs1[]=81&arrs1[]=76&arrs1[]=32&arrs1[]=105&arrs1[]=110&arrs1[]=106&arrs1[]=101&arrs1[]=99&arrs1[]=116&arrs1[]=39&arrs1[]=32&arrs1[]=97&arrs1[]=110&arrs1[]=100&arrs1[]=32&arrs1[]=80&arrs1[]=97&arrs1[]=115&arrs1[]=115&arrs1[]=119&arrs1[]=111&arrs1[]=114&arrs1[]=100&arrs1[]=61&arrs1[]=39&arrs1[]=39&arrs1[]=32&arrs1[]=97&arrs1[]=110&arrs1[]=100&arrs1[]=32&arrs1[]=67&arrs1[]=111&arrs1[]=114&arrs1[]=112&arrs1[]=61&arrs1[]=39&arrs1[]=39&arrs1[]=32&arrs1[]=111&arrs1[]=114&arrs1[]=32&arrs1[]=49&arrs1[]=61&arrs1[]=40&arrs1[]=83&arrs1[]=69&arrs1[]=76&arrs1[]=69&arrs1[]=67&arrs1[]=84&arrs1[]=32&arrs1[]=64&arrs1[]=64&arrs1[]=86&arrs1[]=69&arrs1[]=82&arrs1[]=83&arrs1[]=73&arrs1[]=79&arrs1[]=78&arrs1[]=41&arrs1[]=45&arrs1[]=45&arrs1[]=32&arrs1[]=39'}, True)
>>>
{
    'payloads': [{
        'key': '',
        'score': 3.9800000190734863,
        'source': 'body: querystring_decode ',
        'value': "SELECT * From Table WHERE Name='SQL inject' and Password='' and Corp='' or 1=(SELECT @@VERSION)-- '"
    }, {
        'key': '',
        'score': 2.0899999141693115,
        'source': 'body: querystring_decode ',
        'value': "'SQL inject' and Password"
    }, {
        'key': '',
        'score': 2.180000066757202,
        'source': 'body: querystring_decode ',
        'value': "(SELECT @@VERSION)-- '"
    }, {
        'key': '',
        'score': 0.0,
        'source': 'body: querystring_decode ',
        'value': 'saveedit'
    }],
    'result': 1
}

Customization

The is_sqli API (in sqlchop.py) detects SQLi using score 2.1 as threshold, you can adjust this threshold according to your usage scenario.
    def is_sqli(self, payload):
        ret = self.score_sqli(payload)
        return ret > 2.1  # here you can modify and test this threshold

    def classify(self, request, detail=False):
        ...




Subscribe via e-mail for updates!