gixy/gixy/plugins/ssrf.py

63 lines
2.0 KiB
Python

import re
import gixy
from gixy.plugins.plugin import Plugin
from gixy.core.context import get_context
from gixy.core.variable import compile_script
class ssrf(Plugin):
"""
Insecure examples:
location ~ /proxy/(.*)/(.*)/(.*)$ {
set $scheme $1;
set $host $2;
set $path $3;
proxy_pass $scheme://$host/$path;
}
location /proxy/ {
proxy_pass $arg_some;
}
"""
summary = 'Possible SSRF (Server Side Request Forgery) vulnerability.'
severity = gixy.severity.HIGH
description = 'The configuration may allow attacker to create a arbitrary requests from the vulnerable server.'
help_url = 'https://github.com/yandex/gixy/wiki/ru/ssrf'
directives = ['proxy_pass']
def __init__(self, config):
super(ssrf, self).__init__(config)
self.parse_uri_re = re.compile(r'(?P<scheme>[^?#/)]+://)?(?P<host>[^?#/)]+)')
def audit(self, directive):
value = directive.args[0]
if not value:
return
context = get_context()
if context.block.name == 'location' and context.block.is_internal:
# Exclude internal locations
return
parsed = self.parse_uri_re.match(value)
if not parsed:
return
res = self._check_script(parsed.group('scheme'), directive)
if not res:
self._check_script(parsed.group('host'), directive)
def _check_script(self, script, directive):
for var in compile_script(script):
if var.must_contain('/'):
# Skip variable checks
return False
if var.can_contain('.'):
# Yay! Our variable can contain any symbols!
reason = 'At least variable "${var}" can contain untrusted user input'.format(var=var.name)
self.add_issue(directive=[directive] + var.providers, reason=reason)
return True
return False