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
- Download latest release at https://github.com/chaitin/sqlchop/releases
- Make
- Run
python2 test.py
orLD_LIBRARY_PATH=./ ./sqlchop_test
- Enjoy!
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, returnFalse
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
urlpath
: string, the urlpath of web requestbody
: string, the http body of POST/PUT requestcookie
: string, the cookie content of web requestraw
: string, other general field that needs general decoding.
- Parameter 2: detail, if detail is
True
, detailed payload list will be returned, ifFalse
, only result will be returned, which runs faster. - Return: an object contains
result
andpayloads
result
: int, positive value indicates the web request contains sql injection payloadpayloads
: list of objects containingkey
,score
,value
andsource
key
: string, reservedsource
: string, shows where this payload is embed in original web request and how the payload is decodedvalue
: decoded sqli payloadscore
: the score of the decoded sqli payload
>>> 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):
...