[하루한줄] CVE-2025-48593 : Android의 SDP DB 검증 미흡으로 발생하는 UAF로 인한 Zero-Click RCE

URL

Target

  • Android ≤ 16.0 (Builds Jul 2025 - Oct 2025)

Explain

안드로이드 블루투스에서 UAF를 통한 Zero-Click RCE가 발생하였습니다. 이 취약점은 블루투스 핸즈프리 프로파일(HFP) 클라이언트가 초기화되는 과정에서 Service Discovery 데이터베이스의 상태를 제대로 확인하지 않아 발생합니다.

블루투스 HFP 클라이언트가 동작하려면 상대방 기기의 정보를 알아내기 위해 tSDP_DISCOVERY_DB라는 SDP(Service Discovery Protocol) 데이터베이스를 할당하고 사용합니다.

struct tSDP_DISCOVERY_DB {
  uint32_t mem_size;                                  /* Memory size of the DB        */
  uint32_t mem_free;                                  /* Memory still available       */
  tSDP_DISC_REC* p_first_rec;                         /* Addr of first record in DB   */
  uint16_t num_uuid_filters;                          /* Number of UUIds to filter    */
  bluetooth::Uuid uuid_filters[SDP_MAX_UUID_FILTERS]; /* UUIDs to filter */
  uint16_t num_attr_filters;                          /* Number of attribute filters  */
  uint16_t attr_filters[SDP_MAX_ATTR_FILTERS];        /* Attributes to filter */
  uint8_t* p_free_mem;                                /* Pointer to free memory       */
  uint8_t* raw_data; /* Received record from server. allocated/released by client  */
  uint32_t raw_size; /* size of raw_data */
  uint32_t raw_used; /* length of raw_data used */
};

그러나 이전에 할당된 tSDP_DISCOVERY_DB가 존재하는지 확인하지 않습니다. 이 부분은 bta_hf_client_main.ccbta_hf_client_cb_init 함수에서 발생합니다.

void bta_hf_client_cb_init(tBTA_HF_CLIENT_CB* client_cb, uint16_t handle) {
  log::verbose("");
  
  // Free any memory we need to explicity release
  alarm_free(client_cb->collision_timer);
  
  // release unique pointers
  client_cb->enabled_hf_indicators.clear();
  client_cb->peer_hf_indicators.clear();

  // Memset the rest of the block
  // memset(client_cb, 0, sizeof(tBTA_HF_CLIENT_CB));
  *client_cb = {};
  
  // Re allocate any variables required
  client_cb->collision_timer = alarm_new("bta_hf_client.scb_collision_timer");
  client_cb->handle = handle;
  client_cb->sco_idx = BTM_INVALID_SCO_INDEX;
}

공격자가 특정한 타이밍에 HFP 연결/해제/재초기화 요청을 보내면, 시스템은 bta_hf_client_cb_init를 호출합니다.

이때 만약 이전 세션에서 사용하던 DB 포인터가 아직 메모리에 남아있거나, Free 됐지만 포인터가 초기화되지 않은 상태(Dangling Pointer)라면 취약점이 발생합니다.

이로 인해 새 작업을 수행하면 UAF 취약점이 발생합니다.

공식적인 PoC는 공개되어 있지 않습니다. 하지만, 이를 재현하는 글을 통해 PoC 시나리오를 알 수 있습니다.

hf_client:                  SDP:                            
tBTA_HF_CLIENT_CB           tCONN_CB 1       tCONN_CB 2     
+-------------+             +------------+   +-------------+
|             |             |            |   |             |
| ACTIVE      |             | INACTIVE   |   | ACTIVE      |
|             |             |            |   |             |
|  p_disc_db  |             |   p_db     |   |   p_db      |
+-------------+             +------------+   +----+--------+
                                                  |         
               tSDP_DISCOVERY_DB 1                |         
               +-----------+                      |         
               |           |                      |         
               |           |                      |         
               |           |                      |         
               |           |                      |         
               +-----------+                      |         
                                                  |         
                                                  |         
                                                  |         
               (freed!)                           |         
               +-----------+                      |         
               |           |                      |         
               |           |                      |         
               |           +----------------------+         
               |           |                                
               |           |                                
               +-----------+                                
  1. 첫 번째 SDP 요청에 오류 응답을 보내고, 이로 인해 tSDP_DISCOVERY_DB가 해제됩니다(이 시점에서 안드로이드 에뮬레이터는 A2DP에 연결을 시도하고 또 다른 SDP 요청을 보냅니다).
  2. 두 번째 SDP 요청을 기다립니다. AVRCP 패킷을 전송하여 사용되지 않는 tSDP_DISCOVERY_DB를 재할당하고, raw_data를 내 대상 주소로 설정하는 가짜 객체를 전달합니다.
  3. 두 번째 SDP 요청에 응답합니다. sdp_copy_raw_data 함수가 실행되어 SDP 응답을 대상 주소로 memcpy합니다.
  4. p_db는 첫 번째 SDP 검색 오류 응답에서 bta_hf_client_free_db에 의해 이미 해제되었으므로 두 번째 SDP 응답으로 인해 UAF가 발생합니다.

이 취약점은 2025-11-05 보안 패치(AOSP)로 패치 되었습니다.

void bta_hf_client_cb_init(tBTA_HF_CLIENT_CB* client_cb, uint16_t handle) {
  log::verbose("");
  
  // Free any memory we need to explicity release
  alarm_free(client_cb->collision_timer);
  
  // release unique pointers
  client_cb->enabled_hf_indicators.clear();
  client_cb->peer_hf_indicators.clear();

+  if (client_cb->p_disc_db) {
+    if (!get_legacy_stack_sdp_api()->service.SDP_CancelServiceSearch(client_cb->p_disc_db)) {
+      log::warn("Unable to cancel SDP service discovery peer:{}", client_cb->peer_addr);
+    }
+    osi_free_and_reset((void**)&client_cb->p_disc_db);
+  }
+

  // Memset the rest of the block
  // memset(client_cb, 0, sizeof(tBTA_HF_CLIENT_CB));
  *client_cb = {};
  
  // Re allocate any variables required
  client_cb->collision_timer = alarm_new("bta_hf_client.scb_collision_timer");
  client_cb->handle = handle;
  client_cb->sco_idx = BTM_INVALID_SCO_INDEX;
}

패치는 기존 데이터베이스 존재 여부 확인을 하는 로직이 추가되었습니다. p_disc_db가 비어있는지 검사하여 UAF 취약점이 발생하지 않도록 조치 되었습니다.

Reference