原理:
逻辑漏洞是指由于程序程序逻辑不严谨或者程序设计太复杂,导致一些逻辑分支不能正常处理或者处理错误问题,导致攻击者可能通过此类问题进行一些操作。
攻击偏离了预期的用户行为。
挖掘逻辑漏洞有两个重点:业务流程和HTTP/HTTPS请求的篡改。
一、越权
1.1、水平越权
例如当http://www.xxx.com提供了用户修改资料的的功能,当访问http:///www.xxx.com/userinfo.action?id=1时显示自己资料,并且可以编辑,Userinfo的源代码如下:
public String execute(){
int id = this.user.getUserId();
this.user = new UserProxy.findUserById(id);
return SUCCESS;
}
这段代码对id参数没用进行验证,直接接收用户传来的ID参数,根据用户ID查询信息。
此时,我们更改id值,http:///www.xxx.com/userinfo.action?id=3,那么此时,程序就会返回id=3时的用户信息,这就是水平越权。
水平越权,主要是相同权限用户之间数据的越权操作。
水平越权示例:
以任意修改密码为例:
public String execute(){
int id = Integer.parseInt(request.getParammeter("userId"));
String password = request.getParameter("password");
String password2 = request.getParameter("password2");
if(!("".equals(password)||"".equals(password2))){
return ERROE;
}
if(!password.equals(password2)){
return ERROR;
}
User u = new UserBiz().findUserById(id); //根据ID来获取User具体对象
u.setPassword(password);
boolean flag = new UserBiz().saveOrUpdate(u); //更新对象
if(flag){
return SUCCESS;
}else{
return ERROR;
}
}
这段代码有一个隐藏的逻辑漏洞,就是任意密码修改。假设有两个用户,一个用户时Admin用户,UserId为1.另一个用户时Guest,UserId为2。Guest进行密码修改时,拦截HTTP请求,HTTP请求如下:
POST /user/UpDateUser.action HTTP/1.1
HOST:www.xxx.com
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent:Mozilla/5.0
Referer:http://www.xxx.com/user/userinfo.action
......
id=2&password=myuser123&password2=myusrer123
此时,ID=2.密码为myuser123,当Guest把用户的ID参数修改为1时,Admin用户的密码就会被修改为myuser123.程序会根据ID查询处Admi用户的所有i西南西。然后修改Admin用户的密码。但是增加一个原密码验证,则可以防止此类事情出现。
1.2、垂直越权
垂直越权则是不同权限用户或不同角色用户之间的的越权操作。
垂直越权又分为向高权限操作的向上越权和向低权限操作的向下越权。
二、密码找回逻辑漏洞
常见的密码找回方式:邮箱找回,密保问题找回,手机号找回,原密码找回等。无论何种方式,在找回密码时,除了自己的用户密码之外,如果还可以找回其他用户的密码,那么就存在密码找回漏洞。
三、支付逻辑漏洞
测试支付逻辑漏洞的重点在于对HTTP请求及业务流程的分析。
在测试支付逻辑漏洞时,也有几个侧重点,用户提交的参数:如购买数量、商品价格、折扣、运费、商品信息的辗转页面、跳转支付接口时等参数。
1、商品数量为负数
商品数量为附属的情况多数出现在有站内货币的网站上,当购买一个商品的时候,算法一般为:购买数量X购买单价=支付金额,但是如果购买数量为负数,如-5,那么支付金额将会为负数,如下代码:
public class order extends HttpServlet{
public void doGet(HttpServletRequest,HttpServletResponse response)
throws ServletException,IOException{
this.doPost(request,reponse);
}
publicc void doPost(httpServletRequest,HttpServletResponse response)
throws ServletException,IOException{
User u=(User)request.getSession().getAttribute("User");
u=new userBiz().findUserById(u.getID());
int commodityId= Integer.parseInt(request.getParameter("commodityID"));
int number=Integer.parseInt(request.getParameter("number"));
commodity com = new CommodityBiz().findCommodityById(commodityID); //根据ID查询商品信息
if(comm.getNumber()<number){
reuqest.setAttribute("messafe","商品数量不足,无法购买");
request.getRequestDispatcher("/user/userinfo.do").forword(request,response);
}
double appprice = number*comm.getPrice(); //查询总价格
if(u.getMoney()<allprice){
request.seetAttribute("message","您的金额不足,请及时充值,还差"+(allprice-user.getMoney())+"个金币");
request.getRequestDispatcher("/user/userinfo.do").forward(request,response);
}
u.setMoney(u.getMoney()-allprice); //把用户的金币去除
u.getCommodity().add(commodityID);
UserDao userDao = new UserBiz();
boolean flag=userDao.saveOrUpdate(u); //更新用户金币
comm.setNumber(comm.getNumber()-number);
CommodityDao comDao=new CommodityBiz();
boolean cflag=comDao.saveOrUpdate(comm);
if(flag&& cflag){
request.setAttribute("message","购买成功,余额为:"+u.getMoney());
request.setAttribute("User",u);
request.setAttribute("Commodity",comm);
request.getRequestDispatcher("/user/userinfo.do").forward(request,response);
}else{
request.setAttribute("message","购买失败,出现异常情况");
requesu.getRequestDispatcher("/user/userinfo.do").forward(request,response);
}
}
}
上述代码的执行流程:
1、从session中取得User对象,也就是当前的用户信息。
2、根据User的ID,在数据库中查询当前用户的最新信息。
3、取得商品的ID,根据商品ID查询出的价格、数量等信息。
4、对商品数量进行判断,如果用户购买数量大于库存数量,则跳转页面,结束购物。
5、根据数量计算出总价格。
6、判断用户的Money与商品数量重置,然后保存,如果都成功,则进行下一步,否则页面跳转,结束购物。
7、将用户的Money与商品数量重置,然后保存,如果都成功,则进行下一步,否则页面跳转,结束购物。
8、将最新的用户信息与商品信息放入Session中。
这段代码时一个比较常见的购买流程其中的安全隐患是。假设用户Money=100,商品价格=30,数量=20个。用户购买一个商品后,那么Money=100-30=70,数量为19。这是一个正常的购买流程。
如果此时购买数量为-3,然后购买,当这段servlet在计算价格时,并没有对负数进行验证,而是直接进行了运算:30X(-3)=-90,那么现在商品的几个为-90元。接下来判断用户金额是否足够,然后进行下一步。u.setMoney(u.getMoney()-allprice)的意思就是将用户的Money进行重置,100-(-90)=190,余额反而变多了。
四、指定账户恶意攻击
在一些安全性较高的网站经常遇到输入密码错误三次之后,将锁定账号,就是典型的密码输入错误次数过多而导致的无法登录问题。
一般情况下,正常用户输入密码错误次数不会超过三次,如果超过三次,就有可能是攻击者正在尝试破解密码。
如果攻击者无差别的仅仅为了锁定账号,这种损人不利己的行为,也恰巧算是一种对于账户的恶意攻击。
五、防御
逻辑漏洞产生的原因基本上都是因为攻击者切入原本的业务流程中,以开发人员意料之外的请求或者操作对数据进行访问导致的,所以我们在防御逻辑漏洞时,必须从业务代码设计之初就开始进行改善:
1、确保开发人员和测试人员了解该应用程序服务;
2、避免对用户行为或应用程序其他部分的行为做出隐式假设;
3、确保任何输入的值都是合理的,并对用户输入的任何值做出对应的反应;
4、维护所有事务和工作流的清晰的设计文档和数据流,并注意在每个阶段所做的任何假设。