CTF-位运算绕过报错

博客探讨了在CTF挑战中遇到的PHP位运算绕过报错问题。通过分析,揭示了PHP对数字字符串的处理方式,特别是位运算符如何处理不同类型的操作数。在位运算中,尽管字符串PhPhhff和数字0能够进行运算,但由于0H0INbO被解析为数字0和字符串H0INbO,导致解析错误。博客深入研究了PHP的类型转换和位运算规则,解释了错误产生的真正原因。
摘要由CSDN通过智能技术生成
<?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 类型声明等),则采取以下步骤来确定结果:

  1. 如果 string 是数字,当 string 是整数字符串并且符合 int 类型的范围限制(即是 PHP_INT_MAX 定义的值),则解析为 int ,否则解析为 float 。
  2. 如果上下文允许前导数字和一个 string,如果 string 的前导部分是整数数字字符串且符合 int 类型限制(由 PHP_INT_MAX 定义),则解析为 int ,否则解析为 float 。 此外,还会导致 E_WARNING 级别的错误。
  3. 如果 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。

仔细想想,上面的猜测确实有问题。

  1. 在位运算符判断类型的时候,为什么不是从第一个参数开始判断,而是从第二个参数开始?
  2. 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的原因在这,程序没有得到希望的右括号。

这才是这个问题的真正原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ScyCree

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值