Non-secure network connections in Carnival Cruise’s app

This past summer my family took a cruise on Carnival Cruise Lines to the Eastern Caribbean. There were a total of 17 of us and we had a good time. One of the suggested ways for everyone to stay in touch was to use the Carnival Hub App which is basically their goto app for up to date information on the ship which has a messaging component. For $5 per device for the cruise, it didn’t seem all that unreasonable except that just about everything on the cruise costs extra!

The chat app, like most chat apps, has push notifications. In iOS, there are 2 types of push notifications, local and remote. The remote ones require a persistent connection to Apple’s Push Notification Service (APNS). I suspected that the app used local notifications and stayed open in the background as having several thousand devices connected to either Apple or Google’s push servers over a satellite link would not make much sense. So I pulled out my trusty copy of Charles Proxy and decided to see what traffic was being sent. What I saw just about shocked me.

Connections using the app were NOT using SSL! Since the WiFi was unprotected (it would be cumbersome to give out the WiFI password to so many users), anyone with rudimentary hardware/software could sniff all the traffic. SSL certificates are cheap and easy to deploy, so there is no excuse for every service not to be using them (I use them internally on all services running at my house).

Is it so bad that the app isn’t using SSL as no credit card data is flowing through the app? Absolutely! People could be chatting about which rooms they are in and when they are going to meet giving criminals information about when to go into their rooms. People could also tell their friends/family what they have in their rooms making them targets for criminals (“I put the laptop/camera under the bed”, for example). Not only was chat not SSL protected, all other aspects of the app’s communication were sent in clear text.

Example requests and responses

This request has my Folio number and name; those 2 pieces of information could allow anyone to charge to my room. While they should look at the ship ID (you are given basically a name badge that is your room key and used for purchases), I don’t know if the staff always looked at them. My cabin number was also in the request.

GET /FHMA-leviathan/api/Guest?isKiosk=false HTTP/1.1

{
    "ChatPassword": "efabb219324c47dfbfef469523b495d0",
    "Nickname": "SCOTT GRUBY",
    "DiningRoom": "Northern Lights Upr",
    "DiningTime": "E",
    "DiningTable": "494",
    "MusterStation": "B4",
    "LoyaltyNumber": "XXXXXXX",
    "LoyaltyLevel": "BLUE",
    "NumCruises": "1",
    "DateOfBirth": "",
    "Age": "45",
    "BookingNumber": "XXXXX",
    "BookingSequenceNumber": "18",
    "FolioNumber": "8540",
    "FolioActiveIndicator": "A",
    "FolioType": "G",
    "FolioAccountNum": "8829",
    "ResponsibleParty": "Y",
    "AlcoholRestricted": "N",
    "AtRisk": "",
    "CashBalance": "85.25",
    "AccountType": "MIXED",
    "FolioLimit": "",
    "TotalCharges": "43.66",
    "CabinNumber": "2309",
    "CrewInfoNumber": "",
    "VoyageStartDate": "2018-08-04T10:01:55",
    "VoyageEndDate": "2018-08-11T10:01:55",
    "Duration": "7",
    "FacebookId": null,
    "InitialLogin": "2018-08-04T14:08:02.963",
    "ChatPurchased": "2018-08-04T14:10:15.9",
    "InitialUserAgent": "funhub/2587 CFNetwork/902.2 Darwin/17.7.0",
    "IsResponsibleParty": true,
    "IsFolioCancelled": false,
    "IsFolioDeactivated": false,
    "FirstName": "SCOTT",
    "LastName": "GRUBY",
    "VoyageId": "MC20180804007",
    "AvatarUrl": "http://leviathan.cclfunhub.com/FHMA-leviathan/Avatars/2811.jpg?636690028868502174",
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": false,
    "ChatId": 2811,
    "SelfieUrl": null,
    "DecurtisGuestId": "445911",
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": true
}

Want to know who I have on my chat list? Bingo! (Names were removed.)

GET /FHMA-leviathan/api/contacts HTTP/1.1

[{
    "Relationship": "Chat contact request",
    "IsMinor": false,
    "FirstName": "XXXXXXX",
    "LastName": "XXXXXXX",
    "VoyageId": "MC20180804007",
    "AvatarUrl": "http://leviathan.cclfunhub.com/FHMA-leviathan/Avatars/1248.jpg?636690701830901659",
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": null,
    "ChatId": 1248,
    "SelfieUrl": null,
    "DecurtisGuestId": null,
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": null
}, {
    "Relationship": "Chat contact request",
    "IsMinor": false,
    "FirstName": "XXXXXXX",
    "LastName": "XXXXXXX",
    "VoyageId": "MC20180804007",
    "AvatarUrl": "http://leviathan.cclfunhub.com/FHMA-leviathan/Avatars/2074.jpg?636690009457720910",
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": null,
    "ChatId": 2074,
    "SelfieUrl": null,
    "DecurtisGuestId": null,
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": null
}, {
    "Relationship": "Chat contact request",
    "IsMinor": false,
    "FirstName": "XXXXXXX",
    "LastName": "XXXXXXX",
    "VoyageId": "MC20180804007",
    "AvatarUrl": "http://leviathan.cclfunhub.com/FHMA-leviathan/Avatars/2075.jpg?636690046360212793",
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": null,
    "ChatId": 2075,
    "SelfieUrl": null,
    "DecurtisGuestId": null,
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": null
}, {
    "Relationship": "Chat contact request",
    "IsMinor": false,
    "FirstName": "XXXXXXX",
    "LastName": "XXXXXXX",
    "VoyageId": "MC20180804007",
    "AvatarUrl": null,
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": null,
    "ChatId": 2396,
    "SelfieUrl": null,
    "DecurtisGuestId": null,
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": null
}]

Want to know what my room charges were?

GET /FHMA-leviathan/api/guest/AccountSummary HTTP/1.1

{
    "TotalCharges": "346.17",
    "TotalCash": "0",
    "TotalCredits": "60.92",
    "BalanceDue": "0",
    "AvailableCash": "0",
    "AvailableCashAtFolio": "0",
    "MaxCashForDeposit": "9999",
    "CashBalance": "85.25",
    "AccountType": "MIXED",
    "GuestList": [{
        "GuestCharges": "134.31",
        "GuestChargesLessGrats": "43.66",
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "FolioNumber": "8540",
        "VoyageNumber": "MC20180804007",
        "BookingNumber": "8GM8F5",
        "PaxSeqNumber": "18"
    }, {
        "GuestCharges": "91.65",
        "GuestChargesLessGrats": "1",
        "LastName": "GRUBY",
        "FirstName": "XXXXXX",
        "MiddleName": "XXXXXX",
        "FolioNumber": "8538",
        "VoyageNumber": "MC20180804007",
        "BookingNumber": "8GM8F5",
        "PaxSeqNumber": "20"
    }, {
        "GuestCharges": "120.21",
        "GuestChargesLessGrats": "29.56",
        "LastName": "GRUBY",
        "FirstName": "XXXXXXX",
        "MiddleName": "XXXXX",
        "FolioNumber": "8539",
        "VoyageNumber": "MC20180804007",
        "BookingNumber": "8GM8F5",
        "PaxSeqNumber": "19"
    }],
    "FolioCharges": [{
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "11:28:17AM",
        "ChargeLocation": "INTERNET ACCESS",
        "ReceiptNumber": "379101",
        "ChargedAmount": "0",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "11:40:45AM",
        "ChargeLocation": "NON-REFUNDABLE ONBOARD CREDITS",
        "ReceiptNumber": "020620",
        "ChargedAmount": "-25",
        "IsReceiptAvailable": "N"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "11:40:46AM",
        "ChargeLocation": "REFUNDABLE ONBOARD CREDITS",
        "ReceiptNumber": "020620",
        "ChargedAmount": "-3.64",
        "IsReceiptAvailable": "N"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "2:14:49PM",
        "ChargeLocation": "CHAT PLAN",
        "ReceiptNumber": "381964",
        "ChargedAmount": "5",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "2:51:38PM",
        "ChargeLocation": "BLUE IGUANA BAR",
        "ReceiptNumber": "382739",
        "ChargedAmount": "10.07",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/6/2018",
        "ChargeTime": "6:18:33PM",
        "ChargeLocation": "NORTHRN LS UPPER SVC served in Main Dining Room",
        "ReceiptNumber": "419829",
        "ChargedAmount": "10.64",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/7/2018",
        "ChargeTime": "8:37:19AM",
        "ChargeLocation": "LAUNDRY SELF-SERVICE",
        "ReceiptNumber": "429017",
        "ChargedAmount": "3",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/7/2018",
        "ChargeTime": "11:31:37AM",
        "ChargeLocation": "TOWELS,ROBES&BOOKS",
        "ReceiptNumber": "430321",
        "ChargedAmount": "14.95",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/9/2018",
        "ChargeTime": "2:04:26PM",
        "ChargeLocation": "SERVICE GRATUITY",
        "ReceiptNumber": "876839",
        "ChargedAmount": "90.65",
        "IsReceiptAvailable": "Y"
    }]
}

It appears that the chat application was using the Jabber protocol (XMPP) running on port 5222 (non SSL port). I only grabbed a little of the traffic, but the XMPP protocol would have all the chat conversations appearing in it.

<stream:stream xmlns:stream='http://etherx.jabber.org/streams'
xmlns='jabber:client' xml:lang='en-US.UTF-8' id='1B9C3F49DB9F71'
from='chat.cclfunhub.com'
version='1.0'><stream:features><mechanisms
xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</
mechanism><mechanism>CISCO-VTG-TOKEN</mechanism></mechanisms></
stream:features><success
xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/><stream:stream
xmlns:stream='http://etherx.jabber.org/streams'
xmlns='jabber:client' xml:lang='en-US.UTF-8' id='1B9C3F49DB9F71'
from='chat.cclfunhub.com' version='1.0'><stream:features><bind
xmlns='urn:ietf:params:xml:ns:xmpp-bind'/><session
xmlns='urn:ietf:params:xml:ns:xmpp-session'/></stream:features><
iq id='0E579D08-88CC-4A8E-8A80-C730BA9B0505' type='result'><bind
xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>2811@chat.
cclfunhub.com/FA8CEBAF-6EDF-4B10-8D1E-7E6AF213F07C</jid></bind><
/iq><iq id='63D67AE8-2A53-4047-A9BE-A3E429953B7E'
type='result'/><presence
from='505c1c79-24c3-41d6-89c3-31e687ad3ab4@conference-2-
standalonecluster61f84.chat.cclfunhub.com/3940#ad29759f-2e4d-
4428-bbd2-23f953c285d2' id='tlDWA-7797'
to='2811@chat.cclfunhub.com/FA8CEBAF-6EDF-4B10-8D1E-7E6AF213F07C
'><x xmlns='http://jabber.org/protocol/muc#user'><item
affiliation='owner'
jid='3940@chat.cclfunhub.com/8f1c3bd92d91ad29'
role='moderator'/></x><c hash='sha-1'
node='http://www.igniterealtime.org/projects/smack'
ver='A1TcJY4mFaFrO9M5ctJsIPdHhsU='
xmlns='http://jabber.org/protocol/caps'/><delay
stamp='2018-08-10T04:05:07.36362Z' xmlns='urn:xmpp:delay'/><x
stamp='20180810T04:05:07.36362'
xmlns='jabber:x:delay'/></presence><presence
from='505c1c79-24c3-41d6-89c3-31e687ad3ab4@conference-2-
standalonecluster61f84.chat.cclfunhub.com/2508#4faf58be-ee9d-
4d80-8ee2-2ee662f6629a' id='vj32Z-86255'
to='2811@chat.cclfunhub.com/FA8CEBAF-6EDF-4B10-8D1E-7E6AF213F07C
'><x xmlns='http://jabber.org/protocol/muc#user'><item
affiliation='owner'
jid='2508@chat.cclfunhub.com/887e88d21b5f8201'
role='moderator'/></x><c hash='sha-1'
node='http://www.igniterealtime.org/projects/smack'
ver='A1TcJY4mFaFrO9M5ctJsIPdHhsU='
xmlns='http://jabber.org/protocol/caps'/><delay
stamp='2018-08-10T03:02:13.862187Z' xmlns='urn:xmpp:delay'/><x
stamp='20180810T03:02:13.862187'
xmlns='jabber:x:delay'/></presence><presence
from='505c1c79-24c3-41d6-89c3-31e687ad3ab4@conference-2-
standalonecluster61f84.chat.cclfunhub.com/1248#-iOS-00689BAE-
CC73-43A3-A258-4E9F8BA160A2'
to='2811@chat.cclfunhub.com/FA8CEBAF-6EDF-4B10-8D1E-7E6AF213F07C
'><x xmlns='http://jabber.org/protocol/muc#user'><history
maxchars='0'/><item affiliation='owner'
jid='1248@chat.cclfunhub.com/E22C11CA-B399-4D9B-A3D9-
BE9C35B5A1DD' role='moderator'/></x><delay
stamp='2018-08-10T03:57:54.660208Z' xmlns='urn:xmpp:delay'/><x
stamp='20180810T03:57:54.660208'
xmlns='jabber:x:delay'/></presence>

WiFi Issues

The security of the app wasn’t the only issue in using it. I suspect that the WiFi was also overloaded; with something like 6000 people on the ship, there were several thousand devices connected at all times even if they weren’t communicating at the same time. This made using the app very frustrating; messages didn’t get through and notifications were delayed, if they were received at all. Imagine all the people annoyed that his or her significant other wouldn’t respond even though he or she actually did. I stopped relying on the app in the first few hours of using it!

Recommendations to Carnival

  • Deploy a wildcard SSL certificate that is issued by a major SSL vendor (no self-signed certificates) to each internal server on each ship.
  • Add more WiFi capacity to every ship.
  • Perform load testing of the apps (iOS and Android).
  • Perform load testing of the WiFi network.

I’m not sure if the app has been updated, but here was the information about the version:

System Name: iOS
System Version: 11.4.1
Application Version: 2.2.3 (Build 0)
Ship Name: Magic
Hostname: http://leviathan.cclfunhub.com/FHMA-leviathan
Voyage ID: MC20180804007

Conclusion

While the concept of being able to communicate with others on a cruise especially if you have a large party is great, Carnival’s implementation needs work. In the future, I’m inclined to bring FRS radios; they definitely won’t work everywhere, but could be more reliable in certain situations. Also, meeting your group each morning and going over plans even if people go their separate ways (like people did before technology!) might also be in the cards.

If Carnival wants to get in touch with me about these issues or wants help with the app, I’m available!

Fry’s – The good, the bad, and the ugly

As I had some time to kill today, I decided to go ahead and get a new hard drive for my PowerBook and replace it without waiting until tomorrow. The choices on a Sunday for getting a laptop hard drive are pretty limited, basically only Fry’s Electronics. Fry’s is not my favorite store as their service is not great, return lines are long, and sometimes they try to pass returns off as new (I had bought a monitor from them that turns out had been used and when it broke, it was out of warranty even though it was less than 1 year since I had bought it). However, on the plus side, Fry’s has decent store hours, good selection, OK prices, and carries parts others don’t (where can you find a #6 Torx screwdriver locally?). While I try to avoid shopping at Fry’s, sometimes it is unavoidable.

Nickel and dimed by Sprint

I got my latest Sprint bill today and noticed it was a little higher than normal. Turns out that Sprint charged me $18 to switch handsets; this is after I paid them $350 for the new handset! Do they not get it? On other carriers such as T-Mobile and Cingular, you can pull the SIM out of one phone and put it in another…you don’t even have to tell them. They don’t even have to know about the change as you do it yourself. Since Sprint and Verizon don’t use SIMs (or the CDMA equivalent which didn’t take off), I am forced to talk to a human to change phones; Sprint used to have the ability to do it online, but they scrapped that probably so that they could try to sell you something else while you’re switching handsets. Cingular is looking better and better everyday.

Laughable Sprint response

I sent email to Sprint asking when the next firmware update would be available for my Samsung A900 to address the Bluetooth issues I’ve been having, and part of the response I got back was

We at Sprint perform stringent tests and trials before launching any product, therefore there is no fixed time period for the software launch.

Really? Then how did the phone get out the door with one of the touted features, ability to use a Bluetooth headset, not working?

The more I deal with this phone, the more I want to switch to Cingular/AT&T so that I have a choice of decent phones with decent ability to use a Bluetooth headset.

English speaking customer service reps

Yesterday I called my cable company to switch the IP address of my cable modem as I now have a block of 5 addresses and in order to use them, a change had to be made. I called support, negotiated the phone tree, and spoke to a woman with a heavy accent. I explained what I wanted done (my sales rep told me to call support to get the change made) and after explaining it a few times, I thought that she got it. She said she had to transfer me to a modem person and then when the modem person got on the line, he said “I understand you are having problems connecting to the Internet.” I told him that there was a communications issue and my connection was fine. So I got my cable modem reconfigured and things were fine.

I’m a bit tired of call centers that are either outsourced overseas or have people that aren’t native speakers. I have nothing against people speaking different languages; however, when I’m trying to explain a technical issue or get a problem solved, the language barrier makes it harder to get things done.

Poor timing of Macworld

I’ve come to the conclusion that having Macworld Expo in January is a poor decision. Here I am at the end of the year looking to see what else I can buy for my small business to get the tax deduction. I was thinking about getting a Mac Mini to run as another server and learn about Mac OS X server, but rumors say an Intel based Mac Mini is coming at MacWorld which is in a few weeks. I don’t know if I believe the rumors, but I don’t want to take the chance. So, I lose out on getting it as a deduction for this year. If the expo was say in October/November, companies could get in purchases before the end of the year and consumers could get their gifts for the holiday season. I understand that products may not be ready that early, but shifting the development to have them ready would make a lot more sense to me.

Brand loyalty

Yesterday I went to Home Depot to pick up some attachments for my Dremel to do some glass etching. The Dremel attachments were about $8 each and I needed 4 of them according to the project instructions I had; 2 were diamond tipped and 2 were tungsten carbide. I also saw a Black & Decker kit that contained 4 diamond tipped attachments, cutting wheels, grinding attachment, etc. for $10. I bought the Black & Decker kit as well as one of the Dremel tungsten carbide attachments. I got home, used the Black & Decker kit and was able to return the Dremel attachment.

So, the question is, is the Dremel name worth that much more? In this case, the diamond tipped attachments are made from diamond scrap and/or synthetic diamond and how much different can they be? Personally, I’ll buy the cheaper attachment as long as they work as well. It appears that the Black & Decker ones work fine as I had no trouble using them to etch glass.

Sprint Bill, part 2

I looked at my Sprint bill online today as it just became available. I noticed that the balance was twice that of a normal month! Wow, what that Sprint mess up now? After pouring over the bill for about 30 minutes and checking my credit card statement, it appears that Sprint never charged my card for December. What happened was this…I ordered a phone, Sprint placed a $349.99 credit on my Sprint bill as I paid that amount by credit card. Next, when my payment was due on Dec. 17 for the previous month’s bill, I already had a credit on my account that exceeded this, so they didn’t charge me. On the December bill, there is a charge for the phone ($349.99 + sales tax) as well as the normal charges. So, it looks like they’ll catch up next month in billing me. To further complicate matters, there is a charge for $399.99 for the phone they sent me that I didn’t want as well as a credit to offset that.

What a mess! Next time I should buy a phone from an independent retailer and not Sprint itself as this billing disaster has given me a major headache.

Japenglish and Korenglish

The Japanese and Koreans make some pretty good products these days. However, it appears that they don’t bother to have native English speeakers review text of alerts and such on their products. For instance, on my new Samsung A900 cell phone, an alert popped up that said “Connection is failed”. On the products I work on, we have native speakers of the languages we localize in localize the text so that it makes sense in those languages. You’d expect that a company like Samsung could do this, but I guess not. I’ve seen Sony does the same thing with their CLIEs and TVs.

Sprint’s weird billing practices

I checked my Sprint account yesterday to see if I was credited for the phone I returned (due to them ordering the wrong phone). Well, I had a huge credit, so I call them and after awhile on the phone, found out that they charged my credit card, then issued a credit on my account then bill the phone to my account. I already knew they did this weird stuff; so when I originally ordered my new phone, I didn’t put in my Sprint number so that they couldn’t do this game. Of course, that failed because they sent me the wrong phone. So I checked my credit card statement and found a credit for the original phone. I’m now completely confused and will see what happens when my bill comes. Why can’t Sprint just bill my credit card and not touch my account? Is that so hard to do? Also, why can’t they list detailed charges prior to a bill being generated so that I could see the charges and credits instead of just seeing a -$605 balance? Uggh.