[NSSRound#16 Basic]了解过PHP特性吗

[NSSRound#16 Basic]了解过PHP特性吗

这么多PHP特性你都知道吗

思路

一道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
40
41
42
43
44
<?php
error_reporting(0);
highlight_file(__FILE__);
include("rce.php");
$checker_1 = FALSE;
$checker_2 = FALSE;
$checker_3 = FALSE;
$checker_4 = FALSE;
$num = $_GET['num'];
if (preg_match("/[0-9]/", $num)) {
    die("no!!");
}
if (intval($num)) {
    $checker_1 = TRUE;
}
if (isset($_POST['ctype']) && isset($_POST['is_num'])) {
    $ctype = strrev($_POST['ctype']);
    $is_num = strrev($_POST['is_num']);
    if (ctype_alpha($ctype) && is_numeric($is_num) && md5($ctype) == md5($is_num)) {
        $checker_2 = TRUE;
    }
}
$_114 = $_GET['114'];
$_514 = $_POST['514'];
if (isset($_114) && intval($_114) > 114514 && strlen($_114) <= 3) {
    if (!is_numeric($_514) && $_514 > 9999999) {
        $checker_3 = TRUE;
    }
}
$arr4y = $_POST['arr4y'];
if (is_array($arr4y)) {
    for ($i = 0; $i < count($arr4y); $i++) {
        if ($arr4y[$i] === "NSS") {
            die("no!");
        }
        $arr4y[$i] = intval($arr4y[$i]);
    }
    if (array_search("NSS", $arr4y) === 0) {
        $checker_4 = TRUE;
    }
}
if ($checker_1 && $checker_2 && $checker_3 && $checker_4) {
    echo $rce;
}

可以看到有4个checker,分别考察:

  • intval遇到空数组返回0,非空数组返回1。preg_match遇到数组时,会将其转化为bool类型;可使用数组绕过正则匹配过滤。
  • md5弱类型比较,老生常谈了,这里限制一个使用纯字符串一个使用纯数字。
  • 0e开头科学计数法。变量在长度小于等于3的同时大于114514,用9e9就可以。数字后加字母即可不是数字,从而绕过is_numeric的检查,当数值与字符串比较时,字符串又会被转为数值。
  • array_search函数第三个参数默认是false,则为弱类型比较。array_search返回第一个搜索到的,实测搜索不到时返回0,因此这里随便输入只要不是NSS就可以bypass。

当一个字符串被当作一个数值来取值,其结果和类型如下:如果该字符串没有包含’.’,’e’,‘E’并且其数值值在整形的范围之内 该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。

满足了四个checker之后,跳转到另一个文件,也是代码审计,代码如下:

1
2
3
4
5
6
7
8
<?php
error_reporting(0);
highlight_file(__FILE__);
$nss=$_POST['nss'];
$shell = $_POST['shell'];
if(isset($shell)&& isset($nss)){
    $nss_shell = create_function($shell,$nss);
}

这是一个create_function代码注入。自PHP 7.2起该函数已经被废弃,PHP 8.0起已经被移除。

create_function会根据参数创建匿名函数,内部执行eval。两个参数中第一个是声明的参数部分,第二个是代码体。例如:

1
2
3
4
5
$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
// 等价于
function lambda_1($a,$b){
    return "ln($a) + ln($b) = " . log($a * $b);
}

因此存在代码注入,例如使用}提前闭合即可插入任意代码。payload:shell=&nss=1;}system("cat /flag");/*

总结 & Reference

  1. PHP的类型,主要有:md5 弱类型比较、create_function()代码注入、intval和preg_match遇到数组时的处理、array_search第三个参数默认为false的弱比较处理、科学计数法。

博客园上的一篇wp:【NSSCTF-Round#16】 Web和Crypto详细完整WP - AllFalls - 博客园

博客园上关于create_function()代码注入的文章:create_function()代码注入 - ctrl_TT豆 - 博客园

使用 Hugo 构建
主题 StackJimmy 设计