浅谈php序列化字符串逃逸问题

0x00 前言

这几天做了两道有关php反序列化字符逃逸的题目,故来自己本地实验总结一下,以后可以时不时拿起来看.

这种题目有个共同点:

  1. php序列化后的字符串经过了替换或者修改,导致字符串长度发生变化.
  2. 总是先进行序列化,再进行替换修改操作.

经典题目:

  1. [0CTF 2016]piapiapia (替换变长)
  2. [安洵杯 2019]easy_serialize_php (替换变短)

0x01 漏洞浅析

第一种情况:替换修改之后导致序列化字符串长度变长

  1. 先看一段php序列化的代码(本代码基于7.0.9版本的php,经过测试较低版本的php会使"被转义导致实验失败:
  2. 代码功能很简单,输入name,并和sign一同传入到user数组中,user数组序列化后的字符串经过test函数检测之后,输出反序列化之后的结果.
  3. 关于php反序列化引擎在进行反序列化的时候是以字符长度来进行判断的,关于这一点,前言中的参考文章已经解释的很详细了,这里不再赘述.
  4. 下面进行漏洞测试,通过修改name的值从而改变sign的值.
    1. 关键点在于test函数,这个函数检测并替换了非法字符串,看似增加了代码的安全系数,实则让整段代码更加危险.test函数中检测序列化后的字符串,如果检测到了非法字符'x',就把它替换为'ha'.
    2. 如果输入name=evALx
    3. 可以看到被替换成了'evALha',这个字符串长度为6,从而导致反序列化失败,无法输出结果.利用这个漏洞,就可以对sign的值进行修改.输入name=evALxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";i:1;s:14:"hello hackerrr";}
    4. 可以就看到sign的值被成功修改.
  5. 原因分析
    1. test函数把'x'替换成'ha',换句话来说,就是把一个字符替换成了两个字符,造成了序列化字符串的"膨胀".
    2. 首先来考虑要注入的数据,在这个例子中把sign替换为"hello hackerrr",这个字符串在本实验的序列化结果是i:1;s:14:"hello hackerrr,由于要闭合name的双引号以及结束的花括号,所以payload应该是";i:1;s:14:"hello hackerrr";}
    3. 下面来考虑长度溢出,payload的字符串的长度是29,所以要在name中输入29个x,再加上evAL,整体长度就是62,在经过test函数替换之后,x被替换成了ha,如下图所示:
    4. 溢出的部分成功逃逸,经过双引号闭合name,以及闭合结束时的花括号,导致sign被成功修改.

第二种情况:替换之后导致序列化字符串长度变短

  1. 本地实验代码如下:

  2. 输入name和sign,number值是固定的'2020',经过[序列化->敏感字替换为空(长度变短)->反序列化]的过程之后再输出结果.

  3. 通过输入name和sign修改number:
    payload:name=testtesttesttesttesttest&sign=hello";s:4:"sign";s:4:"eval";s:6:"number";s:4:"2000";}

  4. 原因分析:
    在str_rep函数中如果检测到'php','test'关键字就把其替换为空,那么就利用这一点,我们故意输入敏感字符,替换为空之后来实现字符逃逸.(注意考虑闭合问题)

posted @ 2020-05-11 17:39  泠涯  阅读(923)  评论(1编辑  收藏  举报