2017-05-21 13:56:42 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
from indicators import *
|
|
|
|
|
2017-06-05 20:56:23 +00:00
|
|
|
# Replace the nth occurence of a string
|
2017-06-06 20:51:01 +00:00
|
|
|
# Inspired from https://stackoverflow.com/questions/35091557/replace-nth-occurrence-of-substring-in-string
|
|
|
|
def nth_replace(string, old, new, n):
|
|
|
|
if string.count(old) >= n:
|
2017-06-05 20:56:23 +00:00
|
|
|
left_join = old
|
|
|
|
right_join = old
|
2017-06-06 20:51:01 +00:00
|
|
|
groups = string.split(old)
|
|
|
|
nth_split = [left_join.join(groups[:n]), right_join.join(groups[n:])]
|
|
|
|
return new.join(nth_split)
|
|
|
|
return string.replace(old, new)
|
2017-06-05 20:56:23 +00:00
|
|
|
|
|
|
|
|
2017-05-21 13:56:42 +00:00
|
|
|
# Display the found vulnerability with basic informations like the line
|
2019-04-05 12:25:05 +00:00
|
|
|
def display(path,payload,vulnerability,line,declaration_text,declaration_line, colored, occurence, plain):
|
2017-11-14 12:45:07 +00:00
|
|
|
# Potential vulnerability found : SQL Injection
|
2019-04-05 13:11:57 +00:00
|
|
|
header = "{}Potential vulnerability found : {}{}{}".format('' if plain else '\033[1m', '' if plain else '\033[92m', payload[1], '' if plain else '\033[0m')
|
2017-05-21 13:56:42 +00:00
|
|
|
|
2017-11-14 12:45:07 +00:00
|
|
|
# Line 25 in test/sqli.php
|
2019-04-05 13:11:57 +00:00
|
|
|
line = "n°{}{}{} in {}".format('' if plain else '\033[92m',line, '' if plain else '\033[0m', path)
|
2017-05-21 15:59:11 +00:00
|
|
|
|
2017-11-14 12:45:07 +00:00
|
|
|
# Code : include($_GET['patisserie'])
|
2019-04-05 13:11:57 +00:00
|
|
|
vuln = nth_replace("".join(vulnerability), colored, "{}".format('' if plain else '\033[92m')+colored+"{}".format('' if plain else '\033[0m'), occurence)
|
2017-11-14 12:45:07 +00:00
|
|
|
vuln = "{}({})".format(payload[0], vuln)
|
2017-05-21 15:59:11 +00:00
|
|
|
|
2017-11-14 12:45:07 +00:00
|
|
|
# Final Display
|
|
|
|
rows, columns = os.popen('stty size', 'r').read().split()
|
2019-04-05 13:38:57 +00:00
|
|
|
print("-" * (int(columns)-1))
|
|
|
|
print("Name \t{}".format(header))
|
|
|
|
print("-" * (int(columns)-1))
|
|
|
|
print("{}Line {} {}".format('' if plain else '\033[1m', '' if plain else '\033[0m', line))
|
|
|
|
print("{}Code {} {}".format('' if plain else '\033[1m', '' if plain else '\033[0m', vuln))
|
2017-05-21 15:59:11 +00:00
|
|
|
|
2017-11-14 12:45:07 +00:00
|
|
|
# Declared at line 1 : $dest = $_GET['who'];
|
|
|
|
if not "$_" in colored:
|
|
|
|
declared = "Undeclared in the file"
|
|
|
|
if declaration_text != "":
|
2019-04-05 13:11:57 +00:00
|
|
|
declared = "Line n°{}{}{} : {}".format('' if plain else '\033[0;92m', declaration_line, '' if plain else '\033[0m', declaration_text)
|
|
|
|
#declared = "Line n°\033[0;{}m{}\033[0m : {}".format('0' if plain else '92', declaration_line, declaration_text)
|
2017-05-21 13:56:42 +00:00
|
|
|
|
2019-04-05 13:38:57 +00:00
|
|
|
print("{}Declaration {} {}".format('' if plain else '\033[1m', '' if plain else '\033[0m', declared))
|
2017-05-25 13:54:35 +00:00
|
|
|
|
2019-04-05 12:25:05 +00:00
|
|
|
# Small delimiter
|
2019-04-05 13:38:57 +00:00
|
|
|
print("")
|
2017-05-21 13:56:42 +00:00
|
|
|
|
|
|
|
# Find the line where the vulnerability is located
|
|
|
|
def find_line_vuln(path,payload,vulnerability,content):
|
2019-04-05 13:38:57 +00:00
|
|
|
content = content.split('\n')
|
|
|
|
for i in range(len(content)):
|
|
|
|
if payload[0]+'('+vulnerability[0]+vulnerability[1]+vulnerability[2]+')' in content[i]:
|
|
|
|
return str(i-1)
|
|
|
|
return "-1"
|
2017-05-21 13:56:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
# Find the line where the entry point is declared
|
2017-05-21 15:59:11 +00:00
|
|
|
# TODO: should be an array of the declaration and modifications
|
2017-05-21 13:56:42 +00:00
|
|
|
def find_line_declaration(declaration, content):
|
2019-04-05 13:38:57 +00:00
|
|
|
content = content.split('\n')
|
|
|
|
for i in range(len(content)):
|
|
|
|
if declaration in content[i]:
|
|
|
|
return str(i)
|
|
|
|
return "-1"
|
2017-05-22 22:23:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
# Format the source code in order to improve the detection
|
|
|
|
def clean_source_and_format(content):
|
|
|
|
# Clean up - replace tab by space
|
2019-04-05 13:38:57 +00:00
|
|
|
content = content.replace(" "," ")
|
2017-05-22 22:23:47 +00:00
|
|
|
|
|
|
|
# Quickfix to detect both echo("something") and echo "something"
|
|
|
|
content = content.replace("echo ","echo(")
|
|
|
|
content = content.replace(";",");")
|
|
|
|
return content
|
|
|
|
|
|
|
|
# Check the line to detect an eventual protection
|
|
|
|
def check_protection(payload, match):
|
|
|
|
for protection in payload:
|
|
|
|
if protection in "".join(match):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
# Check exception - When it's a function($SOMETHING) Match declaration $SOMETHING = ...
|
|
|
|
def check_exception(match):
|
|
|
|
exceptions = ["_GET","_REQUEST","_POST","_COOKIES","_FILES"]
|
|
|
|
is_exception = False
|
|
|
|
for exception in exceptions:
|
|
|
|
if exception in match:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
# Check declaration
|
2017-05-23 22:23:05 +00:00
|
|
|
def check_declaration(content, vuln, path):
|
|
|
|
# Follow and parse include, then add it's content
|
|
|
|
regex_declaration = re.compile("(include.*?|require.*?)\([\"\'](.*?)[\"\']\)")
|
|
|
|
includes = regex_declaration.findall(content)
|
2017-06-05 18:18:48 +00:00
|
|
|
|
2019-04-05 13:38:57 +00:00
|
|
|
# Path is the path of the current scanned file, we can use it to compute the relative include
|
2017-05-23 22:23:05 +00:00
|
|
|
for include in includes:
|
2019-04-05 13:38:57 +00:00
|
|
|
relative_include = os.path.dirname(path)+"/"
|
|
|
|
try:
|
|
|
|
path_include = relative_include + include[1]
|
|
|
|
with open(path_include, 'r') as f:
|
|
|
|
content = f.read() + content
|
|
|
|
except Exception as e:
|
|
|
|
return (False, "","")
|
2017-11-12 12:42:25 +00:00
|
|
|
|
2017-05-23 22:23:05 +00:00
|
|
|
|
2019-04-05 13:38:57 +00:00
|
|
|
# Extract declaration - for ($something as $somethingelse)
|
2019-04-05 10:47:17 +00:00
|
|
|
vulnerability = vuln[1:].replace(')', '\)').replace('(', '\(')
|
|
|
|
regex_declaration2 = re.compile("\$(.*?)([\t ]*)as(?!=)([\t ]*)\$"+vulnerability)
|
2017-06-05 18:18:48 +00:00
|
|
|
declaration2 = regex_declaration2.findall(content)
|
|
|
|
if len(declaration2) > 0:
|
2019-04-05 13:38:57 +00:00
|
|
|
return check_declaration(content, "$"+declaration2[0][0], path)
|
2017-06-05 18:18:48 +00:00
|
|
|
|
2019-04-05 13:38:57 +00:00
|
|
|
# Extract declaration - $something = $_GET['something']
|
2019-04-05 10:47:17 +00:00
|
|
|
regex_declaration = re.compile("\$"+vulnerability+"([\t ]*)=(?!=)(.*)")
|
2017-05-22 22:23:47 +00:00
|
|
|
declaration = regex_declaration.findall(content)
|
|
|
|
if len(declaration)>0:
|
2017-05-23 22:23:05 +00:00
|
|
|
|
2019-04-05 13:38:57 +00:00
|
|
|
# Check constant then return True if constant because it's false positive
|
|
|
|
declaration_text = "$"+vulnerability +declaration[0][0]+"="+declaration[0][1]
|
|
|
|
line_declaration = find_line_declaration(declaration_text, content)
|
|
|
|
regex_constant = re.compile("\$"+vuln[1:]+"([\t ]*)=[\t ]*?([\"\'(]*?[a-zA-Z0-9{}_\(\)@\.,!: ]*?[\"\')]*?);")
|
|
|
|
false_positive = regex_constant.match(declaration_text)
|
2017-05-28 20:11:40 +00:00
|
|
|
|
2019-04-05 13:38:57 +00:00
|
|
|
if false_positive:
|
|
|
|
return (True, "","")
|
|
|
|
return (False, declaration_text,line_declaration)
|
2017-05-23 22:23:05 +00:00
|
|
|
|
|
|
|
return (False, "","")
|