Piercing the Veal: Short Stories to Read with Friends

d0nut
16 min readApr 27, 2020

--

Title respectfully inspired by Alyssa Herrera’s Piercing the Veil SSRF blog post

It’s been over a year and a half since I’ve started my bug bounty journey as a hacker. With years of experience triaging reports and working in security, I’ve seen a plethora of bug types, attack vectors, and exploitation techniques. I’ve had such a healthy diversity in exposure to these different concepts that it’s quite honestly surprising that one particular bug class has captured my imagination so effortlessly and decisively as Server-Side Request Forgery has.

Today, I want to share my love for SSRF by discussing what it is, why companies care about it, how I approach testing features I suspect may be vulnerable to SSRF, and lastly share a couple of short stories on SSRFs that I’ve found in my time hacking.

Modern Web Applications

The modern web application consists of a fair bit more than just a couple of php files and fastcgi. These increasingly complex stacks often consist of dozens upon dozens of services running in a production network, mostly isolated from the public internet. Each of these services, generally responsible for one or a few related tasks or models powering the web application, can be spun up and down to help deal with sudden changes in load. It’s an amazing feat that allows the modern web application to scale to millions and hundreds of millions of users.

An example modern web application architecture

In this model, attackers and users both start with the same level of privilege (unprivileged network position — left side of above image) which directly affects the exposed attack surface. The only service these actors can directly interact with (or attack) is the nginx instance with public and private IP addresses. It’s also worth pointing out that because the exposed nginx instance uses vHosts, external parties are also able to interact with the www, app, and link services, though not the machines directly. Regardless, attackers are unable to meaningfully control interactions with the IAM, Redis, Widget Service, and two MySQL instances.

But imagine that this wasn’t true. Imagine, as an attacker, that you were able to talk to any of these services and systems directly. Perhaps, and is often the case, these internal services aren’t secured as well; priority to the security of these systems is yielded to that of the perimeter. It would be a field-day for any attacker.

At this point, our story could diverge: we could talk about how differences in the interpretation of the HTTP standard between the vhost services and nginx could enable Request Smuggling vulnerabilities. We could also talk about how second-order SQLi can occur from data stored in one service and processed by another. We could even discuss the attacks against internal HTTP requests by injecting query parameter-breaking characters such as # or & to introduce new parameters or to truncate them instead. However, I’d like to discuss how SSRF fits into this picture.

What is SSRF?

SSRF, or Server-Side Request Forgery, is a vulnerability where an attacker is able to direct a system in a privileged network position to issue a request to another system with a privileged network position.

Keep in mind the bold sections: there is definitely a need to support features where an attacker can direct a service in a privileged network position to make a request to a system in an unprivileged network position. A perfect example of this is a webhook feature as found on Github or Dropbox. Being able to point a webhook at your burp collaborator instance is not a vulnerability.

Impactful SSRF on left; not right

Of course, there are exceptions to every rule. I use this more strict definition above to help direct the most common instances of SSRF and faux SSRF to their respective determinations: valid or invalid. Case in point: I did once encounter a situation where being able to change the address to an attacker controlled machine without a privileged network position allowed me to read the Authorization header and steal credentials used on an internal api call… so take this with a grain of salt.

The typical feature or place to find SSRF is where some parameter contains a url or domain as the value. Seeing a POST request like the following

POST /fetchPage HTTP/1.1
Host: app.example.com
...
Content-Type: application/x-www-form-urlencoded
url=http%3A%2F%2Ffoobar.com%2fendpoint

is a pretty clear sign that this is the kind of feature that may introduce an SSRF vulnerability. There are some other types of features, like HTML to PDF generators, where a headless browser or similar is used in a privileged network position to generate a preview or PDF. If you’re curious about these kinds of attack vectors, go read the slides that Daeken and Nahamsec put together for DEFCON.

In fact, anywhere where you may be able to control an address to another system is a place where you should try testing for SSRF. Technically, even a HOST header could be a vulnerable parameter.

If It Walks Like a Duck and It Talks Like a Duck..

I didn’t originally intend for this blog post to be a “how to test for SSRF” guide (there are plenty of those), but when I was drawing the outline for the material I felt that I should at least cover some of the behaviors or characteristics that I look for when testing for SSRF.

I’m generally interested in the following questions:

Can I read the response?

Am I able to read the response? If not, is there any additional information given to me based on the availability of the receiving system? If the port isn’t open, does an error get returned? If the system doesn’t speak HTTP but is receiving traffic, what happens?

If I can read the response then proving impact is a breeze: we just need to identify an internal service that responds to whatever protocols we have access to and read a response from it. If we can’t read the response, we might have to come up with interesting side channels like different error messages or see if we can blindly coerce an internal service to issue a request to the internet.

Where are we?

Is the vulnerable service running on some Infrastructure as a Service (IaaS) platform (like AWS or GCP) or are we on something less sophisticated or more custom? This lets me know if I’m able to reach a metadata service and may clue me in to what kinds of systems may be running in the internal network.

Can I redirect?

This is pretty straightforward. What are the rules for redirecting? Are they always rejected? Are they always processed? Or is there some nuance in between?

Redirects are a super common method of bypassing mitigations for SSRF. Occasionally web applications will check if the initial domain resolves to an RFC1918 address and error out if so. These kinds of checks are usually only performed on the initial request and a 302 redirect could be leveraged to tell the client to pivot to the internal network. Beware proxies in front of these internal HTTP clients, though, as they can properly discern if a request should be forwarded to its destination.

What protocols does the client support?

HTTP/HTTPS only? FTP? Gopher?? Supporting additional protocols (especially Gopher) will increase the level of impact and options for exploitation available to you.

Remember to think creatively about how you can demonstrate that you’re able to successfully interact with internal systems. This is much easier when you can read the response, of course, but if you’re able to provide that kind of information via a sidechannel like an error when ports are filtered/blocked or if you’re able to get that service to interact with the outside world as a result of your privileged message then you’re going to be more successful in proving the presence of SSRF.

For inspiration, one of my favorite methods of demonstrating that I have an SSRF is leveraging a localhost smtp services to show that I’m able to trigger the system to send me an email. This is a method that I’ve used twice now in HackerOne SSRF reports.

Lastly, moving away from HTTP and HTTPS protocols can let you bypass the proxies mentioned earlier. Oftentimes these proxies only handle HTTP or HTTPS traffic, so being able to speak a protocol like Gopher can allow you to bypass the internal proxy all-together.

SSRF and Where to Find Them

As stated before, SSRF can show up anywhere a system addressable string shows up (IP, domain name, email address, etc). While I could start enumerating every possible features that could become an SSRF (and still miss a ton of possible examples), I think a better way to learn is reading stories about ones others have found and try to extrapolate other similar attack vectors.

Story 1 — Duck Duck Gopher

(This report is public so go read about it here)

This story starts in the middle of October of 2018: the early part of my bug hunting career. I was hacking on a the advice of a friend on this new private program invite I received for Nintendo (it’s public now). I spent a number of hours hacking trying to find anything remotely interesting but struggling to make progress. I ended up giving up after an unsuccessful couple of hours. I was going to take one last brief look at my HTTP History in Burp Suite before closing it down and going to bed. That’s when I noticed a single request fly by that just seemed too good to be true.

At this time I had DuckDuckGo configured as my default search engine in Chrome (though, admittedly, I pretty much always opened Google instead; so much for privacy ¯\_(ツ)_/¯). At one point I had accidentally submitted a search query to DuckDuckGo and burp had intercepted all of the requests that page made. The one that caught my eye was a request to https://duckduckgo.com/iu. This request had a query parameter named url that appeared to return a page response whenever the parameter contained a url with a domain name of yimg.com

Successful request

However, when you try another domain like google.com you’d encounter an error indicating that they’re doing some sort of filtering to reject requests to other domains. Notice the 403 Forbidden in this response as opposed to the 200 OK above.

Unsuccessful request

On a hunch, I decided to see if this service was actually parsing the url for the whitelist or if they were just using a string.contains fueled check. Sure enough, this was all I needed to bypass the filter and begin investigating this for possible SSRF behavior.

Bypassing a string.contains filter by appending the domain to a fake query parameter

As mentioned above, some of the things I want to investigate are the client’s ability to redirect and to identify what other protocols it supports if possible.

Upon testing for redirect behavior, I did notice that redirects were respected. I pointed DuckDuckGo at an endpoint that would respond with a 302 redirect to a burp collaborator instance and confirmed that the redirect worked. Afterwords, I checked to see if the gopher protocol was supported by using it in the Location header in the redirect. The gopher protocol would allow me to talk to many more services internally so it was useful to learn if this client would support it (and increase the severity of this finding).

I was able to perform a port scan using this SSRF and discovered a number of services running on localhost. One of these services, on port 6868, was Redis which was actually returning some data when hit with HTTP. The json that was returned mentioned a domain that only seemed resolvable internally. Now, with this SSRF, I would be able to port scan that service and begin identifying services I could communicate with.

Here’s a diagram to help demonstrate where we’re at with this attack.

SSRF against duckduckgo

Eventually, I noticed that port 8091 was open on cache-services.duckduckgo.com and was returning a ton of data. I had begun my report with DuckDuckGo a bit earlier than this but wanted to see what else I could do to increase the impact (and learn). Around this point, I had stopped hacking on the target and called it a day.

Aftermath

Unfortunately, DuckDuckGo doesn’t pay for bugs, but they do offer some swag. I ended up getting a DuckDuckGo t-shirt, a really great story, and a public disclosure that I’m quite proud of.

Story 2— In ursmtp

I was invited to a private program that I didn’t have a lot of hope for. It was one of those services that felt like it could be replaced by any other CRUD app on the market. It didn’t really offer much in the way of interesting behavior as far as I could tell and I was getting quite bored of it. While the scope did have a *. domain allowed, I wasn’t finding much interesting on it.

Eventually, I found a feature that allowed a user to update the information on their account. One of the fields you could fill out was your personal website address (similar to the one on twitter). An interesting behavior was that if the website didn’t exist or was inaccessible for some reason, this field would become yellow. If the site did exist, the field would turn green.

Site doesn’t exist vs site does exist

This feedback mechanism made me realize that this was more than a simple CRUD app and this service must be issuing an HTTP request to the specified address. I put a Burp collaborator address in to confirm and sure enough I saw a request come in.

I was able to use the feedback mechanism to perform a local port scan and found a number of services online: SSH, SMTP, DNS, and a few others that I couldn’t identify by port. To get to work on proving the impact here, I ended up performing a similar set of tests as I did with DuckDuckGo: I checked redirect and gopher behavior and was lucky enough to find that both were available.

Now that I had gopher available, I was able to prove some impact by crafting an SMTP message in gopher and firing it at localhost:25 . Sure enough, moments later, a new email showed up in my inbox.

Aftermath

I was awarded $800 for this finding and received a rating of high for this finding.

Story 3— CMSSRF

I was invited to a recently opened private program. If you’ve never been invited to a program that just opened up, then you may not be aware that you’ll get this sense of “blood in the water”: you know that all of your fellow hackers and friends who also got an invite are going to start tearing this program up in the next couple of hours and if you don’t want to miss out on any low hanging fruit, you need to be quick.

I started my process off by deciding to not look at the core domain but jump to interesting subdomains. I ran sublist3r and discovered a couple of subdomains that mentioned cms in their domain name. In my experience, cms services tend to have many problems and that this might be a great place to take a look. I didn’t find much on the home page of this asset so I ran dirsearch to see if there was anything potentially interesting hidden on the asset.

Sure enough, after about 15 minutes of pounding the asset I found an endpoint that mentioned something about user management that would 302 to another endpoint. That endpoint had a login page for some management system. What’s more, there were some javascript files that referenced an API on this asset.

After discovering that the qa subdomain of this asset had unobfuscated javascript, I was able to figured out how to call the api and what calls I had available to me. One of the calls was named createWebRequest and took one url parameter in a POST body.

By this point in my hacking I already knew that this asset was running on AWS so I wasted no time in trying to issue a request to this api endpoint for the AWS metadata ip address. Sure enough, we got a hit.

Response from createwebrequest api

When I tried the AWS keys in the aws cli client, I found that I had an absurd level of access to dozens of S3 buckets, dozens more EC2 instances, Redis, etc. It was a critical in every sense of the word.

Aftermath

I was paid $3,000 (max payout) and the report was marked as a critical.

Story 4— Authenticated Request

This is the story of my most recent SSRF and, in a way, has been the most entertaining SSRF I’ve ever found. I started hacking on this new private program I was invited to. I started to look at the core asset for issues. I found a couple stored XSS at this point and was in a really great mood. I was about to wrap up shop when I took a look at the burp collaborator instance that I had left open. What I saw would surprise me.

As an aside: one of the things I do when I’m signing up for services that I’m going to hack on is that I use a burp collaborator instance to review email. It’s a good way for me to not pollute the email accounts I have with annoying advertisements after I’ve finished hacking on a service and it also lets me see if anything interesting is happening after the fact.

Anyway, when I looked at burp collaborator, I noticed that it had received an HTTP request with a User-Agent that mentioned the service that I was hacking on. I thought to myself, “Did I just accidentally discover a feature that could be vulnerable to SSRF?!”. I set out to figure out how to trigger this again.

Well, putting the timeline of requests together clearly explained what happened. I had just signed up for this service with an email like user@abc123.burpcollaborator.net and seconds later received both an SMTP message (email) and HTTP request for the homepage.

I signed up again with an email address like user@1.2.3.4.xip.io to see if I could check if 302 behavior was respected. After receiving the forwarded request in my burp collaborator instance, I wanted to confirm that gopher worked as I had noticed that this request was fronted by Squid Proxy (which would probably block my attempts to access the internal network).

Similar to the previous stories, I checked the gopher protocol on a 302 redirect and noted that I was able to use it to interact with internal services. Unfortunately, there was no feedback of any kind so I wouldn’t be able to perform a port scan here. I decided to try for a localhost smtp message anyway to see if I could get lucky.

Sure enough, after crafting a message and performing the attack, I received a new email in my inbox proving that this SSRF was real and dangerous.

Aftermath

Well, unlike the previous stories, I have yet to get paid for this finding. The good news is that my report has been triaged as a high so I’m just waiting for a final determination on the payout. I’ll probably post about it on my Twitter (which you should go follow if you haven’t yet).

Story 5 — Owning the Clout

I wish I could say that this story was inspired by Nahamsec and Daeken’s SSRF talk at Defcon but I had found this roughly a year prior to their talk being released. I was hacking on a new program for a company in the financial space. It was a product I had never seen (or heard of) before and was heavily involved in analytics. One of the features allowed you to upload images and store them for use in a couple of others features in the product.

Of course, one of the tests that I want to perform here is “Can I upload HTML” and if so, “What happens if that HTML fetches external resources”?

I tried uploading an HTML file but found that the service rejected the upload. I tried to see if I could lie about the content type in the multipart upload by changing it to say image/jpeg and sure enough it uploaded the document fine.

After making a request to this other endpoint that gave you updates on the status of the document, it would trigger an internal renderer/browser to issue a request to attacker.com.

In this case, I would’ve done more to prove impact, but it was pretty clear in this case that this was super unintended and I would’ve been able to access an internal system if I had an address to hit. I ended up reporting and getting a bounty.

Aftermath

I was compensated $1,000 for this finding and it was rated a medium. In retrospect, I should’ve done more to see if I could prove additional impact. I feel like that would’ve allowed me to be compensated much more highly than I was.

Wrap Up

SSRF is my favorite bug class due to its simplicity in execution, difficulty in mitigation, and the crazy number of ways that SSRF manifests (I’ve even heard of methods to use FTP to trigger it). Having the ability to interact with private, internal networks is incredibly fascinating to me and I hope that after reading this post and the stories within, you’ll feel more empowered to find and explore the inaccessible networks powering the modern world.

Shout-outs to Alyssa Herrera whose Piercing the Veil post inspired me to even look for and become fascinated with SSRF in the first place.

--

--