1 pontos por GN⁺ 2024-02-24 | 1 comentários | Compartilhar no WhatsApp
  • Ao ativar o controlador xDCI oculto no ThinkPad X1 Carbon 6th Gen, o próprio notebook passou a conseguir emular dispositivos USB arbitrários, como teclado ou armazenamento
  • A pista era /sys/class/usb_role/intel_xhci_usb_sw-role-switch, mas apenas a troca de papel do xHCI não bastava; um UDC xDCI baseado em DWC3 também precisava ser exposto como dispositivo PCIe
  • A imagem da BIOS tinha a configuração xDCI Support, mas ela ficava oculta na interface padrão do Setup; após ativá-la, o lspci passou a mostrar o xDCI 8086:9d30 e o driver dwc3-pci
  • O método que funcionou foi desbloquear a página Advanced da BIOS e aplicar um patch na NVRAM; reconfigurar os registradores do PCH ficou limitado a algumas mudanças no PMC e falhou por causa do bloqueio do PSF
  • Depois de ativar o xDCI, g_mass_storage, Raw Gadget, reprodutores do syzkaller e o backend do Facedancer funcionaram sem hardware externo, mas foi preciso usar um cabo USB com VBUS desconectado e alternar para o papel device

A pista do modo dispositivo USB encontrada no ThinkPad

  • Raw Gadget é um módulo de kernel que fornece uma API de espaço de usuário para o subsistema USB Gadget do kernel Linux, e pode ser usado em fuzzing e exploração de hosts USB ao fornecer descritores USB malformados
  • PCs comuns normalmente não têm um USB Device Controller, ou UDC, algo mais comum em computadores de placa única como o Raspberry Pi
    • Trabalhos anteriores exigiam hardware externo, como um Raspberry Pi ou um EC3380-AB baseado em USB 3380
  • No ThinkPad X1 Carbon 6th Gen, foi encontrado intel_xhci_usb_sw-role-switch em /sys/class/usb_role
    • /sys/class/usb_role é a interface para alternar componentes USB de hardware entre os modos host e device
    • Mesmo escrevendo device no arquivo role, no início não apareciam mensagens no dmesg nem entradas em /sys/class/udc/
  • xHCI é o HCD que permite que sistemas x86 operem como host USB, e xDCI é o nome usado pela Intel para a interface do lado device do USB

Estrutura confirmada pelo código do kernel e pela mailing list

  • No kernel Linux, intel_xhci_usb_sw aparece em dois lugares
    • drivers/usb/roles/intel-xhci-usb-role-switch.c implementa o driver USB Role Switch que manipula registradores do xHCI quando device ou host é escrito
    • drivers/usb/host/xhci-ext-caps.c cria o dispositivo de plataforma virtual intel_xhci_usb_sw quando o driver xHCI define a quirk XHCI_INTEL_USB_ROLE_SW
  • O ID do dispositivo PCIe xHCI do ThinkPad era 0x9d2f, correspondendo a PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI no kernel
  • Discussões na mailing list do kernel Linux confirmaram que xHCI e o UDC DWC3 são dispositivos separados, com um mux entre eles
    • O role switch serve para trocar esse mux
    • Se o próprio UDC DWC3 não aparece como dispositivo PCI, então não há UDC disponível para usar em modo device
  • DWC3 é o IP DesignWare Core SuperSpeed USB 3.0 Controller da Synopsys, usado por vários fornecedores, incluindo a Intel
  • Ao verificar o lspci no ThinkPad, inicialmente só aparecia o xHCI 8086:9d2f, e não o ID de dispositivo xDCI 0x9d30 do Sunrise Point-LP, citado em dwc3-pci.c

Vestígios do xDCI no ACPI e na BIOS

  • No ACPI DSDT existiam Device (XDCI) e o método _STA
    • /sys/bus/acpi/devices/device:33/path era \\_SB_.PCI0.XDCI
    • /sys/bus/acpi/devices/device:33/status era 15, o que significa que o dispositivo estava enabled and functioning
    • Mas o xDCI não aparecia no lspci, então ele parecia normal apenas do ponto de vista do ACPI
  • Na tela padrão de Setup da BIOS não havia nenhuma configuração visível relacionada a xDCI ou OTG
  • Foi baixada a imagem de atualização Lenovo BIOS Bootable CD, extraído o binário da BIOS /mnt/bios/FLASH/N23ET86W/$0AN2300.FL1 e examinado com UEFITool
  • Ao procurar strings na PE32 image section do módulo Setup, apareceram os itens abaixo
    • xDCI Support
    • Enable/Disable xDCI (USB OTG Device).
  • Ou seja, a BIOS continha a configuração xDCI Support, mas ela ficava oculta na interface padrão

Ativando o xDCI pela página Advanced da BIOS

  • Seguindo a documentação do projeto x1c6-hackintosh, foi tentado o método para abrir a página Advanced da BIOS oculta
  • Esse método exigia regravar o chip SPI onde a BIOS estava armazenada
    • Em vez de usar um clipe SPI, o chip SPI foi trocado por um soquete para facilitar vários ciclos de remoção e reinserção durante os testes
    • Um módulo FTDI FT2232H Mini e o flashrom foram usados para fazer dump e regravar o conteúdo do chip SPI
  • O patch da BIOS era composto por duas etapas
    • Com UEFIPatch, a referência de GUID da página de BIOS Date/Time foi alterada para apontar para a página Advanced
    • Um patch de bytes separado forçava o TPM para MFG Mode, para que o Boot Guard não detectasse a modificação da BIOS
  • Na BIOS 1.37, aplicar só o primeiro patch fazia o notebook emitir bipes e não inicializar; depois de aplicar também o segundo patch, a página Advanced apareceu
  • Ao alterar xDCI Support para Enabled em Intel Advanced MenuPCI-IO ConfigurationUSB Configuration, o xDCI passou a aparecer após o boot
    • O lspci mostrava 00:14.1 USB controller [8086:9d30]
    • O kernel carregava automaticamente o driver dwc3-pci
    • Surgia dwc3.1.auto em /sys/class/udc/

Porta xDCI e requisitos do cabo

  • Para descobrir a qual porta USB externa o xDCI estava ligado, foi deixado o papel em device e conectado um pendrive em cada porta para testar
    • Em uma porta específica, o sistema operacional não detectava o pendrive, e ele voltava a funcionar quando o papel era restaurado para host
    • Essa porta foi considerada a porta ligada ao xDCI
  • Como a porta xDCI do ThinkPad era USB Type-A, foi necessário um cabo Type-A para Type-A macho-macho para conectar a um computador host
  • Como a troca de papel não desligava a alimentação VBUS da porta, foi usado um cabo com o VBUS interrompido para evitar que o VBUS dos dois dispositivos ficasse diretamente conectado
    • No início, foram cortados dois cabos USB 2.0 para montar manualmente um cabo macho-macho com o VBUS interrompido
    • Depois, foram usados um PortaPow USB Power Blocker e um cabo USB 2.0 macho-macho
  • Ao carregar o driver gadget g_zero, outro notebook reconheceu corretamente o dispositivo USB de teste emulado
  • Segundo a atualização de 11 de junho de 2024, o DataPro USB 3.0 Super-Speed A/A Debugging Cable também interrompe D- e D+ do USB 2.0, o que impede a conexão High-Speed necessária para estabelecer a conexão SuperSpeed
    • Um cabo LogiLink CU0038 com VBUS interrompido manualmente funcionou para emulação de dispositivos SuperSpeed

Tentativa de ativação por software com registradores do PCH

  • Para entender como a BIOS ativava o xDCI, foi examinado Pch/Library/Private/PeiPchInitLib/PchXdci.c no código-fonte da BIOS Kaby Lake
  • O caminho de desativação do xDCI na BIOS desabilitava o xDCI tanto no PMC quanto no PSF
  • Alteração no PMC

    • A ideia era limpar o bit #24 XDCI Function Disable no registrador NST_PG_FDIS_1 do PMC, offset 0x628, para desfazer o power gating do xDCI
    • Foi escrito um módulo de kernel reutilizando o mapeamento de pmc_core_device e pmc->regbase do kernel Linux
    • Logo após o boot, o bit ST_FDIS_LK #31 de ST_PG_FDIS1 estava definido, impedindo a alteração de NST_PG_FDIS_1
    • Depois de suspender e reativar o notebook, o lock bit já não estava definido, e foi possível limpar o bit XDCI Function Disable
  • Alteração no PSF

    • A BIOS desabilitava o xDCI definindo o bit USB Dual Role (OTG) Function Disable no PSF2 PCR do Port ID 0xBB, PID_PSF2
    • O endereço do PCR era calculado combinando PCH_PCR_BASE_ADDRESS 0xFD000000, o Port ID e o offset
    • O módulo de kernel mapeou o PSF2 PCR com ioremap, mas os valores lidos eram 0xffffffff e não mudavam
    • Mesmo revelando o P2SB, o resultado foi o mesmo
    • O RemoveSidebandAccess da BIOS isolava PID_PSF2 do IOSF-SB com EPMASK5 e travava isso com MASKLOCK, impedindo que o sistema operacional reconfigurasse o PSF
    • O acesso ao PCH até permitiu mudar parte das configurações do PMC, mas a falha na reconfiguração do PSF impediu que isso virasse um método de ativação do xDCI

Ativação por patch na NVRAM sem contornar o Boot Guard

  • Como os valores de configuração da BIOS são armazenados na área de NVRAM do chip SPI, foi tentada uma forma de alterar apenas xDCI Support sem abrir a página Advanced oculta
  • Foi usado IFRExtractor-RS para extrair as informações UEFI IFR do módulo Setup
  • A configuração xDCI Support ficava armazenada na variável UEFI PchSetup
    • GUID da VarStore: 4570B7F1-ADE8-4943-8DC3-406472842384
    • VarStoreId: 0x5
    • VarOffset: 0x40
    • Faixa de valores: 0x0 ou 0x1
  • Em efivarfs no Linux, foi verificado /sys/firmware/efi/efivars/PchSetup-4570b7f1-ade8-4943-8dc3-406472842384
    • Os 4 primeiros bytes do arquivo efivarfs são cabeçalho, então o valor real de xDCI Support fica no offset 0x44
    • Com o xDCI desativado, o valor era 00; após ativá-lo pela página Advanced da BIOS, mudava para 01
  • Ao tentar escrever diretamente essa variável do espaço de usuário ou da EFI Shell, surgia o erro EFI_WRITE_PROTECTED
    • Mesmo removendo o atributo immutable do efivarfs, aparecia o erro Read-only file system
    • Alterar pela EFI Shell com chipsec produzia o mesmo erro
  • Ao localizar no dump original da BIOS lido com o programador SPI a sequência de bytes próxima de PchSetup, alterar apenas o bit de xDCI Support para 01 e regravar, o xDCI foi ativado
  • Esse método ainda exige regravar o chip SPI, mas não precisa de patch da página Advanced nem de contorno do Boot Guard, e também não quebra o TPM
  • Na BIOS 1.37, depois do patch na NVRAM, a atualização para 1.61 preservou o valor da configuração da BIOS, sem necessidade de reaplicar o patch

Emulando dispositivos USB com xDCI

  • Todos os exemplos assumem que o xDCI está ativado na BIOS, que intel_xhci_usb_sw-role-switch foi alternado para device e que a porta xDCI está conectada a um host USB por um cabo com VBUS interrompido
  • Drivers gadget legados

    • Os drivers gadget legados do subsistema Linux USB Gadget emulam um dispositivo USB de uma classe específica quando o módulo é carregado
    • g_mass_storage expõe uma imagem de sistema de arquivos como um dispositivo USB de armazenamento em massa
    • Foi criada uma imagem FAT disk.img, colocado Hi from xDCI em file.txt e carregado sudo modprobe g_mass_storage file=./disk.img stall=0
    • No host USB, a unidade foi montada automaticamente e o conteúdo do arquivo pôde ser verificado
  • Raw Gadget

    • Raw Gadget foi executado com o xDCI para testar o exemplo de teclado USB
    • No início, ele travava durante o tratamento de SET_CONFIGURATION
    • A causa era que dwc3 e alguns drivers UDC presumem que podem reconhecer imediatamente uma control request de comprimento zero sem consultar o driver gadget
    • Após aplicar ao Raw Gadget o patch de contorno e também corrigir o GadgetFS, a emulação do teclado funcionou
    • O comportamento relacionado foi documentado no commit de patch de documentação

Uso com syzkaller e Facedancer

  • syzkaller pode usar o Raw Gadget para fazer fuzzing externo da pilha USB do kernel Linux
  • O fuzzing USB padrão do syzkaller usa Dummy HCD/UDC para conectar internamente um dispositivo USB virtual a um controlador host
  • Para usar o xDCI, o código do syzkaller que especifica o UDC foi alterado de dummy_udc para dwc3-gadget, dwc3.1.auto
  • Foi executado com syz-execprog -enable=usb um reproducer do bug WARNING in smsusb_start_streaming/usb_submit_urb encontrado pelo syzkaller
    • Em um notebook com kernel Ubuntu 5.15.0-91-generic atuando como host, o warning de usb_submit_urb foi reproduzido
    • O bug era um WARNING causado porque o driver smsusb não validava o tipo de endpoint USB, sem comportamento prejudicial
  • Facedancer é um framework em Python para emulação de dispositivos USB
    • O backend do Facedancer baseado em Raw Gadget ainda é um protótipo e depende de patches out-of-tree do Raw Gadget
    • Definindo BACKEND=rawgadget, RG_UDC_DRIVER=dwc3-gadget, RG_UDC_DEVICE=dwc3.1.auto e executando o exemplo rubber-ducky.py, tudo funcionou normalmente
  • O USB Proxy baseado em Raw Gadget também rodou com xDCI, e o uso do xDCI permite transformar o notebook em uma ferramenta de sniffer USB ou USB Man-in-the-Middle

Possibilidades em outros dispositivos e por software

  • Pode ser possível ativar xDCI em outros PCs além do ThinkPad X1 Carbon 6th Gen
    • No caso mais simples, pode bastar ativar o xDCI nas configurações da BIOS
    • Também é preciso haver suporte de ACPI e role switching, e a porta xDCI precisa estar ligada a uma porta externa
  • Outros ThinkPads também podem ter a configuração de BIOS do xDCI oculta e protegida, exigindo regravação do chip SPI
    • O método via NVRAM pode ser aplicado sem contornar o Boot Guard
    • Em alguns sistemas mais novos, o chip SPI pode ser protegido contra regravação por RPMC, e esses chips podem ter um R no nome, como W25R128FW
  • Segundo a atualização, Shiny Quagsire já ativou xDCI em um XPS 13 e em um Surface Go 1 alterando a variável PchSetup com grub-mod-setup_var
    • Nesses dispositivos, a variável PchSetup não era protegida, permitindo um método puramente por software
  • Outra abordagem por software pode ser explorar vulnerabilidades da BIOS para alterar variáveis de NVRAM relacionadas ao xDCI, ou atacar a BIOS por DMA durante o boot para executar código arbitrário
  • O “secret” do título se refere menos a algo ocultado de propósito e mais a um recurso não documentado

1 comentários

 
GN⁺ 2024-02-24
Opiniões no Hacker News
  • Sempre me perguntei por que não é comum usar um notebook como teclado e monitor universais para outros computadores.
    É estranho comprar um teclado e um monitor novos para fazer manutenção em máquinas headless, já que o notebook que você já tem poderia cumprir esse papel.

    • O GPD Pocket 3 tem um módulo KVM que faz exatamente isso: recebe entradas HDMI e USB-C e funciona como o monitor, teclado e mouse da máquina conectada: https://gpd.hk/gpdpocket3
      O tablet Minisforum V3, que deve sair em breve, também tem entrada de vídeo, então pode ser usado como monitor externo, mas parece que teclado e mouse não serão compartilhados.
      Seria ótimo se esse tipo de recurso se tornasse mais comum.
    • Exato. Um amigo meu montou um cyberdeck só para isso.
      Dá para alternar o mouse e o teclado para uma porta externa e conectá-los por cabo a outra máquina; na lateral há duas portas HDMI, uma de saída e uma de entrada.
      Com uma chave interna, dá para escolher se a fonte do monitor será a própria máquina do deck ou a entrada externa.
    • Como alguém que lida muito com máquinas headless, a melhor solução que encontrei para usar um notebook como monitor foi este produto: https://www.amazon.com/dp/B0BJ2YDV7Q
      Basicamente, é um chip que captura o sinal HDMI da máquina headless e o expõe ao notebook via USB como uma webcam emulada; os componentes eletrônicos são pequenos o suficiente para caber dentro do cabo.
      Para o teclado, não encontrei nenhuma boa solução além de usar um teclado sem fio padrão e, para as raras vezes em que preciso de mouse, comprar um com trackpad embutido.
    • Sempre achei inconveniente não poder simplesmente conectar um computador a outro.
      Pelo que ouvi, o USB 4 tem um modo que possibilita isso.
    • Com o hidclient, um notebook pode fingir ser um teclado e mouse Bluetooth.
      https://web.archive.org/web/20180610141436/http://anselm.hoffmeister.be/computer/hidclient/index.html.en
      https://github.com/benizi/hidclient
  • Isso me lembra um caso de alguns anos atrás envolvendo algo que Travis Goodspeed fez.
    Uma smart TV recebia atualizações de firmware por arquivos em um pendrive USB comum; a TV lia o arquivo do começo ao fim para verificar a assinatura digital e depois o lia novamente para fazer a atualização.
    Um dispositivo fingindo ser armazenamento USB poderia enviar o arquivo de firmware do fabricante na primeira leitura e, na segunda, enviar um firmware não oficial.

  • Muito interessante. Venho pesquisando opções parecidas que seriam viáveis.
    Tenho um servidor NAS potente para o qual fico procurando desculpas de uso, e também tenho conexões de 40G e 10G espalhadas pela casa.
    O PS5 e o XBox usam discos rígidos USB como armazenamento adicional.
    Fui ver se seria possível expor o armazenamento do NAS ao PS5 e ao XBox, e é possível. Basta montar um compartilhamento do NAS via iSCSI ou NFS e então, com o módulo g_mass_storage, emular um dispositivo de armazenamento USB e expor esse armazenamento ao host USB.
    No momento, além de tempo e custo, o grande obstáculo é a largura de banda que esse sistema entregaria. O ganho não é tão grande.
    O Raspberry Pi é conhecido por suportar USB-OTG, mas fica limitado à velocidade do USB 2.0, e a maioria dos outros SBCs da mesma categoria que encontrei também era assim. A exceção foi o RockPi4; como o RockPi tem uma porta Ethernet gigabit, saturando a Ethernet seria possível fornecer velocidades estáveis de HDD tanto para o PS5 quanto para o Xbox.
    Seria bem interessante criar uma solução capaz de passar de 1GbE usando uma placa de rede PCIe customizada ou uma placa ExpressCard, porque aí daria para saturar de verdade uma interface USB 3.0.

    • Tenho uma configuração de homelab parecida e estou experimentando USB sobre IP com https://www.virtualhere.com.
      Ainda é preciso um cliente para se comunicar com o servidor, mas parece funcionar bem.
      Estou pensando em manter um “cliente USB leve” na mesa, conectar os dispositivos USB nele e então anexá-los ou alterná-los para qualquer host físico ou host de VM desejado no laboratório.
    • Fiquei curioso sobre como o PS5 e o NAS seriam conectados fisicamente.
      Imagino que seja NAS → SBC → PS5; ou seria conectar o NAS diretamente ao PS5 com um cabo USB?
      Também queria saber como fica a execução de conteúdo no PS5. Pelo que entendo, o NVMe interno precisa atender a uma determinada velocidade; isso não se aplica a unidades externas?
    • O Raspberry Pi 5 tem uma porta Ethernet gigabit, e outros SBCs, como a linha NanoPi da FriendlyElec, também têm.
      Na verdade, o NanoPi R6S tem, além de uma porta gigabit, duas portas Ethernet 2.5G.
  • Obrigado pelo texto tão detalhado. A qualidade é ótima.
    O fato de isso ter sido possível me dá esperança de que talvez dê para montar algo como Synergy / Mouse Without Borders sem rede, usando apenas um cabo USB modificado.
    Para quem precisa controlar vários computadores ao mesmo tempo, é um caminho muito bom.

    • Existem switches KVM, e também dispositivos que alternam só teclado e mouse USB, sem vídeo.
      Ainda assim, a abordagem proposta parece interessante.
  • Recentemente pesquisei algo parecido: eu queria conectar duas máquinas USB-C com um único cabo USB-C para ter comunicação a 10 Gbps
    Infelizmente, a maioria das placas Ryzen ainda não tem Thunderbolt, e com Thunderbolt isso seria suportado nativamente com facilidade
    Controladores USB de “dupla função” também eram bem raros antes de USB 3.2/4.0 e, como abordado neste texto, mesmo quando era possível, muitas vezes não havia suporte
    Parece completamente estranho que seja tão difícil colocar duas máquinas em rede usando um cabo USB-C sem Thunderbolt
    Também gostei da parte em que o bit de bloqueio é liberado depois da suspensão. Esse tipo de problema é muito comum. Por exemplo, é um método muito comum também para tirar dispositivos SATA de um estado que impede o secure erase

  • A profundidade é impressionante
    Poder usar um PC como dispositivo USB abre muitas possibilidades interessantes
    É um pouco lamentável que a opção xDCI necessária exista no hardware deste dispositivo, mas não esteja exposta, e que seja preciso hackear o firmware para acessá-la
    O hardware permite, mas o fabricante simplesmente deixou desativado e bloqueou os controles

  • Fico me perguntando se a Lenovo usa isso como um caminho para depurar um ThinkPad a partir de outro ThinkPad
    https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/secure-coding/intel-debug-technology.html
    Atualização: provavelmente sim
    https://www.youtube.com/watch?v=ExUvQa_jB7c
    É um vídeo sobre depuração de firmware Intel com DCI e USB 3.0

  • A parte “Mas lidar com o Raspberry Pi é trabalhoso: conectar cabos, inicializar a placa, acessar o shell etc.” provavelmente poderia ter sido resolvida configurando USB OTG
    Daria para fazer o dispositivo conectado fornecer energia, abrir o shell e então tratar tudo com um script
    Assim seria possível configurar montagens ou executar comandos via SSH, e provavelmente até montar o dispositivo
    Ainda assim, recomendo a tentativa de entender o próprio dispositivo e ler o kernel. É algo em que quero continuar melhorando, com aquela ideia de que nós mesmos devemos consertar nossos equipamentos

    • O ponto central desse experimento é justamente lidar com USB OTG, e o Pi tem só uma porta OTG, então é preciso outro método para acessá-lo
      Eu mesmo já fiz isso
  • Isso me lembrou muito a cultura hacker do início da internet, a ponto de bater uma nostalgia. É um texto realmente excelente
    Quando ESR falava sobre se tornar um hacker, sobre hackers no sentido de Hacker News, ele se referia exatamente a esse tipo de pessoa
    A combinação de curiosidade, persistência e competência técnica é extremamente rara e muito impressionante

  • Tenho um sistema baseado em Celeron N3350/Pentium N4200/Atom E3900, e o BIOS expõe uma chave xDCI
    Ao ativá-la, aparecem algumas outras opções e, ao inicializar no Linux, vejo o dispositivo PCI USB controller: Intel Corporation Device 5aaa (rev 0b)
    Mas o nó de dispositivo UDC não aparece. Acho que vou investigar um pouco mais com as pistas do texto original