I recently ran across a PHP script that handles IPN notifications from Paypal. It looks a bit dodgy to me:

// STEP 1: Read POST data
// reading posted data from directly from $_POST causes serialization 
// issues with array data in POST
// reading raw POST data from input stream instead. 
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
    $keyval = explode('=', $keyval);
    if (count($keyval) == 2)
        $myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
if (function_exists('get_magic_quotes_gpc')) {
    $get_magic_quotes_exists = true;
}
foreach ($myPost as $key => $value) {
    if ($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
        $value = urlencode(stripslashes($value));
    } else {
        $value = urlencode($value);
    }
    $req .= "&$key=$value";
}


// STEP 2: Post IPN data back to paypal to validate

$ch = curl_init($paypal_url);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));

// In wamp like environments that do not come bundled with root authority certificates,
// please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path 
// of the certificate as shown below.
// curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
if (!($res = curl_exec($ch))) {
    // error_log("Got " . curl_error($ch) . " when processing IPN data");
    curl_close($ch);
    exit;
}
curl_close($ch);

Firstly, I'm wondering why it tries to get raw post data from php://input. Secondly, it seems poorly designed in that it checks get_magic_quotes_gpc() for each loop iteration, but lastly and most disturbingly, it would appear that one might be able to POST some data to this script that would in turn POST any data we like back to the paypal gateway, thereby executing some arbitrary gateway command. This might be accomplished by putting a 'cmd=some_other_command' string in one's POST data. Am I imagining this hole exists?

I realize that the script does not seem to reveal (or utilize) any paypal credentials except what is in the POST data, but it still seems pretty weird to me.

    That's actually a verbatim copy of paypal's sample PHP script.

    To answer specifics:

    No vulnerability. IPN isn't exactly part of the payment authorization process, it just notifies you of the success/failure of a payment started on your site.

    1) you send user to paypal
    2) paypal posts to your site with a token and some other junk
    3) your site posts the exact same information back to paypal to acknowledge
    4) paypal acknowledges your acknowledgement and shares transaction details
    5) hunky-dory

    So, if you changed one of the params, paypal would simply abort. IPN is more a protection for you than the user (i.e., to make sure the payment was successful and the user didn't mess with the qty/price in the PayPal button) before sending off merchandise on the assumption (hope?) that it was actually paid for.


    Don't know why they choose to use [font=monospace]php://input[/font]. PayPal's previous example used fputs() and fgets().


    Yes, it does seem badly designed in that it checks magic quotes for each iteration of the loop.
    Of course, there wouldn't be any need to check, if everyone were running 5.4. 😃

      Write a Reply...