CVE-2026-45815

CVE-2026-45815

Area
Native / Embedded
Credit
Public Attribution
Credit Note
CVSS
7.5
CVSS Source
CISA ADP
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE
CWE-617
Impact
Denial of Service
Key Finding
Pending GATT request 뒤 crafted ATT Read Multiple Variable response를 보내면 parser assertion에 도달해 원격 DoS가 가능했습니다.
Published
Jul 24, 2026
Score Note
CISA-ADP CVSS 3.1 7.5 High · Apache CNA 정성 등급 Medium (pending GATT request 조건 반영)
Severity
High
Status
Public
Target
Apache NimBLE · BLE ATT parser
DUT가 Read Multiple Variable 요청을 보낸 직후 peer가 짧거나 잘린 응답을 보내면 ATT parser의 assert에 도달했습니다. 실제 host test utility로 두 malformed response를 분리해 검증했습니다.
Taegu Ha · Apache NimBLE · CWE-617 · Severity Medium
2026년 7월 24일 공개 · Apache NimBLE 1.10.0에서 수정 · 공식 기록

한눈에 보기

🔎
Apache NimBLE가 pending Read Multiple Variable procedure의 응답 길이를 충분히 확인하지 않아, 악성 BLE peer가 short·truncated response를 보내면 두 assertion path에서 host가 종료됐습니다.
구분
확인 내용
프로젝트·컴포넌트
Apache Mynewt NimBLE GATT client · ble_gattc_read_mult_var()
공격 입력
length field가 없거나 declared value보다 짧은 Read Multiple Variable response
필요 조건
공격자가 연결된 BLE peer로서 pending GATT procedure에 응답
취약 지점
mbuf의 2-byte length와 declared value remaining bytes 검증 누락
검증 결과
path 1 assert(*om), path 2 assert(i == num_handles) 도달
영향
BLE host termination 및 remote DoS
수정 원칙
각 field read 전에 remaining length를 확인하고 malformed PDU는 ATT/GATT error로 종료

공격 흐름

검증 결론: 두 가지 truncation 모양이 서로 다른 assertion을 재현해, wire-data validation 부족이 remote peer-triggered process termination으로 이어짐을 확인했습니다.

취약점 개요

BLE ATT의 Read Multiple Variable Response는 각 attribute 값 앞에 2바이트 길이를 붙입니다. parser는 local device가 해당 request를 시작한 상태에서 peer response를 해석합니다. 외부 peer가 아무 때나 packet 하나를 보내는 조건은 아니지만, 정상 request 뒤의 response payload는 peer가 제어합니다.
조사한 구현은 mbuf에서 길이 field를 꺼내거나 요청한 handle 수만큼 attribute를 만들었다는 가정을 assert로 고정했습니다. payload가 1바이트뿐이거나 마지막 attribute가 선언 길이보다 짧으면 복구 가능한 protocol error 대신 process-terminating assertion에 도달했습니다.
공격자가 제어하는 값
BLE_ATT_OP_READ_MULT_VAR_RSP payload
선행 조건
DUT가 먼저 Read Multiple Variable Request를 전송
취약 결과
ATT parser assertion과 host 종료
수정
truncation을 protocol error로 처리

조사 대상

네트워크 parser 안의 assert()를 찾은 뒤 그 조건이 내부 invariant인지 remote input으로 깨질 수 있는지 역추적했습니다. 이 assert는 response opcode dispatch 뒤 mbuf pullup과 handle count 처리에 있었습니다.
도달성을 과장하지 않기 위해 host가 아무 상태도 없을 때 malformed response를 넣지 않았습니다. test utility로 connection을 만들고 실제 ble_gattc_read_mult_var()를 호출해 pending procedure를 생성한 뒤 response를 주입했습니다.
정상적으로 시작된 GATT procedure에 malformed peer response를 주입하면 오류 반환이 아니라 assert로 종료되는가?

원인 분석

첫 입력은 opcode 뒤 payload를 1바이트만 둬 2바이트 attribute length를 pullup할 수 없게 했습니다. 취약 코드는 이 결과를 외부 오류로 처리하지 않고 assert(*om)에 의존했습니다.
두 번째 입력은 length를 2로 선언하고 실제 value는 1바이트만 넣었습니다. parser가 모든 요청 handle에 해당하는 attribute를 만들지 못한 뒤 최종 assert(i == num_handles)에 도달했습니다. 서로 다른 parser 전제 두 개를 별도 mode로 남겼습니다.
repro_gatt_mult_var.c · 두 malformed response
// 2-byte length를 읽을 수 없는 1-byte payload { BLE_ATT_OP_READ_MULT_VAR_RSP, 0x41 } // len=2로 선언했지만 value는 1 byte { BLE_ATT_OP_READ_MULT_VAR_RSP, 0x02, 0x00, 0xaa } ble_gattc_read_mult_var(...); inject_response(...);
입력에서 영향까지의 경로
  1. DUT가 peer에 Read Multiple Variable Request를 보냅니다.
  1. host stack이 pending GATT procedure를 기록합니다.
  1. peer가 짧거나 잘린 response를 보냅니다.
  1. ATT parser가 length와 attribute 수를 복원하지 못합니다.
  1. error 반환 대신 assert가 실행됩니다.

재현 및 검증

Apache NimBLE의 host test utility와 실제 GATT client API를 링크한 nimble-repro를 만들었습니다. connection 생성과 HCI queue 초기화 후 정상 request를 시작하고 L2CAP ATT CID에 response bytes를 직접 주입했습니다.
gatt-shortgatt-trunc mode를 나눠 어느 assert가 깨지는지 고정했습니다. 같은 harness에 EATT 관련 탐색 mode도 남아 있지만 이 CVE 결론에는 Read Multiple Variable의 두 mode만 사용했습니다.
$ make $ ./nimble-repro gatt-short $ ./nimble-repro gatt-trunc
검사
입력·조건
관찰
의미
Procedure 없음
pending request 없이 response
정상 procedure path 아님
선행 조건 확인
짧은 길이
opcode + 1 byte
assert(*om)
length pullup 실패 처리 누락
잘린 값
length 2 + value 1 byte
assert(i == num_handles)
attribute count invariant가 remote input에 노출
재현 harness가 구분한 두 종료 지점
mode: gatt-short injecting READ_MULT_VAR_RSP with 1-byte payload expected: assert(*om) mode: gatt-trunc injecting truncated READ_MULT_VAR_RSP expected: final assert(i == num_handles)

판단 변화

도달 조건

assert가 packet parser에 있다는 사실만으로 임의 BLE advertisement에서 즉시 crash한다고 볼 수 없었습니다. pending GATT request가 필요한 state machine을 재현에 포함하고 severity 설명에도 그 조건을 남겼습니다.

단위 함수

raw parser 함수를 직접 부르면 내부 state를 임의로 꾸몄다는 반론이 생길 수 있었습니다. public client API인 ble_gattc_read_mult_var()로 procedure를 만든 뒤 test utility의 L2CAP 수신 경로에 주입했습니다.

두 Assert

처음 발견한 짧은 payload만 고치면 마지막 handle count assert가 남을 수 있었습니다. length field 자체가 없는 경우와 선언 길이만큼 value가 없는 경우를 분리해 수정 범위를 넓혔습니다.

영향과 수정

DUT가 공격 peer에 Read Multiple Variable Request를 보낸 상황에서 peer가 malformed response로 host stack의 assertion을 발생시켜 연결 장치나 process를 중단시킬 수 있습니다. 공개 평가는 Medium입니다.

확인한 범위

  • 실제 GATT request 이후 response parser 도달
  • 두 malformed payload의 서로 다른 assert
  • peer-controlled ATT payload

제외한 범위

  • request 없이 도달하는 무조건적 remote crash
  • 메모리 손상
  • 코드 실행

수정

1.10.0의 수정은 mbuf 길이와 각 attribute의 실제 남은 bytes를 검증하고, handle 수를 채우지 못한 response를 assert 대신 ATT/GATT error로 종료합니다. 외부 입력 형식 오류는 production invariant가 아니라 정상 오류 경로로 처리해야 합니다.

분석 기준과 증거 보존

항목
최종 감사 결과
소스 기준
mynewt-nimble checkout 07b0b0da8d2fe00f1866f208a51c6be2c12efc68
원본 검증
gatt-short에서 *om, gatt-trunc에서 handle-count assert를 실제 harness로 재확인; 두 실행 모두 exit 134
GitHub 보존 상태
Public 저장소 main의 감사 commit c04cacf; evidence 7개와 root manifest.json에 size·SHA-256 고정
무결성
사례별 SHA256SUMS 검증 통과, root manifest의 모든 file hash와 실제 파일 일치, README 상대 링크 확인 완료
보존 경계·제약
컴파일 binary·object, raw BLE capture와 원본 session은 제외
감사 완료
2026-08-23 · Docker/로컬 원본 대조 후 GitHub push 완료

관련 파일

2026-08-23 최종 감사

재현 source·Makefile·공개 CVE 레코드에 더해 두 assertion의 fresh 실행 결과를 assert-observations.txt로 보존했습니다. 실행 binary와 object file은 제외했습니다.

느낀 점

assert는 개발 중 잘못된 내부 상태를 빨리 찾는 도구지만, 그 전제가 peer packet에 달려 있으면 원격 종료 지점이 될 수 있었습니다. 이후 parser의 assert를 보면 조건식만 읽지 않고 그 값을 마지막으로 쓴 주체까지 거슬러 올라가게 됐습니다.
stateful protocol에서는 byte array 하나보다 그 전에 어떤 request가 나갔는지를 재현하는 편이 훨씬 중요했습니다. 선행 상태를 실제 API로 만든 덕분에 도달 조건을 과장하지 않고도 문제를 설명할 수 있었습니다.