
Technical breakdown of CVE-2026-3854, a GitHub RCE via header injection in git push, explaining the vulnerability, exploitation technique, and mitigation.
Resumo prático de como funciona a vulnerabilidade descoberta pela Wiz Research no pipeline interno de git push do GitHub.com e GitHub Enterprise Server.
Esse material é pra fins educacionais e de pesquisa em segurança ofensiva. A vulnerabilidade já foi corrigida pela GitHub em todas as versões suportadas. Não tente reproduzir contra ambientes que não sejam seus ou que você não tenha autorização explícita por escrito pra testar. Acesso não autorizado a sistemas é crime no Brasil (Lei 12.737/2012, conhecida como Lei Carolina Dieckmann) e pode configurar invasão de dispositivo informático com pena de detenção.
Atacante autenticado consegue RCE no GitHub mandando ; em git push -o. O babeld (proxy interno do github, ponto de entrada de todo SSH) não higieniza, e o ; quebra o header interno X-Stat, sobrescrevendo campos de segurança que o gitrpcd confia cegamente. Com 3 sobrescritas (rails_env, custom_hooks_dir, repo_pre_receive_hooks), o pre-receive hook concatena e executa um binário arbitrário do servidor como user git.
Basicamente conseguimos escrever no header porque não há a higienização do no , e fazemos isso apontando pra um path do servidor alvo, exemplo , e aí conseguimos concatenar com outro "campo" desse header, o . Com isso, se esse campo for , no concat vai ficar + e vai executar diretamente no servidor.
X-Stat;git push/binrepo_pre_receive_hookswhoami/bin/whoamiSó que por default no X-Stat ele já vem esses campos tudo por padrão já preenchido pelo babeld, pois o RPC (gitrpcd) confia cegamente nele pra ler, pois acredita que o user não tem como alterar:
Abaixo é um exemplo de como o babeld preenche os campos no X-Stats:
rails_env=production;
user_id=int:42531;
user_login=paulo.werneck;
repo_id=int:8821;
repo_path=/data/repositories/a/b/cd/ef/12/8821.git;
operator_mode=bool:false;
user_operator_mode=bool:false;
custom_hooks_dir=/data/user/git-hooks;
repo_pre_receive_hooks=[{"id":1,"script":"validate-commit.sh","enforcement":"required"}];
large_blob_rejection_enabled=bool:true;
max_blob_size=int:104857600;
reject_sha_like_refs=bool:true;
push_option_count=int:0
Só que por causa dele não higienizar o ; durante o git push, você consegue escrever diretamente nele, e pra fechar com chave de ouro ele é last-write-wins, a última escrita é o que vale. Então se eu fizer:
git push -o "x;rails_env=production" -o "x;custom_hooks_dir=/bin" -o "x;repo_pre_receive_hooks=[{\"script\":\"whoami\"}]"
Os campos sobrescritos viram:
custom_hooks_dir=/bin
repo_pre_receive_hooks=whoami
(Não é simples assim, igual no exemplo acima de só jogar o whoami. Ele na verdade é um JSON, então ficaria algo mais parecido de [{"script":"whoami"}].)
E aí, numa versão vulnerável, ele vai executar o binário whoami no servidor.
Só que só isso ele ainda vai rodar apenas no sandbox. Por isso é importante alterar mais um parâmetro no header X-Stats, o rails_env=production. Ele precisa ser qualquer coisa sem ser production.
Por fim, o script malicioso ficaria assim:
git push -o "x;rails_env=development" -o "x;custom_hooks_dir=/bin" -o "x;repo_pre_receive_hooks=[{\"script\":\"whoami\"}]"