The first in-band signalling attack I came across was the blue box [1], invented in the late 1960s. It still occasionally worked in the 1980s on older phone systems. It amazes me that we're still creating new systems vulnerable to in-band attacks.
The first in-band signalling attack I came across was the blue box [1], invented in the late 1960s. It still occasionally worked in the 1980s on older phone systems. It amazes me that we're still creating new systems vulnerable to in-band attacks.
If the back-end server is already proxying whatever request it receives from the front-end proxy server, why was it necessary to first get it to redirect an HTTP 1.1 header to an https request? The only thing I could think of was that maybe it has something to do with getting the unencrypted cookie, but according to their description, the forwarded request with the cookie doesn't happen until after the redirect to https anyway.
I know I'm missing something obvious here.
I find it hard to believe that any browser would keep the original `Cookie: ...` headers in a redirected request to a different origin.
I still wouldn't expect an Electron app to subvert basic browser sandboxing by default, particularly where they wouldn't have expected to need to redirect users to other domains with cookies intact. It seems like they'd need to go out of their way to enable that.
I wonder if it has to do with the sign-in tokens they send or otherwise allowing the user to move between the browser and the app within their account. For example, when you're in the app and click "Manage Users" and it sends you to a management dashboard in the browser. or when you click a link with an auth token in the browser and it launches you into the app.
In that case, I'd expect all cookies etc. to be forwarded.
I understand that the backend is parsing different requests than the frontend, but I don't see how the cookies ever get sent to the attacker's domain.
I understand three requests as moving like this:
1. attacker -> frontend -> backend (partially) -> frontend -> attacker
2. victim -> frontend -> backend (including a bit of 1) -> frontend -> victim
3. (after 301) victim -> malicious server
The second request results in a 301, causing the victim's client to make a new request to the target of the 301. This new request does not include cookies, because it is to a different domain.What am I misunderstanding? When do the cookies get sent to the malicious server?
Skip down to the "Explanation of malicious request" section.
If you take all of this together, it's all leading to Slack's back-end server returning to the user's client a 301 response that tells the user's browser to send a new request to the hacker's domain. However, when the browser does this, it's to a different domain, so typically the slack cookies would not get sent with that request, unless I'm missing something.
The "Explanation of malicious request" section doesn't explain this, either. All it explains is that all this effort was to get Slack's server to send a 301 response to the user's browser pointing to the hacker's server, but then it glosses over any information about how the cookies are getting sent with a single sentence, "and all cookies (including 'd') get redirected there too.... :("
My question is, how?
Is something going on where Slack's back-end servers are explicitly setting response headers that allow their cookies to be sent to the hacker's domain? Is there some additional vulnerability that's allowing the browser to send the cookie across domains on a 301 redirect?
Edit: looks like terom above me had the same idea before I did
Edit: this seems likely to not be what he was saying and also not the case.
The TL;DR is that you can make the front-end see something like GET /my/supplied/url\nX:X at the end of your request, but the back-end see it as the beginning of the next request, (and the X:X turns the real GET/POST whatever info a custom header of X:XGET /real/request/url), and Slack returns that request with cookies for that person, but to a domain controlled by you when it comes back. The diagrams included in the post and the accompanying text do a better job of explaining it than I am, I think.
This is my understanding of the bug:
* Attacker sends malformed request to slack servers. Request gets split in two. First part results in something sent back to attacker, next part of request is treated as a prefix to the next legit (victim's) http request made
* Slack backend server responds to victim's request treating it as having part of the attacker's request prefixed. The merged request results in a 301 http redirect response, redirecting the victim to an attacker controlled domain.
* Victim's browser gets the 301, and follows the redirect. When following the redirect, the cookie header is somehow sent to the attacker's site <-- Part i don't understand here
I don't understand why the victim's browser would send the cookie header when following a redirect to a different domain. I don't understand how causing the victim to follow a cross-domain redirect would allow the attacker to extract the victim's cookie.
That's the part I'm stuck on. Is there some browser behavior I'm not remembering where it sends sensitive cookies across domains just because one redirected the browser to the other?
requests.request("GET", "http://localhost/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fget", headers={"x-foo": "bar", "Cookies": "abc=dcv;"}).json()
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Cookies": "abc=dcv;",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0",
"X-Amzn-Trace-Id": "Root=1-5e6bebe1-cd6e71729a818015a06aa7cb",
"X-Foo": "bar"
},
"origin": "91.58.8.128",
"url": "http://httpbin.org/get"
}
I'm running a local copy of httpbin on localhost, so python's request should not send sensitive headers for redirects but it does. Golang is a bit more explicit about it's http client behavior:> • when forwarding sensitive headers like "Authorization", "WWW-Authenticate", and "Cookie" to untrusted targets. These headers will be ignored when following a redirect to a domain that is not a subdomain match or exact match of the initial domain. For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" will forward the sensitive headers, but a redirect to "bar.com" will not.
https://golang.org/pkg/net/http/#Client
Though this might also cause problems if you are using sensitive non standard headers such as X-Token for token authentication, etc.
So while you could probably mitigate this vulnerability on some clients, you are trusting the server to only redirect you to trusted URLs which is not the case here.
Another way would be to make it so the next request is the content of a param which is displayed in some manner. For example, if it's the content to a message post to a slack forum.
Those different than what they outline in the article, but neither seems unlikely to be found on the back-end in some manner. Redirects are often locked down, but not always, and finding something that sends a variable back to you (such as a form port target that you know will error "helpfully") doesn't seem like it would be hard to me.
> I imagine one way to do it is to make the attacking request to a redirect. Since the back-end conflates the attacking request and the next request (or a portion of the next request), you can get that next request, including the headers (i.e. cookies) as the body/payload redirected to that external site. If the attacker controls that external site, they then can see the cookie data.
Are you suggesting that if the backend server was setup as an HTTP proxy, you could get the backend server to proxy the merged request somewhere else?
That's true, but it seems pretty unlikey that the backend servers would be setup that way.
Although i think i might just be misunderstanding what you mean.
> Another way would be to make it so the next request is the content of a param which is displayed in some manner. For example, if it's the content to a message post to a slack forum.
I.e. you're suggesting basically using this as a CSRF attack to post part of the victims request (including secret cookie) somewhere public to retrieve later?
I think this would work in certain situations but would be difficult to pull off. Its a bit more complicated because if the site was using csrf tokens you would need to make sure the victim request was a POST with a token,but that is easy enough - retry until it happens. The bigger issue is the request body would be kind of malformed. If the endpoint you are posting to accepts application/x-www-urlencoded (and not json), i imagine its interpreted super liberally. I guess the biggest issue is if ; is considered a form field separator (since user-agent usually has ; in it). I think some standards tried to promote that, but i dont know if webservers actually implement it. If they do,it would be very difficult for the attacker to control the name of the form field, and thus actually get the part they wanted publicly posted
I'm suggesting that since the back-end server may see the requests as a single request with the second request as part of teh payload of your crafted request, the question then becomes one of "In what ways can I get this site to show one of my params". That's similar to a CSRF, but simpler in that that you don't care that it's unescaped, you just care that it's in the source in some way, since it's what should be displayed back to you for your request.
My form example is straightforward. Imagine you are requesting http://slack.com/help/search. If you reorder the params slightly, such that the "query" param is last, you might get a good chunk of the next request to be sent to the back-end as your query param, which slack will helpfully display for you both on the page and in in the search input on the resulting page. It will clean our newlines, and likely some other characters, but that hardly seems a problem.
> I.e. you're suggesting basically using this as a CSRF attack to post part of the victims request (including secret cookie) somewhere public to retrieve later?
Yep.
> I think this would work in certain situations but would be difficult to pull off. Its a bit more complicated because if the site was using csrf tokens...
I just found one and used it in an example above. Took me less than 5 minutes (I tried a different form that did have a csrf token first).
> The bigger issue is the request body would be kind of malformed.
That's a problem for if you want it to look legit to someone else. If you just have to note that it has the equivalent of /\sCookie:\s(\S+)\s/ in it... well that's not too bad. Removing the newlines makes it all one string, but in the example above they helpfully replace them with spaces. Even if they didn't, it's probably not hard to see where the next header begins.
> I guess the biggest issue is if ; is considered a form field separator (since user-agent usually has ; in it).
Depends on how you do it. My example above uses GET, so any ampersand (which is probably likely to exist in many queries) will throw it off. A POST might offer more options. Even with the GET, you might be able to run it enough times to find a post as the included second request, and that's less likely to have an ampersand in the URL, and you would probably scoop up all the headers at least. If you post with multipart mime, depending on how lenient it is on a final terminator, you might not have any problem at all if you find the right page to post to.
I did just confirm that that same search page doesn't seem to honor params passed in a POST, so that's good (for Slack).
I think the bottom line is that once you can make part or all of someone else's HTTP request show as part of your request's payload, it's a drastically lower bar for exploiting, as there are many possible ways to exfiltrate the data at that point, and they only have to screw up by allowing one of them to work.
But the response to the mangled request goes to the victim not the attacker. You need someway to get it from the victim to the attacker (hence use of redirects in the original bug bounty).
> My example above uses GET, so any ampersand (which is probably likely to exist in many queries) will throw it off.
The point i was trying to make is that various specs suggest that semicolons should be treated as an alternative to amparsands, and they are much more common in headers (especially in user-agent). However i imagine much server software doesnt follow that reccomendation so maybe its a moot thing to worry about.
That's the route they took, but it's unclear to me if it wouldn't work the other way as well (or if that's more likely to work with TE.CL setup than the CL.TE one in place.
If you can use the difference in what is specced to make the back-end think your request is smaller than it is, can you reverse that to make it think it's bigger than it is, so some of the next request leaks in? If that makes the next request invalid, is that gracefully handled by failing that one (victim) request, or does it cause the whole set of requests to fail? I don't know the answer to these questions, but it certainly seems like there's a lot to explore here.
Most of the books I've read basically paint Jobs as a goody-two-shoes type who cringed at the illegality of phreaking - while saying he probably was thinking ahead, knowing he already wanted to start his own company and didn't want stuff like this to come back and haunt him.
It was an interesting dichotomy to me.
Another approach is reliable encapsulation. Ethernet and TCP/IP, for example, are technically all in one channel. But packet content won't be mistaken for protocol information. You could also look at things like SSH and how it has multiple channels: https://tools.ietf.org/html/rfc4254
I'm sure there are other approaches, too, and I hope people will mention them.