Archived article from 20 October 2016. Its content reflects the state of things at the time. Reconstructed from the web archive of secalert.net - view the original.
Summary. During a black-box test under the Slack bug bounty program, an access-control filter in front of the Apache mod_status handler could be bypassed by prefixing the path with several forward slashes. The filter stripped only a single leading slash and checked a prefix without recursing, so a request such as /////server-status slipped through. Unauthenticated access to server-status exposed sensitive request data (callback secrets and API tokens) for any Slack site, and a related slash-count bypass reached an internal admin panel that allowed reading user metadata and, through a reset controller, full account takeover. Both issues were reported, verified and fixed, and each earned a bounty.
Intro
In this post I walk through my thought process while performing a black-box test as part of the Slack bug bounty program.
Thanks to the Slack security team
I would like to thank Leigh Honeywell and Max Feldman of the Slack security team for their courteous, professional communication and coordination throughout the reporting process.
Information gathering
To understand the infrastructure and learn which framework was in use, I started by inspecting the HTTP response headers and saw that Slack was running an Apache httpd server. I then tried to locate common Apache directories and handlers such as /icons/README, /manual/, /server-info and /server-status.
May I access your internal data, please?
Slack ran mod_status on the web server. The status module lets an administrator see how well the server is performing and which resources have been requested by which IP addresses. An attacker can use this information to craft an attack against the server.
https://secalert-hackerone.slack.com/server-statusWhen I tried to reach the server-status handler, the server redirected me to a login page on the *.tinyspeck.com domain, so the path was protected.
Out of scope. Now what?
Brute forcing is not permitted under the rules of the Slack bug bounty program, so the obvious next thought, bypassing the login page with injection techniques, was out: the login page itself lived on a fully qualified domain outside the allowed scope. I had to find a way to stay within the permitted scope of secalert-hackerone.slack.com.
Routing? Filter? Black-box testing
My first thought was that, given Apache httpd and mod_status, the redirect might be driven by the rewrite module. mod_rewrite is a powerful Apache module for rewriting URLs on the fly, but with that power comes risk: it is easy to misconfigure, and misconfigurations can turn into security issues. Take one of the configurations from the mod_rewrite documentation:
RewriteRule ^/somepath(.*) /otherpath$1 [R]If a rule like this were in place, a misconfigured RewriteRule might be bypassable simply by adding a slash. Requesting:
http://yourserver/somepath/secalertredirects and returns http://yourserver/otherpath/secalert as expected. Requesting:
http://yourserver//somepath/secalertbypasses this particular rule. In Slack's case this approach did not work, so I had to think differently.
I began experimenting with different representations of a slash to try to bypass a simple string-based filter.
https://secalert-hackerone.slack.com/%2fserver-status%2f
https://secalert-hackerone.slack.com/%252fserver-statusI also tried the right-to-left override (RTLO) sequence, submitting it followed by the reversed string.
https://secalert-hackerone.slack.com/{u+202e here}sutats-revres
https://secalert-hackerone.slack.com/%e2%80%aesutats-revresNone of this worked at first.
Access control bypass
After a few more tests, I suspected the framework used a route map, and that I might bypass the routing or access control by adding multiple forward slashes: if the filter only checked whether the string started with a particular prefix and stripped a single leading slash without doing so recursively, extra slashes could slip through. This finally worked.
https://secalert-hackerone.slack.com/////server-statusSuccess.
A $50 bounty?
While writing up the report for Slack on HackerOne, I added a few screenshots as proof of concept. At that point I expected the minimum bounty of $50 for a misconfiguration, on the assumption that server-status would not expose anything sensitive as long as the requested resources belonged to my own workspace. Then I logged out of my Slack account and requested server-status while unauthenticated, and it still worked. In other words, an attacker could gain unauthorised access to the requested resources of any Slack site through the server-status handler of a given workspace.
Secrets exposed, higher bounty
I noticed that some of the logged requests looked like /callbacks/chat.php?secret=... and /users.list?token=..., which were clearly sensitive. I included these in the report, which most likely increased the bounty I eventually received. Thanks again to Slack for the generous reward.
Google indexing
Encouraged by the first bounty, I kept looking for more issues. I searched Google for common file extensions on Slack's sites and found cached URLs, which suggested that Slack had a backend admin panel that Google had indexed at some point. Trying to reach those pages redirected me to the login page again, and since Slack had already fixed the previously reported issue, I did not expect much.
Access control bypass, part II
After a few requests, I found that on the specific path to the administrative panel I could bypass the restriction using exactly four slashes, which only worked on this particular controller.
Backend access, second bounty
Slack employees have access to a backend admin panel called mission control. Authorised staff can read a large amount of metadata about Slack users and workspaces by passing an id to the relevant controller. Because the required id was exposed in the rendered HTML of my own workspace, I was able to read the metadata associated with my own account and sent those screenshots to the Slack security team. I also found that an attacker could reset any user's password by guessing their id and sending a request to the corresponding reset controller in mission control, which would allow full account takeover. I received an additional bounty for this issue.
Outro
Be patient. Sometimes a flaw that looks trivial from a purely technical standpoint carries a high business or data-privacy impact, and the affected company may rate the risk very differently from how you did at first.
Timeline
April 11th 2016: issue identified and reported
April 11th 2016: verified by Slack
April 13th 2016: issue fixed
April 13th 2016: bounty received
April 14th 2016: second issue identified and reported
April 14th 2016: issue verified by Slack
April 24th 2016: issue fixed globally
April 24th 2016: additional bounty received
October 20th 2016: this write-up was publishedReferences
- Slack API: users.list method
- Apache httpd: mod_status documentation