# 基础漏洞

# 目录遍历漏洞

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://example.com/?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 次方)
  • 弱比较 ( == ) 会触发这种隐式类型转换。
更新于 阅读次数