之前就对字符串逃逸这一块理解的不是很深刻,下面通过一位师傅的博客来进一步深入理解一下有关php字符串逃逸的相关内容。
贴上师傅的博客地址:https://blog.csdn.net/qq_45521281/article/details/107135706
先来说一下什么是字符串逃逸,就是我们可以构造一些恶意代码,让其在反序列化的时候执行我们想让它执行的。在这过程中造成的字符串的增加或者减少称为字符串逃逸。
替换修改后导致字符串长度增加 (逃逸增加)
先来一个代码简单的分析一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php function filter($str){ return str_replace('bb','ccc',$str); } class A{ public $name='aaaa'; public $pass='123456'; } $AA=new A(); echo serialize($AA)."\n"; $res=filter(serialize($AA)); $c=unserialize($res); echo $c->pass; ?> 看它的返回结果: O:1:"A":2:{s:4:"name";s:4:"aaaa";s:4:"pass";s:6:"123456";} 123456
|
若我们在不直接修改pass的前提下,让pass的值变成hacker,我们该如何实现呢?
这里我们就用到了字符串逃逸增加来实现。
我们可以看到str_replace(‘bb’,’ccc’,$str); 意思就是在变量str中,将每两个b替换成三个c,但序列化之后的字符长度仍然按照没有被替换的。
举个例子说明一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php function filter($str){ return str_replace('bb','ccc',$str); } class A{ public $name='aaaabb'; public $pass='123456'; } $AA=new A(); echo serialize($AA)."\n"; $res=filter(serialize($AA)); echo $res; ?> 运行的结果如下: O:1:"A":2:{s:4:"name";s:6:"aaaabb";s:4:"pass";s:6:"123456";} O:1:"A":2:{s:4:"name";s:6:"aaaaccc";s:4:"pass";s:6:"123456";}
|
根据上面的演示,我们可以进行构造代码,通过bb替换成ccc,来进行字符串的逃逸,从而让pass的值为hacker
下面是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php function filter($str){ return str_replace('bb','ccc',$str); } class A{ public $name='bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"hacker";}'; public $pass='123456'; } $AA=new A(); echo serialize($AA)."\n"; $res=filter(serialize($AA)); $c=unserialize($res); echo $c->pass;
?> 运行的结果: O:1:"A":2:{s:4:"name";s:81:"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"hacker";}";s:4:"pass";s:6:"123456";} hacker 成功的实现目标。
|
思想就是我们要逃逸出来的字符串是”;s:4:”pass”;s:6:”hacker”;} 总共27个字符串,也就是说需要逃逸27个字符串出来,之前是2b逃逸出来一个,那么这需要逃逸27个,也就需要54b。
替换修改后导致字符串长度减少 (逃逸减少)
字符串逃逸减少就是字符串经过一些变化后,字符长度减少,原来的功能性代码变成普通的字符串,然后我们可以利用逃逸进行构造一些恶意的代码。
下面来一个例子看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php function str_rep($string){ return preg_replace('/php|test/','',$string); } $test['name']=$_GET['name']; $test['sign']=$_GET['sign']; $test['number']='2020'; $temp=str_rep(serialize($test)); printf($temp); $fake=unserialize($temp); echo '<br>'; print("name:".$fake['name'].'<br>'); print("sign:".$fake['sign'].'<br>'); print("number:".$fake['number'].'<br>'); ?>
|
代码都是上面师傅里面演示的,这里我是根据自己的理解来讲一下。
把这段代码放在本地,可以看到结果为:
这里可以看到name和sign里面都是没有内容的,所以这里是N。
然后我们看到代码中的str_rep()函数,是将变量string中的php和test替换成空。
然后下面有一个经过这段函数的反序列化,这里就构成了反序列化逃逸减少的漏洞。
反序列化逃逸减少不同于增加。
增加是看要构造的恶意代码字符串长度是多少,就举一个例子,如果匹配到了bb,函数是把它替换成ccc,那么就相当于每一个bb可以逃逸出1个字符,如果我们要构造的恶意代码长度是27位,那么我们就需要54个bb。
而减少我个人感觉是比增加复杂一点,但也不多。拿上面的代码进行演示,如果我想让sign的值是eval,并且数字改成2023,那么这就利用到了字符串逃逸减少。
首先构造我们要实现的目标。”;s:4:”sign”;s:4:”eval”;s:6:”number”;s:4:”2023”;}
减少逃逸是让一些功能性代码变成字符串,这里是让sign变成字符串,而后面构造的sign和number则成为新的功能性代码。
“;s:4:”sign”;s:xx:”前面的字符一共有19个,我们有两种方法,一种是利用php,另外一种是利用test只不过都需要填充字符罢了 “;s:4:”sign”;s:4:”eval”;s:6:”number”;s:4:”2023”;}
“;s:4:”sign”;s:xx:”这里是需要填充的字符”;s:4:”sign”;s:4:”eval”;s:6:”number”;s:4:”2023”;}
如果用7个php,也就是可以逃逸21个,上面是19个,那么我们可以填充2个字符。
如果用5个test,逃逸20个,需要填充一个字符。
结果如上。