D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Я полный 0e1823081...
### var_dump в PHP
var_dump в PHP — это функция, которая выводит информацию о переменной, включая её тип и значение. Она особенно полезна для отладки кода, так как позволяет увидеть не только значение переменной, но и её тип, что помогает выявлять ошибки и понимать, что именно происходит в коде.Код: Скопировать в буфер обмена
Код:
$a = 10;
$b = "Hello, World!";
$c = [1, 2, 3];
$d = null;
var_dump($a);
var_dump($b);
var_dump($c);
var_dump($d);
------------------------------------
int(10)
string(13) "Hello, World!"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
NULL
### Преобразование строк в числа в PHP
В PHP строки могут быть автоматически преобразованы в числа в определенных контекстах (например, во время сравнений).Преобразование строк в целые числа (integer)
Когда строка содержит числовое значение и преобразуется в целое число, PHP просто читает эту строку как число. Если строка начинается с цифр, PHP попытается извлечь из нее числовое значение.
Код: Скопировать в буфер обмена
Код:
$int1 = (int)"123"; // 123
$int2 = (int)"456abc"; // 456
$int3 = (int)"0"; // 0
$int4 = (int)"-789"; // -789
$int5 = (int)"abc123"; // 0 (нельзя преобразовать, точнее можно и это 0...?)
Преобразование строк в числа с плавающей запятой (float)
Строки, содержащие числа с плавающей запятой, преобразуются аналогичным образом. Если строка начинается с числового значения, включая точку, PHP преобразует ее в число с плавающей запятой.
Код: Скопировать в буфер обмена
Код:
$float1 = (float)"123.45"; // 123.45
$float2 = (float)"-678.90"; // -678.9
$float3 = (float)"0.123"; // 0.123
$float4 = (float)"abc456.78"; // 0.0
$float5 = (float)"789.01abc"; // 789.01
В PHP строки могут автоматически преобразовываться в числа в контексте арифметических операций или при использовании с операторами сравнения.
Код: Скопировать в буфер обмена
Код:
$sum = "10" + 20; // 30 (строка "10" преобразуется в число 10)
$kolvo = "10e2" + 1; // 1001 (строка преобразуется в 10e2, что эквивалентно 1000)
$product = "3.14" * 2; // 6.28 (строка "3.14" преобразуется в число 3.14)
if ("100" == 100) {
echo "Equal"; // Выведет "Equal" (строка "100" преобразуется в число 100)
}
### Сравнение
PHP предоставляет два основных типа операторов сравнения: нестрогое сравнение (loose comparison) и строгое сравнение (strict comparison). Понимание их различий важно для правильного использования в коде.Нестрогое сравнение (loose comparison)
Нестрогое сравнение использует оператор == и приводит операнды к одному типу данных перед сравнением. Это может быть удобно, но иногда приводит к неожиданным результатам.
Код: Скопировать в буфер обмена
Код:
var_dump(0 == "0"); // true
var_dump(1 == "1"); // true
var_dump(123 == "123"); // true
var_dump(0 == false); // true
var_dump(0 == null); // true
var_dump(false == null); // true
var_dump("foo" == 0); // (true до PHP 8) (false => PHP 8.0.0)
var_dump("foo" == "0"); // false
Код: Скопировать в буфер обмена
"foo" == 0
Это сравнение превращается в:
Код: Скопировать в буфер обмена
0=0
Строгое сравнение (strict comparison)
Строгое сравнение использует оператор === и проверяет как значение, так и тип данных. Это более безопасный способ сравнения, так как он исключает неявное приведение типов.
Примеры строгого сравнения:
Код: Скопировать в буфер обмена
Код:
var_dump(0 === "0"); // false
var_dump(1 === "1"); // false
var_dump(123 === "123"); // false
var_dump(0 === false); // false
var_dump(0 === null); // false
var_dump(false === null); // false
var_dump("foo" === 0); // false
var_dump("foo" === "0"); // false
var_dump("123" === "123"); // true
Hex
Шестнадцатеричные числа, могут использоваться для сравнения с другими числами. Пример с var_dump() в PHP 8.2.12
Код: Скопировать в буфер обмена
Код:
<?php
var_dump(0xA2 == 162); // bool(true)
var_dump('0xA2' == 162); // bool(false)
var_dump(0xA2 == '162'); // bool(true)
var_dump('0xA2' == '162'); // bool(false)
?>
Во втором случаи '0xA2' - это строка. При loose сравнении шестнадцатеричное число, не преобразуется автоматически в числовое значение при сравнении с использованием оператора ==. Вместо этого строка остаётся строкой, и PHP не интерпретирует её как число.
В третьем примере 0xA2 снова интерпретируется как шестнадцатеричное число, которое равно 162. Строка '162' будет преобразована в число 162. Сравнение даст результат true.
В четвертом примере оба значения являются строками. При loose сравнении строки не преобразуются в числа, если обе стороны сравнения являются строками. Строки '0xA2' и '162' не равны друг другу, поэтому результат будет false.
### А где же уязвимость?
Технически, уязвимость везде где написано == вместо === и обе стороны читаются как цифры. Скажем что есть у нас приватный ключ, который равен "QLTHNDT" и в коде у нас написано что если инпут который даёт ползьователь == нашему ключу, то он получает доступ. Естественно ключ хранится в мд5 формате в бд:Код: Скопировать в буфер обмена
0e405967825401955372549139051580
И скажем мы сравниваем ключ который ввёл пользователь (NOOPCJF), с тем который у нас в бд
Код: Скопировать в буфер обмена
Код:
<?php
$key = 'NOOPCJF';
if (md5($key) == md5(QLTHNDT)){
echo 'Success';
};
?>
Код: Скопировать в буфер обмена
Код:
if ('0e818888003657176127862245791911' == '0e405967825401955372549139051580'){
echo 'Success';
};
'0e818888003657176127862245791911' интерпретируется как 0×8(с степенью)18888003657176127862245791911, что равно 0.
'0e405967825401955372549139051580' интерпретируется как 0×4(с степенью)05967825401955372549139051580, что равно 0.
Поскольку обе строки интерпретируются как число 0, сравнение 0 == 0 дает true.
Я очень даже хорошо понимаю то что возможностей тут не много, но это всего один пример на основе говнокода который я написал. Давайте чекнем то что находили на эту тему.
### CVE-2023-50110
TestLink до 1.9.20 уязвим на Type Juggling для обхода аутентификации, поскольку === не используется.Код: Скопировать в буфер обмена
Код:
public function comparePassword(&$dbH,$pwd)
{
if (self::isPasswordMgtExternal($this->authentication)) {
return self::S_PWDMGTEXTERNAL;
}
// If we are here this means that we are using
// internal password management.
//
// Manage migration from MD5
// MD5 hash check
// This is valid ONLY for internal password management
$encriptedPWD = $this->getPassword();
if (strlen($encriptedPWD) == 32) {
/* Update the old MD5 hash to the new bcrypt */
if ($encriptedPWD == md5($pwd)) {
$this->password = $this->encryptPassword($pwd,$this->authentication);
$this->writePasswordToDB($dbH);
return tl::OK;
}
}
if (password_verify($pwd,$encriptedPWD)) {
return tl::OK;
}
return self::E_PWDDONTMATCH;
}
Давайте напишем симуляцию того что наверху:
Код: Скопировать в буфер обмена
Код:
<?php
$encryptedPass = '0e405967825401955372549139051580';
$pwd = '5lYWomyrSgBi';
if ($encryptedPass == md5($pwd)) {
echo 'Бкрипти $pwd и сохраняет его в БД. ';
};
?>
Код бкрипнет мой пароль и я получу доступ, тк как и выше было написано он возьмёт оба инпута как числа, в итоге которого 0 будет равен 0. Шансы такого? Я лично считаю что где то ближе к нулю.
Один и тот же случай (почти) с CVE-2022-47034. CVE-2020-23356, CVE-2020-23361, CVE-2020-8547, CVE-2020-8088...
### А где же интересные случаи ?
Этот случай взят из программы оффсек, но я по своему написал."А где же интересные случаи?" спросите вы меня. Я просмотрел много CVE-шек и как по мне самый интересный случай с ATutor.
Код: Скопировать в буфер обмена
Код:
25: if (isset($_GET['e'], $_GET['id'], $_GET['m'])) {
26: $id = intval($_GET['id']);
27: $m = $_GET['m'];
28: $e = $addslashes($_GET['e']);
29:
30: $sql = "SELECT creation_date FROM %smembers WHERE member_id=%d";
31: $row = queryDB($sql, array(TABLE_PREFIX, $id), TRUE);
32:
33: if ($row['creation_date'] != '') {
34: $code = substr(md5($e . $row['creation_date'] . $id), 0, 10);
35: if ($code == $m) {
36: $sql = "UPDATE %smembers SET email='%s', last_login=NOW(), creation_date=creation_date WHERE member_id=%d";
37: $result = queryDB($sql, array(TABLE_PREFIX, $e, $id));
38: $msg->addFeedback('CONFIRM_GOOD');
39:
40: header('Location: '.$_base_href.'users/index.php');
41: exit;
42: } else {
43: $msg->addError('CONFIRM_BAD');
44: }
45: } else {
46: $msg->addError('CONFIRM_BAD');
47: }
Значит если m (он же код) будет равен нулю, и если мы сможем как то сделать так чтобы хеш кода был равен тоже нулю (как делали выше 0e0192091....), то сможем поменять пароль пользователя.
Так в чём разница этого случая с предыдущими?
Здесь сравнивается первые 10 символов кода, тоесть хеш может быть и такой: 0e1827381dhsjqosamznftriwoanzgq9.
Значит для генерации кода берётся почта, дата и айди. Почту и айди манипулировать мы можем, скажем что дата это просто год (2024). Тогда я могу отправить такой запрос:
Код: Скопировать в буфер обмена
/changeMail.php?e=test@example.com&m=0&id=10788
В бекенд будет так:
Код: Скопировать в буфер обмена
Код:
$code = substr(md5(test@example.com . 2024 . 10788), 0, 10);
----------
$code = substr('0e075858689f192df8cd60810402d1ca'), 0, 10);
Код: Скопировать в буфер обмена
Код:
$code == $m
----------
var_dump('0e07585868' == '0'); // bool(true)
Реально ли это? Снова нет, но случай интересный.