mirror of https://github.com/CIRCL/AIL-framework
175 lines
5.3 KiB
Python
Executable File
175 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*-coding:UTF-8 -*
|
|
|
|
"""
|
|
The SQLInjectionDetection Module
|
|
================================
|
|
|
|
This module is consuming the Redis-list created by the Web module.
|
|
|
|
It test different possibility to makes some sqlInjection.
|
|
|
|
"""
|
|
|
|
import time
|
|
import datetime
|
|
import redis
|
|
import string
|
|
import urllib.request
|
|
import re
|
|
from pubsublogger import publisher
|
|
from Helper import Process
|
|
from packages import Paste
|
|
from pyfaup.faup import Faup
|
|
|
|
# Config Var
|
|
|
|
regex_injection = []
|
|
word_injection = []
|
|
word_injection_suspect = []
|
|
|
|
# Classic atome injection
|
|
regex_injection1 = "([[AND |OR ]+[\'|\"]?[0-9a-zA-Z]+[\'|\"]?=[\'|\"]?[0-9a-zA-Z]+[\'|\"]?])"
|
|
regex_injection.append(regex_injection1)
|
|
|
|
# Time-based attack
|
|
regex_injection2 = ["SLEEP\([0-9]+", "BENCHMARK\([0-9]+", "WAIT FOR DELAY ", "WAITFOR DELAY"]
|
|
regex_injection2 = re.compile('|'.join(regex_injection2))
|
|
regex_injection.append(regex_injection2)
|
|
|
|
# Interesting keyword
|
|
word_injection1 = [" IF ", " ELSE ", " CASE ", " WHEN ", " END ", " UNION ", "SELECT ", " FROM ", " ORDER BY ", " WHERE ", " DELETE ", " DROP ", " UPDATE ", " EXEC "]
|
|
word_injection.append(word_injection1)
|
|
|
|
# Database special keywords
|
|
word_injection2 = ["@@version", "POW(", "BITAND(", "SQUARE("]
|
|
word_injection.append(word_injection2)
|
|
|
|
# Html keywords
|
|
word_injection3 = ["<script>"]
|
|
word_injection.append(word_injection3)
|
|
|
|
# Suspect char
|
|
word_injection_suspect1 = ["\'", "\"", ";", "<", ">"]
|
|
word_injection_suspect += word_injection_suspect1
|
|
|
|
# Comment
|
|
word_injection_suspect2 = ["--", "#", "/*"]
|
|
word_injection_suspect += word_injection_suspect2
|
|
|
|
def analyse(url, path):
|
|
faup.decode(url)
|
|
url_parsed = faup.get()
|
|
|
|
resource_path = url_parsed['resource_path']
|
|
query_string = url_parsed['query_string']
|
|
|
|
result_path = 0
|
|
result_query = 0
|
|
|
|
if resource_path is not None:
|
|
result_path = is_sql_injection(resource_path.decode('utf8'))
|
|
|
|
if query_string is not None:
|
|
result_query = is_sql_injection(query_string.decode('utf8'))
|
|
|
|
if (result_path > 0) or (result_query > 0):
|
|
paste = Paste.Paste(path)
|
|
if (result_path > 1) or (result_query > 1):
|
|
print("Detected SQL in URL: ")
|
|
print(urllib.request.unquote(url))
|
|
to_print = 'SQLInjection;{};{};{};{};{}'.format(paste.p_source, paste.p_date, paste.p_name, "Detected SQL in URL", paste.p_rel_path)
|
|
publisher.warning(to_print)
|
|
#Send to duplicate
|
|
p.populate_set_out(path, 'Duplicate')
|
|
|
|
msg = 'infoleak:automatic-detection="sql-injection";{}'.format(path)
|
|
p.populate_set_out(msg, 'Tags')
|
|
|
|
#statistics
|
|
tld = url_parsed['tld']
|
|
if tld is not None:
|
|
date = datetime.datetime.now().strftime("%Y%m")
|
|
server_statistics.hincrby('SQLInjection_by_tld:'+date, tld, 1)
|
|
|
|
else:
|
|
print("Potential SQL injection:")
|
|
print(urllib.request.unquote(url))
|
|
to_print = 'SQLInjection;{};{};{};{};{}'.format(paste.p_source, paste.p_date, paste.p_name, "Potential SQL injection", paste.p_rel_path)
|
|
publisher.info(to_print)
|
|
|
|
|
|
# Try to detect if the url passed might be an sql injection by appliying the regex
|
|
# defined above on it.
|
|
def is_sql_injection(url_parsed):
|
|
line = urllib.request.unquote(url_parsed)
|
|
line = str.upper(line)
|
|
result = []
|
|
result_suspect = []
|
|
|
|
for regex in regex_injection:
|
|
temp_res = re.findall(regex, line)
|
|
if len(temp_res)>0:
|
|
result.append(temp_res)
|
|
|
|
for word_list in word_injection:
|
|
for word in word_list:
|
|
temp_res = str.find(line, str.upper(word))
|
|
if temp_res!=-1:
|
|
result.append(line[temp_res:temp_res+len(word)])
|
|
|
|
for word in word_injection_suspect:
|
|
temp_res = str.find(line, str.upper(word))
|
|
if temp_res!=-1:
|
|
result_suspect.append(line[temp_res:temp_res+len(word)])
|
|
|
|
if len(result)>0:
|
|
print(result)
|
|
return 2
|
|
elif len(result_suspect)>0:
|
|
print(result_suspect)
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# If you wish to use an other port of channel, do not forget to run a subscriber accordingly (see launch_logs.sh)
|
|
# Port of the redis instance used by pubsublogger
|
|
publisher.port = 6380
|
|
# Script is the default channel used for the modules.
|
|
publisher.channel = 'Script'
|
|
|
|
# Section name in bin/packages/modules.cfg
|
|
config_section = 'SQLInjectionDetection'
|
|
|
|
# Setup the I/O queues
|
|
p = Process(config_section)
|
|
|
|
# Sent to the logging a description of the module
|
|
publisher.info("Try to detect SQL injection")
|
|
|
|
server_statistics = redis.StrictRedis(
|
|
host=p.config.get("ARDB_Statistics", "host"),
|
|
port=p.config.getint("ARDB_Statistics", "port"),
|
|
db=p.config.getint("ARDB_Statistics", "db"),
|
|
decode_responses=True)
|
|
|
|
faup = Faup()
|
|
|
|
# Endless loop getting messages from the input queue
|
|
while True:
|
|
# Get one message from the input queue
|
|
message = p.get_from_set()
|
|
|
|
if message is None:
|
|
publisher.debug("{} queue is empty, waiting".format(config_section))
|
|
time.sleep(10)
|
|
continue
|
|
|
|
else:
|
|
# Do something with the message from the queue
|
|
url, date, path = message.split()
|
|
analyse(url, path)
|