Attacking Secondary Contexts in Web Applications
Sam Curry
whoami
Sam Curry(@samwcyo)
Full time bug bounty hunter(3 years on-and-off)
Passionate about application security/research(run blog @ samcurry.net)
How I previously thought all HTTP servers worked...
Application files are stored/accessed in webserver folder
/var/www/html/
/usr/share/nginx/html/
… etc …
GET /index.html
Tries to load in /webserver/index.html
GET /folder/index.html
Tries to load in /webserver/folder/index.html
Very straightforward and simple
Different ways web applications do routing
Not actually dealing with stored files, rather using defined routes
Different ways web applications do routing
Sent across middleware and proxies, sometimes through load balancers...
Different ways web applications do routing
Fetching content from APIs
Sending a 2nd HTTP request
Usually a different host
Common lack of input validation
Sometimes carries auth info to API
Underlying authentication models
Sometimes not present…
Methods for identifying application routing
Directory traversal
Does “/api/../” return something different than “/”?
Fuzzing using control characters
%23 (#), %3f (?), %26 (&), %2e (.), %2f (/), %40 (@)
Double/triple URL encoding
Does the behavior suddenly change for certain directories?
Why does “/images/” return different headers than “/”?
Are there any nice bits of information we can catch?
“internal.company.com:8080 returned the following: ‘500 internal server error’”
Identifying application routing - Examples
We can identify /favicon.ico* is being served through CloudFront
What if this was being served through an S3 bucket?
GET /favicon.ico/..%2f..%2fattackersbucket%2fxss.html
(Proxied as https://s3.amazonaws.com/yahoo-bucket/favicon.ico/../../attackersbucket/xss.html)
Identifying application routing - Examples
Requesting the webroot behaves totally normally
Browsing to /api/v1/ reveals different behavior
Different headers, content-type, etc.
We can confirm the routing is separate via traversing backwards to “/” on the API server via “/../../../”
Common issues with secondary contexts
Data is being served across extra layers
Introduces translation issues like HTTP request smuggling
CRLF injection in weird places
Developers do not expect users to be able to control parameters/paths
Functionality you would normally see in a development environment is accessible(?debug=1, /server-status)
Information disclosure
Internal HTTP headers, access token
SSRF and XSS via manipulating response content
Finding an open redirect in 2nd context = server issuing/potentially rendering arbitrary request
Additionally, Apache, NGINX, and many other HTTP servers have proxy functionalities for paths where HTTP requests are forwarded.
Identifying application routing - Examples
Passing in “%23” turns into “#” and makes the underlying request fail as the parameters are dropped
What control do we have over the second request?
How could this be exploited by an attacker?
Identifying application routing - Examples
Traversing backwards allows us to overwrite the API paths
Indexing for user ID is based on the session cookie
Identifying application routing - Examples
We can traverse the internal API, overwrite the user ID, then read a victim’s file
All other API calls are also accessibleGET /files/..%2f..%2f + victim ID + %2f + victim filename
Common issues attacking secondary contexts
APIs will oftentimes not normalize request URLs
Impossible to traverse API calls
Common issues attacking secondary contexts
Underlying authentication makes access control issues impossible
Even if an API is internal, there isn’t any benefit besides widened attack surface
Identifying application routing - Examples
HTTP request loads the specified invoice PDF
IDOR doesn’t work, returns 404 (somewhat interesting)
Are they doing anything weird/exploitable here?
Identifying application routing - Examples
GET /my-services/invoices/..%2finvoices%2fINV08179455/pdf
This works (200 with PDF content)
GET /my-services/invoices/..%2f..%2fmy-services%2finvoices%2fINV08179455/pdf
This doesn’t (404 without PDF content)
This doesn’t really prove anything, but it’s interesting
If it were traversing on the same box/normally, it’d likely load both
This is probably worth at least investigating a little bit
Identifying application routing - Examples
There’s a possibility a directory before “/invoices/” is indexing our uploads(/:userid/invoices/:invoiceid)
If we can guess this directory, we can potentially view other users invoices
Lots of things to guess here...
Identifying application routing - Examples
Intruder (0-1000000) not working
Email not working
Username not working
Error message on another part of the app discloses the following…{"error":"Id [email protected]#vj does not have permission to modify the domain example.com."}
Moment of truth...
… but ...
Identifying application routing - Examples
Attacker can read anyones PDF if they know their…
Email address
Invoice number
An alright bug… I guess....
Is this behavior anywhere else on the app?
Identifying application routing - Examples
Definitely a more interesting part of the website
How is payment information fetched?
Identifying application routing - Examples
Maybe this is stored the same way, but if so…
What is the directory name?
How can we retrieve that unique ID?
Identifying application routing - Examples
Maybe this is stored the same way, but if so…
What is the directory name? (/paymentmethods/)
How can we retrieve that unique ID?
Identifying application routing - Examples
Maybe this is stored the same way, but if so…
What is the directory name? (/paymentmethods/)
How can we retrieve that unique ID? (trick with /subscriptions/)
GET /subscriptions/:id +Same trick from before =Traversing to view payment method IDs
https://www.luminate.com/subscriptions/..%2f..%2f + email + %2f + id
Identifying application routing - Examples
GET /my-services/edit-payment-method?uid=../../[email protected]%23vj/paymentmethods/2c92a00871083a4600fa287ce52fe
Identifying application routing - Examples
Escalated severity from reading users invoices to reading payment information
The only piece of information we need is the victim’s email address
The subscription ID can be brute forced
We obtain the payment ID from the subscription ID traversal
Exploring all possibilities
Although directory traversal is useful for these types of bugs,it isn’t necessary for various attacks
In some cases, API calls behave similarly to a SQL query evaluating to true/false
Impact of course varies per case, but there are lots of interesting possibilities
Case Study - Authy 2FA bypass
Authy - 2FA service, installable library
User -> [Client -> Authy]
Case Study - Authy 2FA bypass
When reading the response from Authy, the server only checked for…
JSON {“success”:true}
HTTP 200 OK
How is the users token sent to Authy?this._request("get", "/protected/json/verify/" + token + "/" + id, {}, callback, qs);
GET /protected/json returns both 200 OK and JSON {“success”:true}
Is it really that simple?
Case Study - Authy 2FA bypass
Universal 2FA bypass for huge portion of Authy libraries(credit: Egor Homakov, @homakov)
Review
Lots of unique opportunities in attacking secondary contexts
Requests often sent internally
Often less restrictive environments
Authorization sometimes seemingly arbitrary (200 v.s. 403 when you control route)
Very complicated problem for developers
Requests sent between servers with different behaviors
Hard to isolate internal APIs where user data isn’t dangerous
Sanitizing for paths is relatively difficult 2-3 proxies deep
Lots of new research relative to similar approaches
Using “Max-Forwards” header to figure out more information about your requests(https://www.agarri.fr/blog/archives/2011/11/12/traceroute-like_http_scanner/index.html)
Thank you Kernelcon!
Questions? Maybe answers?
@samwcyo
Sam Curry