[하루한줄] 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.cc의 bta_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!) |
+-----------+ |
| | |
| | |
| +----------------------+
| |
| |
+-----------+
- 첫 번째 SDP 요청에 오류 응답을 보내고, 이로 인해
tSDP_DISCOVERY_DB가 해제됩니다(이 시점에서 안드로이드 에뮬레이터는 A2DP에 연결을 시도하고 또 다른 SDP 요청을 보냅니다). - 두 번째 SDP 요청을 기다립니다. AVRCP 패킷을 전송하여 사용되지 않는
tSDP_DISCOVERY_DB를 재할당하고, raw_data를 내 대상 주소로 설정하는 가짜 객체를 전달합니다. - 두 번째 SDP 요청에 응답합니다.
sdp_copy_raw_data함수가 실행되어 SDP 응답을 대상 주소로memcpy합니다. 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
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.