File: t-path.sh

package info (click to toggle)
git-lfs 3.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,808 kB
  • sloc: sh: 21,256; makefile: 507; ruby: 417
file content (181 lines) | stat: -rwxr-xr-x 7,905 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env bash

. "$(dirname "$0")/testlib.sh"

begin_test "does not look in current directory for git"
(
  set -e

  reponame="$(basename "$0" ".sh")"
  git init "$reponame"
  cd "$reponame"

  cp "$BINPATH/lfstest-badpathcheck$X" "git$X"

  # This should always succeed, even if git-lfs is incorrectly searching for
  # executables in the current directory first, because the "git-lfs env"
  # command ignores all errors when it runs "git config".  So we should always
  # pass this step and then, if our malicious Git was executed, detect
  # its output below.  If this command does fail, something else is wrong.
  PATH="$BINPATH" PATHEXT="$X" "git-lfs$X" env >output.log 2>&1

  grep "exploit" output.log && false
  [ ! -f exploit ]
)
end_test

begin_test "does not look in current directory for git with credential helper"
(
  set -e

  reponame="$(basename "$0" ".sh")-credentials"
  setup_remote_repo "$reponame"

  clone_repo "$reponame" credentials-1

  git lfs track "*.dat"
  printf abc > z.dat
  git add z.dat
  git add .gitattributes

  GITPATH="$(dirname "$(command -v git)")"
  SHELLPATH="$(dirname "$(command -v sh)")"

  # We add our malicious Git to the index and then remove it from the
  # work tree so it is not found early, before we perform our key test.
  # Specifically, our "git push" below will run git-lfs, which then runs
  # "git credential", so if we are looking for Git in the current directory
  # first when running a credential helper, we will fail at that point
  # because our malicious Git will be found first.
  #
  # We prefer to check for this behavior during our "git-lfs pull" further
  # below when we are populating LFS objects into a clone of this repo
  # (which contains the malicious Git), so for now we remove the malicious
  # Git as soon as possible.
  #
  # As of Go 1.19 we also need to specify the GODEBUG environment variable
  # with a value of "execerrdot=0" in order to avoid occasional failures
  # our "git add" command below.  These failures occur due to a specific
  # set of conditions.  First, if the last-modified time of the .git/index
  # file is within a second of that of z.dat, the "git add" command will
  # refresh the Git index (assuming Git was compiled with USE_NSEC=0, as
  # appears to be the case for Git for Windows), and Git LFS will be invoked
  # to "clean" the z.dat file again.
  #
  # If that occurs, then when Git LFS runs it looks for Git, and until we
  # revise Git LFS to rely on Go's os/exec package to not execute programs
  # found in the current working directory (as described in
  # https://go.dev/blog/path-security), the os/exec package will detect our
  # malicious Git program in the current working directory and report an
  # error.  This occurs when Git LFS first initializes a new exec.Cmd
  # structure, even though Git LFS would then locate the true Git executable
  # from our custom PATH and reset the Path member of the Cmd structure
  # before trying to execute the program.
  #
  # Since we explicitly test Git LFS's avoidance of programs in the current
  # working directory using the "git-lfs pull" command further below, here
  # we just want "git add" to succeed, and so for the time being we disable
  # Go's new security checks for this command only.  We will revisit this
  # when we address the larger issue of re-adopting Go's own logic for
  # locating executable programs.
  cp "$BINPATH/lfstest-badpathcheck$X" "git$X"
  GODEBUG=execerrdot=0 \
    PATH="$BINPATH:$GITPATH:$SHELLPATH" "$GITPATH/git$X" add "git$X"
  rm "git$X"

  git commit -m "Add files"
  git push origin HEAD
  cd ..

  unset GIT_ASKPASS SSH_ASKPASS

  # When we call "git clone" below, it will run git-lfs as a smudge filter
  # during the post-clone checkout phase, and specifically will run git-lfs
  # in the newly cloned repository directory which contains a copy of our
  # malicious Git.  So, if we are looking for Git in the current directory
  # first in most cases (and not just when running a credential helper),
  # then when git-lfs runs "git config" we will fail at that point because
  # our malicious Git will be found first.  This occurs even if we specify
  # GIT_LFS_SKIP_SMUDGE=1 because git-lfs will still run "git config".
  #
  # We could ignore errors from clone_repo() and then search for the output
  # of our malicious Git in the t-path-credentials-2 directory; however,
  # this may be somewhat fragile as clone_repo() performs other steps such
  # as changing the current working directory to the new repo clone and
  # attempting to run "git config" there.
  #
  # Instead, since our key check of "git-lfs pull" below will also detect
  # the general failure case where we are looking for Git in the current
  # directory first when running most commands, we temporarily uninstall
  # Git LFS so no smudge filter will execute when "git clone" checks out the
  # repository.
  #
  # We also remove any "exploit" file potentially created by our malicious
  # Git in case it was run anywhere in clone_repo(), which may happen if
  # PATH contains the "." directory already.  Note that we reset PATH
  # to contain only the necessary directories in our key "git-lfs pull"
  # check below.
  git lfs uninstall
  clone_repo "$reponame" t-path-credentials-2
  rm -f exploit
  pushd ..
    git lfs install
  popd

  # As noted, if we are looking for Git in the current directory first
  # only when running a credential helper, then when this runs
  # "git credential", it will find our malicious Git in the current directory
  # and execute it.
  #
  # If we are looking for Git in the current directory first when running
  # most commands (and not just when running a credential helper), then this
  # will also find our malicious Git.  However, in this case it will find it
  # earlier when we try to run "git config" rather than later when we try
  # to run "git credential".
  #
  # We use a pipeline with "tee" here so as to avoid an early failure in the
  # case that our "git-lfs pull" command executes our malicious Git.
  # Unlike "git-lfs env" in the other tests, "git-lfs pull" will halt when
  # it does not receive the normal output from Git.  This in turn halts
  # our test due to our use of the "set -e" option, unless we terminate a
  # pipeline with successful command like "tee".
  PATH="$BINPATH:$GITPATH" PATHEXT="$X" "git-lfs$X" pull 2>&1 | tee output.log

  grep "exploit" output.log && false
  [ ! -f exploit ]
)
end_test

begin_test "does not look in current directory for wrong binary using PATHEXT"
(
  set -e

  # Windows is the only platform where Go searches for executable files
  # by appending file extensions from PATHEXT.
  [ "$IS_WINDOWS" -eq 0 ] && exit 0

  reponame="$(basename "$0" ".sh")-notfound"
  git init "$reponame"
  cd "$reponame"

  # Go on Windows always looks in the current directory first when creating
  # a command handler, so we need a dummy git.exe for it to find there since
  # we will restrict PATH to exclude the real Git when we run "git-lfs env"
  # below.  If our git-lfs incorrectly proceeds to run the command handler
  # despite not finding Git in PATH either, Go may then search for a file
  # named "." with any path extension from PATHEXT and execute that file
  # instead, so we create a malicious file named "..exe" to check this case.
  touch "git$X"
  cp "$BINPATH/lfstest-badpathcheck$X" ".$X"

  # This should always succeed, even if git-lfs is incorrectly searching for
  # executables in the current directory first, because the "git-lfs env"
  # command ignores all errors when it runs "git config".  So we should always
  # pass this step and then, if our malicious program was executed, detect
  # its output below.  If this command does fail, something else is wrong.
  PATH="$BINPATH" PATHEXT="$X" "git-lfs$X" env >output.log 2>&1

  grep "exploit" output.log && false
  [ ! -f exploit ]
)
end_test