Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-64468 — 비특권 개념 증명 및 Linux 커널 Binder use-after-free(CVE-2026-64468)에 대한 x86_64 로컬 권한 상승, KASAN 실습 환경 및 취약/수정 버전 차이 분석 포함. | Kitploit
도구/GitHubGitHub/aramosf/cve-2026-64468
Privilege EscalationExploit FrameworksVulnerability AnalysisExploitationLearning & EducationBinary Exploitation
GitHubaramosf/cve-2026-64468

CVE-2026-64468

비특권 개념 증명 및 Linux 커널 Binder use-after-free(CVE-2026-64468)에 대한 x86_64 로컬 권한 상승, KASAN 실습 환경 및 취약/수정 버전 차이 분석 포함.

저장소 보기
297281개월 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-64468 — Linux 커널 Binder binder_free_transaction() 프로세스 수명 use-after-free

실제 QEMU/KVM 실행: 패치되지 않은 취약 커널이 use-after-free를 보고하고, 패치된 커널은 보고하지 않음

이 저장소는 업스트림 커밋 f223d27a546c1e1f48d38fd67760e78f068fe8c4으로 수정된 Linux 커널 Binder use-after-free에 대해 다음을 포함합니다:

  • binder_chain_64468.c — 버그에 도달하여 커널이 이를 입증하도록 하는 권한 없는 개념 증명으로, KASAN 실험실과 취약/수정 차등 테스트(lab/, run.sh, verify.sh)를 포함합니다.
  • exploit.c — x86_64용 독립형 로컬 권한 상승 코드입니다. gcc -O2 -pthread -o exploit exploit.c로 컴파일되며, 일반 사용자로 실행되어 루트 셸로 끝납니다.
  • demo/ — 패치되지 않은 커널에서 실제 Debian 13 사용자 영역을 부팅하는 실험실로, 익스플로잇이 대상 시스템의 자체 gcc로 컴파일되고, 이후 장악하는 그 머신에서 실행될 수 있습니다.

모든 것은 어떤 종류의 패치도 없는 스톡 업스트림 커널을 대상으로 일반 사용자(uid/gid 1000, capabilities 없음, namespaces 없음)로 실행됩니다.

경고

이 코드는 의도적으로 커널 객체 수명을 경쟁(race)시킨 후 커널 제어 흐름을 탈취합니다. 경쟁에서 패배하면 커널 힙 상태가 손상되어 머신이 패닉하거나 멈출 수 있습니다. 소유한 격리된 일회용 VM에서만 실행하십시오. 호스트에서는 실행하지 마십시오.

상태 및 범위

상태주장
확인됨취약점은 실제이며, 권한 없는 프로세스에서 도달 가능하고, 업스트림 수정이 이를 제거합니다.
입증됨소멸 중인 binder_proc의 취약한 역참조가 패치되지 않은 커널에서 자연스럽고 반복적으로 발생하며, 패치된 커널에서는 절대 발생하지 않습니다.
입증됨KASAN이 보고한 전체 use-after-free가 패치되지 않은 커널에서 발생합니다.
입증됨공격자가 제어하는 바이트로 해제된 binder_proc을 재확보하고, 커널 제어 흐름을 탈취하며, x86_64에서 권한 없는 사용자로부터 uid 0으로의 권한 상승이 발생합니다.
주장하지 않음특정 벤더 또는 Android 기기에 대한 어떤 결과도 없습니다. 아래 나열된 업스트림 커널만 x86_64에서 테스트되었습니다.
주장하지 않음제공된 익스플로잇이 수정 없이 배포판 커널에서 동작한다는 것. 영향을 받는 시스템을 참조하십시오: 커널별 오프셋이 필요하며, 조사된 모든 범용 배포판에서 binder 장치는 애초에 권한 없는 사용자가 접근할 수 없습니다.

취약점

binder_free_transaction()은 t->lock 하에서 트랜잭션에서 대상 프로세스를 읽고, 해당 잠금을 해제한 다음, 대상의 내부 잠금을 획득합니다:```c spin_lock(&t->lock); target_proc = t->to_proc; spin_unlock(&t->lock);

if (target_proc) {
	binder_inner_proc_lock(target_proc);   /* use after free */
Nothing keeps `target_proc` alive across that gap. A process that is being torn
down in parallel can reach `binder_proc_dec_tmpref() -> kfree()` in between, so
the lock is taken on freed memory. The upstream fix pins `t->to_thread` while
`t->lock` is still held, which keeps the owning process alive until the inner
lock has been used and released.

The vulnerability was reported by **Alice Ryhl** and fixed by **Carlos
Llamas**, both of Google. The
[original report](https://lore.kernel.org/all/[email protected]/)
carries the reference KASAN trace.

### 취약한 접근에 도달하기

*외부* `to_proc`에 도달할 수 있는 유일한 호출자는
`binder_send_failed_reply()`이며, `t->from`이 `NULL`일 때만
`t->from_parent`로 이동합니다:```c
	target_thread = binder_get_txn_from_and_acq_inner(t);
	if (target_thread) { ...; binder_free_transaction(t); return; }
	next = t->from_parent;
	binder_free_transaction(t);
	t = next;

from_parent는 정확히 한 곳에서 할당되며, 그 할당은 몇 줄 앞서 binder의 잘못된 트랜잭션 스택 검사에 의해 보호됩니다. 즉, 스레드는 자신의 스택 최상단이 수신 중인 트랜잭션일 때만 동기 트랜잭션을 보낼 수 있습니다. 따라서 해당 체인의 모든 링크에 대해 자식의 송신자와 부모의 수신자는 동일한 스레드입니다.

이는 뚜렷한 결과를 낳습니다. binder_thread_release()는 proc->inner_lock을 보유한 채 종료되는 스레드의 스택을 순회하며, 다음 두 가지를 모두 기록합니다.``` iteration j : [holds child->lock ] child->from = NULL ; unlock child->lock spin_lock(parent->lock) iteration j+1 : [holds parent->lock] parent->to_proc = NULL

in **인접한 단일 워크의 반복**에서, 각각은 해당
`t->lock` 하에 있습니다. 워커는 워크가 `child->lock`을 해제한 후에만
`child->from == NULL`을 알게 되며, 자체 스냅샷을 위해 `parent->lock`이 필요합니다.
따라서 전체 기회는 해당 워크의 `spin_unlock(&child->lock)`과
`spin_lock(&parent->lock)` 사이의 간격 — 몇 개의 명령어에 불과합니다. 해제 워크는
스핀락을 보유하고 있어 그 지점에서 선점될 수 없습니다. 오직 인터럽트만이 이를 지연시킬 수 있습니다.

이것이 레이스가 좁은 이유이며, 개념 증명과 익스플로잇이 모두
확률적(probabilistic)인 이유입니다.

### 개념 증명이 구축하는 것

`binder_chain_64468.c`는 취약한 접근에 도달하는 가장 짧은 체인을 구성하므로,
워커가 그 간격 내에서 수행하는 작업은 binder가 허용하는 한 최소화됩니다 —
두 번의 `t->lock` 획득과 한 번의 `kfree()`, 외부 `inner_proc_lock` 없이,
`wake_up` 없이, 그리고 응답 전달 없이:```
  B thread i   --e2 (sync, code 0x4442414b)-->  P thread Y_i
  P thread Y_i --e1 (sync, code 0x54414c4c)-->  B thread i   (nested target)

  B thread i   stack: [ e2 outgoing , e1 incoming (top) ]
  P thread Y_i stack: [ e2 incoming , e1 outgoing (top) ]

P가 바인더 fd를 닫으면 binder_deferred_release()가 Y_1..Y_K를 해제하고 마지막으로 binder_proc를 해제하는 동안, 모든 B 스레드가 동시에 BINDER_THREAD_EXIT를 실행합니다:``` binder_thread_release(B_i) nulls e1->to_proc (own proc) and e2->from binder_send_failed_reply(e1) e1->from == NULL once Y_i was released -> binder_free_transaction(e1) target_proc already NULL, kfree(e1) -> binder_free_transaction(e2) target_proc == P <-- vulnerable access

세 번째 프로세스는 바인더 컨텍스트 매니저로, 다른 두 프로세스가 필요로 하는 핸들을 나눠주는 용도로만 사용된다. `exploit.c`는 바로 이 구성을 그대로 재사용한다.

### 두 개의 독립적인 조건

KASAN 리포트가 발생하려면 다음 두 가지가 모두 필요하다:

* **취약한 접근** — 워커가 위의 좁은 창 안에서 `parent->lock`을 획득하여 아직 살아있는 `to_proc`의 스냅샷을 찍어야 한다. 그리고
* **창 안에서의 해제(free) 도달** — 워커가 `t->lock`을 놓은 뒤 피해자의 내부 잠금을 획득하기 전에 CPU를 빼앗겨야 하며, 지연 해제(deferred release)가 완료되어 `binder_proc`가 해제될 때까지 CPU를 되찾지 못해야 한다.

첫 번째 조건에는 KASAN이 필요 없는 자체 오라클(oracle)이 있다. 바인더는 다음을 출력한다:```
binder: binder_free_proc: Unexpected outstanding_txns -1

whenever it happens, because the walker and binder_thread_release() then both decrement the same counter for one transaction. Note this is not a vulnerable/fixed differential by itself — the fix stops the free, not the second decrement — so it appears on both kernels. It is used here only to show the vulnerable code path being exercised.

use-after-free에서 root까지

The proof of concept stops at a 4-byte decrement of freed memory. Turning that into uid 0 needs four things, and none of them come from the bug itself: the bug leaks nothing.

1. 공유 캐시

struct binder_proc is 648 bytes and is allocated with a plain GFP_KERNEL kzalloc — not __GFP_ACCOUNT. It therefore lands in kmalloc-1k, together with every other unaccounted allocation of that size, and is not isolated behind kmalloc-cg-*. That single fact is what makes the object reclaimable at all.

2. 따라잡을 수 있는 재확보 펌프

The moment of the kfree() is not observable from userspace, and neither is the moment the walker touches the object again, so there is nothing to time against. The spray therefore runs as a pump: it allocates and frees kmalloc-1k objects continuously, from the CPU that ran the deferred release, for as long as the walkers are running.

System V messages are used for it. alloc_msg() is a plain unaccounted kmalloc of a 48-byte header plus payload, so a 976-byte message is a 1024-byte allocation; the quota is per queue rather than per uid; and msgrcv() frees synchronously. Measured throughput: ~198,000 allocations per second, zero failures.

add_key/user_key_payload was tried first and is a trap. Its payload is charged against a per-uid byte quota (kernel.keys.maxbytes, 20000 by default) which is released only when the key garbage collector destroys the key, so a tight allocate/free loop exhausts it in milliseconds: measured 27,151 successful allocations against 2,121,009 failures — 98.7% of the spray silently doing nothing, which looks exactly like a spray that never wins the slot. KEYCTL_INVALIDATE made it worse (4,775 successes), because it queues GC work.

3. 재확보된 객체가 담고 있어야 하는 것

The walker touches four fields of the freed binder_proc (offsets measured with pahole on the target build):

OffsetFieldWhat the walker does
108int outstanding_txnsdecrements it
113bool is_frozenreads it
120wait_queue_head_t freeze_waitwalks it if outstanding_txns == 0 && is_frozen
624spinlock_t inner_locktakes and releases it
도구 다운로드