Skip to content

Commit e105b5c

Browse files
committed
fix(git-checkout): fix a critical security issue reported by @drivertom
the security issue would lead to arbitrary file writing on the user's machine, which could be extremely dangerous when some critical file be overwritten (eg: the crontab file, ssh-keys) The root cause of this issue is that the .git folder leakage is an abnormal scene of manipulating the git repository. When we executing `git clone`, there are multiple steps to do. 1. download the internal objects into the `.git` folder and **CHECK the VALIDITY** of all object files. 2. perform `checkout` to extract the current content of the `.git` folder. git will completely *TRUST* the object files of the .git folder cause they are already be checked. But when hackers use `GitHacker` (or other similar tools like git-dumper) to exploit the `.git` folder leakage vulnerability. These tools will just simply download the `.git` folder directly, then perform `git checkout .`. The critical thing is that the developer of these tools just trusts the `git checkout .` will do the check, but unfortunately, `git checkout .` doesn't. git assume that before checking out, the .git folder is already been verified. To **fix** this issue, I just use `git clone instead of `git checkout .` (`git clone` is able to clone a folder on the file system, ref: https://git-scm.com/docs/git-clone#_git_urls). It's a simple but dirty fix. The best way to fix this issue is to let git verify the .git folder when performing `checkout`, but it seems that is not possible. :( To **summarise**, git does the right thing, the developer does the right thing too. All the evil thing comes from the weird scenario of git leakage.
1 parent ecc1f2c commit e105b5c

File tree

1 file changed

+18
-10
lines changed

1 file changed

+18
-10
lines changed

GitHacker/__init__.py

+18-10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
import subprocess
1010
import argparse
1111
import bs4
12+
import tempfile
13+
import shutil
14+
1215

1316
__version__ = "1.0.10"
1417

@@ -24,7 +27,8 @@ class GitHacker():
2427
def __init__(self, url, dst, threads=0x08, brute=True) -> None:
2528
self.q = queue.Queue()
2629
self.url = url
27-
self.dst = dst
30+
self.dst = tempfile.mkdtemp()
31+
self.real_dst = dst
2832
self.repo = None
2933
self.thread_number = threads
3034
self.max_semanic_version = 10
@@ -63,7 +67,7 @@ def directory_listing_enabled(self):
6367
def sighted(self):
6468
self.add_folder(self.url, ".git/")
6569
self.q.join()
66-
self.checkout()
70+
self.git_clone()
6771

6872
def add_folder(self, base_url, folder):
6973
url = "{}{}".format(base_url, folder)
@@ -103,7 +107,6 @@ def blind(self):
103107
content = "{}".format(subprocess.run(
104108
['git', "fsck"],
105109
stdout=subprocess.PIPE,
106-
stderr=subprocess.PIPE,
107110
cwd=self.dst,
108111
))
109112
tn = self.add_hashes_parsed(content)
@@ -112,18 +115,23 @@ def blind(self):
112115
else:
113116
break
114117

115-
self.checkout()
118+
self.git_clone()
116119

117-
def checkout(self):
118-
logging.info("Checkout files...")
119-
subprocess.run(
120-
["git", "checkout", "."],
120+
def git_clone(self):
121+
logging.info("Cloning downloaded repo from {} to {}".format(self.dst, self.real_dst))
122+
result = subprocess.run(
123+
["git", "clone", self.dst, self.real_dst],
121124
stdout=subprocess.PIPE,
122125
stderr=subprocess.PIPE,
123-
cwd=self.dst
124126
)
127+
if b"invalid path" in result.stderr:
128+
logging.info("Remote repo is downloaded into {}".format(self.real_dst))
129+
logging.error("Be careful to checkout the source code, cause the target repo may be a honey pot.")
130+
logging.error("FYI: https://drivertom.blogspot.com/2021/08/git.html")
131+
else:
132+
logging.info("Check it out: {}".format(self.real_dst))
133+
shutil.rmtree(self.dst)
125134

126-
logging.info("Check it out in folder: {}".format(self.dst))
127135

128136
def add_hashes_parsed(self, content):
129137
hashes = re.findall(r"([a-f\d]{40})", content)

0 commit comments

Comments
 (0)