PHP序列化和反序列化语法差异问题

介绍

官方文档中介绍PHP序列化和反序列化如下:

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。 为了能够unserialize()一个对象,这个对象的类必须已经定义过。如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。

简单说序列化是对象转化字符串的过程,反序列化是字符串还原对象的过程。

[[282947]]

环境

文章中所述内容使用环境如下:

  • PHP7.3.1、SDK
  • VSCode
  • C++和C

在网上公开参数反序列化执行流程已经非常详细,但是对于一些细节地方有一些不足,其中就包括序列化和反序列化之间的语法差异问题。

差异问题

1. 序列化

我们通过编译PHP内核源码分析,发现PHP序列化在默认情况下在对象转换中加入:{和}用来拼接成字符串。

  1. [var.c] 
  2. Line:882 
  3. static void php_var_serialize_intern() 
  4.  
  5. Line:896 
  6. if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash) == SUCCESS) { 
  7.                         smart_str_appendl(buf, "C:", 2); 
  8.                         smart_str_append_unsigned(buf, ZSTR_LEN(Z_OBJCE_P(struc)->name)); 
  9.                         smart_str_appendl(buf, ":\"", 2); 
  10.                         smart_str_append(buf, Z_OBJCE_P(struc)->name); 
  11.                         smart_str_appendl(buf, "\":", 2); 
  12.  
  13.                         smart_str_append_unsigned(buf, serialized_length); 
  14.                         smart_str_appendl(buf, ":{", 2); 
  15.                         smart_str_appendl(buf, (char *) serialized_data, serialized_length); 
  16.                         smart_str_appendc(buf, '}'); 
  17.                     } 
  18.  
  19. Line:952 
  20. smart_str_appendl(buf, ":{", 2); 
  21.  
  22. Line:995 
  23. smart_str_appendc(buf, '}'); 

咱们来看上面这段代码,PHP会使用smart_str_appendl为序列化字符串前后拼接:{和},从var.c的第882行开始进入序列化逻辑。在第896行进行序列化字符串拼接,第952行和第995行,对于内嵌方法进行拼接。

2. 反序列化

反序列化是将序列化的字符串,按照一定语法规则进行转化还原。

  1. [var_unserialize.c] 
  2. Line:655 
  3. static int php_var_unserialize_internal() 
  4.  
  5. Line:674 
  6.     YYCTYPE yych; 
  7.     static const unsigned char yybm[] = { 
  8.           0,   0,   0,   0,   0,   0,   0,   0,  
  9.           0,   0,   0,   0,   0,   0,   0,   0,  
  10.           0,   0,   0,   0,   0,   0,   0,   0,  
  11.           0,   0,   0,   0,   0,   0,   0,   0,  
  12.           0,   0,   0,   0,   0,   0,   0,   0,  
  13.           0,   0,   0,   0,   0,   0,   0,   0,  
  14.         128, 128, 128, 128, 128, 128, 128, 128,  
  15.         128, 128,   0,   0,   0,   0,   0,   0,  
  16.           0,   0,   0,   0,   0,   0,   0,   0,  
  17.           0,   0,   0,   0,   0,   0,   0,   0,  
  18.           0,   0,   0,   0,   0,   0,   0,   0,  
  19.           0,   0,   0,   0,   0,   0,   0,   0,  
  20.           0,   0,   0,   0,   0,   0,   0,   0,  
  21.           0,   0,   0,   0,   0,   0,   0,   0,  
  22.           0,   0,   0,   0,   0,   0,   0,   0,  
  23.           0,   0,   0,   0,   0,   0,   0,   0,  
  24.           0,   0,   0,   0,   0,   0,   0,   0,  
  25.           0,   0,   0,   0,   0,   0,   0,   0,  
  26.           0,   0,   0,   0,   0,   0,   0,   0,  
  27.           0,   0,   0,   0,   0,   0,   0,   0,  
  28.           0,   0,   0,   0,   0,   0,   0,   0,  
  29.           0,   0,   0,   0,   0,   0,   0,   0,  
  30.           0,   0,   0,   0,   0,   0,   0,   0,  
  31.           0,   0,   0,   0,   0,   0,   0,   0,  
  32.           0,   0,   0,   0,   0,   0,   0,   0,  
  33.           0,   0,   0,   0,   0,   0,   0,   0,  
  34.           0,   0,   0,   0,   0,   0,   0,   0,  
  35.           0,   0,   0,   0,   0,   0,   0,   0,  
  36.           0,   0,   0,   0,   0,   0,   0,   0,  
  37.           0,   0,   0,   0,   0,   0,   0,   0,  
  38.           0,   0,   0,   0,   0,   0,   0,   0,  
  39.           0,   0,   0,   0,   0,   0,   0,   0,  
  40.     }; 
  41.     if ((YYLIMIT - YYCURSOR) < 7) YYFILL(7); 
  42.     yych = *YYCURSOR; 
  43.     switch (yych) { 
  44.     case 'C': 
  45.     case 'O':    goto yy4; 
  46.     case 'N':    goto yy5; 
  47.     case 'R':    goto yy6; 
  48.     case 'S':    goto yy7; 
  49.     case 'a':    goto yy8; 
  50.     case 'b':    goto yy9; 
  51.     case 'd':    goto yy10; 
  52.     case 'i':    goto yy11; 
  53.     case 'o':    goto yy12; 
  54.     case 'r':    goto yy13; 
  55.     case 's':    goto yy14; 
  56.     case '}':    goto yy15; 
  57.     default:    goto yy2; 
  58.     } 
  59.  
  60. Line:776 
  61. yy15: 
  62.     ++YYCURSOR; 
  63.     { 
  64.     /* this is the case where we have less data than planned */ 
  65.     php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data"); 
  66.     return 0; /* not sure if it should be 0 or 1 here? */ 

通过内核代码能够看到第655行进入反序列化,反序列化是利用词法扫描,判断各项符号转换对应对象。能够看到反序列化中对于}进行了处理,处理中只是对计数器加一并没有其他操作。

实际作用

反序列化语法的差异,对于安全防护设备判断反序列化产生很大的影响。在Snort中,有段规则如下:

  1. alert tcp any any -> any [80,8080,443] (uricontent:".php"; pcre:"/\{\w:.+?\}/"; sid:1; msg:php_serialize;) 

在攻击载荷中可以使用大多数字符代替{},从而导致规则失效。

总结

在红队攻击中可以利用PHP序列化和反序列化语法差异,从而达到绕过防护的目的。

在蓝队防御中建议考虑定义中所述不会保存对象的方法,只会保存类的名字。,拦截保存类的名字,以及语法中相同的字符比如冒号进行防御。

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/150766.html<

(0)
运维的头像运维
上一篇2025-03-13 05:46
下一篇 2025-03-13 05:47

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注