# 基础漏洞
# 目录遍历漏洞
public static function list_files_for_user($username) { | |
$base = "files/".$username; | |
if (!file_exists($base)) { | |
mkdir($base); | |
} | |
return array_diff(scandir($base), array('..', '.')); | |
} |
这个函数的作用是列出用户目录下的所有文件和文件夹,但在第二行的代码中没有对 $username
做严格过滤,可能导致目录穿越漏洞。
# 绕过
public static function addfile($user) { | |
$file = "files/".basename($user)."/".basename($_FILES["file"]["name"]); | |
if (!preg_match("/\.pdf/", $file)) { | |
return "Only PDF are allowed"; | |
} elseif (!move_uploaded_file($_FILES["file"]["tmp_name"], $file)) { | |
return "Sorry, there was an error uploading your file."; | |
} | |
return NULL; | |
} |
basename () 函数返回路径中的文件名部分。
-- basename("/testweb/home.php")返回 home.php
问题出现在第 3 行,如果 $file
的值为 shell.pdf.php
,还是可以绕过。
# in_array () 弱类型比较
$parts = explode('.', $GET['jwt']); | |
$algorithms = array('HS256', 'HS384', 'HS512'); | |
if (3 !== count($parts)) { | |
throw new \InvalidArgumentException('Invalid or malformed JWT supplied.'); | |
} | |
$header = jsondecode(self::decode($parts[0]), true); | |
if (in_array($header['alg'], $algorithms)) { | |
... | |
} |
这是 JWT 的部分验证代码,问题出现在第 7 行
in_array () 函数搜索数组中是否存在指定的值。
参数 描述 needle 必需。规定要在数组搜索的值。 haystack 必需。规定要搜索的数组。 strict 可选。如果该参数设置为 TRUE,则 in_array () 函数检查搜索的数据与数组的值的类型是否相同。
没有将第三个参数设置为 ture,会进行若类型比较,可能绕过验证。
# 判断语句 and
function verify($jwt) { | |
list($h64,$d64,$sign) = explode(".",$jwt); | |
if (!empty($sign) and (JWT::signature($h64.".".$d64) != $sign)) { | |
die("Invalid Signature"); | |
} | |
$header = base64_decode($h64); | |
$data = base64_decode($d64); | |
return JWT::parse_json($data); | |
} |
使用 and 连接两个条件进行判断时,如果第一个条件为真,则会进行对第二个条件再进行判断;如果第一个条件为假,那这个语句必定为假,所以第二个条件也不必判断了。
所以问题在第 3 行,如果 !empty($sign)
为假,程序就不会对后面的 (JWT::signature($h64.".".$d64) != $sign)
这条验证语句进行判断了。所以把构造 jwt 时把 sign 置空就能绕过,即 header.pauload.
# 报错信息导致 xss
function register($user, $hashed_password) { | |
$sql = "INSERT INTO users (login,password) values (\""; | |
$sql.= mysql_real_escape_string($user); | |
$sql.= "\", \""; | |
$sql.= mysql_real_escape_string($hashed_password); | |
$sql.= "\")"; | |
$result = mysql_query($sql); | |
if ($result) { | |
return TRUE; | |
} | |
else | |
echo mysql_error(); | |
return FALSE; | |
} |
mysql_real_escape_string() 调用 mysql 库的函数 mysql_real_escape_string, 在以下字符前添加反斜线:
\x00
、\n
、\r
、\
、'
、"
和\x1a
.
先入为主以为是 SQL 注入。但问题在第 13 行的 mysql_error ();
mysql_error()
返回的是最近一次 MySQL 操作的错误信息,这些错误信息可能包含用户输入或其他不受控制的内容。如果错误信息中包含恶意的 JavaScript 代码或者 HTML 标签,攻击者就可以利用这个漏洞进行 XSS 攻击。
例如,假设用户提供的用户名为 <script>alert('XSS');</script>
,如果这个错误信息未经处理直接输出在网页上,页面会执行恶意的 JavaScript 代码。
# htmlentities () 转义问题导致 XSS
<a href='/profile/<?php echo htmlentities($user); ?>'></a> |
htmlentities(string,flags,character-set,double_encode) 函数可以把一些字符转换为 HTML 实体,flags 默认情况下为 ENT_COMPAT,仅编码双引号。
所以可以构造恶意的 user,如 '><script>alert ('test')</script><a> 进行绕过,触发 XSS
# php 类型转换
if (isset($_GET["count"]) and | |
($_GET["count"] > 0) and | |
($_GET["count"] <= 4)) { | |
echo system("ping pentesterlab.com -c ".$_GET['count']); | |
} |
- 当比较不同类型的值时,PHP 会尝试将它们转换为相同的类型以进行比较。
- 如果一个操作数是字符串,另一个操作数是数字,PHP 会尝试将字符串转换为数字。
构造恶意语句,可以执行命令:
http:/?count=4;cat+/etc/passwd |
实际执行:
ping pentesterlab.com -c 4;cat /etc/passwd
# hash_hmac () 函数
define('KEY', '...'); | |
function sign($data) { | |
return hash_hmac('sha256', $data, KEY); | |
} | |
if (isset($_GET["data"]) and | |
sign($_GET["data"]) === $_GET["signature"]) { | |
echo "Valid Signature"; | |
//... | |
} else { | |
die("Invalid Signature"); | |
} |
这是一个签名验证函数,问题出现在第 6 行。
hash_hmac ('sha256', $data, KEY) 函数,如果传入的 data 是一个数组,则会返回 null
如果这时 signature 也是 null,则会通过比较。
绕过示例:
http://example.com/?data[]=test&signature=
这时 php 会抛出一个异常,但也会通过验证。
# 弱比较运算符(==)
define('KEY', '...'); | |
function sign($data) { | |
return hash_hmac('sha256', $data, KEY); | |
} | |
if (isset($_GET["data"]) and | |
isset($_GET["signature"]) and | |
sign($_GET["data"]) == $_GET["signature"]) { | |
echo "Valid Signature"; | |
//... | |
} else { | |
die("Invalid Signature"); | |
} |
弱比较运算符会进行 类型转换。
如果 $_GET["signature"]
的值是特殊的字符串(如 "0e123456789"
),且 sign($_GET["data"])
返回一个类似的值(例如 "0e0987654321"
),PHP 会将它们 <u> 都解释为科学计数法表示的数字 0
</u>,从而使比较成立。
以 0e
开头的字符串容易被 PHP 弱比较漏洞利用的核心原因是:
- PHP 会将类似
0e12345
的字符串当作科学计数法表示的数字0
。(0*10 的 12345 次方) - 弱比较 (
==
) 会触发这种隐式类型转换。