大家好,又见面了,我是你们的朋友全栈君。
我又回来更新了,这次是关于web方面的文件包含漏洞.我会在后面以详细的角度来写清楚这个漏洞的利用方法. 当然,以下都是我自己的理解,表述什么的都有些野人化了.所以希望各位大佬手下留情.
一.漏洞产生的原因
这个漏洞可以追溯到很久.更准确来说,其实是人为产生的.由于我php学的不是很专业,所以我就拿c语言来举例了.php里面使用的是include命令,c语言使用的是#include预处理命令.作用是相似的. 我新建了两个文件,内容如图. wzc.h:
代码语言:javascript复制#include "stdio.h"
void print_f(){
printf("this is test file");
}
main.c:
代码语言:javascript复制#include "wzc.h"
int main(){
print_f();
return 0;
}
程序运行结果如图:
学过c语言的应该都知道我刚才干了件什么事情.我先写了一个自己创建的头文件库,里面存放了一个”print_f”函数,作用是在屏幕上显示一句话. 然后我使用预处理命令将文件进行了一个包含的操作,这样我后面就可以多次调用这个print_f函数了. 这么做在工程量少的情况下是看不出来有什么作用的,但是当你在做一个大工程时,需要多次实现某个功能的时候,就可以派上用场了,很完美的一个操作,对吧? 但是,凡事都有例外. 假如我是黑客,然后我在某个公司开发的c语言程序的被包含文件中,偷偷把被包含的文件中添加一些后门命令,是不是就产生问题了呢?最起码能让你这个程序崩溃不能运行. 不过这并不是我们文件包含漏洞主要被利用的地方,我们要说的是利用语法来钻空子. 观察我们的代码第一行:
代码语言:javascript复制#include "stdio.h"
在c语言中,被包含的文件名左右的包含符号有两种. 第一种是两个尖括号.(<>) 这种写法编译器会在系统自带的头文件库中寻找所需要的文件进行包含.例如:
代码语言:javascript复制#include <stdio.h>
第二种是两个双引号(“”) 这种写法编译器会首先在c文件目录下寻找需要被包含的文件,如果没有,就会去系统文件夹下寻找. ok,了解这两种写法以后,我们可以继续往下走了. 虽然说如果使用(“”)这种写法,机器会自动寻找当前目录下的规定文件,但是有时候我们的头文件库特别多,需要专门建一个文件夹来存放,那这时候该怎么引用呢? 比如我们如图的形式.
就像这样,当我们文件存放在不同地方的时候,我们的c程序在引用的时候就需要改变一下形式了. main.c:
代码语言:javascript复制#include <stdio.h>
#include "..\include\wzc.h"
#include "..\include\wzc2.h"
int main(){
print_one();
print_two();
return 0;
}
像这样,类似于cmd命令中写路径的时候一样的,”…“代表上一级目录.不过由于需要考虑c语言的转义字符的关系,所以我们需要把反斜杠打成两个才可以. 很好,我想你们应该已经认识到了关于使用c语言的预处理命令来进行文件包含了. 但是,想一想这个问题,假如说被包含的文件名如果可以通过用户自己输入的话,那是不是就很恐怖?如果我在被包含的文件多写几个”…/”,是不是就代表着我们就可以没有限制的访问任何目录下的某个文件的内容了? 当然,由于c语言是编译器处理这个事情的,所以不会产生这个问题. 但是php就不一样了,因为它可以直接使用类似于echo的方法直接使用include,甚至允许用户自己输入被包包含的内容,这样做的后果就不需要我说了吧?十分危险. 因此.一些网站在需要使用这个命令的时候,就会对用户的输入进行一个过滤,从而防止用户使用这个漏洞进行一些非法操作.
二.漏洞的实际利用
1)分析源码
这个是我偶然遇到的一个web题,来自于buuctf网站的web的第一题.如图,我们首先访问靶机,显示的是一张图片.
右键查看源码.
发现在注释中有个source.php,我们尝试进行访问发现成功.
很好,又到了我们喜闻乐见的代码审计环节.我对代码的分析放在了下面.
代码语言:javascript复制<?php
highlight_file(__FILE__);
//这个函数是将文件按照语法的高亮来显示.后面那个__FILE__我没有查到,应该是代表本文件的意思
class emmm
//新建一个emmm类
{
public static function checkFile(&$page)
//类中的checkFile函数
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
//新建一个列表,列表中添加了两个key,并且有两个对应关系.这个列表的作用类似于白名单
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
//这是第一个对page进行判断的地方.检查page变量,判断是否是空变量和判断是否是字符串
if (in_array($page, $whitelist)) {
return true;
}
//判断page变量是否存在于白名单之中
$_page = mb_substr($page,0,mb_strpos($page. '?','?'));
//_page变量储存在page变量中从第一个字符开始搜索"?"号出现的位置,"."代表将"?"号和字符串相连.
if (in_array($_page, $whitelist)) {
return true;
}
//也就是说,这个其实是实现了一个截断的方法,因为我们下面会使用get的方法进行提交参数,但是将我们的"?"号和"?"号后面的内容给截断了,因此就造成了我们无法提交参数的尴尬情况.想想看该怎么办.
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
//这也是进行了一个截断,不过这个是进行一次url解码以后的截断.
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" />";
}
//我们看到有include这个命令,因此我们就可以利用这个漏洞.
//我们观察发现,脚本使用get的方式提交file变量的内容,然后进行检查.假如我们能绕过这三个检查,我们就能成功读取到flag.
?>
2)解法分析
我们大概走了一遍这个流程,大概知道了怎么运行的.因此,就需要更深入的研究了. 我们的目的是为了过三个if保护,代码:
代码语言:javascript复制 if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
这三个条件是: 1)判断file变量是否为空 2)判断file变量是否为字符 3)用checkFile函数进行检查 前两个比较简单,关键是最后一个.刚才分析的过程中,我们最主要过的保护其实的就是关于那个截断的保护. 要知道,我们的目的就是为了使checkFile函数返回ture才可以。四个判断我们经过观察发现,最后一个判断是最容易过的保护。 因此,我们想想该如何绕过这个截断? 其实很简单,我们找一个和”?”作用像同的符号但是不是问号的符号不就行了么? 所以,我们要学会一个最简单的绕过方法–url编码绕过 首先,当我们在百度搜索一些东西的时候,我们会发现我们输入的汉字都变成了一些由百分号开头的奇怪编码. 这些编码就是经过浏览器url编码以后得到的结果.但是假如我们直接在网址栏输入编码后的代码,浏览器是不会进行url编码的. 最后服务器那头解码一次,就得到我们输入的结果了. 因此,我们就可以利用这个原理来进行一个简单的绕过.详细来看下过程.
代码语言:javascript复制 $_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
这个是进行url解码以后进行的一个判断.此处就是我们要利用的地方. 假如我们将编码一次后的问号传过来,服务器解码一次得到问号. 然后网站后台脚本会进行一次解码,上一步出来的是问号,解码出来依然是问号,仍旧能发现我们的操作.因此,我们需要编码两次. 第一次,服务器将编码解码,得到问号编码一次的结果. 第二次解码是由后台脚本解码的.仔细研究一下这段代码的逻辑就明白了. 先解码,解出来是问号,然后将问号后面的都截掉,放到白名单里面一判断,成功返回truth,我们就成功了. 很好,此刻我们已经成功将三个判断都绕过了,接下来执行的就是include命令,将我们需要的文件包含进来就行了.这个用法和我前面说的c语言类似,就不赘述了.这个是关于这个漏洞的官方描述,被收录在了cve上面,英语好的同学可以看看.
文章参考链接:https://www.jianshu.com/p/36eaa95068ca
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/136121.html原文链接:https://javaforall.cn