<?php
echo "\xfa\xfa\xfa\xfa\xfa\xfa\xfa"^"\x8a\x92\x8a\x93\x94\x9c\x95";
echo "\x50\x68\x50\x68\x68\x66\x66"|"\x30\x48\x30\x49\x4e\x62\x4f";
echo urldecode('%30%48%30%49%4e%62%4f');
$v3=$_GET['v3'];
eval("$v3;");
$v2=$_GET['v2'];
eval("$v2;");
?v3=(%fa%fa%fa%fa%fa%fa%fa^%8a%92%8a%93%94%9c%95)()&v2=(%50%68%50%68%68%66%66|%30%48%30%49%4e%62%4f)()
猜猜为啥这俩都是phpinfo(),v3能跑出来,v2就不行?
因为php的位运算符不是对字节使用,而是对数字使用
哈,他妈了个逼的
猜猜%30是啥字符?
哦,%30是字符0
猜猜字符0放开头会咋样?
没错,他没了,他直接就没了。
然后就成了7字节与6字节,然后就顺理成章的报错了
深挖了一下,还不是这个原因
什么叫他妈的惊喜,这就叫他妈的惊喜
怎么解决?%30换成%70,这俩和%50与出来结果一样的
最后附上我看了整整三个小时的报错:
Parse error: syntax error, unexpected ‘H0INbO’ (T_STRING) in D:\phpstudy_pro\WWW\index.php(8) : eval()'d code on line 1
且慢,好像问题并没有这么简单。
如果是%30被识别为0导致的,那么第三个字符应该也不正常才对,因为第三个字符也用了%30。
v2的两段字符分别是:PhPhhff | 0H0INbO
然后深挖了一下,作出了一个错误的猜测:
php是弱类型语言,所以开头是数字的字符串其实是一个非常玄学的东西,官方称之为数字字符串。
https://www.php.net/manual/zh/language.types.numeric-strings.php
当一个 string 需要被当作一个数字计算时,(例如:算术运算, int 类型声明等),则采取以下步骤来确定结果:
- 如果 string 是数字,当 string 是整数字符串并且符合 int 类型的范围限制(即是 PHP_INT_MAX 定义的值),则解析为 int ,否则解析为 float 。
- 如果上下文允许前导数字和一个 string,如果 string 的前导部分是整数数字字符串且符合 int 类型限制(由
PHP_INT_MAX
定义),则解析为 int ,否则解析为 float 。 此外,还会导致E_WARNING
级别的错误。- 如果 string 不是数字,则会抛出一个 TypeError 的异常。
由第二条可知,数字字符串会根据上下文决定自己是个字符串还是个数字,两者都可时优先解析为数字。
巧合的是,位运算符恰好可以对字符串和数字两种类型进行运算,两边是字符串时对ascii码进行位运算,是数字时对数字进行位运算。
https://www.php.net/manual/zh/language.operators.bitwise.php
如果
&
、|
和^
运算符的左右两个操作对象都是字符串,将对会组成字符串的字符 ASCII 值执行操作,结果也是一个字符串。除此之外,两个操作对象都将 转换为整数 ,结果也将会是整数。
所以,数字字符串并不能根据所谓的上下文*(位运算符)*来确定自己的类型,于是就优先解析为数字了。
在解析为数字的时候,程序自动去除数字前方的0和前缀*(这里的0应该是解析为8进制表示的前缀)*,保留后面的有效数字,问题就出在这里。
在去除掉第一个0的时候,程序期待从后面得到一个8进制数字,但却得到了一个字符串H0INbO。
所以,程序报错:
Parse error: syntax error, unexpected ‘H0INbO’ (T_STRING) in D:\phpstudy_pro\WWW\index.php(8) : eval()'d code on line 1
听上去挺有道理哈?
但上边全部的猜测都是错的。
在实际测试中,我把后面的0H0INbO换成了01H0INbO。
如果报错变成了预期在|
之前得到一个数字,但却得到了一个字符串的话,就证明我的猜测是正确的。
但实际情况是,报错没变,0换成任何数字都不变,都只报数字后面的6位字符串。
甚至当我在PhPhhff前方加上数字时,报错变成了PhPhhff。
仔细想想,上面的猜测确实有问题。
- 在位运算符判断类型的时候,为什么不是从第一个参数开始判断,而是从第二个参数开始?
- php识别数字字符串的值会先忽略掉后面的字符,而不是带着字符开始转换。
当我把eval里的东西拿出来,直接放到vscode里的时候,看到代码高亮,我悟了。
0H0INbO根本就不是一个字符串。
PhPhhff是一个字符串不假,PhPhhff即使没有引号,是一种不规范的写法,php仍然将其识别为一个字符串。
但是0H0INbO,他被识别为一个数字0,和一个字符串H0INbO。
所以程序报:
Parse error: syntax error, unexpected ‘H0INbO’ (T_STRING) in D:\phpstudy_pro\WWW\index.php(8) : eval()'d code on line 1
,是因为程序在位运算符后接收到数字0之后,就接收到了全部两个需要的参数,即使他们类型不同,但前面的字符串已经被转换成了ascii码组成的二进制数字。他们仍然可以进行运算。
此时,程序希望得到一个右括号)
来结束语句,因为就(“system”)(“ls”)这种形式的命令来说,system位置的字符串已经由PhPhhff和0的与运算得到了结果。
但程序得到了被识别为字符串的H0INbO。
所以程序报unexpected
的原因在这,程序没有得到希望的右括号。
这才是这个问题的真正原因。