Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A methodology using fuzzing and info disclosure

Frans Rosén
October 01, 2020

A methodology using fuzzing and info disclosure

My keynote from the first BSides Ahmedabad in Oct 2019.

Youtube: https://www.youtube.com/watch?v=rAuuN0OohJc

Frans Rosén

October 01, 2020
Tweet

More Decks by Frans Rosén

Other Decks in Research

Transcript

  1. FRANS ROSÉN Security Advisor @detectify CTO @centra HackerOne #5 @

    /leaderboard/all-time Blogs at labs.detectify.com @fransrosen
  2. FRANS ROSÉN Rundown • Imaginary app structure and methodology on

    breaking it • Real-life vulnerabilities found
 using these techniques
  3. FRANS ROSÉN Rundown • Imaginary app structure and methodology on

    breaking it • Real-life vulnerabilities found
 using these techniques • Many ways to do this, this is just one example!
  4. FRANS ROSÉN Client based micro services business.example.com /api/v1/users /api/v1/users/123 …

    /api/v3/invoices /api/v3/invoices/123 … /api/v2/messages /api/v2/messages/123 … CORS-requests to different apps: SPA + a lot of JS invoice.example.com conversation.example.com api.example.com
  5. FRANS ROSÉN API-endpoints per microservice • invoice.example.com Found in JS:

    /invoices* /accounts* /api /api/v3 /api/v3/invoices* /api/v3/accounts* … Save to path-lists: /api/v3/invoices /api/v3/invoices/1234 /api/v3/accounts /api/v3/accounts/1234
  6. FRANS ROSÉN • invoice.example.com Found in JS: /invoices* /accounts* /api

    /api/v3 /api/v3/invoices* /api/v3/accounts* … Save to path-lists: /api/v3/invoices /api/v3/invoices/1234 /api/v3/accounts /api/v3/accounts/1234 The * tells us it supports direct requests or additional paths for IDs or similar: /invoices/123
  7. FRANS ROSÉN API-endpoints per microservice • conversation.example.com /api/v2/online /api/v2/conversations /api/v2/websocket

    Found in JS: /online /conversations* /websocket /api/v2 /api/v2/conversations* … Save to path-lists:
  8. FRANS ROSÉN Continue to curate lists Find more endpoints: •

    Desktop client • Web-archive • PHP/Java/Golang-SDKs • npm/composer/yarn • Documentation
  9. FRANS ROSÉN Now we have this • List of all

    available endpoints: /conversations* /invoices* /users* …
  10. FRANS ROSÉN Now we have this • List of all

    available endpoints: • Also separate prefixes: /api /api/v3 /api/v2 /api/v1 … /conversations* /invoices* /users* …
  11. FRANS ROSÉN Now we have this • List of all

    available endpoints: • Also separate prefixes: /api /api/v3 /api/v2 /api/v1 … /conversations* /invoices* /users* … • And all subdomains used: invoice.example.com api.example.com business.example.com …
  12. FRANS ROSÉN Now, combine it all: • Test everything on

    everything
 subdomain-list * path-prefix-list * path-suffix-list • Add additional standard fuzz to suffix-list test " '
 # …
  13. FRANS ROSÉN What we might find • New or old

    endpoints not in use
 Might leak more data than the current one

  14. FRANS ROSÉN What we might find • New or old

    endpoints not in use
 Might leak more data than the current one
 
 Example of this happening IRL!
  15. FRANS ROSÉN { "boards": [ { "id": 123, "user": 124,

    "username": "test" … } ] } API currently in use, v2 /api/v2/boards/123
  16. FRANS ROSÉN /api/v4/boards/123 { "includes": {"user": false}, "boards": [ {

    "id": 123, "user": 124, "username": "test" … } ] v4 was a JSON-API not being used
  17. FRANS ROSÉN /api/v4/boards/123 { "includes": {"user": false}, "boards": [ {

    "id": 123, "user": 124, "username": "test" … } ] Tells us what to include
  18. FRANS ROSÉN /api/v4/boards/123? include=user { "includes": {"user": true}, "boards": [

    { "attributes": { "user": { "email": "[email protected]", "phone": "004324235342" … } "New version of JSON-API for message boards leaked emails + phone numbers for all users"
  19. FRANS ROSÉN What we might find • New or old

    endpoints not in use
 Might leak more data than the current one • That the micro-services might connect server-side:
 SSRF, path-traversal, bypass query-strings used…

  20. FRANS ROSÉN What we might find • New or old

    endpoints not in use
 Might leak more data than the current one • That the micro-services might connect server-side:
 SSRF, path-traversal, bypass query-strings used…
 
 Example of this happening IRL!
  21. FRANS ROSÉN Regular API-endpoint to fetch invoices /api/v3/invoices/123 { "invoice":

    { "url": "/api/v3/invoices/ 123?token=xyz&expire=123 } }
  22. FRANS ROSÉN Sent to v3, error with v1? /api/v3/invoices/" {

    "error": "Bad URI: /api/v1/ invoices/\"" }
  23. FRANS ROSÉN Possible explanation Endpoint at /api/v3/invoices/{id} makes an internal

    call to a different service: route('/api/v3/invoices/{id}', () => { return api.call( `http://internal-api/api/v1/invoices/${id}? token=${userToken}` ) })
  24. FRANS ROSÉN Possible explanation Endpoint at /api/v3/invoices/{id} makes an internal

    call to a different service: route('/api/v3/invoices/{id}', () => { return api.call( `http://internal-api/api/v1/invoices/${id}? token=${userToken}` ) })
  25. FRANS ROSÉN Send valid accessible ID, fuzz query-params /api/v3/invoices/123?FUZZ=x& /api/v3/invoices/123%3fFUZZ%3dx%26

    Theory: server-side call: http://internal-api/api/v1/invoices/ 123?token=x&token=xxx
  26. FRANS ROSÉN Send valid accessible ID, fuzz query-params { "error":

    "access denied" } /api/v3/invoices/123?FUZZ=x& /api/v3/invoices/123%3fFUZZ%3dx%26 Theory: server-side call: http://internal-api/api/v1/invoices/ 123?token=x&token=xxx
  27. FRANS ROSÉN Path-traversal, reach outside of invoices/ /api/v3/invoices/%2e%2e%2fFUZZ We know

    token is used in query, move it to fragment: /api/v3/invoices/%2e%2e%2fFUZZ%23 internal-api/api/v1/invoices/../FUZZ#
  28. FRANS ROSÉN Traversing into accounts/ without the token query parameter

    /api/v3/invoices/%2e%2e%2faccounts%23 { "accounts": [ {"account": 123, "name": "Other Business", "email": "[email protected]", "invoices": [ {"id": 123, "amount": 1100.00 …} {"id": 123, "amount": 1100.00 …} … ]
  29. FRANS ROSÉN "Path-traversal getting access to all invoice accounts" /api/v3/invoices/%2e%2e%2faccounts%23

    { "accounts": [ {"account": 123, "name": "Other Business", "email": "[email protected]", "invoices": [ {"id": 123, "amount": 1100.00 …} {"id": 123, "amount": 1100.00 …} … ] We can access all accounts and all their invoices!
  30. FRANS ROSÉN "Path-traversal getting access to all invoice accounts" {

    "accounts": [ {"account": 123, "name": "Other Business", "email": "[email protected]", "invoices": [ {"id": 123, "amount": 1100.00 …} {"id": 123, "amount": 1100.00 …} … ] http://internal-api /api/v1/invoices/../accounts#token=xxx
  31. FRANS ROSÉN Ways forward 1.Locate all APIs/micro-services used 2.Extract all

    API-endpoints we can find 3.Look at other strings in JS-files
  32. FRANS ROSÉN What we might find • Keys or tokens

    expected to be secrets
 Third party apps with unclear docs if tokens should be secrets. 

  33. FRANS ROSÉN What we might find • Keys or tokens

    expected to be secrets
 Third party apps with unclear docs if tokens should be secrets.
 
 Example of this happening IRL!

  34. FRANS ROSÉN Zendesk SSO-key • This JSON should be signed

    with the SSO-key: { "iat": 123, "jti": "uuid", "name" "x", "email": "[email protected]", "external_id": "UUID" }
  35. FRANS ROSÉN Zendesk SSO-key • This JSON should be signed

    with the SSO-key: { "iat": 123, "jti": "uuid", "name" "x", "email": "[email protected]", "external_id": "UUID" } unix timestamp random unique ID
  36. FRANS ROSÉN Zendesk SSO-key • This JSON should be signed

    with the SSO-key: { "iat": 123, "jti": "uuid", "name" "x", "email": "[email protected]", "external_id": "UUID" } unix timestamp random unique ID name for user, will be updated email for user, will be updated
  37. FRANS ROSÉN Zendesk SSO-key • This JSON should be signed

    with the SSO-key: { "iat": 123, "jti": "uuid", "name" "x", "email": "[email protected]", "external_id": "UUID" } unix timestamp random unique ID name for user, will be updated email for user, will be updated UserID to hijack account
  38. FRANS ROSÉN Zendesk SSO-key • Send JWT for UserID you

    want to hijack: https://example.zendesk.com/access/jwt? eYAAA.eYBBB.XXX
  39. FRANS ROSÉN Zendesk SSO-key • Send JWT for UserID you

    want to hijack: https://example.zendesk.com/access/jwt? eYAAA.eYBBB.XXX • Zendesk will reply with a session as the UserID: https://example.zendesk.com/hc/en-us? flash_digest=0cbfae0bec0bfea08cbfec0
  40. FRANS ROSÉN "Account hijack on support panel due to publicly

    disclosed Zendesk SSO-key" • Send JWT for UserID you want to hijack: https://example.zendesk.com/access/jwt? eYAAA.eYBBB.XXX • Zendesk will reply with a session as the UserID: https://example.zendesk.com/hc/en-us? flash_digest=0cbfae0bec0bfea08cbfec0
  41. FRANS ROSÉN What we might find • Keys or tokens

    expected to be secrets
 Third party apps with unclear docs if tokens should be secrets.
 
 

  42. FRANS ROSÉN What we might find • Keys or tokens

    expected to be secrets
 Third party apps with unclear docs if tokens should be secrets.
 
 Another example of this happening IRL!

  43. FRANS ROSÉN Algolia API-key Intended to be public a =

    algolia('AFBKDE54', '7ace8b8c7fbea78caebcf78ecbfaebca7 8f'); idx = a.index('userdb');
  44. FRANS ROSÉN a = algolia('AFBKDE54', '7ace8b8c7fbea78caebcf78ecbfaebca7 8f'); idx = a.index('publicdb');

    App-ID Public API-key Algolia API-key Intended to be public Index name
  45. FRANS ROSÉN HTTP/1.1 200 OK {"result": [ {"id": 123, "user":

    "x"}, … ]} POST /1/indexes/publicdb/query
  46. FRANS ROSÉN HTTP/1.1 200 OK {"result": [ {"id": 123, "user":

    "x", "email": "[email protected]", "phone": "003234234.."}, … ]} Another index with sensitive data
  47. FRANS ROSÉN HTTP/1.1 200 OK {"result": [ {"id": 123, "user":

    "x", "email": "[email protected]", "phone": "003234234.."}, … ]} Another index with sensitive data
  48. FRANS ROSÉN "Emails + phone for all users disclosed due

    to sensitive data in public AlgoliaDB" HTTP/1.1 200 OK {"result": [ {"id": 123, "user": "x", "email": "[email protected]", "phone": "003234234.."}, … ]}
  49. FRANS ROSÉN What we might find • Keys or tokens

    expected to be secrets
 Third party apps with unclear docs if tokens should be secrets. • Secret ENV variables dumped in CI-minification
 If any minifications or 
 
 Example of this happening IRL!

  50. FRANS ROSÉN Ways forward 1.Locate all APIs/micro-services used 2.Extract all

    API-endpoints we can find 3.Look at other strings in JS-files 4.Create wordlists
  51. FRANS ROSÉN Wordlists! • For every program: • Build /

    combine • Apply context! /api/v1/payment /api/v1/payment-methods /api/v1/shipping
  52. FRANS ROSÉN Wordlists! • For every program: • Build /

    combine • Apply context! /api/v1/payment /api/v1/payment-methods /api/v1/shipping /api/v1/shipping-methods Also add this
  53. FRANS ROSÉN Ways forward 1.Locate all APIs/micro-services used 2.Extract all

    API-endpoints we can find 3.Look at other strings in JS-files 4.Create wordlists 5.Fuzz, fuzz, fuzz
  54. FRANS ROSÉN Fuzz with / without • All HTTP-methods POST

    PATCH DELETE PUT… • IDs or not /users/1 vs /users • / in the end /payments/ vs /payments • File extension users.json vs users
  55. FRANS ROSÉN Fuzz combination • Again, combine paths + endpoints

    on subdomains • Skip paths, try all methods, add regular fuzz characters: \ " ' & # .. ö % ? NULL
  56. FRANS ROSÉN • Curate your own context specific wordlists •

    Combine with regular fuzzing • Understand and learn what is being disclosed
 and how to abuse it. Takeaways
  57. FRANS ROSÉN Takeaways That’s it, thank you!
 Any questions? •

    Curate your own context specific wordlists • Combine with regular fuzzing • Understand and learn what is being disclosed
 and how to abuse it.