flawopen.com/Teardowns/CVE-2024-32002
Imagine a courier delivering an internal company package to 'Room 101'. An attacker places a sign on door 101 saying 'Room 101 has moved to the Executive Safe'. Instead of checking if that sign is legitimate, the courier blindly follows the arrow into the safe and drops off a bomb. In Git, the courier was the submodule cloner, the deceptive sign was a symlink, and the safe was the hidden .git/hooks/ directory where executable scripts automatically trigger.
git clone --recursive <url>In builtin/submodule--helper.c and dir.c, Git initialized the submodule worktree without verifying whether any parent directory element along the clone path was an existing symlink pointing outside the working tree.
// Git clone created submodule directory blindly
static int clone_submodule(const struct module_clone_data *clone_data)
{
struct strbuf sb = STRBUF_INIT;
// BUG: clone_data->path could traverse an existing symlink
strbuf_addf(&sb, "%s", clone_data->path);
safe_create_leading_directories(sb.buf);
return do_clone(clone_data);
}
// Fixed: Refuse to clone into or through symlinked path components
static int clone_submodule(const struct module_clone_data *clone_data)
{
struct strbuf sb = STRBUF_INIT;
// FIX: Validate that no parent component is a symlink
if (path_has_symlinks(clone_data->path))
die(_("fatal: submodule path '%s' contains a symlink"),
clone_data->path);
strbuf_addf(&sb, "%s", clone_data->path);
return do_clone(clone_data);
}
sub (or case-variant sUb) that points to .git/modules/sub (the internal Git metadata store).SUB/hooks, the operating system resolved SUB through the symlink sub directly into .git/modules/sub/hooks/.post-checkout inside the hooks directory. As part of completing the clone, Git immediately fired the hook, executing the attacker's arbitrary shell commands with the user's permissions.Path/A and path/a collide at the OS filesystem layer even if your memory hashmap considers them distinct keys.parent + '/' + child) is unsafe unless you verify that no intermediate directory in the chain is an alias (using lstat or O_NOFOLLOW)..git/ remain strictly inviolable.