$30 off During Our Annual Pro Sale. View Details »

Killing 🐦with 🐛🐛

Killing 🐦with 🐛🐛

A journey from subdomain #SELFXSS to site-wide #CSRF @Twitter. A private talk I delivered in 2016.

filedescriptor

August 25, 2016
Tweet

More Decks by filedescriptor

Other Decks in Technology

Transcript

  1. Killing with
    A journey from subdomain #SELFXSS to site-wide
    #CSRF @Twitter

    View Slide

  2. Whoami
    @filedescriptor
    XSS enthusiast
    Bug bounty hunter
    Pen-tester of Cure53
    2

    View Slide

  3. File Upload XSS
    ads.twitter.com
    3

    View Slide

  4. Uploaded File: https://ton.twitter.com/i/ton/data/ta_data/3185435460/1471786489032.txt
    4

    View Slide

  5. Nice. Where is the XSS?
    5
    Won’t execute
    Because the file is not interpreted as HTML

    View Slide

  6. File type
    • Content-Type (a.k.a. MIME type) identifies the file
    type
    • Browsers will determine how to interpret the file
    based on the Content-Type header
    • In this case, the file is served with text/plain
    • …which represents a plain text document
    6

    View Slide

  7. Can browsers ignore
    Content-Type?
    Spoiler alert: yes
    7

    View Slide

  8. MIME Sniffing
    • If the Content-Type for a file is unrecognised,
    browsers will try to fix it with MIME Sniffing
    • …which determines the file type by inspecting the
    content with different algorithms (e.g. Magic
    Number)
    • But text/plain is a perfectly recognisable MIME
    type, how do we activate MIME Sniffing in this
    case?

    View Slide

  9. MIME Sniffing & IE
    • Supposedly, the Content-Type header takes the
    highest precedence according to the specs
    • However, older versions of Internet Explorer will first
    guess the file type by performing MIME Sniffing
    • If a HTML sequence is found within the first

    256 bytes, then the file is parsed as HTML
    9

    View Slide

  10. 10
    Enabled by default

    View Slide

  11. Still won’t execute
    11

    View Slide

  12. Anti-Sniffing
    • Turns out the header X-Content-Type-Options:
    nosniff is also served with the file
    • This header tells browsers not to perform MIME
    Sniffing
    • Now Internet Explorer plays by the rules
    12

    View Slide

  13. Fuzzing file extension
    • The upload endpoint only accept certain file types
    • Namely, CSV file (.csv) and TXT file (.txt)
    • We could try different file extensions and hopefully
    the validation is based on a blacklist
    • The idea is that, even though we cannot upload
    HTML files, there are other file types that can
    execute JavaScript
    13

    View Slide

  14. Fuzzing file extension (Cont.)
    Extension Results Content-Type
    .csv Accepted text/csv
    .html Rejected N/A
    .htm Rejected N/A
    .xml Rejected N/A
    .svg Rejected N/A
    .swf Rejected N/A
    .whatever Accepted No Content-Type
    14

    View Slide

  15. The return of MIME Sniffing
    • Unrecognised extensions are accepted
    • No mapping for the corresponding MIME type
    • No Content-Type for the file
    • MIME Sniffing is then activated on browsers to
    determine the file type
    • X-Content-Type-Options: nosniff is ignored in this
    case
    15

    View Slide

  16. 16

    View Slide

  17. 17

    View Slide

  18. 18

    View Slide


  19. 19

    View Slide

  20. Not so fast
    • ton.twitter.com hosts only
    static files, no damage can be
    done &
    • Files on ton.twitter.com can
    only be read by the uploader
    (Self-XSS)
    20

    View Slide

  21. Ideas of making it harmful
    • Find a page on the main domain (twitter.com) which sets
    document.domain="twitter.com"
    • An old technique for sub/parent domain communication

    ❌ No such page was found
    • Steal session id
    • The session cookie is set on *.twitter.com!

    ❌ Cannot read due to httpOnly
    • Control CSRF token


    21

    View Slide

  22. Where the CSRF token is stored
    • Rails style cookie
    • _twitter_sess=BAh7CSIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGx
    lcjo6Rmxhc2g6OkZsYXNo250ASGFzaHsABjoKQHVzZWR7ADoPY3Jl
    YXRlZF9hdGwrCABm1thVAToMY3NyZl9p250AZCIlYjRhMzU1MjBiM
    2U3MGQ1ZDlmZTliMTUxZDkwNjk2YWE6B2lkIiVhODVm250AYzY0Zj
    IxYTRiZDcwN2EyZTAxZDIwNGNjNDJlNA%253D%253D--
    e7a90f98ff4d84ddf3340403841cdd8624f8ecce
    • Decoded:

    {“flashIC:'ActionController::Flash::FlashHash{:@used{
    :created_atl
    +:csrf_id"%b4a35520b3e70d5d9fe9b151d90696aa:id"%a85fc
    64f21a4bd707a2e01d204cc42e4
    Base64 encoded serialised data
    Signature
    22

    View Slide

  23. Observation
    • _twitter_sess is accessible to subdomains
    • This cookie is used to store all kinds of
    miscellaneous data, but not readable due to
    httpOnly
    • However, this cookie is not bound to the session
    (session ID is stored in a separate cookie)
    • Could we replace victim’s _twitter_sess with ours,
    and force victim to use our CSRF token?
    23

    View Slide

  24. httpOnly in action
    no reading, no writing, ugh
    24

    View Slide

  25. httpOnly in action
    no reading, no writing, ugh
    25

    View Slide

  26. Cookie Tossing
    26

    View Slide

  27. Cookie Tossing
    • Cookie key consists of the tuple (name, domain, path)
    • Subdomain can set cookies for parent domain and the
    effective path
    • foo is a different cookie from foo; path=/api. Their
    cookie flags are also separate
    • However, the Cookie header only contains the name
    and value for each of the cookies
    • Server will see two cookies with the same name
    27

    View Slide

  28. 28

    View Slide

  29. Cookie Precedence
    • Specs don’t tell servers how to handle duplicated
    cookies
    • Usually the first occurrence is picked
    • But how do browsers arrange their order?
    29

    View Slide

  30. –RFC 6265
    “Cookies with longer paths are listed before
    cookies with shorter paths.”
    30

    View Slide

  31. 31

    View Slide

  32. Side-wide CSRF
    • Most of the APIs on Twitter are under the path /i/
    • e.g. https://twitter.com/i/tweet/create, https://twitter.com/i/
    user/follow
    • Running the below code snippet on ton.twitter.com…
    • document.cookie =
    '_twitter_sess=ATTACKER_SESS;
    domain=.twitter.com; path=/i'
    • …overrides (shadows) victim's CSRF token!
    32

    View Slide

  33. Not so fast
    • ton.twitter.com hosts only
    static files, no damage can be
    done ✅
    • Files on ton.twitter.com can
    only be read by the uploader
    (Self-XSS) &
    33

    View Slide

  34. Ideas of getting rid of the “self-”
    • Check if the upload endpoint is vulnerable to CSRF

    ❌No dice
    • Social Engineering
    • Ask the victim to upload our crafted file

    ❌Infeasible
    • Login & Logout CSRF


    34

    View Slide

  35. Attack flow
    1. Logout victim’s session
    2. Login to attacker’s account
    3. Navigate victim to the XSS file
    4. Override and fixate CSRF token
    5. Logout attacker’s account
    6. Next time victim logs in, attacker’s CSRF token will be
    used
    35

    View Slide

  36. Can we do better?
    • Now the attack is feasible, but
    • …victim needs to re-login
    • Victim also needs to visit attacker’s website again
    to trigger the CSRF
    • Can we authorise a request without involving
    session?
    36

    View Slide

  37. 37

    View Slide

  38. OAuth
    • An open protocol to allow secure authorisation to access protected resources
    • Twitter uses OAuth 1.0a, while the majority uses OAuth 2.0
    • Instead of session, OAuth uses access_token for request authorisation
    • Example:

    GET /1.1/statuses/user_timeline.json HTTP/1.1

    Authorization: OAuth
    oauth_consumer_key="DC0sePOBbQ8bYdC8r4Smg",oauth_signature_
    method="HMACSHA1",oauth_timestamp="14683423091",oauth_nonce
    ="9537061051",oauth_version="1.0",oauth_token="3185435460-
    FTrisxX5Wc7c4KZxEcUPApBIAkobAMHTYbVNU4k",oauth_signature="m
    VOi8NqfO8HzvKLpVv44LlN9eis%3D"

    Host: api.twitter.com
    OAuth parameter
    38

    View Slide

  39. XSS ❤ OAuth
    • ton.twitter.com accepts OAuth requests
    • GET /i/ton/data/ta_data/3185435460/xss.whatever HTTP/1.1

    Authorization: OAuth
    oauth_consumer_key="DC0sePOBbQ8bYdC8r4Smg",oauth_signature_
    method="HMACSHA1",oauth_timestamp="1468329288",oauth_nonce=
    "1930461031",oauth_version="1.0",oauth_token="3185435460-
    FTrisxX5Wc7c4KZxEcUPApBIAkobAMHTYbVNU4k",oauth_signature="l
    xJ7Of7imlvGnLQiHFqTO4EhLwA%3D"

    Host: ton.twitter.com
    • HTTP/1.1 200 OK

    […]


    alert(1)
    39

    View Slide

  40. One step away
    • The file can now be accessed without a session
    • However, we need browsers to attach the OAuth
    parameter in the Authorisation header
    • There is no way to do it without violating SOP, at
    least for navigation
    • Dead end? Let’s check the spec…
    40

    View Slide

  41. 41

    View Slide

  42. • GET /i/ton/data/ta_data/3185435460/xss.whatever HTTP/1.1

    Authorization: OAuth
    oauth_consumer_key="DC0sePOBbQ8bYdC8r4Smg",oauth_signatu
    re_method="HMACSHA1",oauth_timestamp="1468329288",oauth_
    nonce="1930461031",oauth_version="1.0",oauth_token="3185
    435460FTrisxX5Wc7c4KZxEcUPApBIAkobAMHTYbVNU4k",oauth_sig
    nature="lxJ7Of7imlvGnLQiHFqTO4EhLwA%3D"

    Host: ton.twitter.com
    • https://ton.twitter.com/i/ton/data/ta_data/3185435460/
    xss.whatever?
    oauth_consumer_key=DC0sePOBbQ8bYdC8r4Smg&oauth_signature
    _method=HMACSHA1&oauth_timestamp=1468329288&oauth_nonce=
    1930461031&oauth_version=1.0&oauth_token=3185435460FTris
    xX5Wc7c4KZxEcUPApBIAkobAMHTYbVNU4k&oauth_signature=lxJ7O
    f7imlvGnLQiHFqTO4EhLwA%3D
    42

    View Slide

  43. 43

    View Slide


  44. 44

    View Slide

  45. Putting everything together
    1. Upload a HTML file with an
    unknown extension
    2. Attach OAuth parameter to
    the file URL
    3. Make victim visit the file
    4. The payload uses Cookie
    Tossing to override and
    fixate victim’s CSRF token
    5. The payload performs
    actions on victim’s behalf
    45

    View Slide

  46. What went wrong
    • File upload should validate the file type using a
    white-list filter
    • Static files should be served from a separate
    sandboxed domain
    • CSRF token should be bound to user’s session
    46

    View Slide

  47. 47

    View Slide

  48. Mitigating Cookie Tossing &
    stuff
    • Drop duplicated cookies
    • GitHub’s approach
    • Cookie Prefixes
    • Currently only available on Chrome
    • If a cookie name begins with __Host-, the cookie cannot be accessed/modified from
    subdomains
    • Same-Site Cookies
    • Also available on Chrome only
    • Cookies with the SameSite flag will not be included the the request is issued cross-
    origin
    • The “ultimate” CSRF killer
    48

    View Slide

  49. 49

    View Slide

  50. End
    Questions? Comments?
    Thanks a lot!

    View Slide