Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 65 additions & 7 deletions gixy/plugins/alias_traversal.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import gixy
from gixy.plugins.plugin import Plugin
from gixy.core.regexp import Regexp
from gixy.core.variable import compile_script

import re

class alias_traversal(Plugin):
"""
Insecure example:
Insecure examples:
location /files {
alias /home/;
}
location ~ /site/(l\.)(.*) {
alias /lol$1/$2;
}
"""
summary = 'Path traversal via misconfigured alias.'
severity = gixy.severity.HIGH
Expand All @@ -17,15 +23,67 @@ class alias_traversal(Plugin):
directives = ['alias']

def audit(self, directive):
severity = self.severity

for location in directive.parents:
if location.name != 'location':
continue

if not location.modifier or location.modifier == '^~':
if location.modifier in ['~', '~*']:
prev_var = None
sets = []
for var in compile_script(directive.path):
sets.append((var, prev_var, var.regexp))
prev_var = var

# /images/(.*)/lol <- location_regex
# alias /app/static/$1; < - sets[]
# [('/app/static/', None), ('.*', '/app/static/')]
location_regex = re.sub(r"\\(.)", r"\1", location.path)

up_to_char = 0
for var, prev_var, is_regex in sets:
if is_regex:
regex_to_find = '(' + str(var.value) + ')'
found_char = location_regex.find(regex_to_find, up_to_char)
if found_char < 0:
continue

up_to_char = found_char

pre_location_ends_with_slash = False
if up_to_char >= 0:
if location_regex[up_to_char-1] == '/' or var.must_startswith('/'):
pre_location_ends_with_slash = True

if not pre_location_ends_with_slash:
if str(prev_var.value)[-1] == '/':
if var.can_startswith('.'):
if var.can_contain('/'):
# location /site(.*) ~ { alias /lol/$1; }
self.report_issue(directive, location, gixy.severity.HIGH)
else:
# location /site([^/]*) ~ { alias /lol/$1; }
self.report_issue(directive, location, gixy.severity.LOW)
else:
# location /site(.*) ~ { alias /lol$1; }
self.report_issue(directive, location, gixy.severity.LOW)
else:
if str(prev_var.value)[-1] != '/' and not var.must_startswith('/'):
# location /site/(.*) ~ { alias /lol$1; }
self.report_issue(directive, location, gixy.severity.LOW)

elif not location.modifier or location.modifier == '^~':
# We need non-strict prefixed locations
if not location.path.endswith('/'):
self.add_issue(
severity=gixy.severity.HIGH if directive.path.endswith('/') else gixy.severity.MEDIUM,
directive=[directive, location]
)
break
if directive.path.endswith('/'):
self.report_issue(directive, location, gixy.severity.HIGH)
else:
self.report_issue(directive, location, gixy.severity.LOW)
return

def report_issue(self, directive, location, severity):
self.add_issue(
severity=severity,
directive=[directive, location]
)
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /images(.*) {
alias /app/static/$1;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_2.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /images(.*) {
alias /app/static$1;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_2_fp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /images/(.*)$ {
alias /app/static/$1;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_3.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /images(.*)/lol {
alias /app/static/$1;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_3_fp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /images(/.*) {
alias /app/static$1;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_4.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /site/(.*) {
alias /lol$1;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_4_fp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /images([^\.]*) {
alias /app/static/$1;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_5.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /site(.*) {
alias /lol$1;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_6.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~ /site(2)(.*) {
alias /lol/$2;
}
3 changes: 3 additions & 0 deletions tests/plugins/simply/alias_traversal/regex_fp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
location ~* ^/files-(en|fr|es) {
alias /home/;
}