icewing

PHP代码审计(一)
这是一篇很早之前记录的文章,现在重新整理一下。大致看了一下可能会到7.10篇幅可能比较长,所以分开来写。因为个人水...
扫描右侧二维码阅读全文
03
2019/06

PHP代码审计(一)

这是一篇很早之前记录的文章,现在重新整理一下。大致看了一下可能会到7.10篇幅可能比较长,所以分开来写。
因为个人水平有限,文中可能会出现错误的地方,还望多多指正。本文仅供交流学习。

1.1 反射型 XSS

0x01 漏洞背景

XSS 攻击全称跨站脚本攻击,是为不和层叠样式表 (Cascading Style Sheets, CSS) 的缩写混淆,故将跨站脚本攻击缩写为 XSS , XSS 是一种在 web 应用中的计算机安全漏洞,它允许恶意 web 用户将代码植入到提供给其它用户使用的页面中。
非持久型跨站:反射型跨站脚本漏洞,最普遍的类型。

0x02 漏洞代码及分析

  • 漏洞代码:
<?php
$username = $_GET['user'];
echo " 用户名: ".$username;
?>

将用户输入的 user 内容直接输出到页面,未经过任何过滤。可导致 XSS 漏洞攻击

  • 修复建议:
    在接收输入后进行 htmlspecialchars 过滤,或者在输出前 htmlspecialchars 过滤
  • 建议修复代码:
<?php
$username = $_GET['user'];
echo " 用户名: ".htmlspecialchars($username);
?>

1.2 存储型 XSS

0x01 漏洞背景

XSS 攻击全称跨站脚本攻击,是为不和层叠样式表 (Cascading Style Sheets, CSS) 的缩写混淆,故将跨站脚本攻击缩写为 XSS , XSS 是一种在 web 应用中的计算机安全漏洞,它允许恶意 web 用户将代码植入到提供给其它用户使用的页面中。
存储型跨站:危害大、恶意代码存储服务器 ( 数据库 )

0x02 漏洞代码及分析

  • 漏洞代码:
if (!empty($_POST['content'])) {
    $content = addslashes($_POST['content']);
    if ($mysqli - >query("insert  into  test  (content)values('$content')")) {
        echo '发言成功';
    } else {
        echo '发言失败';
    }
}

$data = $mysqli - >query("select  content  from  test");
if ($data - >num_rows !== '0') {
    $arr = $data -> fetch_all();
    foreach($arr as $a) {
        echo '发言:'.$a[0].'<br>';
    }
}

用户发言的内容未进行字符串编码,直接存储到数据库中,并取出数据库中带有恶意代码的内容输出到网页中,导致 XSS 攻击

  • 修复建议:
    在接收输入后进行 htmlspecialchars 过滤,或者在输出前 htmlspecialchars 过滤
  • 建议修复代码:
echo ' 发言: '.htmlspecialchars($a[0]).'<br>';

1.3 DOM XSS

0x01 漏洞背景

XSS 攻击全称跨站脚本攻击,是为不和层叠样式表 (Cascading Style Sheets, CSS) 的缩写混淆,故将跨站脚本攻击缩写为 XSS , XSS 是一种在 web 应用中的计
算机安全漏洞,它允许恶意 web 用户将代码植入到提供给其它用户使用的页面中。
DOM XSS :一种特殊类型的反射型 XSS ,它是基于 DOM 文档对象模型的一种漏洞。

0x02 漏洞代码及分析

  • 漏洞代码 1 :
<?php
    $img = isset($_GET['img']) ? htmlspecialchars($_GET['img']) : 'default.jpg';
?>
<img src="" id="image">
<script>
    var test = document.getElementById('image');
    test.src = '<?php echo $img;?>';
</script>

使用了 htmlspecialchars 进行过滤,但单引号没有过滤,导致 DOM XSS

  • 修复建议:
    在接收输入后进行 htmlspecialchars 过滤,并且还有对单引号和反斜杠进行转义
  • 建议修复代码:
$img = isset($_GET['img']) ? htmlspecialchars(addslashes($_GET['img'])) : 'default.jpg';
  • 漏洞代码 2:
<?php
    $content = isset($_GET['content']) ? htmlspecialchars($_GET['content']) : ' 无内容 ';
    $content = str_replace("'","\'",$content);
?>
<div id="content">
这是测试内容
</div>
<script>
    var test = document.getElementById('content');
    test.innerHTML = '<?php echo $content;?>';
</script>

innerHTML 、 documen.write 这种直接修改 html 内容的方法,应对其内容进行严格过滤,否则通过 8 进制或 16 进制编码可绕过 htmlspecialchars 过滤

  • 建议修复代码:
$content = isset($_GET['content']) ? htmlspecialchars(addslashes($_GET['content'])) : ' 无内容 ';

2.1 文件读取漏洞

0x01 漏洞背景

任意文件读取漏洞,是 web 安全里高危的漏洞,它可以泄露源码、数据库配置文件等等,导致网站处于极度不安全状态。

0x02 漏洞代码及分析

  • 漏洞代码 1 :
<?php
    $template = isset($_GET['template']) ? $_GET['template'] : 'index.html';
    $file = file_get_contents('2.1/'.$template);
    echo $file;
?>

未对 ../ 进行过滤,导致可以跨目录读取文件。未对后缀名进行限制,导致可以读取 php 文件内容。

  • 修复建议:
    不允许 .. 等跨目录字符出现,使用 addslashes 防止 %00 截断,且限制后缀白名单
  • 建议修复代码:
$template = isset($_GET['template']) ? addslashes($_GET['template']) : 'index.html';
if(strstr($template, '..') || substr($template,-5,5) != '.html')
{
    echo ' 输入不合法 ';
    exit;
}
  • 漏洞代码 2 :
<?php
    $action = isset($_GET['action']) ? $_GET['action'] : 'login';
    $fp="./2.1/".$action."/index.html";
    $f= fopen($fp,'r');
    $strout = fread($f,filesize($fp));
    fclose($f);

    echo $strout;
?>

未对 ../ 进行过滤,导致可以跨目录读取文件。未过滤 %00 字节,在 <PHP5.3.4 版本中,可在截断后自定义路径和文件名

  • 修复建议:
    不允许 .. 等跨目录字符出现,使用 addslashes 防止 %00 截断
  • 建议修复代码:
$action = isset($_GET['action']) ? addslashes($_GET['action']) : 'login';
if(strstr($template, '..'))
{
    echo ' 输入不合法 ';
    exit;
}

2.2 文件上传漏洞

0x01 漏洞背景

上传文件的时候,如果服务器端脚本语言,未对上传的文件进行严格的验证和过滤,就有可能上传恶意的脚本文件,从而控制整个网站,甚至是服务器。

0x02 漏洞代码及分析

  • 漏洞代码 1 :
< ?php
if (isset($_FILES['file'])) {
    if (($_FILES["file"]["type"] == "image/gif" || $_FILES["file"]["type"] == "image/jpeg" || $_FILES["file"]["type"] == "image/pjpeg") && $_FILES["file"]["size"] < 20000) {
        if ($_FILES["file"]["error"] > 0) {
            echo "Return Code: ".$_FILES["file"]["error"]."<br />";
        } else {
            echo "Upload: ".$_FILES["file"]["name"]."<br />";
            echo "Type: ".$_FILES["file"]["type"]."<br />";
            echo "Size: ".$_FILES["file"]["size"] / 1024." Kb<br />";
            echo "Temp file: ".$_FILES["file"]["tmp_name"]."<br />";
            if (file_exists("2.2/upload/".$_FILES["file"]["name"])) {
                echo $_FILES["file"]["name"]." already exists. ";
            } else {
                move_uploaded_file($_FILES["file"]["tmp_name"], "2.2/upload/".$_FILES["file"]["name"]);
                echo "Stored in: "."2.2/upload/".$_FILES["file"]["name"];
            }
        }
    } else {
        echo "Invalid file";
    }
} ? >

仅对 Content-type 验证,因为 Content-type 可以伪造导致可以绕过限制图片验证,导致任意文件上传

  • 修复建议:
    对后缀进行校验,采用白名单模式。保存文件名采用随机文件名 + 后缀的方式保存
  • 建议修复代码:
< ?php
if (isset($_FILES['file'])) {
    $ext = substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.') + 1);
    $filename = time().rand(100, 999).'.'.$ext;
    if (($ext == "jpg" || $ext == "jpeg" || $ext == "png") && $_FILES["file"]["size"] < 20000) {
        if ($_FILES["file"]["error"] > 0) {
            echo "Return Code: ".$_FILES["file"]["error"]."<br />";
        } else {
            echo "Upload: ".$_FILES["file"]["name"]."<br />";
            echo "Type: ".$_FILES["file"]["type"]."<br />";
            echo "Size: ".$_FILES["file"]["size"] / 1024." Kb<br />";
            echo "Temp file: ".$_FILES["file"]["tmp_name"]."<br />";
            if (file_exists("2.2/upload/".$filename)) {
                echo $filename." already exists. ";
            } else {
                move_uploaded_file($_FILES["file"]["tmp_name"], "2.2/upload/".$filename);
                echo "Stored in: "."2.2/upload/".$filename;
            }
        }
    } else {
        echo "Invalid file";
    }
} ? >
  • 漏洞代码 2
< ?php $imageinfo = getimagesize($_FILES['userfile']['tmp_name']);
if ($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg') {
    echo "Sorry, we only accept GIF and JPEG images\n";
    exit;
}
$uploaddir = '2.2/upload/';
$uploadfile = $uploaddir.basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
    echo "File is valid, and was successfully uploaded.\n";
} else {
    echo "File uploading failed.\n";
} ? >

仅通过读取文件头判断文件类型,可被绕过;后缀名未进行校验,导致任意文件上传、

  • 修复建议:
    参考漏洞代码 1 的修复方式
  • 漏洞代码 3
< ?php $blacklist = array(".php", ".phtml", ".php3", ".php4");
foreach($blacklist as $item) {
    if (preg_match("/{$item}\$/i", $_FILES['userfile']['name'])) {
        echo "We do not allow uploading PHP files\n";
        exit;
    }
}
$uploaddir = '2.2/upload/';
$uploadfile = $uploaddir.basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
    echo "File is valid, and was successfully uploaded.\n";
} else {
    echo "File uploading failed.\n";
} ? >

扩展名黑名单过滤不严,可上传 .php5 文件进行绕过。导致任意文件上传

  • 修复建议:
    参考漏洞代码 1 的修复方式

2.3 文件写入漏洞

0x01 漏洞背景

在生成配置文件或者缓存文件的时候,未对传入的内容进行过滤,导致 php 代码执行漏洞

0x02 漏洞代码及分析

  • 漏洞代码 1 :
< ?php
if ($isset($_POST['debug'])) {
    $setting = $_POST['debug'];
    $content = "<?php\r\n";
    $content. = "\$YCF['debug'] = '".$setting."';\r\n";
    $content. = "?>";
    file_put_contents("2.3/config.php", $content);
} ? >

未对传入内容进行任何过滤,用单引号绕出去可以引起代码执行漏洞

  • 修复建议:
    进行 addslashes 函数过滤。且不要拼接在双引号内,否则同样会引起代码执行漏洞
  • 建议修复代码:
$setting = addslashes($_POST['debug']);

2.4 文件删除漏洞

0x01 漏洞背景

删除文件时对路径或文件名过滤不严,会导致任意文件删除

0x02 漏洞代码及分析

  • 漏洞代码 1 :
< ?php
if (@$_GET['action'] == 'del') {
    if (isset($_GET['img'])) {
        $img = $_GET['img'];
        unlink('2.4/'.$img);
        echo ' 删除图片成功 ';
    }
} ? >

对删除文件过滤不严,导致任意文件删除。

  • 修复建议:
    不允许 .. 等跨目录字符出现。进行 addslashes 函数过滤
  • 建议修复代码:
$img = addslashes($_GET['img']);
if(strstr($img, '..'))
{
    echo ' 输入不合法 ';
    exit;
}

3.1 文件包含漏洞

0x01 漏洞背景

如果允许客户端用户输入控制动态包含在服务器端的文件,会导致恶意代码的执行及敏感信息泄露,主要包括本地文件包含和远程文件包含两种形式。通常
是由于 include 、 require 引起恶意包含

0x02 漏洞代码及分析

  • 漏洞代码 1 :
< ?php
if ($_GET['func']) {
    include $_GET['func'];
} else {
    include '3.1/default.php';
} ? >

在未过滤的情况下,可包含带有恶意代码的 JPG 文件、日志文件,甚至可以远程包含

  • 修复建议:
    不允许 .. 等跨目录字符出现,对前缀跟后缀都进行限制,使用 addslashes 过滤 %00 防止被截断包含
  • 建议修复代码:
< ?php
if ($_GET['func']) {
    $file = addslashes($_GET['func']);
    if (strstr($file, '..')) {
        echo ' 输入不合法 ';
        exit;
    }
    include '3.1/'.$file.'.php';
} else {
    include '3.1/default.php';
} ? >

3.2 preg_replace 漏洞

0x01 漏洞背景

preg_replace /e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)。提示:要确保 replacement 构成一个合法
的 PHP 代码字符串,否则 PHP 会在报告在包含 preg_replace() 的行中出现语法解析错误。

0x02 漏洞代码及分析

  • 漏洞代码 1 :
<?php
    echo preg_replace("/test/e",$_GET["h"],"jutst test");
?>
  • 漏洞代码 2 :
<?php
    function test($str)
    {
    }
    echo preg_replace("/s*[php](.+?)[/php]s*/ies", 'test("\1")', $_GET["h"]);
?>

语法不当的情况下,会导致代码执行漏洞

  • 修复建议: 避免使用 /e 模式,第一、二个参数不让用户可控。第二个参数单引号改成双引号
  • 建议修复代码:
echo preg_replace("/s*[php](.+?)[/php]s*/ies", "test('\1')", $_GET["h"]);

最后修改:2019 年 06 月 10 日 04 : 44 PM
生活需要一些仪式感,比如手冲一杯咖啡:)

发表评论