ocd+i反序列化正则bypass

bypass反序列化正则

当执行反序列化时,使用正则 "/[oc]:\d+:/i" 进行拦截,代码见图 3-1-20,主要拦截了这类反序列化字符:

O:4:"demo":1:{s:5:"demoa";a:0:{}} 

图 3-1-20 代码:

function sugar_unserialize($value) 
{ 
preg_match('/[oc]:\d+:/i', $value, $matches); 
if (count($matches))
{ 
return false; 
} 
return unserialize($value); 
} 

##这个正则表达式是用来防御 PHP 反序列化漏洞的,它专门用来拦截序列化字符串中的对象(Object)和自定义类(Class)。
我们来逐字拆解这个正则表达式 /[oc]:d+:/i 的含义:

正则表达式拆解:
1. / ... /i
   - 这是正则表达式的定界符。
   - 末尾的 i 代表不区分大小写。这意味着 o 和 O、c 和 C 都会被匹配。

2. [oc]
   - 这是一个字符集合,表示匹配字母 o 或者字母 c。
   - 在 PHP 序列化字符串的格式中:
     - O 代表 Object(对象)。
     - C 代表 Custom Object(自定义对象,通常用于 SplFixedArray 或实现了 Serializable 接口的类)。

3. :
   - 匹配一个冒号字符。

4. d+
   - d 代表匹配任意数字(0-9)。
   - + 代表匹配一次或多次。
   - 这表示匹配类名的长度(例如 5 或 12)。

5. :
   - 匹配后面的第二个冒号。

匹配的目标格式:
这个正则表达式旨在匹配 PHP 序列化字符串中定义对象的标准格式:

- O:8:"ClassName":1:{...}
  - O:表示这是一个对象。
  - :8::表示类名 "ClassName" 的长度是 8。
  - 正则中的 [oc]:d+: 正好对应 O:8: 这部分结构。

- C:16:"MyClass":100:{...}
  - C:表示这是一个自定义对象。
  - 正则同样匹配 C:16:。

防御原理:
PHP 反序列化漏洞(如 unserialize() 导致的代码执行)通常利用的是魔术方法(如 wakeup、destruct)。这些魔术方法只有在实例化对象时才会被触发。
- 如果攻击者只能传入数组(a:)或字符串(s:),通常无法触发代码执行。
- 只有传入对象(O: 或 C:),才能利用 POP 链(面向属性编程)触发漏洞。
结论:
这个正则表达式通过禁止输入中包含对象定义(即把 O:... 和 C:... 拦截掉),强制 unserialize() 函数无法生成对象,从而从根本上阻断了大多数 PHP 反序列化攻击链。

这是反序列中最常见的一种形式,那么如何进行绕过呢?通过对 PHP 的 unserialize() 函数进行分析,发现 PHP 内核中最后使用 php_var_unserialize 进行解析,代码见图 3-1-21。

图 3-1-21 代码:

case 'O': goto yy13; 
yy13: 
    yych = *(YYMARKER++YYCURSOR); 
    if (yych == ':') goto yy17;
    goto yy3; 
yy17: 
    yych = *++YYCURSOR; 
    if (yybm[0+yych] & 128) { 
    goto yy20; 
    } 
    if (yych == '+') goto yy19; 
yy19: 
    yych = *++YYCURSOR; 
    //判断字符是否为数字 
    if (yybm[0+yych] & 128) {
    goto yy20;
    } 
    goto yy18; 

上面的代码主要是解析 "O:" 语句段,跟入 yy17 段中,还会存在 "+" 的判断。所以,如果输入 O:+4:"demo":1:{s:5:"demoa";a:0:{}},可以看到当 "O:" 后面为 "+" 时,就会从yy17 跳转到 yy19 处理,然后继续对 "+" 后面的数字进行判断,意味着这是支持 "+" 来表达数字,从而对上面的正则进行绕过。

posted @ 2026-06-18 16:08  Doll_Marker  阅读(8)  评论(0)    收藏  举报