Skip to content

n_gsm vulnerabilities (CVE-2024-36016)

Linux n_gsm receive path'inde bir out-of-bounds write: GSM 0710 mux'unu frame'in ortasında basic ve advanced option mode arasında geçirmek gsm->state/gsm->len değerlerini stale bırakır, böylece gsm0_receive() gsm->buf'ın ötesine yazmaya devam eder.

Mechanism

Note

n_gsm (drivers/tty/n_gsm.c) gelen bir byte stream'inin GSM 07.10 framing'ini decode eder. İki framing varyantını destekler:

  • Basic option mode — bir length field kullanır; driver kaç payload byte'ının kaldığını gsm->len içinde, ne kadarını kopyaladığını ise gsm->count içinde takip eder ve sabit buffer gsm->buf'a biriktirir.
  • Advanced option mode — framing farklı şekilde sınırlandırılır ve kopyayı bound'lamak için gsm->len kullanılmaz.

gsmld_receive_buf() byte'ları mode'a özgü receiver'a besler (basic için gsm0_receive(), advanced için gsm1_receive()). Receiver'lar gsm->state tarafından sürülen, byte-byte ilerleyen bir state machine'dir.

Bug, bir mode reconfiguration sırasında ortaya çıkan bir out-of-bounds write'tır. Protocol peer, bir frame uçuştayken mux'un option mode'unu değiştirebilir ve bu reconfiguration gsm->state veya gsm->len'i reset etmez. CVE açıklaması tam sırayı şöyle anlatır:

  1. A tarafı n_gsm'i basic option mode'da configure eder.
  2. B tarafı data length 1 ilan eden bir basic-mode frame header'ı gönderir.
  3. A tarafı advanced option mode'a geçer.
  4. B tarafı 2 data byte gönderir — bu gsm->len'i aşar ama advanced mode'da gsm->len'e bakılmadığı için byte'lar kabul edilir.
  5. A tarafı tekrar basic option mode'a geçer.
  6. B tarafı data göndermeye devam eder. Ne gsm->state ne de gsm->len reset edilmediği için gsm0_receive() gsm->buf'ın ötesine yazar.

İhlal edilen invariant: "framing mode her yeniden configure edildiğinde, receive state machine'in length ve state sayaçları reset edilmelidir." Cross-mode reconfiguration, basic-mode bookkeeping'ini (gsm->len, gsm->state) advanced mode'un hiç bound'lamadığı bir data'yı tarif eder halde bıraktı, böylece basic-mode kopyası buffer'ın sonunu aştı.

Walkthrough

Bu peer'ın sürüklediği bir memory corruption'dır: bir gsm-mux tty'sinin diğer ucunu kontrol eden bir saldırgan (ya da local bir LPE kurulumunda her iki ucu) mode toggle'larını sürükler. Kavramsal olarak:

  1. gsm line discipline'ı attach et ve mux'u basic mode'a al (A tarafı):

    int ldisc = N_GSM0710;
    ioctl(fd, TIOCSETD, &ldisc);
    // GSMIOC_SETCONF with adaption/encoding = basic option mode
    ioctl(fd, GSMIOC_SETCONF, &cfg_basic);
    
  2. B tarafı olarak, len == 1 iddia eden bir basic-mode frame header'ı yaz, sonra A tarafını advanced mode'a reconfigure et ve 2 payload byte besle (advanced mode gsm->len'i yok sayar):

    write(peer_fd, basic_header_len1, sizeof(basic_header_len1));
    ioctl(fd, GSMIOC_SETCONF, &cfg_advanced);   // switch mid-frame
    write(peer_fd, two_payload_bytes, 2);       // overruns gsm->len
    
  3. A tarafını tekrar basic mode'a geçir ve byte stream'lemeye devam et; gsm->state/gsm->len hiç reset edilmediği için gsm0_receive() gsm->buf'ın sonunun ötesine kopyalar:

    ioctl(fd, GSMIOC_SETCONF, &cfg_basic);
    write(peer_fd, overflow_stream, N);         // writes past gsm->buf
    

Warning

Overflow, mux'un receive buffer'ına bitişik kernel memory'ye yapılan linear bir write'tır ve hem length hem content açısından peer tarafından kontrol edilebilir. slab grooming ile birleştiğinde bu bir kernel heap out-of-bounds write olur — crash veya privilege escalation'a yol açabilen bir memory-corruption primitive'i. CVSS, vendor scoring'ine bağlı olarak yaklaşık 7.7 (High) / 6.4 olarak raporlandı.

Upstream fix gsm0_receive()'i (ve gsm1_receive()'i) sertleştirir:

  • payload-copy bound'unu bir equality test'inden (gsm->count == gsm->len) bir less-than test'ine (gsm->count < gsm->len) değiştir, böylece desync olmuş bir sayaç artık hedeflenen length'in ötesine adım atamaz.
  • gsm->len ve gsm->mru üzerinde MAX_MRU'ya karşı açık upper-bound check'leri ekle, böylece bu field'ların corruption'ı buffer'dan büyük bir kopyaya yetki veremez.

Upstream çözüm: "tty: n_gsm: fix possible out-of-bounds in gsm0_receive()", geniş çapta backport edildi (fix commit'leri arasında 0dbb44b063a651d7b9f061dab6b1030d648042cf ve mainline 774d83b008eccb1c48c14dc5486e7aa255731350 var).

Detection

  • KASAN, mode-toggle sırası tekrar oynatıldığında gsm0_receive içinde bir slab-out-of-bounds write raporlar.
  • Davranışsal: bir frame decode edilirken basic ve advanced option mode arasında hızlı GSMIOC_SETCONF toggle'lama normal modem trafiği değildir ve güçlü bir göstergedir.
  • kernel'in gsm0_receive/gsm1_receive MAX_MRU bound check'lerini içerdiğini doğrula.

Mitigation

  • gsm0_receive() OOB fix'ini içeren bir kernel'e patch'le (CVE-2024-36016 çözümü).
  • GSM 0710 multiplexing kullanmayan sistemlerde n_gsm modülünü blocklist'e al.
  • gsm line discipline'ı attach etmeyi CAP_NET_ADMIN arkasına alarak receive path'i unprivileged koddan erişilemez tut.

References