How I Exploited P&O Cruises' Web App to Gain Benefits on my Trip to Spain and Portugal

In this blog post, I’m going to show you how it was possible to exploit Broken Access Control on a popular cruise company’s web app. I was able to use the devtools in my browser client to book restaurants on my holiday, when I shouldn't have been able to.


Broken Access Control is one of the most common vulnerabilities for web applications and is the current top OWASP application risk. When exploited, users can gain unauthorised access to restricted resources, or sensitive information and systems. 

Top 10 Web Application Security Risks and how their positions have changed from 2017 to 2021. Open Web Application Security Project/OWASP

With an increasing number of JavaScript frameworks, it’s becoming easier than ever for developers to create flashy and fast websites. Logic is being moved from back-end to front-end, and page loads are reduced along with server processing. These benefits are great for developers, but it’s easy to forget that we may no longer be the ones in control of our applications. On the front-end, we can hide new features behind feature toggles and assume that because a user can’t see something, they won’t click it. We must keep in mind that the browser client is the one in control of running front-end code. And so, any websites that heavily lean on front-end processing are more at risk of being insecure. 

Modern clients have a suite of developer tools for; debugging, setting breakpoints, editing of variables, and modification of the DOM. If code isn’t working, developers often jump to these tools to debug and make the relevant fix. What is stopping users from behaving maliciously, using the same tools we do, to alter the application’s behaviour? This is a solid reason as to why developers should never implicitly trust data from a browser client unless this data can be verified on the server.

Developers are undoubtedly responsible for minimising code vulnerabilities. What is often overlooked is that if we have the required skillset to create these issues, then we already have the skills needed to identify and exploit flaws in other company’s web apps! By exposing weaknesses elsewhere, developers can explore risks from a different perspective, improving security awareness overall, and leading to better protection of resources and systems within our own projects. 



So, what did I do?


Some background information: If you book a cruise with P&O, there's a web app you use while on the ship to book things like entertainment, dining, and excursions. Around two weeks before departure, you're able to book a select few of these in advance.

For this blog post, we'll focus on dining reservations. If you've ever been on a cruise, then you'll be aware of how quickly restaurant reservations are booked out when you get on the ship, especially the fancier, high-end restaurants. This often leaves you unable to secure a table at your favourite restaurants. If you don't book at the beginning of your holiday, and you're in peak season, then there's a high chance you won't be able to get the restaurant you want, or if you can, then it's at some undesirable seating time.




Looking at a screenshot of the web app, you can see that only one restaurant was available for me to book before departure (Oct/Nov 2022), The Limelight ClubIf you browse this part of the website, you'll see ADD TO BASKET for items you can book, and no button available if you're not given access.

With only one restaurant available to book, this didn't really help me with my holiday planning (and potentially avoiding those long dinner queues!) With two full weeks on the ship, I knew I'd need 7-14 bookings for the whole trip, depending on whether I used the buffets or restaurants for lunchtime. If only there was a way to book all of the restaurants for lunch and dinner when it suited me...

Going into DETAILS on an unavailable restaurant, e.g. The Chef's Table, takes you to this page:



As you wait while this page loads, you might notice something interesting.


The ADD TO BASKET appeared for a very brief time! This made me wonder if the request was being processed by the browser client, and not by the server.

The default state for this application is to show ADD TO BASKET on render, and hide the button once it has retrieved some additional data from the server telling it if the product is available. Because we see the button on the initial page load, we can deduce that all of the rendering is happening within an AJAX request processed by the client and not the server.

Due to the way the button 'hide' has been implemented, it is only hidden from the user, and not removed from the DOM. The code to hide the button simply applies a class called 'e347-hide' to the button which specifies 'display: none;'.




Using my browser, I simply searched for this button in the DOM, and removed this style. The server trusted that the client had stopped invalid requests, and allowed me to continue and book the selected item. It turned out that you could do this with almost any restaurant (or entertainment) once you knew what you were looking for!



Below is a screenshot showing what happened when I clicked the (now available) 
ADD TO BASKET button.


At this point, I was unsure if any bookings would be valid when on the ship. However, it was looking increasingly more likely that the booking system was wired up. There were no other obstacles after unveiling the buttons, and I was able to book as normal.


Below are some video captures of the process. The first video shows the whole journey of a single booking.





The second video shows what happened when the style was removed, and the buttons were revealed.





I went ahead and booked all of the restaurants I wanted to, and some had a cover charge, which I paid. Credit card details accepted and I even received email confirmations of my bookings. Bit of a gamble, in case I lost my money, but my curiosity got the better of me here! 

[A little disclaimer here... I do not recommend paying money to any company in these circumstances! My bookings may not have gone through, and I'd have lost quite a bit of money. I would have had a very awkward time calling Customer Services to try and claw it back!]

I waited eagerly for my trip to see if my bookings were 'real'. P&O has a special web app you can only use once on board, and this manages all of your bookings in real time. It shows you bookings you've made and has a diary, etc.

Once on board, I opened the app and saw all of my bookings in my diary! I'd made a booking for the first day, so went along to the restaurant. Lo and behold, they had me on the system, and seated me at my table!

I was able to enjoy all of my bookings and my cover charges had gone through without a problem.

An assumption I made based on everything I discovered was that P&O must have been trialling a new booking system, where they would allow more bookings to their guests in the future. For some reason, they had turned booking functionality off (or so they thought), possibly waiting for a 'go' date. Who knows, it may all be available now at the time of this blog post. You're unable to view the web app unless you have a holiday booked, are checked-in, and and have a couple of weeks' left before your trip, so I have no way of checking! I'd love to hear from anyone who is able to view this system!

What's the takeaway from all of this?


This is a clear example of Broken Access Control, and P&O is fortunate that the only consequence of failing to secure access control is taking more of my money. However, it does show that developers should not blindly rely on front-end processing which gives the browser client control of their app.

Avoiding this issue would have been a simple fix. One should verify that the user can book an item on the server as part of the request. Client functionality can remain the same, but by having final verification performed on the server, then requests can be trusted, and potential exploitation via devtools can be avoided.

I contacted P&O in December (2022) to let them know about this vulnerability, and will update if I get a response.


Comments