forked from swisskyrepo/Vulny-Code-Static-Analysis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
functions.py
136 lines (107 loc) · 5.39 KB
/
functions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import re
# Replace the nth occurrence of a string
# 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:
left_join = old
right_join = old
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)
# Display the found vulnerability with basic information like the line
def display(path, payload, vulnerability, line, declaration_text, declaration_line, colored, occurrence, plain):
# Potential vulnerability found : SQL Injection
header = "{}Potential vulnerability found : {}{}{}".format('' if plain else '\033[1m', '' if plain else '\033[92m', payload[1], '' if plain else '\033[0m')
# Line 25 in test/sqli.php
line = "n°{}{}{} in {}".format('' if plain else '\033[92m', line, '' if plain else '\033[0m', path)
# Code : include($_GET['patisserie'])
vuln = nth_replace("".join(vulnerability), colored, "{}".format('' if plain else '\033[92m') + colored + "{}".format('' if plain else '\033[0m'), occurrence)
vuln = "{}({})".format(payload[0], vuln)
# Final Display
rows, columns = os.popen('stty size', 'r').read().split()
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))
# Declared at line 1 : $dest = $_GET['who'];
if "$_" not in colored:
declared = "Undeclared in the file"
if declaration_text != "":
declared = "Line n°{}{}{} : {}".format('' if plain else '\033[0;92m', declaration_line, '' if plain else '\033[0m', declaration_text)
print("{}Declaration {} {}".format('' if plain else '\033[1m', '' if plain else '\033[0m', declared))
# Small delimiter
print("")
# Find the line where the vulnerability is located
def find_line_vuln(payload, vulnerability, content):
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"
# Find the line where the entry point is declared
# TODO: should be an array of the declaration and modifications
def find_line_declaration(declaration, content):
content = content.split('\n')
for i in range(len(content)):
if declaration in content[i]:
return str(i)
return "-1"
# Format the source code in order to improve the detection
def clean_source_and_format(content):
# Clean up - replace tab by space
content = content.replace(" ", " ")
# 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"]
for exception in exceptions:
if exception in match:
return True
return False
# Check declaration
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)
# Path is the path of the current scanned file, we can use it to compute the relative include
for include in includes:
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, "", ""
# Extract declaration - for ($something as $somethingelse)
vulnerability = vuln[1:].replace(')', '\\)').replace('(', '\\(')
regex_declaration2 = re.compile("\\$(.*?)([\t ]*)as(?!=)([\t ]*)\\$" + vulnerability)
declaration2 = regex_declaration2.findall(content)
if len(declaration2) > 0:
return check_declaration(content, "$" + declaration2[0][0], path)
# Extract declaration - $something = $_GET['something']
regex_declaration = re.compile("\\$" + vulnerability + "([\t ]*)=(?!=)(.*)")
declaration = regex_declaration.findall(content)
if len(declaration) > 0:
# 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)
if false_positive:
return True, "", ""
return False, declaration_text, line_declaration
return False, "", ""