Hidden in plain sight: Brute-forcing Slack private files
ibuildings.nl
ibuildings.nl
- Slack chose to use a 6-hexadigit/24-bit "secret code" as the only/final code required to download "privately" shared files. That's way too short; people have botnets almost that big, such that even aggressive IP-based rate-limiting wouldn't stand a chance.
They might have also made these fairly common mistakes (which served to compound the vulnerability):
- Returning different/distinguishable error codes when the request matches correctly on some parts but not all. This allows attackers to guess each in turn.
- Considering values such as the "file ID" to provide additional security/entropy, when in fact these IDs are generated semi-sequentially, and thus a moderately-sophisticated attacker can narrow the search space dramatically.
- Considering values such as the "filename" to provide more security/entropy; however, you can make no guarantees about the length or uniqueness of filenames, so you shouldn't consider that a security feature at all.
Way to go slack.
If you have any critical data passing through slack, when you get owned, you won't be able to say say it wasn't entirely preventable.
I thought that went without saying, nut yes, it must be cryptographically random (not ideally—it must be).
Although, I'm a bit afraid of the birthday paradox, maybe we should use 20 bytes instead....
It pains me to see people inventing weird id schemes for every app and api.
This rationalization is illogical, which usually means someone is in conflict. From a logical standpoint, externally, it could be they are fixing something OR don't know about it OR don't care.
Given the conflicting rationalization, I'd say they didn't know about it and then made up an excuse instead of owning it.
The biggest 'fault' here I think lies squarely with HackerOne. They should've enforced their own guidelines and given me the option to publish in their system after 180 days. But I still don't have that option.
The 180 day guidance you reference falls under a "Last Resort" clause when "... the Response Team [is] unable or unwilling to provide a disclosure timeline". (which, at first glance, might not have been the case here?)
These "Last Resort" scenarios have not yet been fully codified. As a safety precaution, the workflow is still initiated manually with support as these scenarios are extremely rare and littered with edge cases. We've been learning a lot from studying disclosures like this one and you can expect to see the "Last Resort" workflow codified in the product in the future.
Now that the report has been Resolved, you should see the normal disclosure options available. Please always feel free to send me a note if you have any questions or feedback on our disclosure workflows - especially if we don't support your preferred route.
A few years ago, I wrote a little js tool to browse random Imgur images by guessing their urls (i.imgur.com/<5-digit code>) until it found one that succeeded. It would add the found image to an infinite-scrolling page. It was kinda fun to browse, and a lot of people seemed to enjoy playing with it.
After a couple years, though, Imgur suddenly started blocking access to their images on my site. It turned out they were blocking based on the referrer header.
I emailed them asking what was up, and apparently they were attempting to ensure the privacy of public-url images by manually going after any tools like mine (if you google 'random imgur', you'll find dozens).
I didn't bother circumventing this, I didn't want to be a jerk just to prove a point. I did try to point out that there were a number of ways to get around something as simple as a referrer block, but I don't think the customer support person I was dealing with was really interested in discussing the issue and I let it drop.
1. log into slack
2. share a private file
3. go to:
> https://api.slack.com/web 4. generate API token
5. copy the link to your private file, and paste as plain text.
6. a file id is in the link, somewhere
7. try out the file id by visiting links like:
> https://slack.com/api/files.info?token=#secret!&file=???????...? 8. see also:
> https://api.slack.com/methods/files.info 9. in the JSON output you will CTRL+F to see an address like:
> https://slack-files.com/#########-?????????-!!!!!!!!! 10. slackbot warns you, and only you, once and only once
that someone found that link. this notification may
get buried, or forgotten about. it is your only chance
to revoke the public link.
11. if you forget about that link, and it goes viral with
millions of visits, lots of luck gentlemen!Here's an example of an image uploaded via the GH issue tracker. Definitely public.
https://cloud.githubusercontent.com/assets/95562/7319912/200...
Slack, on the other hand, didn't have a big unguessable number... they had a very small number you could brute-force.
It's the difference between avoiding collisions between cooperating entities, and avoiding collisions from malicious entities.
For example, a version 1 UUID is just the combination of the computer's MAC address and the current time. This is sufficient to guarantee uniqueness (as long as you don't duplicate MAC addresses, and you wait at least 100 nanoseconds between generating successive UUIDs) but will be pretty easy to guess if you have a rough idea of when the UUID was generated and which manufacturer might have made the NIC on the machine where it was generated.
More abstractly, a version 4 UUID is just a couple of identifying bits and 122 random bits, but the nature of the random bits is not specified. Your UUID generator may well use a PRNG that is not cryptographically secure, which could still be good enough for cooperative uniqueness, but not good enough to avoid predictability. (For example, a PRNG seeded with the machine's MAC address and boot time would fit this.)
In short, don't rely on a UUID being secret unless you know exactly how it's being generated, all the way down to the underlying random number generator. And if you're going that far, you might as well just read 16 bytes from /dev/random and be done with it.
It's much more important to prevent malicious users from being able to access these files without the help of legitimate users. Which seems like an obvious thing to do, but it's what Slack has failed at here. It's impossible to tell from that one GitHub URL whether they get this right or not.
Now no-one can share files publicly in our companies slack channels