Но и тут чего-то не хватает... Ну сменили мы пароль, ну захватили активную сессию. А как украсть деньги у участника, если все транзакции подтверждаются уникальными кодами с его карточки, выданной прямо перед началом конкурса? И вот тут обнаруживается последняя шестая уязвимость - подделка идентификатора карты и её кода, проверяемых сервером.
Application\View\templates\Payments\confirmtan.pht ml
Код:
Confirm transaction
From
escapeHtml($accountFrom->getNumber()) ?>
To
escapeHtml($accountTo->getNumber()) ?>
Sum
getSum() ?> currencySymbol($accountFrom->getCurrency()) ?>
">
Password #getId() ?>
getCardId() ?>">
Wrong password
Confirm
getId() ?>/" class="btn btn-danger">Delete
Как видно из исходника - на сервер передаётся hidden-переменная
cart_id, содержащая идентификатор проверяемой карты. Если подменить это значение на идентификатор своей карты, сервер спросит TAN-код именно вашей карты, а не карты того, в чей аккаунт вы залогинились вследствие предыдущего эксплойта.
Application\Controller\PaymentsController.php
Код:
...
public function confirmTanAction()
{
$id = $this->request->getParam('id');
$transaction = $this->tService->fetchTransactionById($id);
if ($this->user->getOtpMethod() != 'tan')
throw new ForbiddenException();
if (!$transaction)
throw new Exception\TransactionNotFoundException();
if ($transaction->getUserId() != $this->user->getId())
throw new ForbiddenException();
if ($transaction->getConfirmed())
$this->redirect('/payments/commit/id/' . $transaction->getId());
$userService = $this->serviceManager->get('userService');
$accountFrom = $userService->fetchAccountById($transaction->getFrom());
$accountTo = $userService->fetchAccountById($transaction->getTo());
$return = array(
'transaction' => $transaction,
'accountFrom' => $accountFrom,
'accountTo' => $accountTo
);
if ($this->request->isPost()) {
$cardId = $this->request->getPost('card_id');
$tan = $this->tService->fetchLastTan($cardId);
if ($tan->getCode() == $this->request->getPost('otp')) {
$tan->setUsed(true);
$this->tService->updateTan($tan);
$transaction->setConfirmed(true);
$this->tService->updateTransaction($transaction);
$this->redirect('/payments/commit/id/' . $transaction->getId());
} else {
$return['error'] = true;
}
} else {
$cardId = $this->user->getCardId();
$tan = $this->tService->fetchLastTan($cardId);
}
$return['tan'] = $tan;
return $return;
}
Значение Card_ID просто берётся из POST запроса, и сверяется с случайным TAN-кодом карты с соответствующим идентификатором. В ходе конкурса как раз было продемонстрировано как "матрёшка" из трёх подряд уязвимостей привела к опустошению кошелька одного из участников.
Ну что сказать, Браво Организаторам !!!
Когда используются вот такие "логические" цепочки из уязвимостей - довести их завершение до логической концовки становится ещё интереснее. Ведь fail на любом этапе привёл бы к падению всей кампании. А это риск. И прямо дух захватывает от ожидания "большого ку$h-а".
Спасибо всем, кто участвовал в конкурсе вместе со мной, кто болел за участников, организаторам за неповторимую атмосферу и не самые простые головоломки. Надеюсь, я увижусь со всеми Вами и в следующем году, ведь
третье место только подстегнуло мой интерес к завоеванию Олимпа в данном конкурсе, тем более что все предпосылки к этому были.
(c) BigBear, 2014
Напоследок, небольшое фото с итоговыми счетами всех участников.
P.S. Исходники скриптов в аттаче, ссылка на доклад об округлении сумм в ДБО - http://2013.zeronights.ru/includes/docs/Adrian_Furtuna_-_Practical_exploitation_of_rounding_vulnerabilitie s_in_internet_banking_applications.pdf