[鹤城杯 2021]EasyP

[鹤城杯 2021]EasyP wp

参考博客:

basename()绕过小结

request导致的安全性问题分析

源码分析

首先进入题目,看到代码:

复制代码
<?php
include 'utils.php';

if (isset($_POST['guess'])) {
    $guess = (string) $_POST['guess'];
    if ($guess === $secret) {
        $message = 'Congratulations! The flag is: ' . $flag;
    } else {
        $message = 'Wrong. Try Again';
    }
}

if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])) {
    exit("hacker :)");
}

if (preg_match('/show_source/', $_SERVER['REQUEST_URI'])){
    exit("hacker :)");
}

if (isset($_GET['show_source'])) {
    highlight_file(basename($_SERVER['PHP_SELF']));
    exit();
}else{
    show_source(__FILE__);
}
?> 

其中第一段代码是让我们传入一个 GET 参数 guess 。这个参数值先会经过转义然后跟 secret 强比较。我没有办法绕过,也猜不到 secret 的值是多少,所以这段代码的作用相当于没有。

第二段代码对 $_SERVER['PHP_SELF'] 进行了正则匹配:
复制代码
if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])) {
    exit("hacker :)");
}
$_SERVER['PHP_SELF']

$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。

PHP_SELF 获取当前执行脚本的文件名,与 document root 有关。

例如,在地址为 http://example.com/foo/bar.php 的脚本中使用 $_SERVER['PHP_SELF'] 将得到 /foo/bar.php。FILE 常量包含当前(例如包含)文件的完整路径和文件名。 如果 PHP 以命令行模式运行,这个变量将包含脚本名。

正则解读
复制代码
/utils\.php\/*$/i
  • /utils\.php\/*$/i : 正则表达式的开始和结束都有斜杠('/')表示正则表达式的开始和结束。
  • utils\.php : 该部分用于匹配字符串 'utils.php'。'.' 表示匹配点字符,点字符在正则表达式中具有特殊意义,因此需要转义。
  • \/*$ : 该部分用于匹配可选的斜杠('/')。'/*' 表示匹配零个或多个斜杠,'$' 表示匹配字符串的结尾。
  • i : 该部分表示在匹配时忽略大小写。

综上所述:就是匹配以 utils.php+0个或多个 / 结尾 的字符串。

第三段代码对 $_SERVER['REQUEST_URI'] 做了一个正则匹配:
复制代码
if (preg_match('/show_source/', $_SERVER['REQUEST_URI'])){
    exit("hacker :)");
}
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 解码。利用这一点可以进行绕过。

这里可以参考博客:

PHP中$_SERVER["QUERY_STRING"]函数

正则解读

这段正则就是输入的 URL 链接中不能有 "show_source" 字符串。、

最后一段代码:
复制代码
if (isset($_GET['show_source'])) {
    highlight_file(basename($_SERVER['PHP_SELF']));
    exit();
}else{
    show_source(__FILE__);
}

获取一个 GET 参数 show_source ,将 $_SERVER['PHP_SELF'] 的值经过 basename 函数处理后显示出来。

basename() 函数

basename() 函数返回路径中的文件名部分。

语法

复制代码
basename(path,suffix)
参数 描述
path 必需。规定要检查的路径。
suffix 可选。规定文件扩展名。如果文件有 suffix,则不会输出这个扩展名。

举例

php 复制代码
<?php
$path = "/testweb/home.php";

//显示带有文件扩展名的文件名
echo basename($path);

//显示不带有文件扩展名的文件名
echo basename($path,".php");
?> 

输出:

undefined 复制代码
home.php
home
basename() 函数绕过

basename 函数有这样一个特性:在使用默认语言环境设置时,basename() 会删除文件名开头的非 ASCII 字符。

比如:

$_SERVER['PHP_SELF'] 获取到的值 basename() 函数处理后的结果
/dir/index.php index.php
/dir/%ffindex.php index.php
/dir/index.php/%ff index.php
/dir/index.php/%2b +

上面的 %ff 就是一个非 ASCII 字符。而当路径的最后为 ASCII 字符时,basename 函数就会返回该字符。

具体可以去看:basename()绕过小结

绕过

经过上面的分析,GET 传入的参数 show_source 会被匹配到,因此对 "show_source" 做一个 URL 编码后再传入。

此外,在页面请求时传入 /index.php/utils.php/大

服务器会认为请求的页面是 index.php ,因此可以正常返回页面;

$_SERVER['PHP_SELF'] 获取到的值是 /index.php/utils.php/大 ,因为不是以 utils.php/ 结尾,所以可以绕过正则;

因为中文是非 ASCII 字符,所以经过 basename 函数处理后得到的结果就是 utils.php ;

这样就可以读到 utils.php 文件的内容。

测试发现:直接传入 /utils.php/大 的话没有回显。

payload
复制代码
http://node4.anna.nssctf.cn:28806/index.php/utils.php/大?%73%68%6f%77%5f%73%6f%75%72%63%65=1

返回结果:

拿到 flag 。

相关推荐
BingoGo1 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack1 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理5 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
一次旅行5 天前
网络安全总结
安全·web安全
QQ5110082855 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php