近日刚刚入门CTF,在攻防世界作了一些WEB方向的题,在高手进阶区发现多数涉及到文件包含漏洞的题都会给出源代码,让我们先进行审计再利用漏洞。很多题都是乍一看以为很难,仔细观察后发现难度并不高。
0x01 读懂PHP代码
我们就以攻防世界WEB方向高手进阶区的web2为例:通过这道题可以接触到一些PHP函数,和一些简单的逆向思路,很适合新手入门。那么我就来带大家一同了解一下解题的思路。
题目的描述就只有解密,于是我们首先来看题目源码:
<?php $miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws"; function encode($str){ $_o=strrev($str); // echo $_o; for($_0=0;$_0<strlen($_o);$_0++){ $_c=substr($_o,$_0,1); $__=ord($_c)+1; $_c=chr($__); $_=$_.$_c; } return str_rot13(strrev(base64_encode($_))); } highlight_file(__FILE__); /* 逆向加密算法,解密$miwen就是flag */ ?>
这里还有高亮的版本方便大家查看:
接下来是解题的思路:
首先要明白各个函数的作用:
函数:strrev([字符串])
作用:翻转字符串
示例:strrev(“abc”) 输出:cba
函数:substr([字符串],[从几位开始],[截取多少位])
作用:返回字符串的一部分
示例:substr(“abcdef”,3,2) 输出:de
函数:ord([字符串])
作用:返回字符串首个字符的ASCII值
示例:ord(“S”) 输出:83
ord(“Shanghai”) 输出:83
函数:chr([ASCII值])
作用:从指定的ASCII值返回字符,可以被指定为十进制、八进制、或16进制
八进制值被定义为带前置0,十六进制被指定为带前置0x
示例:chr(61) // 十进制 输出:=
chr(061) // 八进制值 输出:1
chr(0x61) // 十六进制值 输出:a
函数: str_rot13([字符串])
作用:对字符串执行ROT13编码。
ROT13编码:把每一个字母在字母表中向前移动 13 个字母。数字和非字母字符保持不变。
注释:编码和解码都是由相同函数完成的,如果把已编码的字符再次编码,则会解码。
示例:str_rot13(“I love Shanghai”) 输出:V ybir Funatunv
str_rot13(“V ybir Funatunv”) 输出:I love Shanghai
函数:base64_encode([字符串])和base64_decode([字符串])
作用:对字符串进行base64编码和解码。
明白各个函数的作用之后阅读代码,简单梳理就可以明白代码作用:
整个加密的流程就是:
- 将字符串翻转
- 循环取出每个字符的ASCII值+1后再转换回字符
- 之后对字符串进行base64编码,然后再翻转回来,最后进行ROT13编码。
于是我们解密的思路就很清晰了:
整个解密的流程就是:
- 对密文进行ROT13编码先将ROT13编码还原。
- 然后将字符串翻转,然后进行base64解码。
- 取出每个字符的ASCII值-1后在转换回字符。
- 最后对字符串再次翻转就可以的到最后解密的明文。
编写PHP代码如下:
<?php $miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws"; $_=""; $a= base64_decode(strrev(str_rot13($miwen))); for($_0=0;$_0<strlen($a);$_0++){ $_c=substr($a,$_0,1); $__=ord($_c)-1; $_c=chr($__); $_=$_.$_c; } $_o=strrev($_); echo $_o."</br>"; highlight_file(__FILE__); ?>
运行之后即可得到明文:
运行结果:
做完了这一道题,我们知道了如何去读懂PHP代码,并且能够分析出代码的作用。接下来让我们再来看一看文件包含漏洞。
0x02 什么是文件包含漏洞
在程序开发时,开发人员希望代码更加灵活,所以通常会把被包含的文件设置为变量,这样方便进行动态调用,但是如果动态包含的文件路径的参数可以从客户端修改,那么通过修改动态调用的参数,就可以从客户端调用任意文件,从而造成文件包含漏洞。
漏洞产生原因:
网页中实现了动态文件包含(包含的文件路径为一个变量);
动态包含的文件路径参数在客户端可以修改(通过POST或GET等方式从客户端获取);
文件包含的特点
无视文件后缀名读取文件:
在包含文件时,PHP会直接读取文件的源码,不会去考虑文件的类型,就算是包含图片文件时,也会直接读取图片的源码。
无条件解析代码:
文件包含在读取文件源码的同时,如果遇到了符合PHP语法规范的代码,也会去执行。这样我们将一个PHP文件不管后缀名改成什么,在包含时都会正常解析。
文件包含类型:
文件包含可以分为:本地文件包含和远程文件包含。
本地文件包含就是可以读取和打开本地文件。
远程文件包含则是可以远程加载文件。(通过HTPP、FTP、PHP 伪协议)
想要远程文件包含,需要在php.ini中开启远程文件包含。(allow_url_include = on)
0x03 相关函数
在PHP中提供了四个文件包含的函数,他们之间有略微的区别:
函数 |
区别 |
include( ) |
文件包含失败时,会产生警告,但是脚本会继续运行。 |
include_once( ) |
与include( )作用是一样的,只是文件只会被包含一次。 |
require( ) |
文件包含失败时,会产生错误,会直接结束脚本执行。 |
require_once( ) |
与require( )作用是一样的,只是文件只会被包含一次。 |
0x04 利用方式
文件包含漏洞的利用方式有以下几种。
读取敏感文件:
我们可以利用文件包含文件读取系统中的任意文件,来查看系统的配置、日志等信息。
直接包含木马文件:
可以配合网站的上传功能,上传一个图片木马等文件,然后利用包含漏洞包含图片木马就可以正常解析。
PHP封装协议:
通过PHP的file协议访问本地系统文件:
?[参数名]=file://[文件路径]
传输PHP文件:
?[参数名]=php://filter/read=convert.base64-encod/resource=[php文件]
之后将得到的字符进行base64解码即可。
执行PHP命令:
?[参数名]=php://input
执行的PHP代码需要通过POST方式上传。
实战演示:
这里我们再以攻防世界中的另一道题:Web_php_include,来演示漏洞的利用。
这道题没有什么描述,那么我们直接进入试验场景。
题目源码如下:
<?php show_source(__FILE__); echo $_GET['hello']; $page=$_GET['page']; while (strstr($page, "php://")) { $page=str_replace("php://", "", $page); } include($page); ?>
这里同样有高亮的版本供大家查看:
上来就是一串php代码,代码很简单,我们就先看看这段代码是什么意思。
首先我们要知道这中间用到的函数的作用:
函数:strstr([被查找的字符串],[要查找的字符串])
作用:寻找某字符串在另一字符串中第一次出现的位置,并返回查找到字符串的位置之后的全部字符串。
示例:strstr(“Helloworld!”,”world”) 返回:world!
备注:当没有查找到符合的字符串时,strstr函数会返回 FALSE(布尔值)。
此函数还有一个可选参数。默认值为 “false” 的布尔值。如果设置为 “true”,它将返回要查找字符串第一次出现之前的字符串部分。如:strstr(“Helloworld!”,”world”,true) 返回:Hello
同时此函数对大小写敏感,如需进行大小写不敏感的搜索,可以使用 stristr()。
如果只是要査找某字符串是否存在于另一字符串中,则建议使用 strpos这个函数, strpos函数执行的速度会比 strstr快,而且使用更少的内存。
函数:str_replace([查找的字符串],[替换的字符串],[被查找的字符串])
作用:替换字符串中的一些字符
示例:str_replace(“world”,”Peter”,”Hello world!”) 返回:Hello Peter!
备注:该函数是区分大小写的。可以使用 str_ireplace() 函数执行不区分大小写的搜索。
同时该函数还有一个可选参数,为一个变量,作用是对替换数进行计数。
明白各个函数的作用之后阅读代码,就可以明白代码作用:
获取hello参数中的值,显示在页面中。
获取page参数中的值,检测其中是否有“php://”如果有就将“php://”替换为“”(相当于删除字符串中的“php://”)
然后包含page参数中的文件。
分析题目的本意应是阻止我们使用php伪协议去包含任意文件。
比较简单的解决方法就是strstr和str_replace都是区分大小写的,只需要将php大写,变为PHP://即可绕过代码的过滤。
但是在这里我们使用一种比较巧妙的方法:
首先经过测试我们看到传入的hello参数会回显到页面中:
然后我们尝试直接传入php代码结果发下代码会直接被注释掉,并不会执行:
这时我们可以通过http协议直接包含自身,并向包含的文件中传入php代码,来使php代码执行。
构造URL:
http://220.249.52.133:43238/?page=http://127.0.0.1/?hello=<?system(‘ls’)?>
这时我们的php代码就会运行:
可以看到目录下有三个php文件,其中fl4gisisish3r3.php十分可疑,我们直接来看一下他的内容:
http://220.249.52.133:43238/?page=http://127.0.0.1/?hello=<?show_source(“fl4gisisish3r3.php”)?>
这样我们就得到了我们需要的flag:
ctf{876a5fca-96c6-4cbd-9075-46f0c89475d2}
我们就成功利用文件包含漏洞解出了这一道题。
来源:freebuf.com 2020-11-12 14:06:34 by: ATL安全团队
请登录后发表评论
注册