Git: Malicious repositories can execute remote code while cloning
openwall.com
openwall.com
https://github.com/gitster/git/commit/684dd4c2b414bcf648505e...
(Surprise, the root cause is a cache)
> There are only two hard things in Computer Science...
You write it on local media and kept it on-premises?
Cloud is the new thing, I hear.
I see it with HAND "have a nice day" too
I see that's wrong, but ignorance has made the internet seem just that little bit warmer all these years!
three most difficult things in CS:
2) Naming Things
1) Cache Invalidation
4) off by one errors
3) Concurrency
There are only two hard problems in distributed systems:
2. Exactly-once delivery
1. Guaranteed order of messages
2. Exactly-once delivery
0) naming things
1) cache invalidation
42) asynchronous callbacks
2) off by one errors
what that you?
on edit: I'm going to let that 'what that you' stand because one of the hardest things about HN posts is grammatical correctitude.
And the problem with state is that you have to make sure all your state transitions don't cause bugs. What we know as a "cache" is essentially creating new state representing existing state, with all new transitions...
I know this is overly philosophical, and in practical scenarios we readily (although not always unambiguously) differentiate between "cache" and "state", but the point about transitions and that being a major source of bugs still stands.
If you want to unify state and cache, you might want to go down a different route:
Think of log based filesystems (or a log based data base).
Instead of defining your operations in terms of state, you define them as pure functions of the log.
So your log is full of operations. Writing just means appending a symbolic operation like `write(key, value)` to your log.
And you define the result of `read(key)`: scan backwards through the log until you hit the last instance of `write(x, value)` and the return that `value`.
Now state means: compact your log by replacing a swath of `write` log entries with one big `snapshot` operation that encompasses many key/value pairs.
Alternatively, you can also define state to mean caching your `read` operations.
In this approach, it's no coincidence that the log is a data structure that has a linear shape: the evolution of state over time is also linear.
(With some cleverness you can replace the linear structure with eg a DAG; and then also think about how you merge divergent states.)
On the other hand, the whole point of normalization in databases is to avoid redundancy and have "single source of truth".
I find the concepts of normalization and denormalization applicable and helpful outside databases as well, though a different terminology is often used.
(1) cache invalidation
(2) off-by-one errors
1) naming
2) cache invalidation
...
3) off-by-one errors
Alas.
- a good name should be descriptive
- avoid being overly clever, call a spade a spade
- don't optimise for generalisation, naming things is a time to be specific
- aim for short but not at the expense of losing context
- avoid redundancy in naming of things nearby, leverage spatial context
- avoid qualifiers or type information where possible - type should be obvious from context and use, if it's not qualify or refactor
Anything else?
1) naming
4) concurr2) cache invalidationency
...
3) off-by-one errors
0) Race consegmentation fault (core dumped)
(I know I was ninja’d but didn’t see until after)
The formulation of this joke I tend to see is,
The two hardest problems in programming:
(1) cache invalidation
(2) appropriately naming things
(3) off-by-one errors
(Cache invalidation is essentially the same problem as managing mutable state -- "Out of the Tar Pit" frames mutable state as either essential or incidental, the latter being rederivable in principle from essential state. Incidental mutable state is no more and no less than a cache, and usually one with an informal and undocumented invalidation policy.)
(And naming things has a very real technical counterpart in addressing, which comes up obviously in networking, but you can also see its shadows in quite a lot of concerns around architecture and modularity.)
(1) cache invalidation
(3) off-by-one errors
(2) appropriately naming things
(4) parallel execution [leading to race conditions / ordering bugs]
1) Naming 3) Cache Invalidation 2) Off-by-one errors 3) In-order once-only delivery of distributed messages
And an almost fanatical devotion to the Pope
1) naming
4) concurr2) cache invalidation
ency
3) off-by-one errors
1) Those who number lists starting at 1.
1) Those who number lists starting at 0.
2.5) Stan Kelly-Bootle, who proposed a compromise.For the simplest example: compare the old C-style for loop vs a Python style for-each loop.
Oh, and slicing. I will never get Python slicing right the first time. The fact that the range is [begin, end) is just never the way I expect it to work.
In [0, len) ')' means less than. As in 0≤ x< len.
Along the same lines a lot of GPU programming tutorials warn of inconsistencies between threads and it has never been a problem since I just assume I cannot rely on consistency or order of execution, seeing each thread as separate and independent.
1. Make it work
2. Make it right
3. Make it fast (make sure you need to) a.k.a. optimize
4. Make it scale
Couldn’t it just as well be attributed to improper file path normalization? If we had only lower case ASCII file systems it would not have caused a problem.
Have you seen the mayhem that some of mine cause when you clone them and then type ./configure && make, like you have been socially engineered into doing?
I don't personally audit all the code I run, but I hope someone is doing it. That being said, source code being public is much better than the alternative of just downloading binaries from who knows where.
I don't trust anything absolutely, and I don't see a way past it.
1. Download container description (Dockerfile)
2. Upon image build it "compiles things" (e.g. processes/assembles javascript)
3. Build fails, because it pulls architecture incompatible library (or does not pull architecture mandated library)
4. Fix build scripts, rebuild container image
5. Verify container
6. Pull repo
7. Reproduce changes, commit
8. Push
Nothing apart clone-edit-push happens on the repo. The code can be executed on a remote, hardened, isolated system. With proliferation of containers I guess this scenario will become more and more common among ops people.It's huge in the context of non-programming uses of Git. If some people are just sharing some text documents with Git, then it's a big deal.
This is likely on the rise.
E.g. if you look at a site like Github, there is a lot of non-code content in it. Some people stash that content, and other people believe that content to just be harmless files that will never perpetrate an exploit just from being cloned.
If ./configure is checked in as part of the official repository of a moderately well known project, I doubt any committer would be stupid enough to insert a backdoor into ./configure or the Makefiles.
What can happen if an apostate project is not on GitHub: Some (usually several) faithful persons decide to correct the situation and put multiple unofficial mirrors on GitHub, and other faithful people clone from a random one of these.
In that case however, they get what they deserve.
What is the simple test for whether this is the case or not?
Is this a default-on scenario?
No, LFS is something you would have to explicitly enable, however it is pretty common to do so if you want to store binary blobs in Git.
As suggested in GitHub's announcement post[1], you can test this with the following:
`git config --show-scope --get-regexp 'filter\..*\.process'` (replace the single quotes by double quotes on Windows Command Prompt)
> Is this a default-on scenario?
On Windows yes, because Git-for-Windows configures Git LFS by default.
[1]: https://github.blog/2021-03-09-git-clone-vulnerability-annou...
Short version: there is no exact equivalent for POSIX symlinks on Windows, and the closest thing is unavailable for non-admins by default unless Developer Mode is enabled and a relatively recent Windows 10 version is used. Therefore, symlink emulation support is only turned on by default when that scenario is detected.
[1]: https://github.com/git-for-windows/git/wiki/Symbolic-Links
More independent projects sometimes does it to, for example curl.
What kind of platforms use case-insensitive filesystems?
EDIT: This document describes some of the challenges: https://www.usenix.org/legacy/publications/library/proceedin...
Nah. Using a file system means putting up with its semantics. HFS+ was case-insensitive; they were deploying an upgrade to millions of existing filesystems.
If you mount, say, an NFS volume, MacOS does the expected thing.
IIRC, one place where it does rear its head in when a file is renamed in a git commit to a value that downcases to the same value as the prior name. For example `Foo.txt`->`foo.txt`.
I have `core.ignorecase = true` in my `.gitconfig` for this very reason.
The Linux kernel is one of them: several of the headers that get installed to /usr/include/linux/netfilter have conflicts on a case-insensitive filesystem. https://github.com/torvalds/linux/tree/master/include/uapi/l...
Debhelper used to be one of those until I convinced them to change it: they had a Debian/ directory for the Perl module Debian::Debhelper as well as a debian/ directory for the packaging metadata. https://bugs.debian.org/873043
(I suspect I'm a little unusual in wanting to have checkouts of Linux and Debhelper on my Mac homedir.)
Why would you introduce complexity into the filesystem to try to normalize file names when you can simply, not? I mean, have you _seen_ the mess that is Unicode normalization? Hundreds of different glyphs or whatever that are all considered equivalent, but are actually composed of different bytes. The filesystem should try to make sense of all that, and consider them equivalent paths?
Even if you say "well, just capitalization, not Unicode normalization," there's the whole German letter ẞ => ss (or is it ß?) and similar friends like the Turkish dotted I that have popped up as articles on HN. Absolutely glad Linux filesystems by and large do not attempt to take that on, and treat paths as a bucket of bytes instead.
All for what benefit - so you can type File.txt in the terminal and have the OS find file.txt? That is much more appropriate for the Application layer to resolve, rather than the filesystem.
Bugs like this come from over-engineering. Filesystems should be simple, and follow the principle of least surprise.
Wait I'll try
...nope, after `touch makefile` and `touch Makefile`, I still see just `makefile`. OK
Many fun times trying to copy/move/remove a file and not being able to do so because the input and name stored on fs is actually different bytewise...
Seems like linux has the only sane filesystems not trying to mangle paths at all.
In the end, the file system doesn’t exist in isolation, it is there to support users, and most of them won’t care how many bytes “é” takes to store.
Unix, by not even defining the way to interpret the bytes of file names (one can’t even assume that names consisting of only bytes that correspond to ASCII letters and digits should be interpreted as ASCII) makes it impossible to show file names to users. That’s insane.
At the FS layer, I think that's better. Makes things simpler for programs. For non-techie humans, unicode can be normalized at upper levels, like the GUI file manager or toolkit library that does save dialogs, etc.
That's if humans being confused because of lack of normalization of unicode is a real practical issue and not just something that can happen but never does.
This has mostly been useful for working on shared repositories where, say, a Linux user (or other user on a case-sensitive filesystem) pushes two branches, say `feature/foo` and `feature/Foo` which works fine for them, but on a case-insensitive filesystem, git gets very upset.
Leaky abstractions all the way down.
What idiot would name files like that? I said.
You, they answered.
I don't do that sort of thing anymore.
A related bug is a developer will make a webpage called /a/ but link to /A/ and then the link will be broken in production. At this point, I have seen this same bug enough times to be able to fix it reasonably quickly, but it definitely wastes time for the team.
Starting with Windows 10, the aforementioned key still works, but there's also a per-directory case-sensitive flag that forces all DOS and Windows programs to have case-sensitive operations unconditionally. This is made to great effect in both WSL1 and Cygwin.
Its a notable problem with git + Windows that has gotten better over time but still leads to a lot of WTF moments. For many this event is the first time they hear that window's filesystem is case insensitive.
https://www.collabora.com/news-and-blog/blog/2020/08/27/usin...
Note: I also learned this today. Had no clue.
$ sudo git clone ...
How is it getting my root password?
Also, once they get ACE they can modify your bashrc to make sudo an alias for “sudo rm -rf / ;” or all sorts of other evil trickery.
Edit: although I am struggling to think of one that clones from an untrusted source.
Uh... really? Like what?
After installing git via brew and removing the one in CommandLineTools, /usr/bin/git is showing the latest version.
me@local % git --version
git version 2.30.2
I don't know if this is recommended or if it will have negative consequences that i don't know about, but it seemed like the way I could accomplish it. Given that /usr/bin/git is working with the homebrew installed git, I'm hopeful that everything will be good.FTFY
Can we get the title changed to "on macOS and Windows?"
I was worried for a second, but this is meaningless.
I've actually thought about converting my whole $HOME to that way, but I do have a few files that would conflict if I did that. I honestly don't think it's that bad of an idea.
There's also the possibility of a git repo on a SMB share. That's not a use case I have, but it's not too difficult to imagine in a corporate environment.
Case sensitivity is one of the things that really bothers me on Linux, it causes me to make mistakes for no reason. If I ever really switched to Linux full-time, I’d probably want to change that.
Case sensitivity wasn’t designed; the first Unix couldn’t spare the CPU cycles to do case insensitive matching, which was the norm at the time.
Don't you think that 65 ≠ 97 is sufficient of a reason?..
I mean, 'A' ≠ 'a', in ASCII, Unicode and even EBCDIC. In computers, those are two distinct characters. This fact won't change no matter how you rationalize your expectations.
Thus, pretending that "y.txt" is the same as "Y.txt" is an elaborate lie. Even acknowledging that it's a "white", well-intentioned lie (designed to preserve the mistaken expectation that "y.txt" is the same as "Y.txt") — I don't like when computers lie to me; do you?
As every lie, this one has weird consequences. One of them is the today's RCE in OP. Another one was CVE-2014-9390. Myriads others.
Linux rejects the whole notion of filename case-insensitivity, and demonstrates how computers actually work. It becomes easier on developers and more secure on users.
Lastly, don't feel that I'm attacking you; I'm opposing an idea. So, here's a tip: you can set up case-insensitive filename completion in bash, so that TAB will correct your casing mistakes for you. It's a simply one-line change involving putting `set completion-ignore-case on` into an inputrc.
In computers yes, but i am a human and to me as a human 'A' and 'a' are the same letter.
We do have the options to ignore the fact and say "What bytes? I don't care. Guess what I mean, and lie to me as well as you can so I can stay happy in my ignorance" — but, see, coordinating good support for that isn't easy. Minor wrinkles in it continue causing burns, sometimes RCEs. Maybe "doing in Rome as Romans do" isn't such a bad advice after all?
It's almost comical: people continue insisting on "letters not code points" knowing very well how computers are bad with guesswork and under-defined notions. Issues stemming from that keep coming up. What if, instead, the norm accepted that 'A' ≠ 'a' and stopped creating problems which computers are known to deal poorly with?
65 ≠ 'A'.
Of course, you should still update because you're a config change away from being vulnerable, but GP's point of it not being a big deal if (and only if, don't know if that's correct) nobody uses it stands.
Seems like a weird edge case to me. I guess Apple and Microsoft should push out OS updates to cover it.
That said, I did snicker at the comment :) I had no idea it was that old.
The "junctions" are unusable as "windows symlinks".
For one, you can't create any as a normal non-admin user without specific authorization by default.
I don't think this is true at all. Too many things will break if you turn this on.
Anyway, what I'm actually thinking when something like this is disclosed is how many more similar things must be known to a team of malicious professionals at Unit 8200 or whatever. I don't think I would reasonably suspect "git clone" being capable of something like that. How many more things I don't suspect to be dangerous actually are? It feels almost pointless to worry about it.
That isn't correct; it's a bug that manifests on case-insensitive[1] filesystems.
My colleagues who run a linux VM (our product targets Linux only) tend to git-clone onto a mounted NTFS partition so they can access the source from both host and guest. This bug will affect them even though they are running on Linux.
My other colleagues who run an actual Linux box tend to use a fast removable drive to git-clone (so they can work on it from home), and said drives tend to be FAT, which will also be susceptible to this bug.
If, on the third hand, you're running Windows and using ext4 as a filesystem (removable drive, mounted partition, whatever), then this bug should not affect you.
TLDR; the OS doesn't matter, the filesystem does.
[1] They aren't, not really; NTFS is case-sensitive! It preserves the case when writing filenames and ignores it when reading filenames.