1 |
|
输出的结果是
1 | hello world! |
也就是说,PHP中的单引号会直接解析成字符串,而如果是双引号,那么PHP会首先去看双引号里面有没有变量,如果有变量的话那么会先进行变量解析,即替换成它的值。
首先看一下文档里面是怎么定义复杂解析的
举一个例子容易了解花括号的作用:
1 |
|
输出的结果是:
1 | hello world! |
另外一个例子:
1 |
|
输出结果:
1 | there are many apples |
结合两个例子可以知道,花括号起到标记变量的界限的作用,在第二个例子中如果没有花括号那么php将会把变量识别为$fruits进而使结果异常。另外也说明php会尽量多地取组合可用的字符作为变量名。
1 |
|
输出结果:
1 | Hed9eh0g |
可以看出在双引号包裹的${}
这种形式内部如果有方法名,则这个方法是可以执行的,本例子中首先执行了echo语句,然后再return对应的值Hed9eh0g,与外层形成新的结果${Hed9eh0g}
,此时php会将其识别为一个变量,然后进行解析并替换成其对应的结果。
如果再在getname()外层包裹一层${}
,那么结果将会报错,因为根据刚才的推理可知php最终会识别$test为一个变量,而这是一个我们没有定义过的变量。
虽然会报错,但是不影响getname函数的echo语句的执行。
1 |
|
两种payload,分别是:?str={${phpinfo()}}
和?str=${${phpinfo()}}
eval函数将字符串当作php代码执行,因此,通过图中代码清晰可见相当于定义了str变量,赋值为一个字符串{${phpinfo()}}
。
$str = "{${phpinfo()}}"
,花括号定义了变量的边界,因此该条语句先执行括号中内容,获取函数返回值,并以返回值的string命名变量再赋值给str变量。
并且第二个payload会报一个错误
函数phpinfo,会返回 true
,因为 true
,是bool类型的变量,然后进行类型转化,转化为字符串1
,所以调用的参数就是$1
,这也是上面第二个payload报错为Undefined variable: 1
的原因。
如果将addslashes 用单引号包裹
1 | <?php |
再试一下这两个payload
{${phpinfo()}}
执行失败
${${phpinfo()}}
执行成功。
复杂变量解析的前提是双引号包裹,第二次实验中,addslashes函数部分被单引号包裹,所以只是简单的字符串,但是前面$str
却是被双引号包裹,所以可以进行复杂变量解析。
那么payload1的{${phpinfo()}}
和payload2的${${phpinfo()}}
看样子都可以进行解析之后执行函数?但事实上却只有payload2可以实现。问题出在哪?
这里注意了,$str
此时是左值,也即位于赋值语句的左侧,而左值必须得是一个变量,也即必须由字符$开头,显然payload1的开头字符是{,因此它压根不是一个变量,此时前面我们所谈的变量解析的特性都没有用。