1.前言
在渗透测试过程中,我们经常见到各种各样的后台,通常来说,能直接进入后台,或者能调用后台的功能,能帮助攻击者快速打开一些突破口,或者通过后台中的敏感信息实现“以战养战”的效果。抛去登录口的大量常见问题以外,我们最常用、最简单、最直接的进入后台手法无疑就是密码喷洒以及爆破,而围绕着这个点,防守方和运维实际上会使用非常多的限制手法,常见的主要可归纳为四类:
Ⅰ.编码&加密&验签类(使用现有编码&加密、使用自写加密、对数据包进行验签)
Ⅱ.验证码类(图形验证码、滑动验证码、计算验证码等等)
Ⅲ.错误次数限制类(根据单个用户名错误次数 根据总错误次数)
Ⅳ.特殊登录方式&多因素认证类(手机、邮箱验证码登录 qq、微信、钉钉扫码登陆等)
对于第一类,我们已经有诸如Selenium甚至按键精灵等取巧的解决方案,大部分情况下前端调试分析出加密逻辑也不是难事,第四类的对抗又比较复杂,本文着重分析广大渗透测试和攻击者最常遇到的验证码类和错误次数限制类的绕过手法。
2.常规图形验证码的若干对抗思路
在日常测试中,我们是很经常能遇到一些验证码的,其中以图形验证码为最多,他们类似这样:
或者是这样
但不管怎么说,他们都属于比较正常的图形验证码,关于图形验证码的对抗思路,实际上是最多的
①常规图形验证码的复用、固定问题
我在与验证码对抗中遇到的最多的问题,它的情景大概是这样的,我们在登录框输入一个验证码,然后抓包
这时候我们换一个用户名或者密码
只要你固定这个验证码不变即可,我们就能无限使用这个数据包
(改变验证码就可能会导致数据包失效)
我们会发现,在这个案例里,验证码其实就是一个局外人,只要你保持它不变,其它任何因素随意变都没事,不会影响这个数据包的有效性,因此我们就可以任意重放,这就是验证码复用、固定问题
②常规图形验证码的前端校验问题
也算是比较常见的问题,看下面的案例
还是先在登录框输入好各种东西,并输入正确的验证码
登录时抓包,我们会发现数据包里没有任何与验证码有关的东西
这也就说明,验证码这一因素根本就不会进入后端校验!那么这个数据包我们就可以肆无忌惮的随意使用
③常规图形验证码与登录验证码的分离校验问题
这是一个非常蠢的问题,比较类似于前端校验,为什么说他蠢呢?有部分开发者设计验证码的思路是在这样的:
先对用户输入的验证码返回到后端进行校验——校验通过后进入登录流程,单独发送一个登陆数据包进行登录
发现问题没?验证码和登陆数据包并不绑定,而是分开的,这就意味着攻击者完成验证码校验流程后,可以随意使用后续的登录数据包,案例如下:
先输入正确的验证码
点击登录,发现它是先发送一个校验验证码有效性的数据包
随后进入登录流程
可以看到登陆数据包里没有任何和验证码有关的信息,意味着我们可以无限使用
④常规图形验证码的置空问题
置空问题包括前面的复用、固定问题其实与CSRF中的CSRF-Token的诸多绕过手法有异曲同工之妙,这个问题产生的根源在于,开发者光顾着判断验证码有效性,却忘了判断验证码是否存在,这就导致我们可以绕过验证码有效性校验的流程,验证码置空问题分为两种手法
置空验证码值、或令其为null:
尝试删去vaildateCode的参数值,发现数据包仍然有效
置空整个验证码,包括参数名和参数值:
还是以上面那个案例为例,但这回我们直接把整个vaildateCode都删去:
注意此时vaildateCode已经不存在了,但该数据包却依旧有效。需要注意的是这两种虽然都叫置空,实际上是不一样的,因为开发者判断是否为空一般有两种思想,一种是判断参数值是否为空,一种是判断参数是否存在,要注意区分。
⑤常规图形验证码的刷新接口拦截造成的复用问题
笔者于某次护网中的意外发现,我猜测其原因大概是这样的,用户每次请求生成验证码的接口,就会刷新数据库中的验证码值,从而实现不断变换验证码,如果我们拦截住或者drop掉验证码生成的数据包,那么验证码值永远不会刷新,因此可以永远复用,确实是个很有趣的思路。我们来看看这个案例。
填好登录框信息然后抓包
发一份到Repeater
然后进行放包,注意拦截
这里会抓到一个刷新验证码的数据包,注意这个数据包我们要始终保持拦截,不要放它通过
此时我们可以无限使用之前的数据包
一旦我们把刷新验证码的数据包放掉,验证码成功刷新,那么原先的数据包会立即失效
这个姿势似乎对部分若依有效
⑥常规图形验证码中的万能验证码问题
笔者目前暂未在实战中碰到过类似的问题,仅在部分文章中见到过类似的记载,大意就是开发者在设计登录图片验证码功能时,前期为了测试可能不小心遗留了一些万能验证码,包括但不限于:
1111
8888
9999
6666
true
1
等等,但是笔者在实战中从未遇到过,故仅作记录(此外值得注意的是,虽然和本文无关,但是一些手机验证码和邮箱验证码场景也可能出现这种问题,笔者在实战中倒是碰到过这种,有空也可以给师傅们看个乐)
⑦验证码与COOKIE绑定问题
有些时候验证码与COOKIE绑定,在COOKIE中会有些和验证码相关的参数,其实和上面大同小异,可以先试试把整个COOKIE删掉,然后观察能不能复用验证码。如果不行可以再试试把Cookie以及POST中的验证码相关参数删掉,观察能不能绕过验证码
⑧常规图形验证码的可识别问题
前面使用的手法都是取巧的手法,思路上和逻辑漏洞类似,而验证码识别则是实打实的硬对抗,burp上其实已经有非常成熟的插件了
这里比较推荐使用captcha-killer-modified这款插件,网上关于配置和使用这款插件的文章有非常多,本文便不再赘述。这款插件还是非常强大的,根据我的体验,在面对位数较少的纯数字图形验证码,并且图形验证码里没有干扰因素的前提下,这款插件的识别成功率还是非常高的,面对一般的字母数字混合的图形验证码,其识别成功率就会下降一些了,但一旦验证码中有大量干扰因素,并且采用字母数字混合的较长的验证码,那识别成功率会急剧衰减。
总之识别的方法虽然还不错,但是考虑到其效率较低,可以考虑作为对抗验证码的最终防线,尽量避免一上来就使用验证码识别插件!
3.滑动验证码、算数验证码等复杂验证码的若干对抗思路
随着对抗的不断进步,越来越多形态千奇百怪的验证码也慢慢涌现出来,其中又以滑动验证码和算数验证码居多
你最常见到的滑动验证码可能长这样:
或者这样:
总之都是要你滑动什么东西的。
而常见的算数验证码可能是这样:
这两种新类型的验证码确实让对抗难度上升了,但同时,他们也存在很多问题,这些问题和传统的图形验证码是非常相似的,下面也来介绍一下
①滑动验证码、算数验证码的纯前端校验问题
和上面提到的传统图形验证码的前端校验问题类似,开发者的设计思路是 前端完成滑动验证码or算数验证码校验——>进入发送登录数据包流程,不包含任何验证码校验信息
也就是说,我们完成前端滑动验证or算数验证后,得到的登录数据包可以重复使用,和之前是类似的,案例如下:
一个滑动验证码界面,完成滑动校验后,进入登录流程,可以看到在这个登陆包就是正常的登陆包,没有任何验证码相关校验信息
那么这个数据包我们就可以重复使用
②滑动验证码、算数验证码的分离校验问题
这个问题和上面是非常类似的,上面我们说,验证码纯前端校验,完成后直接进行登录。而在这个案例中,开发者聪明了一点点,在滑动验证码or算术验证完成后,先发送一个数据包在后端进行校验,后端校验成功,发包给浏览器告知成功,随后进入登录流程。
这同样存在问题,因为在这个案例里,滑动验证码、算数验证码仍旧对登录流程的数据包没有任何影响,因为你验证码有效性校验和你数据包有效性校验是分开的,这么说可能有点抽线,所以我们再来看案例
完成拼接后,先是调用腾讯的滑动验证码,发送三个超复杂的校验数据包
返回几个校验成功的信息
然后呢?然后没有然后了,进入登录流程,我们又发现这个数据包里没有任何校验因素,可以随意使用
因此这告诉开发者一个重要的教训,那就是滑动验证码确实是相对安全的校验因素,但前提是,要把它和登陆数据包绑定在一起做校验,做到一个包对应一次滑动验证或算数验证,那么开发人员要如何把验证与登录联系在一起呢?联系他们的桥梁往往就是类token机制
③token机制下滑动验证码的token复用问题
又是经典再现,开发者可能聪明了点,知道要在滑动验证后生成一串token,进入登陆数据包来辅助校验,但这又让滑动验证重新遭受了和传统图形验证码类似的风险,其中之一就是token复用,下面来看案例
完成验证后点击登录,发现确实有token在辅助校验数据包有效性
但是呢,我们固定这个token,随意修改数据包内内容,却发现数据包仍然可用
可以看出,如果要在滑动验证码、算数验证码等复杂验证码中引入类token机制,那么不可避免地容易面临和常规图形验证码类似的诸多问题,因为此时我们只需要将token看作是“从外表上看起来更复杂的常规图形验证码”
只要把这个思路确定下来(即,把复杂验证码中的token机制理解为“从外表上看起来更复杂的一般验证码”),那么实际上我们可以继续套用常规图形验证码的攻击思路!
④token机制下滑动验证码的token置空问题
根据上面的思想,我们不难推断出滑动验证码里的token也可能存在置空的问题,毕竟它只是“看起来复杂”
我们还是以上面那个验证码的数据包为例(它真的有非常多问题)
我们发现直接删去__RequestVerificationToken的参数值,该数据包仍旧有效
包括删去参数值+参数名,也还是仍然有效
⑤滑动验证码的可识别问题
我在搜索类似的文章的时候,似乎也看到有人能够对抗滑动验证码(指的是正面“硬碰硬”而不是取巧),但是似乎没见到有人搞出能和burp联动的自动化工具(也可能是笔者见识浅薄),总之在这里记录一下,以后如果有成熟的解决方案再做补充。师傅们如果了解类似项目也欢迎推荐一下。
所以我们不难看出,虽然滑动验证码、算数验证码等机制确实相对传统的图形验证码要安全很多,但它也可能存在很多问题(这些问题主要是人为的)。首先就是如果这种复杂验证码不能有效和登录数据包、登录流程绑定在一起,那么他就是无效的(前两个愚蠢的案例)。而复杂验证码若要和登录流程绑定,一个最常用的方法就是生成token加入到登陆数据包中,用于辅助判断有效性,但是如果引入token,我个人认为又落回到传统图形验证码的境界了,只不过在传统图形验证码里,我们的校验因素是简短的字符串,而token不过是看起来更长的字符串罢了,该有的问题还是要有
这里只是以滑动验证码、算数验证码等常见的复杂验证码来举例,实际上上文提到的攻击思路对其它更奇葩更冷门的复杂验证码也可能是有效的,还是那句话,这些更复杂的验证码本身可能是安全的,但人写出来的代码和流程却不一定。
4.错误次数限制机制的若干对抗思路
什么是错误次数限制?相信读者能很快反应过来。我们在日常测试中还能遇到一种限制策略,比如你要爆破admin账户的密码,却提示你这个用户的密码只能错五次,超过五次“锁”admin账号。在更严峻的对抗场景下,甚至都不是单个用户密码错五次,只要你总的错误次数超过五次,则限制登录。
这种看似很安全的机制,实际上会因为具体实现方式的不同而产生各种问题,我们下面就来具体分析一下。
①内网地址保护造成的错误次数限制机制绕过
我们来设想这样一个情况,假设某个网站有用户密码错误次数限制,比方说admin,连错5次则封账号10分钟,这里我们假设它封账号是真封,让账户失效那种。
这会造成什么严重后果呢?黑客只要每隔10分钟去爆破一下admin用户,那就等于admin用户永远都不能登陆了,这会造成很严重的后果,显然这样的设计是不科学的(但我好像真的见过这样的奇葩案例)
所以,大部分开发者在开发的时候,就可能对本地地址127.0.0.1或内网ip地址如192.168.1.1做特殊的保留,外网ip如果错误次数达到一定量,那确实无法登录,但用户在内网仍旧可以登录这个账号,这种机制可以相对保证安全,又能防止黑客的爆破行为对业务造成影响,但显然,这也是可以被利用的,因为攻击者只需要使用XFF头把自己的IP伪装成内网,那也不用受到错误次数限制了!案例如下:
这里我们快速发包触发错误次数限制机制
X-Forwarded-For改地址为内网IP,成功绕过
②密码喷洒造成的错误次数限制绕过
前文我们提到的密码错误次数限制机制是针对单个用户的,那既然如此我们可以转爆破为喷洒,比方说可以固定密码为123456来对不同用户名进行喷洒(也是笔者最喜欢的手法,大部分情况下比密码爆破成功率高并且更高效),反正我们的最终目的是进后台,先喷洒个普通用户或测试用户之后,在寻找越权的方法或者别的漏洞,也未尝不可。
因为这种手法比较简单且大家都能理解,所以就不配图了
③使用XFF头获取IP地址造成的错误次数限制机制绕过
我们前文提到一种错误次数限制,那就是根据错误次数的总数来限制,一般来说可能是某个用户错到一定次数才限制登陆,但如果采用错误次数总数的机制,可就没那么好办了,这就意味着一般的密码喷洒也是行不通的。
但是我们可以站在开发者的角度仔细思考一下这种机制,前面我们说,对单个用户进行密码错误次数的限制,其实是比较容易的,毕竟盯着单个用户就行,规则也好写。但是怎么样才能对错误次数总数来进行判断呢?大部分开发者很容易就能想到一个答案——看某个IP输错了多少次密码不就行了
正是这样的思想给了我们绕过的空间,特别是当开发者使用XFF头来获取用户IP的时候,案例如下:
(还是使用和上文类似的环境,一套代码,开发者修复了内网地址保护的问题后,又产生了一个新的问题)
这里可以看到密码喷洒无效,开发者是对错误次数总数进行限制的,这里我们用XFF头换成内网地址,发现依旧有错误次数限制
(前面还可以,但是次数一多就会触发限制)
这时我们生成一份ip表,采用pitchfork模式,标记XFF参数值和用户名或者密码:
分别设置为iplist和用户名字典
这里注意取消转义,否则burp会把ip表里的点号进行url编码导致其失效
成功绕过错误次数总数的限制
可见,贸然使用XFF头以及其它类似的请求头获取IP,是非常危险的行为,除了可能导致XFF头注入等问题,还能被攻击者用于各种绕过。
④开发者自写的用于辅助计算错误次数的机制
部分开发者为了秀一秀自己的技术水平,或者是业务上确实有其他需求,喜欢搞点别的玩意来给错误次数计数,这个说起来有点抽象,我直接放一个我在某企业SRC的案例
最后成功登录目标的一个后台,这告诉我们,在遇到此类机制时,需要注意到底是哪个点负责确认用户的IP或者别的什么信息,在一般场景下通常是XFF等请求头,但少部分情况下,有可能是开发者自写的机制,总之,一定要细心观察分析!
⑤高并发请求造成的错误次数限制机制的不完全绕过
在渗透测试领域,比较常用到高并发的场景是在逻辑漏洞、条件竞争漏洞方向,但实际上它在绕过登录错误次数限制机制方面也能发挥一定作用,为什么这么说?
其实从上面的案例中读者也能发现一些奇怪的点,比方说这张图:
你不是说只能错五次吗,但是看这张图里,明明有6个”385”,也就是说至少有6个数据包是成功进入判断的,这明显和表面上的“错五次”不一样,事实上也确实如此,我在挖掘某家企业SRC时就遇到过类似的案例,下面截取部分报告内容:
由此可见,善用高并发工具,是有可能绕过较为严格的错误次数限制的,虽然不能完全绕过,但多爆破几十条常见密码,出货的可能性也会上升一些,所以也算是一种可以采用的技巧。
5.后记
很多攻击者在面对图形验证码机制、错误次数限制机制时,往往容易产生“怯战心理”,从而放弃密码喷洒、爆破等技巧,实际上大可不必,有时候此类辅助校验机制并没有看上去那么安全,而密码喷洒和爆破作为进入后台最简单直接的手法,轻易放弃容易错失大量机会,未免太过可惜。以上就是笔者在近年来与各种登录辅助校验机制对抗的经验,可能还有很多点没有提到,望各位师傅不吝赐教。