FL漏洞知识点总结
这里将通过参考文章和做题一起进行总结,并且文件包含漏洞,很多都利用了文件上传漏洞,所以这里也会总结一些文件上传漏洞的知识。
最后更新于
这里将通过参考文章和做题一起进行总结,并且文件包含漏洞,很多都利用了文件上传漏洞,所以这里也会总结一些文件上传漏洞的知识。
最后更新于
在PHP语法篇的10.文件里面我们讲了include和require和include_one和require_one这些,如果大家忘记了,可以返回去看看。
其实我看到老师写的书上也还在说这个,但是我看官方手册上面allow_url_include自php7.4就废除了,还发现就算是再新的书都有一定的滞后性,大家还是以手册为主。虽然废除了,但是我们还是讲解这两个在php.ini配置文件中的作用
这个选项允许include,require,include_one和require四个函数的使用。
这个设置项需要开启 allow_url_fopen 。
本选项激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象例如文件。默认的封装协议提供用 ftp 和 http 协议来访问。
我们在RCE漏洞知识点总结的5.php伪协议里面讲了php伪协议,忘记了也可以去看看
这里总结一下四个主要的协议是用在哪些方面。
在有以上的知识的基础上我们要介绍这个FL漏洞了。主要是分为本地文件包含漏洞和远程文件包含漏洞。
本地文件包含漏洞是指能打开并且包含本地文件的漏洞,大部分都是这个漏洞。不受allow_url_fopen和allow_url_include的影响
远程文件包含漏洞是指能够包含远程的文件并且执行他们,这个远程是我们可控的,所以危害较大,并且要求allow_url_fopen和allow_url_include都打开为on才能够执行。
这里主要是要进行目录穿越或者是遍历任意文件时用到,虽然有很多,但是也不知道有用没用,都积累一下吧。
linux下:
windows下
在开头我们也放了php伪协议,虽然有相同的运用之处,但是也有一些差别,我们会专门就这个漏洞再来补充他们在这个漏洞的用法。有些不怎么要用或者之前已经详细讲过的就直接跳过去了。
访问本地文件系统。
条件:无要求
姿势:
可以直接读取到POST上没有经过解析的原始数据。我们之前提到过,input后面可以加上post的body执行php代码。
enctype="multipart/form-data"
的时候 php://input
是无效的。
条件:
allow_url_include=On
姿势:
后面的注释的php代码需要通过post传递。
当出现file_get_contents函数时,我们就要想到用php://input了
解释一下上面的php语句吧。可能因为之前没有见到过可能不太理解。这里了解两个函数之后就容易理解了,就是后面的木马代码是写入到前面fopen打开的文件里面的。
fputs()/fwrite():写入文件
并且是把data写入到scream里面。
fopen():打开文件或者url。
filename可以是文件,可以是url。
并且模式和c语言打开文件一样这里贴张图大家看吧
这个之前就讲的详细了,没啥好补充的。
这个之前没讲过,但是在这里要用到,补充一下。
是php解压缩包的协议,并且不管后面是什么都会解压缩,无差别平等攻击
要求:
phpversion>=5.3
姿势:
写一个php代码比如<?php echo"hellp";?>,然后打包成zip压缩模式的压缩包
注意,这个压缩模式必须是zip,不能是7zip,rar这种。此外,压缩包的后缀名可以不一定是zip,还可以是111,222,txt都可以。
然后指定绝对目录
当然相对目录也可以
只要把php代码修改一下变成执行命令的语句就可以,其他都不变,这里就不测试了因为步骤都是一样的
同理
和上面的phar()的作用是一样的,就是用法不一样。我们直接说区别了其他都是一样的。他们的区别主要是在路径上。
zip://只能用绝对路径不能用相对路径。
并且压缩文件包和压缩文件之间要用#连接,并且这个#得经过url编码,也就是%23。
就是之前的两句话之前已经很详细地说了。
首先我们先介绍一下session是什么
session简单理解就是会话。打个生动的比喻,session是位于服务器端的保险柜。比如我们去健身的时候,前台小姐姐会给我们一把钥匙和与它对应的保险柜,我们从开始健身到最后离开健身房就是一个会话,途中可能会去买瓶水放到保险柜里,或者从保险柜里拿个换洗衣物之类的,都是这个中间产生的数据,保险柜记录了我们产生的数据。
条件:
如果要使用包含session,那么我们需要知道session的路径并且他的内容部分是我们可控的。
姿势:
我们可以查看phpinfo()文件里面的session的路径存放处,在session下面的session_save_path
第二竖排是local value(局部变量),第三竖排是master value(主变量),local value会覆盖master value。所以我们看第二竖排
我的session就位于/var/lib/php/sessions下。
sessions目录下的文件的命名是sess_[phpsessid]。而phpsessid是我们的cookie里面可以看到的,我们甚至可以控制cookie中的phpsessid的值。然后发送的时候,产生的session文件的命名就是我们的命名规则中的结果。
比如我们首先执行一个php代码
这里session_start()一定要有,不然没有session
我们可以通过editthecookie插件看到phpsessid是is4978nk7n9nuajv21qe688svg
然后从前面我们就知道我们的session文件存在/var/lib/php/sessions下面,然后我们进行查看出现了一个新的sess_xxxxx文件,这个文件就是产生的session文件。
接下来我们将要讲如何利用包含session,其实session的利用并不是固定化的,我们要通过进入session文件,然后通过观察session文件中的可控变量,然后写入payload。
我们接下来通过自己实验的一个例子理解这个观察可控变量。
首先搭建这个php代码环境
对于这段代码我们首先介绍$_SESSION,这个之前没有见到过,我直接把那个文章的那句话复制下来,因为他已经解释的比手册好很多了。
PHP 会将会话中的数据设置到
$_SESSION
变量中。当 PHP 停止的时候,它会自动读取
$_SESSION
中的内容,并将其进行序列化,然后发送给会话保存管理器来进行保存。对于文件会话保存管理器,会将会话数据保存到配置项
session.save_path
所指定的位置。
上述代码中的$username是可控的,并且能被写入到session中。所以我们对session写入一句话木马或者其他执行代码的语句。然后我们使用username去访问sessions下面的文件,就是去包含这个文件。
如果还有base64加密的话,还需要用php://filter/read=convert.base64-decode/resource=
因为web服务器很多时候都会将请求写入到日志文件中,比如apache这种。
条件:
需要知道服务器日志的存储路径,且日志文件可读。
除了路径,我们需要看一下日志文件长什么样子
上面就包含了我刚刚用burpsuite发送的请求。所以只要是请求发送了他都能记录下来。并且我们发现包含进去的其实是路径里面的内容,以及user-agent也能包含,所以我们的payload也是写在这个地方。
从上述的日志中我们已经知道了我们把payload放置的地方,然后发送请求。但是这里会遇到一些情况。
①编码错误
有时候我们用burp发送的到日志中会乱码,我们就要把我们的语句用url进行编码才能执行成功。
②log位置被修改
就比如我的电脑上的位置就和我们上面写的常见的路径都不一样,这个时候我们可以通过修改配置文件再包含。
下面是一些默认的配置文件的路径(其实这些默认和我电脑完全不一样)
这里首先解释什么是environ
environ:
英文单词都学过environment(环境),这个environ其实就是环境变量。他的位置是默认的的
在linux下面是/proc/self/environ,在win下面没有
那篇文章里面写它包含了user-agent(http请求头里面的),虽然我在我的文件里面没有看到。
所以在知道了use-agent是能知道位置的,那么我们就在user-agent里面写入php代码,然后去包含这个environ文件就可以执行我们的payload了。
先解释fd吧
fd(file describe):文件描述符
fd是一个子目录(就是下面还有文件),并且可以看到下面的文件名是数字。linux的目录我们都知道是树状的,找文件要一层一层的找下去,但是这样很费事件,所以为了提高效率,就有了fd,这个数字就相当一个索引,我们如果要查找某个进程,只要查找这个索引就可以了。0是标准输入,1是标准输出,2是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是3,再打开一个文件文件描述符就是4......
文件路径在/proc/self/fd下,但是怎么利用没看到说,就先这样吧。
我们直接把木马做成他想要的形式就可以了。在后面的文件上传漏洞我们仔细讲。
本来以为可以不做这个,结果发现5道题全是竞争条件,被迫学习。首先对原理进行讲解,因为自己原理都理解了很久。session部分直接用手册上的来,附加自己的一些理解和注释,虽然没啥理解。
其实我理解的条件竞争,就是一直发包一直发包让他在清空前就写入,就是瞎猫碰上死耗子。
ctfshow上面的都是这一个,利用session进行条件竞争。
我们首先要了解一些配置手册里面关于session上传的内容。
然后我们来了解session_upload_progress(会话上传进度)
当 启用
session.upload_progress.enabled INI
选项时,PHP 将能够跟踪正在上传的单个文件的上传进度。此信息对于实际上传请求本身并不是特别有用,但在文件上传期间,应用程序可以向单独的端点(例如 通过XHR )发送 POST 请求以检查状态。当上传正在进行时,以及当 POST 与
session.upload_progress.name INI
设置设置 为同名的变量时, 上传进度将在$_SESSION
超全局 中可用。当 PHP 检测到此类 POST 请求时,它将在$_SESSION
中填充一个数组,其中索引是session.upload_progress.prefix
和session.upload_progress.name INI
选项的串联值。通常通过读取这些 INI 设置来检索密钥,即
然后是手册给我们展示的两个代码,第一个是一个文件上传的html编写的表单
这段代码的意思是第一行是交代了发送的位置是upload.php这个文件。第二行是隐藏了一个name,这个name是通过获取当前会话中文件上传进度的会话变量名来跟踪他的上传进度,后面是上传的两个文件file1,file2。
第二个是存储在session中的数据大致是这样。
这些就是需要理解的一些基础原理,做题会在ctfshow里面先做题再总结方法,网上都是直接讲题目,方法其实感觉没有。总之就是用bp或者用python脚本来发包。等熟悉了再来总结了。
我算是发现了有防御就会有绕过。这里有很多绕过方法
像这种,我们自己输入的文件前面还带一些其他的路径的,我们可以通过路径遍历来绕过。../走到上一个目录,所以对上面的/var/www/html/.$file我们在我们平时需要输入的路径前加上../../../就变成了我们的$file。如果要达到/var,前面甚至只需要两个../
当然别人也可以对../进行过滤,我们就需要对他做一些变化。
../
..%2F
URL编码(编码器生成,不知道为什么编码器只变了/)
../
%2E%2E%2F
通过url编码,并且.的编码为%2e
../
%2e%2e/
同理
..\
%2e%2e%5c
......
..\
%2e%2e\
.......
..\
..%5c
......
②二次编码
这个二次编码指的是url的二次编码,其实他就是对url编码了一次的字符串再做了一次url编码。一个url编码的形式是%xx,两次url编码的形式就是%xxxx,从后面的2位变成了4位,所以展现几个为
../
%252e%252e%252f
url二次编码
..\
%252e%252e%255c
url二次编码
文章中提到了include有两种情况,一种是结果file是个路径,是个url,一个是file是个伪协议。我们也按照上面写的来。
url拼接之后就是
我们可以用query(?)和fragement()来绕过。
条件:
allow_url_fopen()=on
allow_url_include()=on
首先我们解释一下url中的query是什么
URL查询(Query)是指在URL(Uniform Resource Locator,统一资源定位符)中的问号(?)后面添加的键值对。我们的文件里面如果没有需要添加的键值对的话是就算后面有?也不起作用。
姿势:
在我们常写的file后面加一个?就是新的file里面应该有的
如果我们要写入木马或者命令的话,只要将remoteinfo.txt的内容变成一句话木马就行。
首先我们解释一下什么是url里面的fragment
URL片段(URL fragment)是一个附加到URL的字符串,以#为前缀。它旨在帮助浏览器识别特定内容或部分页面,并不会发送到服务器,也不会影响页面的加载。
姿势:
在我们常写的file后面加一个#就是新的file里面应该有的
这里主要用的是两个解压缩的两个zip://和phar://,用法在上面已经提过了。其实它的原理就是在我们知道了后缀的文件路径后,我们可以在我们本来需要zip文件下面去构造这个/test/test.zip这个文件。然后让他们去执行,这个test.zip里面就写上我们需要的执行代码或者木马。
(1)zip://
zip后面要用绝对路径,并且在zip后面需要用#隔开
(2)phar://
phar后面也要用绝对路径,但是可以不用#隔开
在我们需要的执行的代码后面加./././很多很多的./,在linux下超过4096字节,在win下超过256字节,就可以成功。相当于把后面的后缀给舍去了
姿势:
%00截断原理:
截断的核心,就是 chr(0)
这个字符。先说一下这个字符,这个字符不为空 (Null)
,也不是空字符 ("")
,更不是空格。 当程序在输出含有 chr(0)
变量时 chr(0)
后面的数据会被停止,换句话说,就是误把它当成结束符,后面的数据直接忽略,这就导致漏洞产生。
条件:
magic_quotes_gpc = Off(当On时,所有的'
(单引号),"
(双引号),/
(反斜线)和NULL
字符都会被自动加上一个反斜杠\
进行转义。然后这时再用%00
会变成\0
,被转义了)
姿势:
纯属是做题目的时候出现了死亡绕过,就在这里写一下了,不然都不知道这是个啥。
需要绕过的目前看到的是exit()和die()这两个在和file_put_contents()。参考文章里面是exit(),但是其实做题的时候我做到的是die(),但是绕过方法都是一样的。
简单介绍一下他们吧。
die()/exit():die()等同于exit()
输出一个消息并且退出当前脚本
file_put_contents我们之前就已经讲过了,就是将后面的data写入前面的文件的函数。
这里有三种情况
这个代码的执行过程我们接下来会说说。
file_put_contents($filename,"<?php exit();".$content);
向指定的文件中写入一段 PHP 代码,并确保当这个文件被包含时,会立即退出执行,然后写入额外的内容 $content
。所以就是我们能写入一句话但是不能执行。所以我们要如何绕过这个exit呢
方法有四种
用base64是为了让<?php exit();这部分用base64解码,然后解码之后是乱码,php代码就不知道写的啥了。所以就实现了对exit的绕过。但是我们的关键语句用的base64解码后写入的,所以在读取时就是base64编码的读取结果。要用base64解码写入的话需要用php://filter这个
上述就是网shell.php里面写入了<?php exit(); aPD9waHAgcGhwaW5mbygpOz8+,这个代码的解码片段,然后再执行里面的php代码。
后面的PD9waHAgcGhwaW5mbygpOz8+解码后是<?php phpinfo(); ?>是我们想写的执行语句。这个a的存在是为了让前面的phpexit解码并且不影响后面的我们要写的语句。(base64每四个一解码),phpexit是7个,加个a变成8个才不会影响后面的我们要写入的句子。
先解释一下什么是rot13吧
ROT13 编码是把每一个字母在字母表中向前移动 13 个字母得到。
道理还是相同的,而且我们不用考虑添加一个a来凑到4的倍数之类的,只需要把rot13编码后的传入就可以了,顺便提醒一下,这个时候的过滤器的read变成了convert.string.rot13
不能开启短标签,如果开启了短标签的话,前面内容就会解析,导致代码错误
对于iconv字符编码转换进行绕过的手法,其实类似于上面所述的base64编码手段,都是先对原有字符串进行某种编码然后再解码,这个过程导致最初的限制exit去除,而我们的恶意代码正常解码存储。
其实后面看到只要是编码方式就可以进行过滤,并且原理是一样的,我们只要变换协议和编码就行。这里展现了一些编码https://www.php.net/manual/zh/mbstring.supported-encodings.php
这里介绍几种。
对目标字符串进行2位一反转,也就是说,字符串的数目得是偶数。不然就会被截断。可以去这个里面去看看工具,很多很偏门的工具,虽然我没找到在线的,但是我让chatgpt生成了一个脚本。
https://onlinetexttools.com/convert-unicode-to-ucs2
这个时候用到的过滤器是convert.iconv.UCS-2LE.UCS-2BE
,
姿势:
对目标字符串进行4位一反转,也就是说,字符串的数目得是4的倍数。不然就会被截断。脚本给上
这个时候用到的过滤器是convert.iconv.UCS-4LE.UCS-4BE
,
原理是一样的,就不细写了。
用了一个过滤器string.strip_tags
string.strip_tags
可以过滤掉html标签和php标签里的内容
所以这个过滤思路就是首先让他把<?php exit();?>去除掉,然后再来base64进行解码,所以这个就要求我们先补充一个?>,并且要先把<?php exit();?>先去掉,而且后面我们的语句不能先解码。
这个在php7.3以上不能用
.htaccess
的预包含利用这里先给大家看姿势再解释吧
这里还是用的string.strip_tags去去除前面的exit,然后把后面的代码写入到.htaccess文件(apache配置文件)里面。后面的代码是一个配置,PHP 配置 auto_prepend_file
,使得在执行所有 PHP 脚本之前,会先执行 D:\\phpstudy_pro\\www\\flag.php
这个文件。
file_put_contents($content,"<?php exit();".$content);
这种情况下文件名和文件内容一致,但是我们需要前面的文件时php结尾,所以我们需要添加.php,并且需要利用filter这个还是一样的。所以我们写出来的姿势就变成了。
具体解释可以看,讲得很透彻了。
一.base64编码
如果rot被过滤了,可以采用rot的二次编码
这里有一个rot二次编码的脚本。
直接写过滤姿势了,因为原理前面讲过了。这里写的ucs-2,其实4也可以。
file_put_contents($filename,$content."\nxxxxxxx");
没有结束,直接输入就行了。
欢迎大家给我留言,🤭