Class: Aikido::Zen::Scanners::SSRFScanner
- Inherits:
-
Object
- Object
- Aikido::Zen::Scanners::SSRFScanner
- Defined in:
- lib/aikido/zen/scanners/ssrf_scanner.rb
Defined Under Namespace
Modules: Headers Classes: RedirectChains, Request, Response
Class Method Summary collapse
-
.call(request:, sink:, context:, operation:) ⇒ Aikido::Zen::Attacks::SSRFAttack?
Checks if an outbound HTTP request is to a hostname supplied from user input that resolves to a “dangerous” address.
-
.track_redirects(request:, response:, context: Aikido::Zen.current_context) ⇒ void
Track the origin of a redirection so we know if an attacker is using redirect chains to mask their use of a (seemingly) safe domain.
Instance Method Summary collapse
- #attack? ⇒ Boolean private
-
#initialize(request_uri, input, redirects) ⇒ SSRFScanner
constructor
private
A new instance of SSRFScanner.
Constructor Details
#initialize(request_uri, input, redirects) ⇒ SSRFScanner
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of SSRFScanner.
77 78 79 80 81 |
# File 'lib/aikido/zen/scanners/ssrf_scanner.rb', line 77 def initialize(request_uri, input, redirects) @request_uri = request_uri @input = input @redirects = redirects end |
Class Method Details
.call(request:, sink:, context:, operation:) ⇒ Aikido::Zen::Attacks::SSRFAttack?
Checks if an outbound HTTP request is to a hostname supplied from user input that resolves to a “dangerous” address. This is called from two different places:
-
HTTP library sinks, before we make a request. In these cases we can detect very obvious attempts such as a request that attempts to access localhost or an internal IP.
-
DNS lookup sinks, after we resolve a hostname. For HTTP requests that are not obviously an attack, we let the DNS resolution happen, and then check again, now knowing if the domain name provided actually resolves to an internal IP or not.
NOTE: Because not all DNS resolutions might be happening in the context of a protected HTTP request, the request
argument below might be nil and we can then skip this scan.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/aikido/zen/scanners/ssrf_scanner.rb', line 34 def self.call(request:, sink:, context:, operation:, **) return if context.nil? return if request.nil? # See NOTE above. context["ssrf.redirects"] ||= RedirectChains.new context.payloads.each do |payload| scanner = new(request.uri, payload.value, context["ssrf.redirects"]) next unless scanner.attack? attack = Attacks::SSRFAttack.new( sink: sink, request: request, input: payload, context: context, operation: "#{sink.operation}.#{operation}" ) return attack end nil end |
.track_redirects(request:, response:, context: Aikido::Zen.current_context) ⇒ void
This method returns an undefined value.
Track the origin of a redirection so we know if an attacker is using redirect chains to mask their use of a (seemingly) safe domain.
66 67 68 69 70 71 72 73 74 |
# File 'lib/aikido/zen/scanners/ssrf_scanner.rb', line 66 def self.track_redirects(request:, response:, context: Aikido::Zen.current_context) return unless response.redirect? context["ssrf.redirects"] ||= RedirectChains.new context["ssrf.redirects"].add( source: request.uri, destination: response.redirect_to ) end |
Instance Method Details
#attack? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/aikido/zen/scanners/ssrf_scanner.rb', line 84 def attack? return false if @input.nil? || @input.to_s.empty? # If the request is not aimed at an internal IP, we can ignore it. (It # might still be an SSRF if defined strictly, but it's unlikely to be # exfiltrating data from the app's servers, and the risk for false # positives is too high.) return false unless private_ip?(@request_uri.hostname) origins_for_request .product(uris_from_input) .any? { |(conn_uri, candidate)| match?(conn_uri, candidate) } end |