文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox-Retired

HackTheBox-Active

VulnHub

代码审计

PHP代码审计

大数据安全

机器学习

基础学习

Python

Python基础

Python安全

Java

Java基础

Java安全

算法

Leetcode

随笔

经验

技术

 2020-07-14   1k

PHP复杂变量解析学习

0x01 基础知识

单引号和双引号的区别

1
2
3
4
<?php
$test='hello world!';
echo "$test";
echo '$test';

输出的结果是

1
2
hello world!
$test

也就是说,PHP中的单引号会直接解析成字符串,而如果是双引号,那么PHP会首先去看双引号里面有没有变量,如果有变量的话那么会先进行变量解析,即替换成它的值。

变量的识别

首先看一下文档里面是怎么定义复杂解析的

举一个例子容易了解花括号的作用:

1
2
3
4
5
<?php
$test="hello world!";
echo "${test}";
echo "<br>";
echo "{$test}";

输出的结果是:

1
2
hello world!
hello world!

另外一个例子:

1
2
3
<?php
$fruit="apple";
echo "there are many {$fruit}s";

输出结果:

1
there are many apples

结合两个例子可以知道,花括号起到标记变量的界限的作用,在第二个例子中如果没有花括号那么php将会把变量识别为$fruits进而使结果异常。另外也说明php会尽量多地取组合可用的字符作为变量名。

花括号中使用函数,方法,静态类变量和类常量

1
2
3
4
5
6
7
8
<?php
$Hed9eh0g="test";
function getname(){
echo "Hed9eh0g";
echo "<br>";
return "Hed9eh0g";
}
echo "${getname()}";

输出结果:

1
2
Hed9eh0g
test

可以看出在双引号包裹的${}这种形式内部如果有方法名,则这个方法是可以执行的,本例子中首先执行了echo语句,然后再return对应的值Hed9eh0g,与外层形成新的结果​${Hed9eh0g},此时php会将其识别为一个变量,然后进行解析并替换成其对应的结果。
如果再在getname()外层包裹一层​${},那么结果将会报错,因为根据刚才的推理可知php最终会识别$test为一个变量,而这是一个我们没有定义过的变量。
虽然会报错,但是不影响getname函数的echo语句的执行。

0x02 练习

1
2
3
4
5
6
<?php
highlight_file(__FILE__);

$str = @(string)$_GET['str'];
eval('$str = "'.addslashes($str).'";');
?>

两种payload,分别是:?str={${phpinfo()}}?str=${${phpinfo()}}

eval函数将字符串当作php代码执行,因此,通过图中代码清晰可见相当于定义了str变量,赋值为一个字符串{${phpinfo()}}

$str = "{${phpinfo()}}",花括号定义了变量的边界,因此该条语句先执行括号中内容,获取函数返回值,并以返回值的string命名变量再赋值给str变量。

并且第二个payload会报一个错误

函数phpinfo,会返回 true,因为 true,是bool类型的变量,然后进行类型转化,转化为字符串1,所以调用的参数就是$1,这也是上面第二个payload报错为Undefined variable: 1的原因。

0x03 继续思考

如果将addslashes 用单引号包裹

1
2
3
4
5
6
<?php
highlight_file(__FILE__);

$str = @(string)$_GET['str'];
eval("$str = '".addslashes($str)."';");
?>

再试一下这两个payload

{${phpinfo()}}执行失败

${${phpinfo()}}执行成功。

复杂变量解析的前提是双引号包裹,第二次实验中,addslashes函数部分被单引号包裹,所以只是简单的字符串,但是前面$str却是被双引号包裹,所以可以进行复杂变量解析。
那么payload1的{${phpinfo()}}和payload2的​${${phpinfo()}}看样子都可以进行解析之后执行函数?但事实上却只有payload2可以实现。问题出在哪?
这里注意了,​$str此时是左值,也即位于赋值语句的左侧,而左值必须得是一个变量,也即必须由字符$开头,显然payload1的开头字符是{,因此它压根不是一个变量,此时前面我们所谈的变量解析的特性都没有用。

0x04 参考文章

AD攻防实验室

https://www.anquanke.com/post/id/176331#h2-4

Copyright © ca01h 2019-2021 | 本站总访问量