PHPCVE-2026-109618深度分析"

admin 2026-10-24 15:06:21 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

PHPCVE-2026-109618深度分析

原创

YMsora
YMsora

YMs0ra的安全漫路

2026年10月22日 12:11
上海
标题已修改

去阅读

后续会对109619也分析,非常深入zend的两个洞,很有意思,ai还是太恐怖了

依旧波及范围极广,我让ai调取了php更新的补丁,并进行了审计 在于清理机制以及回调,header链表等等多个问题

heap->overflow = 1;
zend_try {
    zend_error_noreturn(E_ERROR, format, ...);
} zend_catch {
} zend_end_try();
heap->overflow = 0;
zend_bailout();     /* ← 走 longjmp,跳过清理 */
exit(1);

在内存耗尽的时候会设置为 heap->overflow = 1;
也就是应急模式,开启了配额缓冲,并且对于zend报错进行格式化输出,也就是zend\_try的内容 当应急退出后便进入了zend\_bailout(),他的宏实现是longjmp(EG(bailout), 1)

继续跟进zend.c

CG(unclean_shutdown) = 1;
CG(active_class_entry) = NULL;
CG(in_compilation) = 0;
CG(memoize_mode) = 0;
EG(current_execute_data) = NULL;
LONGJMP(*EG(bailout), FAILURE);    /* ← 非局部跳转 */

CG和EG是编译期和执行期的全局结构体,在heap->overflow = 0;之后, EG current\_execute\_data的状态也就被滞空了, 紧接着是LONGJMP(\*EG(bailout), FAILURE);跳转回预设 也就是之前的zend\_bailout();目的点

#define zend_try
    {
        JMP_BUF *__orig_bailout = EG(bailout);
        JMP_BUF __bailout;
        EG(bailout) = &__bailout;
        if (SETJMP(__bailout)==0) {
#define zend_catch
        } else {                   //跳转位置
            EG(bailout) = __orig_bailout;
#define zend_end_try()
        }
        EG(bailout) = __orig_bailout;
    }

在跳转else的catch分支后并没有执行清理,中间清理的代码是被略过的 略过之后

zend_result php_request_startup(void)
{
    zend_result retval = SUCCESS;
    ...
    zend_try {
        ...
        sapi_activate();
        ...
        php_hash_environment();
        zend_activate_modules();
        PG(modules_activated)=1;
    } zend_catch {
        retval = FAILURE;
    } zend_end_try();
    SG(sapi_started) = 1;
    return retval;                   // ← 返回 FAILURE
}

在跳转的时候,EG,SG,BG都是半成品,下面retval返回FAILURE,并且状态并没有回滚 这时,有两个处理逻辑。 FPM / LSAPI 会直接杀掉并重启worker 但是 Apache mod\_php 他会执行 php\_request\_shutdown(NULL); 与此同时

void php_request_shutdown(void *dummy)
{
    bool report_memleaks;
    EG(flags) |= EG_FLAGS_IN_SHUTDOWN;
    report_memleaks = PG(report_memleaks);
    /* EG(current_execute_data) points into nirvana and therefore cannot be safely accessed
    * inside zend_executor callback functions.
    */
    EG(current_execute_data) = NULL;
    php_deactivate_ticks();
    /* 0. Call any open observer end handlers that are still open after a zend_bailout */
    if (ZEND_OBSERVER_ENABLED) {
        zend_observer_fcall_end_all();
    }
    /* 1. Call all possible shutdown functions registered with register_shutdown_function() */
    if (PG(modules_activated)) {
        php_call_shutdown_functions();
    }
    /* 2. Call all possible __destruct() functions */
    zend_try {
        zend_call_destructors();
    } zend_end_try();
    /* 3. Flush all output buffers */
    zend_try {
        php_output_end_all();
    } zend_end_try();
    /* 4. Reset max_execution_time (no longer executing php code after response sent) */
    zend_try {
        zend_unset_timeout();
    } zend_end_try();
    /* 5. Call all extensions RSHUTDOWN functions */
    if (PG(modules_activated)) {
        zend_deactivate_modules();
    }
    /* 6. Shutdown output layer (send the set HTTP headers, cleanup output handlers, etc.) */
    zend_try {
        php_output_deactivate();
    } zend_end_try();
    /* 7. Free shutdown functions */
    if (PG(modules_activated)) {
        php_free_shutdown_functions();
    }
    /* 8. Destroy super-globals */
    zend_try {
        int i;
        for (i=0; i<NUM_TRACK_VARS; i++) {
            zval_ptr_dtor(&PG(http_globals)[i]);
        }
    } zend_end_try();
    /* 9. Shutdown scanner/executor/compiler and restore ini entries */
    zend_deactivate();
    /* 10. free request-bound globals */
    php_free_request_globals();
    /* 11. Call all extensions post-RSHUTDOWN functions */
    zend_try {
        zend_post_deactivate_modules();
    } zend_end_try();
    /* 12. SAPI related shutdown*/
    zend_try {
        sapi_deactivate_module();
    } zend_end_try();
    /* free SAPI stuff */
    sapi_deactivate_destroy();
    /* 13. free virtual CWD memory */
    virtual_cwd_deactivate();
    /* 14. Destroy stream hashes */
    zend_try {
        php_shutdown_stream_hashes();
    } zend_end_try();
    /* 15. Free Willy (here be crashes) */
    zend_arena_destroy(CG(arena));
    zend_interned_strings_deactivate();
    zend_try {
        shutdown_memory_manager(CG(unclean_shutdown) || !report_memleaks, 0);
    } zend_end_try();
    /* Reset memory limit, as the reset during INI_STAGE_DEACTIVATE may have failed.
    * At this point, no memory beyond a single chunk should be in use. */
    zend_set_memory_limit(PG(memory_limit));
    /* 16. Deactivate Zend signals */
#ifdef ZEND_SIGNALS
    zend_signal_deactivate();
#endif
#ifdef PHP_WIN32
    if (PG(com_initialized)) {
        CoUninitialize();
        PG(com_initialized) = 0;
    }
#endif
#ifdef HAVE_DTRACE
    DTRACE_REQUEST_SHUTDOWN(SAFE_FILENAME(SG(request_info).path_translated), SAFE_FILENAME(SG(request_info).request_uri), (char *)SAFE_FILENAME(SG(request_info).request_method));
#endif /* HAVE_DTRACE */
}

因为EG(current\execute\_data)至null所以 任何 executor callback 里都不能安全访问它 , 上游默认是unclean的状态, 于是在这之中是可控制的,zend\_call\_destructors() 调用了\\_destruct() zend\_deactivate\_modules();扩展了扩展 RSHUTDOWN, 些步骤会去遍历、析构、调用那些被 longjmp 跳过清理指向无效内存的全局状态 比如读坏指针或者调坏函数指针,在 shutdown 阶段被执行 而这几部分坏指针是攻击者可控的也就可RCE 并且可知还有个问题,就是攻击可以小limit造成大链表的内存破坏 看看以下的结构体以及处理逻辑

typedef struct { char *key; char *value; } mime_header_entry;
multipart_buffer *self;
zend_llist *header;
key = estrdup(line);
smart_string_appends(&buf_value, value);
zend_llist_add_element(header, &entry);

key的value是每一个都会被添加进链表的结点,然后复制到堆上

#define SMART_STRING_OVERHEAD   (ZEND_MM_OVERHEAD + 1)
#define SMART_STRING_START_SIZE 256
#define SMART_STRING_START_LEN  (SMART_STRING_START_SIZE - SMART_STRING_OVERHEAD)
#define SMART_STRING_PAGE       4096

首次分配固定按 256 字节起步;之后每次增长按 4096 对齐,用 erealloc2 扩容。也就是说 value 缓冲总是保留一个 4KB 量级的容量,而不是刚好等于内容长度。 所以对于 X-N: v 这种很短的 header,在smart\_string value 累积 后是可以共享缓冲,4096字节对齐增长的 还有几种放大方法,也就不做赘述了 以上,bye

PHPCVE-2026-109618深度分析" 网络安全文章

PHPCVE-2026-109618深度分析"

本文深度分析PHPCVE-2026-109618漏洞,涉及Zend引擎在内存耗尽应急模式下的清理机制缺陷。通过longjmp非局部跳转跳过关键清理步骤,导致EG等全局状态不一致,进而在shutdown阶段可被利用造成内存破坏,最终可能实现远程代码执行。文章还探讨了通过构造特定header链表放大内存破坏的方法,并指出Apachemod\\_php与FPM等不同SAPI下的差异化影响。
印度这家CA,把全球信任库当成了厕纸" 网络安全文章

印度这家CA,把全球信任库当成了厕纸"

印度CA机构eMudhra(emSign品牌)因八张中间证书私钥过于简单(从1开始试最多255次即可破解)发生严重安全事件,涉及HTTPS证书和代码签名证书。Chrome、Firefox、Apple、Microsoft等主流浏览器迅速响应封禁,eMudhra于10月18日吊销证书。对普通用户影响有限,但开发者和运维人员需及时更新运行时信任库,防止中间人攻击风险。
评论:0   参与:  0