Saturday, May 09, 2026 AM03:21:24 HKT
This commit is contained in:
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# automated basic git tagging
|
||||
# 1) edit the version number in
|
||||
# c/libinjection_sqli.c
|
||||
# pyton/setup.py
|
||||
# 2) git add and commit
|
||||
# 3) run this
|
||||
# 4) done!
|
||||
#
|
||||
|
||||
# get tag number
|
||||
TAG=`grep 'LIBINJECTION_VERSION' ../c/libinjection_sqli.c | awk -F '"' '{print $2}' | tr -d '[[:space:]]'`
|
||||
|
||||
TAG="v${TAG}"
|
||||
|
||||
echo "TAG = ${TAG}"
|
||||
echo "Tagging locally"
|
||||
git tag -a "${TAG}" -m ${TAG}
|
||||
echo "Sharing..."
|
||||
git push origin "${TAG}"
|
||||
|
||||
git tag
|
||||
echo "DONE"
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
LIBINJECTION
|
||||
==========================
|
||||
|
||||
Libinjection is a small C library to detect SQLi attacks in user input with the following goals:
|
||||
|
||||
* Open. Source code is on [GitHub](https://github.com/client9/libinjection/).
|
||||
* Low _false-positives_. When there are high false positives, people tend to turn off any WAF or protection.
|
||||
* Excellent detection of SQLi.
|
||||
* High performance (currently [over 500,000 TPS](https://libinjection.client9.com/cicada/artifacts/libinjection/libinjection-speed/latest/console.txt))
|
||||
* Easy to test and QA
|
||||
* Easy to integrate and extend
|
||||
|
||||
### [Try it now](/diagnostics)
|
||||
|
||||
### Easy to integrate
|
||||
|
||||
* Standard C code, and compiles as C99 and C++, with bindings to
|
||||
* [Python](https://github.com/client9/libinjection/wiki/doc-sqli-python)
|
||||
* [PHP](https://github.com/client9/libinjection/wiki/doc-sqli-php)
|
||||
* [Lua](https://github.com/client9/libinjection/tree/master/lua)
|
||||
* Small - about [1500 lines of code](https://libinjection.client9.com/cicada/artifacts/libinjection/libinjection-loc/latest/console.txt) in three files
|
||||
* Compiles on Linux/Unix/BSD, Mac and Windows
|
||||
* No threads used and thread safe
|
||||
* No recursion
|
||||
* No (heap) memory allocation
|
||||
* No extenal library dependencies
|
||||
* [400+ unit tests](https://github.com/client9/libinjection/tree/master/tests)
|
||||
* [98% code coverage](https://libinjection.client9.com/cicada/artifacts/libinjection/libinjection-coverage-unittest/latest/lcov-html/libinjection/src/index.html)
|
||||
* [BSD License](https://github.com/client9/libinjection/blob/master/COPYING)
|
||||
|
||||
Third-Party Ports
|
||||
---------------------
|
||||
|
||||
* [java](https://github.com/Kanatoko/libinjection-Java)
|
||||
* At least two .NET ports exists
|
||||
* Another python wrapper
|
||||
|
||||
Applications
|
||||
---------------------
|
||||
|
||||
* [ModSecurity](http://www.modsecurity.org/) - since 2.7.4 release
|
||||
* [IronBee](https://www.ironbee.com) - since May 2013
|
||||
* Proprietary Honeypot
|
||||
* Proprietary WAF, Russia
|
||||
* Proprietary WAF, Japan
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 944 KiB |
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import re
|
||||
import libinjection
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
logre = re.compile(r' /diagnostics\?([^ ]+) HTTP')
|
||||
|
||||
notsqli = set([
|
||||
'1ov',
|
||||
'UEvEv',
|
||||
'v',
|
||||
'Uv',
|
||||
'Uv,',
|
||||
'UoEvE',
|
||||
'1v',
|
||||
'sov',
|
||||
'1nn',
|
||||
'UonnE',
|
||||
'no1',
|
||||
'Evk',
|
||||
'E1k',
|
||||
'E11k',
|
||||
'Ek',
|
||||
'Uv,Ev',
|
||||
'UvEvk',
|
||||
'UvEv,',
|
||||
'Uvon'
|
||||
])
|
||||
|
||||
def doline(logline):
|
||||
"""
|
||||
...GET /diagnostics?id=%22union+select HTTP/1.1
|
||||
"""
|
||||
mo = logre.search(logline)
|
||||
if not mo:
|
||||
return
|
||||
|
||||
sqli= False
|
||||
fp = None
|
||||
for key, val in urlparse.parse_qsl(mo.group(1)):
|
||||
val = urllib.unquote(val)
|
||||
extra = {}
|
||||
argsqli = libinjection.detectsqli(val, extra)
|
||||
if argsqli:
|
||||
fp = extra['fingerprint']
|
||||
print urllib.quote(val)
|
||||
sqli = sqli or argsqli
|
||||
|
||||
if False: # and not sqli:
|
||||
#print "\n---"
|
||||
#print mo.group(1)
|
||||
for key, val in urlparse.parse_qsl(mo.group(1)):
|
||||
val = urllib.unquote(val)
|
||||
extra = {}
|
||||
argsqli = libinjection.detectsqli(val, extra)
|
||||
if not argsqli and extra['fingerprint'] not in notsqli:
|
||||
print "NO", extra['fingerprint'], mo.group(1)
|
||||
print " ", val
|
||||
|
||||
if __name__ == '__main__':
|
||||
for line in sys.stdin:
|
||||
doline(line)
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import sys
|
||||
from urlparse import *
|
||||
import urllib
|
||||
import libinjection
|
||||
|
||||
from tornado import template
|
||||
from tornado.escape import *
|
||||
|
||||
import re
|
||||
import calendar
|
||||
|
||||
months = {
|
||||
'Jan':'01',
|
||||
'Feb':'02',
|
||||
'Mar':'03',
|
||||
'Apr':'04',
|
||||
'May':'05',
|
||||
'Jun':'06',
|
||||
'Jul':'07',
|
||||
'Aug':'08',
|
||||
'Sep':'09',
|
||||
'Oct':'10',
|
||||
'Nov':'11',
|
||||
'Dec':'12'
|
||||
}
|
||||
|
||||
# "time_iso8601":"2013-08-04T03:51:18+00:00"
|
||||
def parse_date(datestr):
|
||||
elems = (
|
||||
datestr[7:11],
|
||||
months[datestr[3:6]],
|
||||
datestr[0:2],
|
||||
datestr[12:14],
|
||||
datestr[15:17],
|
||||
datestr[18:20],
|
||||
)
|
||||
|
||||
return ( "{0}-{1}-{2}T{3}:{4}:{5}+00:00".format(*elems), calendar.timegm( [ int(i) for i in elems] ) )
|
||||
|
||||
|
||||
apachelogre = re.compile(r'^(\S*) (\S*) (\S*) \[([^\]]+)\] \"([^"\\]*(?:\\.[^"\\]*)*)\" (\S*) (\S*) \"([^"\\]*(?:\\.[^"\\]*)*)\" \"([^"]*)\" \"([^"]*)\"')
|
||||
|
||||
def parse_apache(line):
|
||||
mo = apachelogre.match(line)
|
||||
if not mo:
|
||||
return None
|
||||
(time_iso, timestamp) = parse_date(mo.group(4))
|
||||
try:
|
||||
(method, uri, protocol) = mo.group(5).split(' ', 2)
|
||||
except ValueError:
|
||||
(method, uri, protocol) = ('-', '-', '-')
|
||||
data = {
|
||||
'remote_addr': mo.group(1),
|
||||
'time_iso8601': time_iso,
|
||||
'timestamp' : timestamp,
|
||||
'request_protocol': protocol,
|
||||
'request_method': method,
|
||||
'request_uri': uri,
|
||||
'request_length': '',
|
||||
'request_time': '',
|
||||
'status': mo.group(6),
|
||||
'bytes_sent': '',
|
||||
'body_bytes-sent': int(mo.group(7)),
|
||||
'http_referrer': mo.group(8),
|
||||
'http_user_agent': mo.group(9),
|
||||
'ssl_cipher': '',
|
||||
'ssl_protocol': ''
|
||||
}
|
||||
return data
|
||||
|
||||
# http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
|
||||
def chunks(l, n):
|
||||
"""
|
||||
Yield successive n-sized chunks from l.
|
||||
"""
|
||||
for i in xrange(0, len(l), n):
|
||||
yield l[i:i+n]
|
||||
|
||||
def breakify(s):
|
||||
output = ""
|
||||
for c in chunks(s, 40):
|
||||
output += c
|
||||
if ' ' not in c:
|
||||
output += ' '
|
||||
return output
|
||||
|
||||
def doline(line):
|
||||
|
||||
line = line.replace("\\x", "%").strip()
|
||||
try:
|
||||
data = json.loads(line)
|
||||
except ValueError, e:
|
||||
data = parse_apache(line)
|
||||
|
||||
if data is None:
|
||||
sys.stderr.write("BAD LINE: {0}\n".format(line))
|
||||
return None
|
||||
|
||||
if not data.get('request_uri','').startswith("/diagnostics"):
|
||||
return None
|
||||
|
||||
urlparts = urlparse(data['request_uri'])
|
||||
if len(urlparts.query) == 0:
|
||||
return None
|
||||
|
||||
qsl = [ x.split('=', 1) for x in urlparts.query.split('&') ]
|
||||
|
||||
target = None
|
||||
for k,v in qsl:
|
||||
if k == 'id':
|
||||
target = v
|
||||
break
|
||||
|
||||
if target is None:
|
||||
#print "no 'id'"
|
||||
return None
|
||||
|
||||
# part one, normal decode
|
||||
target = urllib.unquote_plus(target)
|
||||
|
||||
# do it again, but preserve '+'
|
||||
target = urllib.unquote(target)
|
||||
|
||||
sstate = libinjection.sqli_state()
|
||||
# BAD the string created by target.encode is stored in
|
||||
# sstate but not reference counted, so it can get
|
||||
# deleted by python
|
||||
# libinjection.sqli_init(sstate, target.encode('utf-8'), 0)
|
||||
|
||||
# instead make a temporary var in python
|
||||
# with the same lifetime as sstate (above)
|
||||
try:
|
||||
targetutf8 = target.encode('utf-8')
|
||||
#targetutf8 = target
|
||||
except UnicodeDecodeError, e:
|
||||
targetutf8 = target
|
||||
#if type(target) == str:
|
||||
# sys.stderr.write("Target is a string\n")
|
||||
#if type(target) == unicode:
|
||||
# sys.stderr.write("Target is unicde\n")
|
||||
#sys.stderr.write("OOps: {0}\n".format(e))
|
||||
#sys.stderr.write("Encode error: {0}\n".format(target))
|
||||
|
||||
|
||||
try:
|
||||
libinjection.sqli_init(sstate, targetutf8, 0)
|
||||
except TypeError:
|
||||
sys.stderr.write("fail in decode: {0}".format(targetutf8))
|
||||
if type(target) == str:
|
||||
sys.stderr.write("Target is a string\n")
|
||||
if type(target) == unicode:
|
||||
sys.stderr.write("Target is unicde\n")
|
||||
return None
|
||||
|
||||
sqli = bool(libinjection.is_sqli(sstate))
|
||||
|
||||
return (target, sqli, sstate.fingerprint, data['remote_addr'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
s = """
|
||||
174.7.27.149 - - [29/Jul/2013:01:30:19 +0000] "GET /diagnostics?id=x|x||1&type=fingerprints HTTP/1.1" 200 1327 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36" "-"
|
||||
"""
|
||||
s = """
|
||||
{"timestamp":1371091563,"remote_ip":"219.110.171.2","request":"/diagnostics?id=1+UNION+ALL+SELECT+1<<<&type=fingerprints","method":"GET","status":200,"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/536.30.1 (KHTML, like Gecko) Version/6.0.5 Safari/536.30.1","referrer":"https://libinjection.client9.com/diagnostics","duration_usec":160518 }
|
||||
{"timestamp":1371091563,"remote_ip":"219.110.171.2","request":"/diagnostics?id=2+UNION+ALL+SELECT+1<<<&type=fingerprints","method":"GET","status":200,"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/536.30.1 (KHTML, like Gecko) Version/6.0.5 Safari/536.30.1","referrer":"https://libinjection.client9.com/diagnostics","duration_usec":160518 }
|
||||
"""
|
||||
if len(sys.argv) == 2:
|
||||
fh = open(sys.argv[1], 'r')
|
||||
else:
|
||||
fh = sys.stdin
|
||||
|
||||
targets = set()
|
||||
table = []
|
||||
for line in fh:
|
||||
parts = doline(line.strip())
|
||||
if parts is None:
|
||||
continue
|
||||
|
||||
# help it render in HTML
|
||||
if parts[0] in targets:
|
||||
continue
|
||||
else:
|
||||
targets.add(parts[0])
|
||||
|
||||
# add link
|
||||
# add form that might render ok in HTML
|
||||
# is sqli
|
||||
# fingerprint
|
||||
table.append( (
|
||||
"/diagnostics?id=" + url_escape(parts[0]),
|
||||
breakify(parts[0].replace(',', ', ').replace('/*', ' /*')),
|
||||
parts[1],
|
||||
parts[2],
|
||||
parts[3]
|
||||
)
|
||||
)
|
||||
|
||||
table = reversed(table)
|
||||
|
||||
loader = template.Loader(".")
|
||||
|
||||
txt = loader.load("logtable.html").generate(
|
||||
table=table,
|
||||
now = str(datetime.datetime.now()),
|
||||
ssl_protocol='',
|
||||
ssl_cipher=''
|
||||
)
|
||||
|
||||
print txt
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
fname=$1
|
||||
|
||||
echo '{% extends "base.html" %}'
|
||||
echo '{% block body %}'
|
||||
#github-markup $fname
|
||||
curl -H 'Content-Type: text/x-markdown' --data-binary @$fname https://api.github.com/markdown/raw
|
||||
echo '{% end %}'
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Sync ModSecurity / libinjection
|
||||
#
|
||||
|
||||
# explode on error
|
||||
set -e
|
||||
|
||||
#
|
||||
# CLONE LIBINJECTION
|
||||
#
|
||||
if [ ! -d libinjection ]; then
|
||||
git clone https://github.com/client9/libinjection.git
|
||||
else
|
||||
(cd libinjection; git pull)
|
||||
fi
|
||||
|
||||
pwd
|
||||
|
||||
#
|
||||
# CLONE MODSECURITY
|
||||
#
|
||||
if [ ! -d ModSecurity ]; then
|
||||
git clone https://github.com/client9/ModSecurity.git
|
||||
else
|
||||
( cd ModSecurity; git pull )
|
||||
fi
|
||||
pwd
|
||||
|
||||
#
|
||||
# Use right branch
|
||||
#
|
||||
(cd ModSecurity; git checkout remotes/trunk )
|
||||
|
||||
pwd
|
||||
|
||||
#
|
||||
# COPY IN NEW LIBINJECTION
|
||||
#
|
||||
cp libinjection/COPYING.txt ModSecurity/apache2/
|
||||
cp libinjection/c/libinjection.h ModSecurity/apache2/libinjection
|
||||
cp libinjection/c/libinjection_sqli.c ModSecurity/apache2/libinjection
|
||||
cp libinjection/c/libinjection_sqli.h ModSecurity/apache2/libinjection
|
||||
cp libinjection/c/libinjection_sqli_data.h ModSecurity/apache2/libinjection
|
||||
|
||||
|
||||
#
|
||||
# REGENERATE / BUILD
|
||||
#
|
||||
cd ModSecurity
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
make distclean
|
||||
|
||||
#
|
||||
# ADD NEW BITS
|
||||
#
|
||||
git add apache2/libinjection/COPYING.txt
|
||||
git add apache2/libinjection/libinjection.h
|
||||
git add apache2/libinjection/libinjection_sqli.h
|
||||
git add apache2/libinjection/libinjection_sqli.c
|
||||
git add apache2/libinjection/libinjection_sqli_data.h
|
||||
|
||||
# this file seems to get modified, reset just to be safe
|
||||
git checkout standalone/Makefile.in
|
||||
|
||||
git commit -m 'libinjection sync'
|
||||
|
||||
#
|
||||
# PUSH TO SPECIAL BRANCH
|
||||
#
|
||||
echo "pushing to remotes/trunk"
|
||||
git push origin remotes/trunk
|
||||
|
||||
#
|
||||
# PROFIT
|
||||
#
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
mysql_ops = (
|
||||
'AND',
|
||||
'&&',
|
||||
'=',
|
||||
'&',
|
||||
'|',
|
||||
'^',
|
||||
'DIV',
|
||||
'/',
|
||||
'<=>',
|
||||
'>=',
|
||||
'>',
|
||||
'<<',
|
||||
'<=',
|
||||
'<',
|
||||
'LIKE',
|
||||
'-',
|
||||
'%',
|
||||
'MOD',
|
||||
'!=',
|
||||
'<>',
|
||||
'NOT LIKE',
|
||||
'NOT REGEXP',
|
||||
'OR',
|
||||
'||',
|
||||
'+',
|
||||
'REGEXP',
|
||||
'>>',
|
||||
'RLIKE',
|
||||
'NOT RLIKE',
|
||||
'SOUNDS LIKE',
|
||||
'*',
|
||||
'XOR'
|
||||
)
|
||||
|
||||
print '# mysql implicit conversions tests'
|
||||
|
||||
for op in mysql_ops:
|
||||
if op == '+':
|
||||
op = '%2B'
|
||||
|
||||
print "A' {0} 'B".format(op)
|
||||
print "A '{0}' B".format(op)
|
||||
print "'{0}'".format(op)
|
||||
print "' {0} '".format(op)
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# A 'nullserver' that accepts input and generates output
|
||||
# to trick sqlmap into thinking it's a database-driven site
|
||||
#
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import urllib
|
||||
|
||||
import tornado.httpserver
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import libinjection
|
||||
|
||||
class ShutdownHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
global fd
|
||||
fd.close()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class CountHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
global count
|
||||
self.write(str(count) + "\n")
|
||||
|
||||
def boring(arg):
|
||||
if arg == '':
|
||||
return True
|
||||
|
||||
if arg == 'foo':
|
||||
return True
|
||||
|
||||
if arg == 'NULL':
|
||||
return True
|
||||
|
||||
try:
|
||||
float(arg)
|
||||
return True
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return False;
|
||||
|
||||
class NullHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self):
|
||||
global fd
|
||||
global count
|
||||
params = self.request.arguments.get('id', [])
|
||||
sqli = False
|
||||
|
||||
if len(params) == 0 or (len(params) == 1 and boring(params[0])):
|
||||
# if no args, or a single value with uninteresting input
|
||||
# then just exit
|
||||
self.write("<html><head><title>safe</title></head><body></body></html>")
|
||||
return
|
||||
|
||||
for arg in params:
|
||||
sqli = libinjection.detectsqli(arg)
|
||||
if sqli:
|
||||
break
|
||||
|
||||
# we didn't detect it :-(
|
||||
if not sqli:
|
||||
count += 1
|
||||
args = [ arg.strip() for arg in params ]
|
||||
#fd.write(' | '.join(args) + "\n")
|
||||
for arg in args:
|
||||
extra = {}
|
||||
sqli = libinjection.detectsqli(arg, extra)
|
||||
logging.error("\t" + arg + "\t" + str(sqli) + "\t" + extra['fingerprint'] + "\n")
|
||||
#for arg in param:
|
||||
# fd.write(arg + "\n")
|
||||
# #fd.write(urllib.quote_plus(arg) + "\n")
|
||||
self.set_status(500)
|
||||
self.write("<html><head><title>safe</title></head><body></body></html>")
|
||||
else:
|
||||
self.write("<html><head><title>sqli</title></head><body></body></html>")
|
||||
|
||||
import os
|
||||
settings = {
|
||||
"static_path": os.path.join(os.path.dirname(__file__), "static"),
|
||||
"cookie_secret": "yo mama sayz=",
|
||||
"xsrf_cookies": True,
|
||||
"gzip": False
|
||||
}
|
||||
|
||||
application = tornado.web.Application([
|
||||
(r"/null", NullHandler),
|
||||
(r"/shutdown", ShutdownHandler),
|
||||
(r"/count", CountHandler)
|
||||
], **settings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
global fd
|
||||
global count
|
||||
|
||||
count = 0
|
||||
|
||||
fd = open('./sqlmap-false-negatives.txt', 'w')
|
||||
|
||||
import tornado.options
|
||||
#tornado.options.parse_config_file("/etc/server.conf")
|
||||
tornado.options.parse_command_line()
|
||||
|
||||
http_server = tornado.httpserver.HTTPServer(application)
|
||||
http_server.listen(8888)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Starts a bogus webserver that logs all input
|
||||
# Then runs sqlmap
|
||||
#
|
||||
|
||||
./nullserver.py --logging=none &
|
||||
|
||||
if [ ! -d "sqlmap" ]; then
|
||||
git clone https://github.com/sqlmapproject/sqlmap.git
|
||||
else
|
||||
(cd sqlmap; git pull)
|
||||
fi
|
||||
|
||||
SQLMAP=./sqlmap/sqlmap.py
|
||||
URL=http://127.0.0.1:8888
|
||||
|
||||
HPP=
|
||||
${SQLMAP} ${HPP} -v 0 --titles -p id --level=5 --risk=3 --url=${URL}/null?id=1
|
||||
${SQLMAP} ${HPP} -v 0 --titles -p id --level=5 --risk=3 --url=${URL}/null?id=1234.5
|
||||
${SQLMAP} ${HPP} -v 0 --titles -p id --level=5 --risk=3 --url=${URL}/null?id=foo
|
||||
|
||||
HPP=--hpp
|
||||
${SQLMAP} ${HPP} -v 0 --titles -p id --level=5 --risk=3 --url=${URL}/null?id=1
|
||||
${SQLMAP} ${HPP} -v 0 --titles -p id --level=5 --risk=3 --url=${URL}/null?id=1234.5
|
||||
${SQLMAP} ${HPP} -v 0 --titles -p id --level=5 --risk=3 --url=${URL}/null?id=foo
|
||||
|
||||
curl -o /dev/null ${URL}/shutdown
|
||||
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
import datetime
|
||||
import sys
|
||||
import logging
|
||||
import urllib
|
||||
import urlparse
|
||||
try:
|
||||
import libinjection
|
||||
except:
|
||||
pass
|
||||
|
||||
from tornado import template
|
||||
import tornado.httpserver
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import tornado.wsgi
|
||||
import tornado.escape
|
||||
import tornado.options
|
||||
|
||||
def breakapart(s):
|
||||
""" attempts to add spaces in a SQLi so it renders nicely on the webpage
|
||||
"""
|
||||
return s.replace(',', ', ').replace('/*',' /*')
|
||||
|
||||
# http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
|
||||
def chunks(l, n):
|
||||
""" Yield successive n-sized chunks from l.
|
||||
"""
|
||||
for i in xrange(0, len(l), n):
|
||||
yield l[i:i+n]
|
||||
|
||||
def breakify(s):
|
||||
output = ""
|
||||
for c in chunks(s, 20):
|
||||
output += c
|
||||
if ' ' not in c:
|
||||
output += ' '
|
||||
return output
|
||||
|
||||
def print_token_string(tok):
|
||||
"""
|
||||
returns the value of token, handling opening and closing quote characters
|
||||
"""
|
||||
out = ''
|
||||
if tok.str_open != '\0':
|
||||
out += tok.str_open
|
||||
out += tok.val
|
||||
if tok.str_close != '\0':
|
||||
out += tok.str_close
|
||||
return out
|
||||
|
||||
def print_token(tok):
|
||||
"""
|
||||
prints a token for use in unit testing
|
||||
"""
|
||||
out = ''
|
||||
if tok.type == 's':
|
||||
out += print_token_string(tok)
|
||||
elif tok.type == 'v':
|
||||
vc = tok.count;
|
||||
if vc == 1:
|
||||
out += '@'
|
||||
elif vc == 2:
|
||||
out += '@@'
|
||||
out += print_token_string(tok)
|
||||
else:
|
||||
out += tok.val
|
||||
return (tok.type, out)
|
||||
|
||||
def alltokens(val, flags):
|
||||
|
||||
if flags & libinjection.FLAG_QUOTE_SINGLE:
|
||||
contextstr = 'single'
|
||||
elif flags & libinjection.FLAG_QUOTE_DOUBLE:
|
||||
contextstr = 'double'
|
||||
else:
|
||||
contextstr = 'none'
|
||||
|
||||
if flags & libinjection.FLAG_SQL_ANSI:
|
||||
commentstr = 'ansi'
|
||||
elif flags & libinjection.FLAG_SQL_MYSQL:
|
||||
commentstr = 'mysql'
|
||||
else:
|
||||
raise RuntimeException("bad quote context")
|
||||
|
||||
parse = {
|
||||
'comment': commentstr,
|
||||
'quote': contextstr
|
||||
}
|
||||
args = []
|
||||
sqlstate = libinjection.sqli_state()
|
||||
libinjection.sqli_init(sqlstate, val, flags)
|
||||
count = 0
|
||||
while count < 25:
|
||||
count += 1
|
||||
ok = libinjection.sqli_tokenize(sqlstate)
|
||||
if ok == 0:
|
||||
break
|
||||
args.append(print_token(sqlstate.current))
|
||||
|
||||
|
||||
parse['tokens'] = args
|
||||
|
||||
args = []
|
||||
fingerprint = libinjection.sqli_fingerprint(sqlstate, flags)
|
||||
for i in range(len(sqlstate.fingerprint)):
|
||||
args.append(print_token(libinjection.sqli_get_token(sqlstate,i)))
|
||||
parse['folds'] = args
|
||||
parse['sqli'] = bool(libinjection.sqli_blacklist(sqlstate) and libinjection.sqli_not_whitelist(sqlstate))
|
||||
parse['fingerprint'] = fingerprint
|
||||
# todo add stats
|
||||
|
||||
return parse
|
||||
|
||||
class PageHandler(tornado.web.RequestHandler):
|
||||
def get(self, pagename):
|
||||
if pagename == '':
|
||||
pagename = 'home'
|
||||
|
||||
self.add_header('X-Content-Type-Options', 'nosniff')
|
||||
self.add_header('X-XSS-Protection', '0')
|
||||
|
||||
self.render(
|
||||
pagename + '.html',
|
||||
title = pagename.replace('-',' '),
|
||||
ssl_protocol=self.request.headers.get('X-SSL-Protocol', ''),
|
||||
ssl_cipher=self.request.headers.get('X-SSL-Cipher', '')
|
||||
)
|
||||
|
||||
class XssTestHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
settings = self.application.settings
|
||||
|
||||
ldr = template.Loader(".")
|
||||
|
||||
args = ['', '', '', '', '', '', '', '', '', '']
|
||||
|
||||
qsl = [ x.split('=', 1) for x in self.request.query.split('&') ]
|
||||
for kv in qsl:
|
||||
print kv
|
||||
try:
|
||||
index = int(kv[0])
|
||||
val = tornado.escape.url_unescape(kv[1])
|
||||
print "XXX", index, val
|
||||
args[index] = val
|
||||
except Exception,e:
|
||||
print e
|
||||
|
||||
self.add_header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
self.add_header('Pragma', 'no-cache')
|
||||
self.add_header('Expires', '0')
|
||||
self.add_header('X-Content-Type-Options', 'nosniff')
|
||||
self.add_header('X-XSS-Protection', '0')
|
||||
|
||||
self.write(ldr.load('xsstest.html').generate(args=args))
|
||||
|
||||
class DaysSinceHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
lastevasion = datetime.date(2013, 9, 12)
|
||||
today = datetime.date.today()
|
||||
daynum = (today - lastevasion).days
|
||||
if daynum < 10:
|
||||
days = "00" + str(daynum)
|
||||
elif daynum < 100:
|
||||
days = "0" + str(daynum)
|
||||
else:
|
||||
days = str(daynum)
|
||||
|
||||
self.render(
|
||||
"days-since-last-bypass.html",
|
||||
title='libinjection: Days Since Last Bypass',
|
||||
days=days,
|
||||
ssl_protocol=self.request.headers.get('X-SSL-Protocol', ''),
|
||||
ssl_cipher=self.request.headers.get('X-SSL-Cipher', '')
|
||||
)
|
||||
|
||||
class NullHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
arg = self.request.arguments.get('type', [])
|
||||
if len(arg) > 0 and arg[0] == 'tokens':
|
||||
return self.get_tokens()
|
||||
else:
|
||||
return self.get_fingerprints()
|
||||
|
||||
def get_tokens(self):
|
||||
ids = self.request.arguments.get('id', [])
|
||||
|
||||
if len(ids) == 1:
|
||||
formvalue = ids[0]
|
||||
else:
|
||||
formvalue = ''
|
||||
|
||||
val = urllib.unquote(formvalue)
|
||||
parsed = []
|
||||
parsed.append(alltokens(val, libinjection.FLAG_QUOTE_NONE | libinjection.FLAG_SQL_ANSI))
|
||||
parsed.append(alltokens(val, libinjection.FLAG_QUOTE_NONE | libinjection.FLAG_SQL_MYSQL))
|
||||
parsed.append(alltokens(val, libinjection.FLAG_QUOTE_SINGLE | libinjection.FLAG_SQL_ANSI))
|
||||
parsed.append(alltokens(val, libinjection.FLAG_QUOTE_SINGLE | libinjection.FLAG_SQL_MYSQL))
|
||||
parsed.append(alltokens(val, libinjection.FLAG_QUOTE_DOUBLE | libinjection.FLAG_SQL_MYSQL))
|
||||
|
||||
self.add_header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
self.add_header('Pragma', 'no-cache')
|
||||
self.add_header('Expires', '0')
|
||||
self.add_header('X-Content-Type-Options', 'nosniff')
|
||||
self.add_header('X-XSS-Protection', '0')
|
||||
|
||||
self.render("tokens.html",
|
||||
title='libjection sqli token parsing diagnostics',
|
||||
version = libinjection.version(),
|
||||
parsed=parsed,
|
||||
formvalue=val,
|
||||
ssl_protocol=self.request.headers.get('X-SSL-Protocol', ''),
|
||||
ssl_cipher=self.request.headers.get('X-SSL-Cipher', '')
|
||||
)
|
||||
|
||||
def get_fingerprints(self):
|
||||
#unquote = urllib.unquote
|
||||
#detectsqli = libinjection.detectsqli
|
||||
|
||||
ids = self.request.arguments.get('id', [])
|
||||
if len(ids) == 1:
|
||||
formvalue = ids[0]
|
||||
else:
|
||||
formvalue = ''
|
||||
|
||||
args = []
|
||||
extra = {}
|
||||
qssqli = False
|
||||
|
||||
sqlstate = libinjection.sqli_state()
|
||||
|
||||
allfp = {}
|
||||
for name,values in self.request.arguments.iteritems():
|
||||
if name == 'type':
|
||||
continue
|
||||
|
||||
fps = []
|
||||
|
||||
val = values[0]
|
||||
val = urllib.unquote(val)
|
||||
if len(val) == 0:
|
||||
continue
|
||||
libinjection.sqli_init(sqlstate, val, 0)
|
||||
pat = libinjection.sqli_fingerprint(sqlstate, libinjection.FLAG_QUOTE_NONE | libinjection.FLAG_SQL_ANSI)
|
||||
issqli = bool(libinjection.sqli_blacklist(sqlstate) and libinjection.sqli_not_whitelist(sqlstate))
|
||||
fps.append(['unquoted', 'ansi', issqli, pat])
|
||||
|
||||
pat = libinjection.sqli_fingerprint(sqlstate, libinjection.FLAG_QUOTE_NONE | libinjection.FLAG_SQL_MYSQL)
|
||||
issqli = bool(libinjection.sqli_blacklist(sqlstate) and libinjection.sqli_not_whitelist(sqlstate))
|
||||
fps.append(['unquoted', 'mysql', issqli, pat])
|
||||
|
||||
pat = libinjection.sqli_fingerprint(sqlstate, libinjection.FLAG_QUOTE_SINGLE | libinjection.FLAG_SQL_ANSI)
|
||||
issqli = bool(libinjection.sqli_blacklist(sqlstate) and libinjection.sqli_not_whitelist(sqlstate))
|
||||
fps.append(['single', 'ansi', issqli, pat])
|
||||
|
||||
pat = libinjection.sqli_fingerprint(sqlstate, libinjection.FLAG_QUOTE_SINGLE | libinjection.FLAG_SQL_MYSQL)
|
||||
issqli = bool(libinjection.sqli_blacklist(sqlstate) and libinjection.sqli_not_whitelist(sqlstate))
|
||||
fps.append(['single', 'mysql', issqli, pat])
|
||||
|
||||
pat = libinjection.sqli_fingerprint(sqlstate, libinjection.FLAG_QUOTE_DOUBLE | libinjection.FLAG_SQL_MYSQL)
|
||||
issqli = bool(libinjection.sqli_blacklist(sqlstate) and libinjection.sqli_not_whitelist(sqlstate))
|
||||
fps.append(['double', 'mysql', issqli, pat])
|
||||
|
||||
allfp[name] = {
|
||||
'value': breakify(breakapart(val)),
|
||||
'fingerprints': fps
|
||||
}
|
||||
|
||||
for name,values in self.request.arguments.iteritems():
|
||||
if name == 'type':
|
||||
continue
|
||||
for val in values:
|
||||
# do it one more time include cut-n-paste was already url-encoded
|
||||
val = urllib.unquote(val)
|
||||
if len(val) == 0:
|
||||
continue
|
||||
|
||||
# swig returns 1/0, convert to True False
|
||||
libinjection.sqli_init(sqlstate, val, 0)
|
||||
issqli = bool(libinjection.is_sqli(sqlstate))
|
||||
|
||||
# True if any issqli values are true
|
||||
qssqli = qssqli or issqli
|
||||
val = breakapart(val)
|
||||
|
||||
pat = sqlstate.fingerprint
|
||||
if not issqli:
|
||||
pat = 'see below'
|
||||
args.append([name, val, issqli, pat])
|
||||
|
||||
self.add_header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
self.add_header('Pragma', 'no-cache')
|
||||
self.add_header('Expires', '0')
|
||||
self.add_header('X-Content-Type-Options', 'nosniff')
|
||||
self.add_header('X-XSS-Protection', '0')
|
||||
|
||||
self.render("form.html",
|
||||
title='libjection sqli diagnostic',
|
||||
version = libinjection.version(),
|
||||
is_sqli=qssqli,
|
||||
args=args,
|
||||
allfp = allfp,
|
||||
formvalue=formvalue,
|
||||
ssl_protocol=self.request.headers.get('X-SSL-Protocol', ''),
|
||||
ssl_cipher=self.request.headers.get('X-SSL-Cipher', '')
|
||||
)
|
||||
|
||||
import os
|
||||
settings = {
|
||||
"static_path": os.path.join(os.path.dirname(__file__), "static"),
|
||||
"template_path": os.path.join(os.path.dirname(__file__), "."),
|
||||
"xsrf_cookies": False,
|
||||
"gzip": False
|
||||
}
|
||||
|
||||
application = tornado.web.Application([
|
||||
(r"/diagnostics", NullHandler),
|
||||
(r'/xsstest', XssTestHandler),
|
||||
(r'/bootstrap/(.*)', tornado.web.StaticFileHandler, {'path': '/opt/bootstrap' }),
|
||||
(r'/jquery/(.*)', tornado.web.StaticFileHandler, {'path': '/opt/jquery' }),
|
||||
(r'/robots.txt', tornado.web.StaticFileHandler, {'path': os.path.join(os.path.dirname(__file__), "static")}),
|
||||
(r'/favicon.ico', tornado.web.StaticFileHandler, {'path': os.path.join(os.path.dirname(__file__), "static")}),
|
||||
(r"/([a-z-]*)", PageHandler)
|
||||
], **settings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tornado.options.parse_command_line()
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(process)d %(message)s")
|
||||
|
||||
application.listen(8888)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
Reference in New Issue
Block a user