
Hidden in plain sight: Brute-forcing Slack private files - relaxnow
https://www.ibuildings.nl/blog/2015/11/hidden-plain-sight-brute-forcing-slack-private-files
======
dantillberg
Summary from my read of this: (the article does a great job of couching the
process of exploiting this, as well as motivating why these numbers are too
low, but here are the vulnerabilities...)

\- 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.

~~~
joshstrange
Also important to note it took Slack over a year to fix this issue often with
long stretches of silence even with requests from the bug hunter.

~~~
jwilkins
Interestingly, I put the same bug in at HackerOne 9 months ago. It was closed
as not applicable. So they had at least two independent reports of the same
bug and failed to understand it, acknowledge it and then fix it.

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.

------
wtbob
The correct answer for using URLs as capabilities (which is what a 'secret
URL' really is: a capability to a resource, which can be handed out, copied
&c.) is to use a 256-bit value as part of the URL. Thus, rather than
'[http://example.invalid/TEAM-DOC-SHORT-RAND/'](http://example.invalid/TEAM-
DOC-SHORT-RAND/') use
'[http://example.invalid/w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxT...](http://example.invalid/w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI').
If you're really paranoid, double the length. I guarantee it won't be guessed,
in either case.

~~~
relaxnow
Don't forget that it should ideally be cryptographically random. If the
sequence is predictable (like based on an auto incrementing number or on time)
then you might still be able to guess a 256-bit number.

~~~
wtbob
> Don't forget that it should ideally be cryptographically random.

I thought that went without saying, nut yes, it must be cryptographically
random (not ideally—it must be).

------
kordless
> We apologize for the delayed reply. We track these issues via our internal
> bug system, and only reply to the reporter once the bug is resolved
> internally. We generally ignore messages asking for updates, as we receive a
> high volume of these (even for non-issues).

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.

~~~
relaxnow
It probably means that they're not prioritising vulnerability reports. Which
is their prerogative honestly, but it doesn't make researchers happy to work
with you.

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.

~~~
arice
Great feedback, thanks.

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.

------
fishtoaster
This reminds me of my experience with Imgur's private images.

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.

~~~
Drdrdrq
I had a similar experience, though I was on the other side. Under brute force
login attack IT guy suggested I change login HTTP method from GET to POST
(which is more appropriate anyway). While I agreed with him that this is
better, I pointed out that this is very easy to circumvent. However he proved
me wrong - the attacks stopped after that (and I am quite sure it is not
because they gained access). Not all attackers are very determined...

------
tptacek
What kills me is that this seems like such an unforced error. Just make
256-bit random tokens. They're private URLs. Who cares how ugly they are?

~~~
qyv
Not to mention making public file sharing explicit instead of implicit, or at
least giving the option to exclude specific files from a public URL.

------
NKCSS
Damn, over a year to fix this? It's good that they did in the end, but the
timeline is just crazy.

~~~
elcct
Maybe someone needed time to brute force all those tasty files hanging around
;)

------
thmpp
The best part of the story actually comes at the end, back-and-forth messaging
with slack about the bug report

~~~
braum
definitely, I'm disappointed with slacks responses. We did a trial and have
had some correspondence with their support team which has been excellent to
date. So I assumed they were above some of this silicon valley elitism. I'm
glad to see this kind of public disclosure. We have been a customer since that
initial trial, we stopped using hipchat.

~~~
relaxnow
To be fair, most of the bad correspondence was from 2014. Their new
representative 'Leigh' appears to be doing excellent work. Also we're still
happy users of Slack, I would just never trust them with secrets :-).

------
macNchz
If I were responsible for security at Slack, the thought of potentially
leaking uploaded files like this would keep me up at night. Slack has gained
such wide adoption-think of the things that people are sharing with their
coworkers all day, every day. Someone with ill intent could have found so many
valuable things.

------
the_wolf
Simple how-to instructions:

    
    
      1. log into slack
    
      2. share a private file
    
      3. go to: 
    

> [https://api.slack.com/web](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=???????...](https://slack.com/api/files.info?token=#secret!&file=?????????)?
    
    
      8. see also:
    

>
> [https://api.slack.com/methods/files.info](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/#########-?????????-!!!!!!!!](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!

------
wzm
Do a google search for
site:[https://s3.amazonaws.com/uploads.hipchat.com/](https://s3.amazonaws.com/uploads.hipchat.com/)
, Atlassian does the same thing, and hasn't really addressed customer
questions about it for a long time.
[https://help.hipchat.com/forums/138883-suggestions-
ideas/sug...](https://help.hipchat.com/forums/138883-suggestions-
ideas/suggestions/5985543-sent-files-should-not-be-public-amazon-s3-links-t)
is a long running Atlassian support ticket for this.

------
thom_nic
Github does something similar, if you drag an image into the textarea in their
issue tracker, it uploads the image to (I think) a public URL. I've considered
what this could mean for teams with private projects who might e.g. attach
screenshots with sensitive information.

Here's an example of an image uploaded via the GH issue tracker. Definitely
public.

[https://cloud.githubusercontent.com/assets/95562/7319912/200...](https://cloud.githubusercontent.com/assets/95562/7319912/2004f03a-ea67-11e4-852f-f558af75b078.gif)

~~~
willvarfar
The good news is that github puts a uuid in the url, so its unguessable.

Slack, on the other hand, didn't have a big unguessable number... they had a
very small number you could brute-force.

~~~
BillinghamJ
UUIDs are (usually) generated in a systematic fashion, so large parts of them
are often possible to determine ahead of time.

~~~
gruez
Not in guid v4

~~~
mikeash
Unfortunately that is not necessarily true, as nothing requires a UUIDv4
generator to use a cryptographically secure source of randomness.

------
delinka
This should indeed inform potential users of additional security risks of
using Slack, especially for communication of corporate confidential
information. That said, this isn't a realistic threat to my own _social_ use
of Slack.

------
jkern
Similarly to using a hash function that is purposefully slow, wouldn't it be a
good idea to introduce some artificial latency when responding to requests for
urls like this?

------
rhuddleston
I see slack now has the option "Disable public file URL creation"

Now no-one can share files publicly in our companies slack channels

