浅析自动绑定漏洞
nowill 漏洞分析 17271浏览 · 2017-06-28 23:20

浅析自动绑定漏洞(mass assignment vulnerability)

一.自动绑定

软件框架有时允许开发人员自动将HTTP请求参数绑定到程序变量或对象中,以使开发人员更容易使用该框架开发。这有时会造成伤害。攻击者有时可以使用这种方法来创建,修改,更新开发人员或者业务本身从未打算设计到的参数,而这些新参数反过来又会影响程序代码中不需要的新变量或对象。

本人比较熟悉Java,因此就拿Spring MVC中的自动绑定作为举例,假设有一个用户提交基本信息的功能:

首先看前端html:

<form action=“addUserInfo” method="post">
     <input name=username type=text><br>
     <input name=phone type=text><br>
     <input name=email text=text><br>
     <input type=submit>
  </form>

很简单,一个form表单,通过post提交username,phone,email到addUserInfo。

我们看一下用户的实体类:

public class User {
     private String userid;
     private String username;
     private String phone;
     private String email;
     private boolean isAdmin;

     //Getters & Setters
   }

可以看到User的实体类存在除了username,phone,email还有比较重要的userid和isAdmin参数。

我们接下来看一下controller层如果利用了自动绑定可以写出的处理方式:

@RequestMapping(value = "/addUserInfo", method = RequestMethod.POST)
  public String submit(User user) {
     userService.add(user);
     return "successPage";
  }

ok,到这里大家也许应该可以看明白了。

二.潜在危害

结合上面的demo,我们可以看到,controller层使用的自动绑定对象的方式,也就是使用了post请求的参数直接绑定到了User,虽然前端form表单中仅出现了username,phone,email,但是如果使用burp等工具向addUserInfo发送了

POST /addUserInfo

username=daheike&password=bypass&email=test@test.com&isAdmin=true

或者

POST /addUserInfo

username=daheike&password=bypass&email=test@test.com&userid=adminid

那么如果后续使用了被污染的对象的被污染的属性,那么就会产生一定的安全问题。

三.案例

[1].2012年,GitHub被黑客利用了自动绑定漏洞。用户能够将其公钥上传到任何组织,从而在其存储库中进行更改:https://github.com/blog/1068-public-key-security-vulnerability-and-mitigation

[2].某P2P系统对象自动绑定漏洞可任意充值:https://threathunter.org/topic/593ff6bc9c58e020408a79d4#595485920084b15859bc7219

四.总结

在案例2中,我猜测可能是编写程序的人员处理不当,导致用户设置地址时自动绑定到了用户对象中,并且orm中并未严格区分业务,将整个用户对象更新,插入到了用户表中,导致了漏洞的触发;那么问题可能就不仅仅是文中的一处看,可能多处都会有该问题存在。

自动绑定漏洞并非仅在Spring MVC中可能出现,只要有自动绑定功能的框架都可能出现这样的逻辑问题。

但是可能对于大部分安全人员来说,可能面对的是一个黑盒的环境,很难发现controller层或者orm是否存在这样的问题,那么可能就需要多个表单的交叉对比,猜测活着推测出可能出现问题的点了。

五.修复建议

通用修复方法是:避免将输入直接绑定到域对象,只有用户可以编辑的字段才包含在DTO中。以上文中的deom为例:

public class UserRegistrationFormDTO {
     private String username;
     private String password;
     private String email;

     //Getters & Setters
   }

或者可以修改controller层,仅接收用户可以修改的参数:

@RequestMapping(value = "/addUserInfo", method = RequestMethod.POST)
  public String submit(String username,String phone,String email) {
     userService.add(username,phone,email);
     return "successPage";
  }

也可以设置白名单:

@Controller
  public class UserController
  {
     @InitBinder
     public void initBinder(WebDataBinder binder, WebRequest request)
     {
        binder.setDisallowedFields(["isAdmin"]);
     }

     ...
  }

六.其他语言框架修复建议

1.NodeJS + Mongoose

白名单

var UserSchema = new mongoose.Schema({
    userid    : String,
    username    : String,
    password  : String,
    email     : String,
    isAdmin   : Boolean,
  });

  UserSchema.statics = {
      User.userCreateSafeFields: ['username', 'password', 'email']
  };

  var User = mongoose.model('User', UserSchema);
_ = require('underscore');
  var user = new User(_.pick(req.body, User.userCreateSafeFields));

黑名单

var massAssign = require('mongoose-mass-assign');

  var UserSchema = new mongoose.Schema({
    userid    : String,
    username    : String,
    password  : String,
    email     : String,
    isAdmin   : { type: Boolean, protect: true, default: false }
  });

  UserSchema.plugin(massAssign);

  var User = mongoose.model('User', UserSchema);
/** Static method, useful for creation **/
  var user = User.massAssign(req.body);

  /** Instance method, useful for updating  **/
  var user = new User;
  user.massAssign(req.body);

  /** Static massUpdate method **/
  var input = { username: 'bhelx', isAdmin: 'true' };  
  User.update({ '_id': someId }, { $set: User.massUpdate(input) }, console.log);
2.Ruby On Rails

http://guides.rubyonrails.org/v3.2.9/security.html#mass-assignment

3.Django

https://coffeeonthekeyboard.com/mass-assignment-security-part-10-855/

4.ASP.NET

http://odetocode.com/Blogs/scott/archive/2012/03/11/complete-guide-to-mass-assignment-in-asp-net-mvc.aspx

5.PHP Laravel + Eloquent

白名单:

<?php

  namespace App;

  use Illuminate\Database\Eloquent\Model;

  class User extends Model
  {
     private $userid;
     private $username;
     private $password;
     private $email;
     private $isAdmin;

     protected $fillable = array('username','password','email');

  }

黑名单:

<?php

  namespace App;

  use Illuminate\Database\Eloquent\Model;

  class User extends Model
  {
     private $userid;
     private $username;
     private $password;
     private $email;
     private $isAdmin;

     protected $guarded = array('userid','isAdmin');

  }
6.Grails

http://spring.io/blog/2012/03/28/secure-data-binding-with-grails/

7.Play

https://www.playframework.com/documentation/1.4.x/controllers#nobinding

8.Jackson (JSON Object Mapper)

http://www.baeldung.com/jackson-field-serializable-deserializable-or-not

http://lifelongprogrammer.blogspot.com/2015/09/using-jackson-view-to-protect-mass-assignment.html

9.GSON (JSON Object Mapper)

https://sites.google.com/site/gson/gson-user-guide#TOC-Excluding-Fields-From-Serialization-and-Deserialization

http://stackoverflow.com/a/27986860

10.JSON-Lib (JSON Object Mapper)

http://json-lib.sourceforge.net/advanced.html

11.Flexjson (JSON Object Mapper)

http://flexjson.sourceforge.net/#Serialization

七.参考/翻译

Mass Assignment Cheat Sheet:https://www.owasp.org/index.php/Mass_Assignment_Cheat_Sheet#Solutions

12 条评论
某人
表情
可输入 255
hades
2017-07-04 00:15 0 回复

    好样的 辛苦了 cryin


cryin
2017-07-03 23:56 0 回复

前几天刚好研究这个漏洞,Spring MVC 有个很不错的例子https://github.com/GrrrDog/ZeroNights-HackQuest-2016 &nbsp; 可以看bobao那篇译文。补充两个东西


@ModelAttribute注解


在Spring mvc中,注解@ModelAttribute是一个非常常用的注解,其功能主要在两方面:



  • 运用在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用;

  • 运用在方法上,会在每一个@RequestMapping标注的方法前执行,如果有返回值,则自动将该返回值加入到ModelMap中;


@ModelAttribute注释一个方法的参数,从Form表单或URL参数中获取


@RequestMapping(value = &quot;/home&quot;, method = RequestMethod.GET)
&nbsp;&nbsp;&nbsp;&nbsp;public String home(@ModelAttribute User user, Model model) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (showSecret){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;model.addAttribute(&quot;firstSecret&quot;, firstSecret);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return &quot;home&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;

view端通过${user.name}即可访问。


@ModelAttribute注释一个方法,该方法会在此controller每个@RequestMapping方法执行前被执行



    @ModelAttribute("showSecret")
    public Boolean getShowSectet() {
        logger.debug("flag: " + showSecret);
        return showSecret;
    }

在ZeroNights-HackQuest-2016例子justiceleague里面还有个@SessionAttributes注解


@SessionAttributes详解

在默认情况下,ModelMap 中的属性作用域是 request 级别,也就是说,当本次请求结束后,ModelMap 中的属性将销毁。如果希望在多个请求中共享 ModelMap 中的属性,必须将其属性转存到 session 中,这样 ModelMap 的属性才可以被跨请求访问。


Spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 session 中,以便下一个请求对应的 ModelMap 的属性列表中还能访问到这些属性。这一功能是通过类定义处标注 @SessionAttributes("user") 注解来实现的。只要不去调用 SessionStatus 的 setComplete() 方法,这个对象就会一直保留在 Session 中,从而实现 Session 信息的共享


nowill
2017-08-06 16:13 0 回复

感觉fuzz都很看运气,还是靠代码审计,如果条件允许黑盒应该也是可以测出来的,比如burp改个参数然后不小心前端回显了什么的。这种问题也有可能导致SSRF


sqvds
2017-07-31 18:13 0 回复

这种除了代码审计、fuzz还有什么可以发现出来这种漏洞呢


hades
2017-06-29 00:49 0 回复

挺浅显易懂的 ~· 防御方面能深入一点就好了  


nowill
2017-06-28 23:39 0 回复
nowill
2017-06-28 23:34 0 回复

还有什么攻击思路希望可以和各位大佬共同探讨


svenll
2017-08-04 18:41 0 回复

这个有时候可以达到定时攻击的效果

至于防御我更倾向于

在后台重新new一个你要接受的对象,把你需要的参数set进去,不要直接使用前台直接绑定的对象直接get属性获取参数

这样更方便


hades
2017-07-30 19:59 0 回复

^^


nowill
2017-07-30 19:17 0 回复

。。服了,居然还有这种操作


cryin
2017-07-06 20:13 0 回复

引用第6楼svenll于2017-07-07 11:28发表的 Re浅析自动绑定漏洞 :

在这个自动绑定里面我发现了一个普遍的可以拒绝服务的方法,就是在提交数据时添加绑定对象的pk值或者id值,这个值一般在数据库中是自增的,而integer有个最大值,提交时把这个id值给成integer的最大值,写入成功后,此功能从此数据就不能写入了。 [url=https://xianzhi.aliyun.com/forum/job.php?action=topost&tid=1801&pid=3083][/url]


这个tips不错,int型一般默认是11位。。只要给个足够大的数之后确实可以达到拒绝服务的效果~~


svenll
2017-07-06 19:28 0 回复

在这个自动绑定里面我发现了一个普遍的可以拒绝服务的方法,就是在提交数据时添加绑定对象的pk值或者id值,这个值一般在数据库中是自增的,而integer有个最大值,提交时把这个id值给成integer的最大值,写入成功后,此功能从此数据就不能写入了。