Slide 1

Slide 1 text

FRANS ROSÉN A methodology using fuzzing and info disclosure

Slide 2

Slide 2 text

FRANS ROSÉN Security Advisor @detectify CTO @centra HackerOne #5 @ /leaderboard/all-time Blogs at labs.detectify.com @fransrosen

Slide 3

Slide 3 text

FRANS ROSÉN Rundown • Imaginary app structure and methodology on breaking it

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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!

Slide 6

Slide 6 text

FRANS ROSÉN Structure of our imaginary app

Slide 7

Slide 7 text

FRANS ROSÉN Client based micro services business.example.com SPA + a lot of JS

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

FRANS ROSÉN Ways forward 1.Locate all APIs/micro-services used 2.Extract all API-endpoints we can find

Slide 12

Slide 12 text

FRANS ROSÉN API-endpoints per microservice • invoice.example.com

Slide 13

Slide 13 text

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:

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

FRANS ROSÉN API-endpoints per microservice • conversation.example.com /api/v2/online /api/v2/conversations /api/v2/websocket Found in JS:

Slide 17

Slide 17 text

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:

Slide 18

Slide 18 text

FRANS ROSÉN Continue to curate lists Find more endpoints: • Desktop client • Web-archive • PHP/Java/Golang-SDKs • npm/composer/yarn • Documentation

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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* …

Slide 21

Slide 21 text

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 …

Slide 22

Slide 22 text

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 " '
 # …

Slide 23

Slide 23 text

FRANS ROSÉN What we might find • New or old endpoints not in use
 Might leak more data than the current one


Slide 24

Slide 24 text

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!

Slide 25

Slide 25 text

FRANS ROSÉN { "boards": [ { "id": 123, "user": 124, "username": "test" … } ] } API currently in use, v2 /api/v2/boards/123

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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"

Slide 29

Slide 29 text

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…


Slide 30

Slide 30 text

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!

Slide 31

Slide 31 text

FRANS ROSÉN Regular API-endpoint to fetch invoices /api/v3/invoices/123

Slide 32

Slide 32 text

FRANS ROSÉN Regular API-endpoint to fetch invoices /api/v3/invoices/123 { "invoice": { "url": "/api/v3/invoices/ 123?token=xyz&expire=123 } }

Slide 33

Slide 33 text

FRANS ROSÉN Different ID than my own invoices /api/v3/invoices/124 { "error": "access denied" }

Slide 34

Slide 34 text

FRANS ROSÉN Fuzzing, double quote: /api/v3/invoices/" { "error": "Bad URI: /api/v1/ invoices/\"" }

Slide 35

Slide 35 text

FRANS ROSÉN Sent to v3, error with v1? /api/v3/invoices/" { "error": "Bad URI: /api/v1/ invoices/\"" }

Slide 36

Slide 36 text

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}` ) })

Slide 37

Slide 37 text

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}` ) })

Slide 38

Slide 38 text

FRANS ROSÉN Send valid accessible ID, fuzz query-params /api/v3/invoices/123?FUZZ=x& /api/v3/invoices/123%3fFUZZ%3dx%26

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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:

Slide 42

Slide 42 text

FRANS ROSÉN Path-traversal, reach outside of invoices/ /api/v3/invoices/%2e%2e%2fFUZZ

Slide 43

Slide 43 text

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#

Slide 44

Slide 44 text

FRANS ROSÉN Traversing into accounts/ without the token query parameter /api/v3/invoices/%2e%2e%2faccounts%23

Slide 45

Slide 45 text

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 …} … ]

Slide 46

Slide 46 text

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!

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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. 


Slide 50

Slide 50 text

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!


Slide 51

Slide 51 text

FRANS ROSÉN Zendesk SSO-key

Slide 52

Slide 52 text

FRANS ROSÉN Zendesk SSO-key zdkey: "fafc5aef56caefa56fcea65" zdaccessurl: "https://example.zendesk.com /access/jwt?"

Slide 53

Slide 53 text

FRANS ROSÉN Zendesk SSO-key • Not an API-key, used for JWT-signing for simple SSO

Slide 54

Slide 54 text

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" }

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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.
 
 


Slide 62

Slide 62 text

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!


Slide 63

Slide 63 text

FRANS ROSÉN Algolia API-key Intended to be public a = algolia('AFBKDE54', '7ace8b8c7fbea78caebcf78ecbfaebca7 8f'); idx = a.index('userdb');

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

FRANS ROSÉN POST /1/indexes/userdb/query?x- algolia-api-key=PublicApiKey&… {"params":"query=*&hitsPerPage=1"} Try another index + Scoped API-key

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

FRANS ROSÉN POST /1/indexes/userdb/query?x- algolia-api- key=AnotherPublicApiKey&… {"params":"query=*&hitsPerPage=1"} Unscoped API-key

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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.."}, … ]}

Slide 73

Slide 73 text

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!


Slide 74

Slide 74 text

FRANS ROSÉN What we might find

Slide 75

Slide 75 text

FRANS ROSÉN What we might find

Slide 76

Slide 76 text

FRANS ROSÉN What we might find

Slide 77

Slide 77 text

FRANS ROSÉN

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

FRANS ROSÉN Wordlists! • For every program:

Slide 80

Slide 80 text

FRANS ROSÉN Wordlists! • For every program: • Build / combine

Slide 81

Slide 81 text

FRANS ROSÉN Wordlists! • For every program: • Build / combine

Slide 82

Slide 82 text

FRANS ROSÉN Wordlists! • For every program: • Build / combine

Slide 83

Slide 83 text

FRANS ROSÉN Wordlists! • For every program: • Build / combine • Apply context!

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

FRANS ROSÉN Fuzz combination • Again, combine paths + endpoints on subdomains • Skip paths, try all methods, add regular fuzz characters: \ " ' & # .. ö % ? NULL

Slide 89

Slide 89 text

FRANS ROSÉN • Curate your own context specific wordlists Takeaways

Slide 90

Slide 90 text

FRANS ROSÉN • Curate your own context specific wordlists • Combine with regular fuzzing Takeaways

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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.