{"id":406033,"date":"2024-06-29T18:40:48","date_gmt":"2024-06-29T18:40:48","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=406033"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=406033","title":{"rendered":"<span>20 years of payment processing problems<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/mu\/4w\/nn\/mu4wnncoijkmden9frkwagelwj0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/mu\/4w\/nn\/mu4wnncoijkmden9frkwagelwj0.png\"\/><br \/>  <i>Thanks to <a href=\"https:\/\/habr.com\/en\/users\/yarbabin\/\" class=\"user_link\">yarbabin<\/a> for the logo<\/i><\/p>\n<p>  Electronic payment systems have existed on the Internet for a long time, and some bugs in them are twenty years old. We&#8217;ve found critical vulnerabilities allowing us to steal money and drive up the balance. Today we will analyze typical implementations of payment processing and related security issues.<\/p>\n<p>  <a name=\"habracut\"><\/a><\/p>\n<h3>Overview of payment systems and typical API implementations<\/h3>\n<p>  Few people know, but the first (anonymous!) payment system was <a href=\"https:\/\/en.wikipedia.org\/wiki\/DigiCash\" rel=\"nofollow noopener noreferrer\">DigiCash<\/a>, which appeared back in 1989, followed in 1996 by a more well-known (mainly among carders) system <a href=\"https:\/\/en.wikipedia.org\/wiki\/E-gold\" rel=\"nofollow noopener noreferrer\">E-gold<\/a>.<\/p>\n<p>  But let&#8217;s go back to the present and list the main modern major payment systems\/e-payment services that allow you to accept payments on your own website:<\/p>\n<ul>\n<li>PayPal<\/li>\n<li>WebMoney<\/li>\n<li>YooMoney (former Yandex.Money)<\/li>\n<li>Qiwi<\/li>\n<li>Alipay<\/li>\n<li>etc.<\/li>\n<\/ul>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ob\/6n\/qw\/ob6nqw-m2tqoyj3uakm6gcz8kp0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ob\/6n\/qw\/ob6nqw-m2tqoyj3uakm6gcz8kp0.png\"\/><\/p>\n<p>  As well as dozens of lesser-known systems with names you are not likely familiar with, not to mention the emergence of hundreds of new ones specializing in cryptocurrencies.<\/p>\n<p>  Despite the apparent simplicity, the payment processing, in terms of creating a secure software implementation, is a complex process that still causes problems for both large trading platforms and new electronic payment systems, which periodically enter the market with \u00abnew and convenient\u00bb APIs and other ways of integration. What does a typical payment processing look like? First, let&#8217;s take a look at the current implementation described by PayPal, the so-called PayPal Express Checkout.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/tg\/ov\/vg\/tgovvgpxw_yldi4aspeldaew8ui.png\" data-src=\"https:\/\/habrastorage.org\/webt\/tg\/ov\/vg\/tgovvgpxw_yldi4aspeldaew8ui.png\"\/><\/p>\n<p>  This implementation can be considered relatively safe, and here&#8217;s why:<\/p>\n<ul>\n<li>Payment parameters are not transmitted explicitly, a token is used instead<\/li>\n<li>The server of the payment system does not send the results to some URL on its own, instead your website has to request them and process the response<\/li>\n<li>In general, the interaction layout is implemented in such a way that a potential developer has a minimum of opportunities to \u00abshoot himself in the foot\u00bb<\/li>\n<\/ul>\n<p>  And now let&#8217;s look at the diagram offered by WebMoney:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ak\/7h\/b6\/ak7hb6fdztceagpksjltqqiuyig.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ak\/7h\/b6\/ak7hb6fdztceagpksjltqqiuyig.png\"\/><\/p>\n<p>  The process diagram doesn&#8217;t make any sense. Also, the diagram does not reflect a number of nuances, such as request signing. Or that the URL which receives the technical payment information from the payment system and the URL where the user is redirected to (to view the payment details) should be different. The architecture used by WebMoney often emerges elsewhere, in one form or another, usually in other payment systems that have been created in the Commonwealth of Independent States.<\/p>\n<h3>Typical Problems<\/h3>\n<p>  Excessive complexity of the payment process leads to financial losses. For example, 10 years ago I published <a href=\"https:\/\/kaimi.io\/2011\/03\/steam-games-for-free\/\" rel=\"nofollow noopener noreferrer\">a note<\/a> about the problem of integration of the Global Collect Services system with WebMoney, which allowed to confirm payments without actually paying on Steam, Battle.net and some other platforms.<\/p>\n<p>  What was the problem? Earlier I mentioned the URLs on the merchant side that are supposed to accept the payment information. According to <a href=\"https:\/\/wiki.wmtransfer.com\/projects\/webmoney\/wiki\/Web_Merchant_Interface\" rel=\"nofollow noopener noreferrer\">the documentation<\/a>, WebMoney has three entities:<\/p>\n<ul>\n<li><b>Success URL<\/b> \u2014 the URL (on the merchant&#8217;s website) to which the buyer&#8217;s web browser will be redirected if the payment in the Web Merchant Interface service is successful. The URL can be prefixed with \u00abhttp:\/\/\u00bb or \u00abhttps:\/\/\u00bb.<\/li>\n<li><b>Fail URL<\/b> \u2014 the URL (on the merchant&#8217;s website) to which the buyer&#8217;s Internet browser will be redirected if the payment in the Web Merchant Interface service has not been completed for some reason. The URL can be prefixed with \u00abhttp:\/\/\u00bb or \u00abhttps:\/\/\u00bb.<\/li>\n<li><b>Result URL<\/b> \u2014 the URL (on the merchant&#8217;s website) to which the Web Merchant Interface service sends an HTTP POST or SMTP notification about the payment with its complete details. The URL can be prefixed with \u00abhttp:\/\/\u00bb, \u00abhttps:\/\/\u00bb or \u00abmailto:\u00bb.<\/li>\n<\/ul>\n<p>  What some developers do after reading the documentation:<\/p>\n<ol>\n<li>Use a single URL, which allows to figure out the address of the handler (also the handler, including the Result URL, can be displayed in the payment form on the WebMoney site, but this does not always happen and probably depends on the settings).<\/li>\n<li>Incorrectly implement signature verification for the request that comes to the Result URL. This allows the customer to substitute payment details.<\/li>\n<li>Check the signature, but don&#8217;t check the amount that was sent to the Result URL. This allows you to get a $100 item by paying, for example, $0.01.<\/li>\n<li>Check the signature, the amount, but not the format of the transferred amounts. Remember, I mentioned sending payment parameters through the client&#8217;s browser? WebMoney correctly handles the value of 1e1 or 0xFF, but the comparison of such numbers on older versions of PHP, taking into account the nuances of comparisons in the PHP language, can lead to the most unexpected consequences.<\/li>\n<li>Not exactly a payment system problem, but what about race conditions and identical internal payment IDs on the merchant&#8217;s server? Hello, balance multiplication.<\/li>\n<li>&#8230;<\/li>\n<\/ol>\n<p>  <\/p>\n<h3>Signature of requests\u200b<\/h3>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/2z\/oc\/v-\/2zocv-mdqxiftpsglr6jabeme5q.png\" data-src=\"https:\/\/habrastorage.org\/webt\/2z\/oc\/v-\/2zocv-mdqxiftpsglr6jabeme5q.png\"\/><\/p>\n<p>  How it worked:<\/p>\n<ol>\n<li>When paying via WebMoney, the users, in accordance with the specifications of the payment system, were redirected to the WebMoney site, where they could see the amount of payment, account number and other parameters.<\/li>\n<li>After pressing the \u00abNext\u00bb button and authenticating in the system, information about the URL responsible for processing the payment result (Result URL) became available.<\/li>\n<li>The user could generate a request to the target URL, which, according to the WebMoney specification (well, almost), informed the payment system that the payment was successful.<\/li>\n<li>Profit!<\/li>\n<\/ol>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/qx\/nh\/f0\/qxnhf00cuksfvur6ysgpm4vt3t4.png\" data-src=\"https:\/\/habrastorage.org\/webt\/qx\/nh\/f0\/qxnhf00cuksfvur6ysgpm4vt3t4.png\"\/><\/p>\n<p>  Global Collect payment processing system has bumped into several problems:<\/p>\n<ol>\n<li>Known unified payment results handler.<\/li>\n<li>Lack of signature verification (and no signature in the request as such).<\/li>\n<li>Use of data transmitted through the user&#8217;s browser as a trusted source of payment information (although, according to WebMoney specifications, this could be done through a callback coming from WebMoney servers).<\/li>\n<\/ol>\n<p>  All this allowed making dummy transactions and buying anything that used Global Collect processing without paying. The problem was eliminated only after ~2 weeks of wide exploitation.<\/p>\n<p>  Another similar problem, but a bit more complicated, was recently discovered in <a href=\"https:\/\/hackerone.com\/reports\/1295844\" rel=\"nofollow noopener noreferrer\">Smart2Pay<\/a>.<br \/>  Another issue related to request signature is <a href=\"https:\/\/en.wikipedia.org\/wiki\/Length_extension_attack\" rel=\"nofollow noopener noreferrer\">Length Extension Attack<\/a>.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/sc\/c0\/kj\/scc0kjomifcxbs72glk7jw0s8hm.png\" data-src=\"https:\/\/habrastorage.org\/webt\/sc\/c0\/kj\/scc0kjomifcxbs72glk7jw0s8hm.png\"\/><\/p>\n<p>  According to Wikipedia, this is a type of attack on a hashing function that adds new information to the end of the original message. In this case, the new Hash value can be calculated even if the content of the original message remains unknown. You can learn more <a href=\"https:\/\/web.archive.org\/web\/20130608035646\/http:\/\/happybearsoftware.com\/you-are-dangerously-bad-at-cryptography.html\" rel=\"nofollow noopener noreferrer\">here<\/a>. The problem was encountered only a couple of times when the developers decided to implement their \u00abcool\u00bb VK-style request signing (who did not invent an algorithm themselves by the way). Below is an illustration on how to appropriately generate a signature and how to \u00abshoot yourself in the foot.\u00bb<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/__\/e7\/hg\/__e7hgnuxfkwnmv07metsjnpans.png\" data-src=\"https:\/\/habrastorage.org\/webt\/__\/e7\/hg\/__e7hgnuxfkwnmv07metsjnpans.png\"\/><\/p>\n<p>  For exploitation, you can use one of the following tools:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/bwall\/HashPump\" rel=\"nofollow noopener noreferrer\">https:\/\/github.com\/bwall\/HashPump<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/iagox86\/hash_extender\" rel=\"nofollow noopener noreferrer\">https:\/\/github.com\/iagox86\/hash_extender<\/a><\/li>\n<\/ul>\n<h3>\u201cResult URL\u201d disclosure\u200b<\/h3>\n<p>  The full URL used by the payment system to notify the site if the payment is successfully credited was displayed with the parameters (including the signature) on a web site where top-up via WooPay (via SMS) was available.<br \/>  The logic is simple enough; you need to figure out a way to trigger an exception so that the web application displays an error.<br \/>  If you choose to pay via SMS and enter a random invalid phone number, you get:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/bs\/zp\/b6\/bszpb6qaj9k4floic6kjoivqihs.png\" data-src=\"https:\/\/habrastorage.org\/webt\/bs\/zp\/b6\/bszpb6qaj9k4floic6kjoivqihs.png\"\/><\/p>\n<p>  Repeat the HTTP request a couple of hundred times. After banning our client, the web application throwed an exception. Its text contained the secret URL. By following it, we could finalize the payment.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4b\/v3\/z8\/4bv3z8cwzilzaaccvyehm6wfhjy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/4b\/v3\/z8\/4bv3z8cwzilzaaccvyehm6wfhjy.png\"\/><\/p>\n<h3>Payment attributes<\/h3>\n<p>  Let&#8217;s move on to the problem of checking payment attributes.<\/p>\n<p>  One of the options for integrating with YooMoney (former Yandex.Money) is a <a href=\"https:\/\/yoomoney.ru\/docs\/payment-buttons\/using-api\/forms\" rel=\"nofollow noopener noreferrer\">money transfer form<\/a> or its <a href=\"https:\/\/web.archive.org\/web\/20210225030211\/https:\/\/yookassa.ru\/docs\/payment-solution\/payment-form\/basics\" rel=\"nofollow noopener noreferrer\">old implementation<\/a>. It can be identified by the presence of HTTP requests to the following URLs:<\/p>\n<pre><code class=\"bash\">https:\/\/yoomoney.ru\/eshop.xml https:\/\/yoomoney.ru\/quickpay\/confirm.xml<\/code><\/pre>\n<p>  Right when you send the request, you need to substitute the payment amount in it:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ig\/fm\/4l\/igfm4ldxpefgmnaqp5ud889bdjk.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ig\/fm\/4l\/igfm4ldxpefgmnaqp5ud889bdjk.png\"\/><\/p>\n<p>  There is a high probability that the payment will be successful. And then the web site either checks the amount or not. Since the payment metadata has the user ID and\/or payment ID, this is enough to purchase something.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/rm\/9q\/nl\/rm9qnlezw3ewviejyytuc-qlvze.png\" data-src=\"https:\/\/habrastorage.org\/webt\/rm\/9q\/nl\/rm9qnlezw3ewviejyytuc-qlvze.png\"\/><\/p>\n<p>  A small example:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ad\/fv\/vv\/adfvvvkkxckzp3v4zzvqe-1fxlg.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ad\/fv\/vv\/adfvvvkkxckzp3v4zzvqe-1fxlg.png\"\/><\/p>\n<p>  The screenshot shows the Telegram voice assistant bot subscription, but the payment amount is not checked, allowing you to purchase it for an arbitrary price. Change <code>1990<\/code> to <code>19<\/code> to get it for real cheap. This kind of vulnerability is pretty common (for example, there are many well-known bot services in Telegram which suffer from the same issue), including on popular international resources (an old example \u2013 the purchase of a <a href=\"https:\/\/kaimi.io\/2011\/09\/almost-for-free-minecraft\/\" rel=\"nofollow noopener noreferrer\">Minecraft<\/a> license). You can still encounter this even in 2022.<\/p>\n<p>  Another relevant example, but not related to the payment amount, but rather to the currency, was present in <a href=\"https:\/\/en.wikipedia.org\/wiki\/Qiwi\" rel=\"nofollow noopener noreferrer\">QIWI<\/a>. You could top up the wallet balance by sending SMS to a short number, and the currency was transmitted through the client&#8217;s browser at several stages (selecting the currency and amount, sending SMS), where the server trusted the client&#8217;s data. As a result, $100 was credited to the account, for a payment of 100 rubles.<\/p>\n<p>  What about 2022?<br \/>  Let&#8217;s look at an Armenian bank chat <a href=\"https:\/\/t.me\/Inecobank_forum\/6333\" rel=\"nofollow noopener noreferrer\">https:\/\/t.me\/Inecobank_forum\/6333<\/a>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/_x\/hb\/rf\/_xhbrfjj0lehe2-71pgq4fu0hfc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/_x\/hb\/rf\/_xhbrfjj0lehe2-71pgq4fu0hfc.png\"\/><br \/>  <code>Hello! Is there any way to transfer money from a ruble account immediately into card account in drams (bypassing current bank account in drams) so as not to violate the currency legislation of the Russian Federation?<\/code><\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/yn\/rv\/wm\/ynrvwmkmjupshr_hjtupenz-9t8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/yn\/rv\/wm\/ynrvwmkmjupshr_hjtupenz-9t8.png\"\/><\/p>\n<p>  <code>In general, before I was told that this was impossible, I made a transfer of 100,000 rubles from my current account to the card account in drams using its details. 100,000 drams have been credited. 100,000 rubles were deducted. All in INECO. Apparently there is no automatic verification of the payment currency. Now I'm dealing with support. Moral - don't do this.<\/code><\/p>\n<p>  This means the problem is still relevant.<\/p>\n<h3>Formats and types comparison<\/h3>\n<p>  An interesting problem was discovered on <a href=\"https:\/\/anti-captcha.com\/\" rel=\"nofollow noopener noreferrer\">Anticaptcha<\/a> service, well known in certain circles (a service for solving captchas for money with API interface). User&#8217;s personal account allowed performing a number of operations, including withdrawal of the unused balance to WebMoney. WebMoney handles the amount of payment in different formats quite normally (e.g. <code>1e1<\/code> or <code>0xFF<\/code>), but comparing such numbers, especially in older versions of PHP, taking into account the nuances of comparison in PHP, having the \u00abmost perfect code\u00bb, led to the most unexpected consequences. In a hexadecimal notation, the comparison of the current balance with the amount requested for withdrawal did not work correctly, which allowed the account balance to go negative.<br \/>  An example of a PHP code fragment that could lead to this:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/d6\/ki\/_z\/d6ki_zunjaij--reyekucso5ys0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/d6\/ki\/_z\/d6ki_zunjaij--reyekucso5ys0.png\"\/><\/p>\n<p>  If you input the amount <code>1e9<\/code> having a balance of<code> $20<\/code>, the validating logic will make sure that <code>20>19<\/code>, removing everything except digits, but the payment system will treat <code>1e9<\/code> as <code>1000000000<\/code>.<\/p>\n<p>  Another mistake is the peculiarities of typecasting.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/5j\/jm\/hn\/5jjmhnpbmmp4-lpmljskoxfcmhw.png\" data-src=\"https:\/\/habrastorage.org\/webt\/5j\/jm\/hn\/5jjmhnpbmmp4-lpmljskoxfcmhw.png\"\/><\/p>\n<p>  NodeJS is another example of a language with dynamic typing. When adding a string to a number, it will concatenate <code>1+\"1\" = \"11\"<\/code>. However, if we subtract a number from the string, the string is converted to the number <code>\"11\"-1 = 10<\/code>.<br \/>  The most popular data exchange format is JSON:<\/p>\n<pre><code class=\"json\">{\"amount\":100}<\/code><\/pre>\n<p>  It is obvious, that this JSON is correct: there is the amount parameter with the value 100. But this will also be a valid JSON:<\/p>\n<pre><code class=\"json\">{\"amount\": \"100\"}<\/code><\/pre>\n<p>  Here, the type of the amount parameter is a string. Depending on the algorithm of JSON processing (by the way, interesting <a href=\"https:\/\/bishopfox.com\/blog\/json-interoperability-vulnerabilities\" rel=\"nofollow noopener noreferrer\">relatively related article<\/a>), someone may add the value of this parameter to the number <code>1337<\/code>, and the result will be <code>1337100<\/code>, and not what was originally intended.<\/p>\n<h3>Business logic flaws<\/h3>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ng\/cr\/3d\/ngcr3d0wdolab6mgpymy54yyjse.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ng\/cr\/3d\/ngcr3d0wdolab6mgpymy54yyjse.png\"\/><\/p>\n<p>  A payment is initiated in remote banking, to confirm it, you need to enter the code from the SMS. The payment is saved as uncompleted and is available for editing in the remote banking mobile app. We edit the payment, then enter the confirmation code in the browser, and the final transfer is made with one amount, but the account is debited with another.<br \/>  Another example:<\/p>\n<ol>\n<li>Open the balance top up interface (the balance is $1000, the top up amount is $100).<\/li>\n<li>The web app remembers your current balance.<\/li>\n<li>Meanwhile, we spend money (send it to the second account).<\/li>\n<li>After completing the transaction, the balance will be $1100.<\/li>\n<\/ol>\n<p>  What also deserves a special mention, is the shopping cart logic on resources where several currencies are supported. A vulnerability that was found in the Xbox regional store a few years ago (and has re-appeared a couple more times after the fix):<\/p>\n<ol>\n<li>You add the product to the shopping cart for the minimum price in rubles.<\/li>\n<li>You look for expensive games in the store, the prices of which are listed in US dollars.<\/li>\n<li>Add them to your cart.<\/li>\n<li>The store calculates the total cost of items, but items with USD prices are recalculated to RUB at a ratio of one to one.<\/li>\n<\/ol>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/dk\/97\/tn\/dk97tnvoawrbfrc5-1k9uwoleho.png\" data-src=\"https:\/\/habrastorage.org\/webt\/dk\/97\/tn\/dk97tnvoawrbfrc5-1k9uwoleho.png\"\/><\/p>\n<p>  The screenshot above shows that the cost of games in the shopping cart is indicated in rubles, but the actual amount hints that it should have been in dollars (or should be completely different after conversion at the appropriate exchange rate).<\/p>\n<p>  Votes on <a href=\"http:\/\/vk.com\/\" rel=\"nofollow noopener noreferrer\">VKontakte <\/a>were added by sending the paid SMS while having a near-zero balance. You send an SMS, the telecom operator can&#8217;t take the money (there is no <a href=\"https:\/\/en.wikipedia.org\/wiki\/Overdraft\" rel=\"nofollow noopener noreferrer\">overdraft<\/a>), but the votes are still replenished.<br \/>  Another vector via SMS is a transfer from your account to someone else&#8217;s account in the QIWI payment system. This was done by sending a message to a dedicated short number:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/jw\/tq\/br\/jwtqbrdqfvvc5chcxidhctuhazi.png\" data-src=\"https:\/\/habrastorage.org\/webt\/jw\/tq\/br\/jwtqbrdqfvvc5chcxidhctuhazi.png\"\/><\/p>\n<p>  <code>Transfer money to another user. Send an SMS to 7494 with the text 'perevod' or 'transfer', enter the wallet number and the amount of the transfer separated with a space. For example: perevod 9161234567 500. You will receive an SMS with a one-time code - send it back.<\/code><\/p>\n<p>  That short number is in fact an alias of a real phone number that is used by the SMS gateway for integration with the API. Apply a little social engineering to the support service:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/kv\/cv\/wi\/kvcvwiknntbtzqmz-bhdudihgy0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/kv\/cv\/wi\/kvcvwiknntbtzqmz-bhdudihgy0.png\"\/><\/p>\n<p>  <code>Good afternoon. Full number is +7 925 424 74 94.<\/p>\n<p>  Request type: other topic.<br \/>  Client software version: WEB v3.0.<br \/>  Message: Good afternoon! Is there an alternative to number 7494 - the \"Content block\" service is enabled (sending and receiving paid SMS \/ MMS from short numbers is prohibited, as well as calls to paid short numbers), and it is very convenient to use the number, but it's impossible because the number is short. Thank you.<\/code><\/p>\n<p>  The next step is to use spoofing services (to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Caller_ID_spoofing\" rel=\"nofollow noopener noreferrer\">spoof Caller ID<\/a>) to send an SMS from a number of an account with a lot of money. This method has never been tested by me, although in theory it looks extremely promising. Some experts say that it is possible to link a credit card via SMS (in a similar way), and then to drain money from the card.<\/p>\n<p>  Now let&#8217;s examine the refund operation. If you consider the canonical refund process, it becomes obvious that at each stage it is possible to skip or incorrectly implement some of the checks, which will lead to financial losses.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ak\/pp\/5k\/akpp5kofng4jtxwbmpmnn69y6em.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ak\/pp\/5k\/akpp5kofng4jtxwbmpmnn69y6em.png\"\/><\/p>\n<p>  In practice, there were web sites where the refund amount was equal to the current price of the product. Instead they should have used an actually paid amount from the relevant transaction record. Together with periodic discounts, this led to obvious results. The situation is rare, but sometimes it occurs in one form or another.<\/p>\n<h3>Rounding, integer overflow, and negative numbers<\/h3>\n<p>  A frequent category of problems is rounding errors. Common problems with rounding may look like this:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/e9\/uk\/kc\/e9ukkchxqqtuclc39w0lhyjoid8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/e9\/uk\/kc\/e9ukkchxqqtuclc39w0lhyjoid8.png\"\/><\/p>\n<ol>\n<li>A user converts 0.29 RUB to US dollars.<\/li>\n<li>If the value of one dollar is 60 RUB, the amount of 0.29 RUB corresponds to USD 0.0048333333333333333333333333.<\/li>\n<li>This amount will be rounded to two decimal places, i.e. to 0.01 USD (one cent).<\/li>\n<li>Then the user converts 0.01 USD back to rubles and receives 0.60 RUB.<\/li>\n<li>Thus, the user \u00abwins\u00bb 0.31 RUB.<\/li>\n<\/ol>\n<p>  The problem can still be found in large financial institutions (various banks and exchanges).<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/lc\/je\/ks\/lcjeksesfu4jhcm-gcknuqfvlt8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/lc\/je\/ks\/lcjeksesfu4jhcm-gcknuqfvlt8.png\"\/><\/p>\n<p>  If you look closely, you can see the vulnerability, although it is already fixed. Overflows and bugs with negative amount transactions can be encountered periodically, even with banks from the top 100 list. Transaction with a negative amount is a trivial bug when working with signed numbers, and yes, it <b>still happens too<\/b>.<br \/>  A less trivial example of overflow is the calculation of the order amount when adding a large number of items to the cart.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/uf\/9a\/oj\/uf9aojace_fkc6-cujwhirnecpw.png\" data-src=\"https:\/\/habrastorage.org\/webt\/uf\/9a\/oj\/uf9aojace_fkc6-cujwhirnecpw.png\"\/><\/p>\n<p>  Another example is the compatibility of large numbers when transferring between systems. In HTTP-request, the transmitted number will be a string, but the processing of large numbers may be different, i.e. a top up request is sent for more than <a href=\"https:\/\/www.geeksforgeeks.org\/int_max-int_min-cc-applications\/#:~:text=Value%20of%20INT_MAX%20is%20%2B2147483647.\" rel=\"nofollow noopener noreferrer\">INT_MAX<\/a>+2, on the local system, the number is processed correctly, but the payment system receives an invoice for payment of <code>1$<\/code>.<br \/>  Keep in mind that the target system may not use a 32-bit variable to store the value, but a 64-bit one.<br \/>  To get a better idea, you can play with the numbers on Vkontakte. VKontakte earlier used 32-bit integers. To reach the <code>id1<\/code> page by overflowing the id, it was necessary to pass <code>2^32+1<\/code>.<br \/>  Pavel Durov&#8217;s page could be opened under the following URL: <a href=\"https:\/\/vk.com\/id4294967297\" rel=\"nofollow noopener noreferrer\">https:\/\/vk.com\/id4294967297<\/a>. But now everything has been converted to 64-bit integers, so to get <code>id1<\/code>, you need to pass <a href=\"https:\/\/www.wolframalpha.com\/input?i=%282%5E64%2B1%29\" rel=\"nofollow noopener noreferrer\">2^64+1<\/a>.<br \/>  These are the same pages:<br \/>  <a href=\"https:\/\/vk.com\/id1\" rel=\"nofollow noopener noreferrer\">https:\/\/vk.com\/id1<\/a> == <a href=\"https:\/\/vk.com\/id18446744073709551617\" rel=\"nofollow noopener noreferrer\">https:\/\/vk.com\/id18446744073709551617<\/a><br \/>  Now let&#8217;s consider, that in some web application, an operation with <code>id=100<\/code> can only be performed by an administrator. And what if this is an operation with <code>2^32+100<\/code>?<br \/>  By the way, sometimes you can miscalculate the numbers, and go deeply negative, never getting the profit.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ue\/xm\/af\/uexmafespdostumy4kq-j5ryqha.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ue\/xm\/af\/uexmafespdostumy4kq-j5ryqha.png\"\/><\/p>\n<h3>Race condition<\/h3>\n<p>  Let&#8217;s move on to the race condition issue. <a href=\"https:\/\/en.wikipedia.org\/wiki\/Race_condition\" rel=\"nofollow noopener noreferrer\">According to Wikipedia<\/a>, a race condition or race hazard is the condition of an electronics, software, or other system where the system&#8217;s substantive behavior is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when one or more of the possible behaviors is undesirable.<\/p>\n<p>  A conventionally canonical example:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/1u\/rh\/pv\/1urhpvhopfm_ccnm8x78holckws.png\" data-src=\"https:\/\/habrastorage.org\/webt\/1u\/rh\/pv\/1urhpvhopfm_ccnm8x78holckws.png\"\/><\/p>\n<ol>\n<li>We perform a transaction to transfer funds from the available balance.<\/li>\n<li>We perform the same operation N times, and let&#8217;s consider we should run out of balance at (N-1)th request or earlier. Sending requests with minimal delay may exploit the race condition and overcome this limitation (here <a href=\"https:\/\/ru.wikipedia.org\/wiki\/HTTP_pipelining\" rel=\"nofollow noopener noreferrer\">HTTP-pipelining<\/a>, HTTP2 features (multiple requests within one TCP session), etc. come to rescue).<\/li>\n<li>We get a negative balance, which is not permitted under normal circumstances.<\/li>\n<\/ol>\n<p>  A small example related to the cryptocurrency exchange.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/we\/od\/t3\/weodt3ghj3afymdonht4o0-z1ji.png\" data-src=\"https:\/\/habrastorage.org\/webt\/we\/od\/t3\/weodt3ghj3afymdonht4o0-z1ji.png\"\/><\/p>\n<p>  The operation algorithm was as follows:<\/p>\n<ol>\n<li>We create a take profit of 0,1 BTC, when the price of Bitcoin is $100,000.<\/li>\n<li>The exchange withdraws (locks) 0,1 BTC from the account balance.<\/li>\n<li>We delete the take profit by sending 438695936458926734 requests.<\/li>\n<li>The exchange \u00abreturns\u00bb 0.1 \u00d7 N BTC, where N is the number of simultaneous operations.<\/li>\n<\/ol>\n<p>  This category of problems is not specific to financial transactions. This also includes problems like <a href=\"https:\/\/en.wikipedia.org\/wiki\/Time-of-check_to_time-of-use\" rel=\"nofollow noopener noreferrer\">TOCTOU<\/a>, when, for example, the application checks the file signature, then some time passes, and then the application reads the contents of the file (which can be substituted by that time).<\/p>\n<p>  One of the problems was present on <a href=\"http:\/\/xss.is\/\" rel=\"nofollow noopener noreferrer\">xss.is<\/a> in the system for transferring BTC between accounts.<\/p>\n<p>  You can use Burp Suite with the <a href=\"https:\/\/portswigger.net\/bappstore\/9abaa233088242e8be252cd4ff534988\" rel=\"nofollow noopener noreferrer\">Turbo Intruder<\/a> plugin for testing. And you can read more about this category of problems in <a href=\"https:\/\/lab.wallarm.com\/race-condition-in-web-applications\/\" rel=\"nofollow noopener noreferrer\">this article<\/a>.<\/p>\n<p>  So, we deposit 0.1337 BTC and send a lot of transfer requests.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/3c\/3f\/t8\/3c3ft8jk9mylcsc6bivlitzkdi8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/3c\/3f\/t8\/3c3ft8jk9mylcsc6bivlitzkdi8.png\"\/><\/p>\n<p>  We can see that the transfer was completed more times than it was possible having a limited amount of money on the balance:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/qf\/ds\/zm\/qfdszmlbzf9jqb5buphsnsirfu4.png\" data-src=\"https:\/\/habrastorage.org\/webt\/qf\/ds\/zm\/qfdszmlbzf9jqb5buphsnsirfu4.png\"\/><\/p>\n<p>  We send the cryptocurrency back. We keep transferring money back and forth between different accounts, generating money out of thin air:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/dm\/e9\/8b\/dme98b6d0u5pqforhh2_ftbcmee.png\" data-src=\"https:\/\/habrastorage.org\/webt\/dm\/e9\/8b\/dme98b6d0u5pqforhh2_ftbcmee.png\"\/><\/p>\n<p>  Finally, we get much more money on the balance (2.1337 BTC). However, in reality there was no such deposit, so you can withdraw as much as the connected wallet has (with all user deposits).<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/7v\/41\/rh\/7v41rhfie97tnhxvthyivrydnqu.png\" data-src=\"https:\/\/habrastorage.org\/webt\/7v\/41\/rh\/7v41rhfie97tnhxvthyivrydnqu.png\"\/><\/p>\n<p>  I think there are other forums where deposits and withdrawals are possible without manual confirmation.<\/p>\n<h3>Summary<\/h3>\n<p>  Implementing a secure payment processing is a complex task that should be handled by experienced developers. The resulting product needs to be comprehensively tested, otherwise we will observe childish security problems from the early noughties for decades to come, especially when new cool payment methods (hello, cryptocurrencies) and related payment systems appear. And we haven&#8217;t even mentioned <a href=\"https:\/\/en.wikipedia.org\/wiki\/Random_number_generator_attack\" rel=\"nofollow noopener noreferrer\">attacks on pseudorandom number generators<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Padding_oracle_attack\" rel=\"nofollow noopener noreferrer\">Padding Oracle<\/a>, and many other fun things that deserve a separate article.<\/p>\n<p>  \u00a9 Kaimi &amp; <a href=\"https:\/\/bo0om.ru\/20-years-of-payment-processing-problems-en\" rel=\"nofollow noopener noreferrer\">Bo0oM<\/a>\u200b<br \/>  Thanks to <a href=\"https:\/\/github.com\/dragon-dreamer\" rel=\"nofollow noopener noreferrer\">d_x<\/a> for English version of the article.<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/684074\/\"> https:\/\/habr.com\/ru\/articles\/684074\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/mu\/4w\/nn\/mu4wnncoijkmden9frkwagelwj0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/mu\/4w\/nn\/mu4wnncoijkmden9frkwagelwj0.png\"\/><br \/>  <i>Thanks to <a href=\"https:\/\/habr.com\/en\/users\/yarbabin\/\" class=\"user_link\">yarbabin<\/a> for the logo<\/i><\/p>\n<p>  Electronic payment systems have existed on the Internet for a long time, and some bugs in them are twenty years old. We&#8217;ve found critical vulnerabilities allowing us to steal money and drive up the balance. Today we will analyze typical implementations of payment processing and related security issues.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-406033","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/406033","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=406033"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/406033\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=406033"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=406033"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=406033"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}