可以发现使用的是thinkphp3.2,由此可见thinkphp版本相对比较老,所以可以直接审计thinkphp核心代码。
直接查看下后台登陆功能,burp抓包如下:
根据路由s=/admin/public/login.html,可以看到调用了admin目录下的public控制器。
注释掉验证码,直接burp跟进$uid = $User->login($username, $password);
注意到调用了UcenterMemberModel.class.php中的login方法,看到截图断点,跟进断点where方法。
查看上面注意到如果$where是数组,将会被直接赋值给$this->option[‘where’]。继续跟进find方法。
断点处有对$options进行赋值和验证操作,方法如下,这里可以注意到表达式过滤的方法默认为空。
所以之前的$where的值可以没有过滤的赋值给$options。
如上继续跟进$this->db->select($options)方法。
跟进buildSelectSql方法,此时的获取到的$sql就是我们最终要执行的sql语句。
具体可以跟进$this->query方法如下所示:
不存在任何过滤。也就是说我们能够成功构造$sql语句就能够最终获取到我们想要的数据。回到buildSelectSql,重点关注蓝圈中的方法。
继续跟进。
也就是说将$sql中的值进行了替换,换成options数组中对应的数据。重点关注parseWhere函数,这个是我们这次传参可以控制的,跟进。
可以看到会进入到foreach循环,看下foreach中的代码,发现对key进行安全检测,根据传过来的参数可以知道key为username。根据逻辑如果key正常,将会进入parseWhereItem(460行),跟进。
如上代码可以看到,其中btween和in的正则处理方式,并不存在^$,所以我们此处可以构造payload来实现注入。
如果想要登录后台,我们可以根据上面的分析构造指定的用户名密码来实现登录。
这里查看下登录代码。
通过用户名查询获取用户密码,判断密码加密后是否等于数据库获取到的密码,成功则登录成功,不过因为salt是随机生成的,我们无法解密,所以不好注入构造密码。
查看加密算法。
发现当str为空的时候,返回空。故可以用union select来达到指定password为’’。
- payload构造。
username[]=like 1)and 1 in (2) union select 1,2,'',4,5,6,7,8,9,10,11%23&username[]=0&password=&verify=yzm
成功绕过登录管理员用户。