[WUSTCTF 2020]朴实无华:PHP 下的 intval 绕过技巧

文章目录

  • 参考
  • 环境
  • intval()
      • 主角登场
      • 截断而非四舍五入
      • 进制转化
      • 字符串解析规则
          • 十进制字符串
          • 其他进制
  • 科学计数法
      • e/E 表示法
  • intval() 在 PHP 不同版本中的表现差异
      • PHP7.2.5 版本及以上
      • PHP7.2.5 版本以下
  • [WUSTCTF 2020]朴实无华
      • 可能性分析
      • 绕过

参考

项目 描述
搜索引擎 BingGoogle
AI 大模型 文心一言通义千问讯飞星火认知大模型ChatGPT
PHP 手册 PHP Manual
WriteUp NSSCTF

环境

项目 描述
PHP 5.5.05.6.87.0.07.2.57.4.98.0.08.2.9

intval()

主角登场

在 PHP 中,intval() 函数用于将其他类型的数据转化为整型数据。

intval(mixed $value, int $base = 10): int

其中:

项目 描述
$value 需要使用 intval() 进行转化的数据,该参数的值可以是一个整型数值,但转化结果与原数据不会存在任何区别(由该函数的功能决定)。
$base 指定被转化数据 $value 采用的进制(默认为十进制),intval() 将据此解析 $value 并将其转化为 十进制整型数值

截断而非四舍五入

intval() 函数将 浮点数(含有小数点的数值) 转化为 整数数值 的方式是以小数点为基准将浮点数分为两部分,原浮点数的整数部分即为转化结果。对此,请参考如下示例:




var_dump(intval('948.53975'));
var_dump(intval(0.454397));

执行效果

int(948)
int(0)

进制转化

intval() 函数的 $base 参数仅在 $value 参数的数据类型为 字符串 时生效。因此,当你需要将一个 采用 $base 进制的数值 转化为 十进制数值 时,需要 先将该数值转化为字符串 再交由 intval() 函数进行处理。




# 尝试将八进制数值 36363.33, 8 转化为十进制整数值
var_dump(intval(36363.33, 8));
# 尝试将字符串(包含八进制数值) 36363.33 转化为十进制整数值
var_dump(intval('36363.33', 8));

执行效果

int(36363)
int(15603)

字符串解析规则

十进制字符串

$base 参数的值为默认值 10 时且 $value 参数的值为字符串类型的数据时,intval() 函数的解析规则如下:

  1. 若字符串的 首个字符不为数字且不为空格等空白字符,则将该字符串转化为零。
  2. 若字符串的 首个字符不为数字但为空格等空白字符,则尝试读取其余字符,忽略遇到到数字字符前的所有空白字符,在遇到非数字字符时停止对字符串的读取并将已读取字符转化为数值
  3. 若字符串的 首个字符为数字,则尝试读取其余字符,在遇到非数字字符(除符合科学计数法格式的字符 e 或 E外)时停止对字符串的读取并将已读取字符转化为数值

举个栗子




var_dump(intval(' 457.935HELLO'));
var_dump(intval('Hello45995'));
var_dump(intval('  0000053.0494   '));
var_dump(intval('0x2f'));
var_dump(intval('0b10101'));

执行效果

int(457)
int(0)
int(53)
int(0)
int(0)
其他进制

intval() 函数的 $base 参数的值不为默认值且 $value 的数据类型为字符串时,intval() 的解析规则以十进制字符串的解析规则为基础,正常解析其他进制下的特有数字字符(十六进制数包含 abc 等特有数字字符)及前缀(如以 0x0X 为前缀的数值常用于表示十六进制数)。 对此,请参考如下示例:




# 十六进制字符串
var_dump(intval('   0X2FHELLO', 16));
var_dump(intval('00000x2FHELLO', 16));
var_dump(intval('2FHELLO', 16));
var_dump(intval('0x18', 16));
# 十进制字符串
var_dump(intval('0x18'));

# 二进制字符串
var_dump(intval('0b03940', 2));
var_dump(intval(' 0B1010101', 2));
var_dump(intval('H010101', 2));
var_dump(intval('1010101', 2));

执行效果

int(47)
int(0)
int(47)
int(24)
int(0)
int(0)
int(85)
int(0)
int(85)

注:

在使用 intval() 处理其他进制的字符串时,需要注意到字符串中的 数值部分 是否以相对应的前缀开头。十六进制数值以 0x0X 为前缀,而不是 0000X70x 等。




var_dump(intval('0x2b', 16));
var_dump(intval('0000x2b', 16));
var_dump(intval('    0X2B', 16));

var_dump(intval('0b10011', 2));
var_dump(intval('0000B10011', 2));
var_dump(intval('    0B10011', 2));

执行效果

int(43)
int(0)
int(43)
int(19)
int(0)
int(19)

科学计数法

e/E 表示法

在 PHP 中,eE 均表示 科学计数法(Scientific Notation)。科学计数法由 基数指数 两部分组成,常用于 表示非常大或非常小的数值。

在科学计数法中,基数 通常 是一个浮点数,介于 110 之间,而指数是一个整数,表示要将基数乘以 10 的多少次方。基数与指数之间以字符 eE 进行分隔。

举个栗子




# 1 * 10 ^ 2
var_dump(intval('1E2'));
# 3.688 * 10 ^ 2
var_dump(intval('3.688e2'));
# 9999 * 10 ^ -3
var_dump(intval(9999E-3));

执行效果

上述示例在 PHP8.0.0 中执行,得到结果如下:

int(100)
int(368)
int(9)

intval() 在 PHP 不同版本中的表现差异

PHP7.2.5 版本及以上

intval()PHP7.2.5 及以上版本 中的表现符合预期,与我们对科学计数法的认知相符。

PHP7.2.5 版本以下

intval() 函数PHP7.2.5 以下的某个版本以前(仅测试了 PHP7.2.5 与 PHP7.0.0,PHP7.0.0 中,intval() 的表现存在异常,而在 PHP7.2.5 则表现正常。) 表现异常,intval() 的异常行为具体如下:




var_dump(intval('1E2'));
var_dump(intval(1E2));

var_dump(intval('3.688e2'));
var_dump(intval(3.688e2));

var_dump(intval('9999E-3'));
var_dump(intval(9999E-3));

执行效果

int(1)
int(100)
int(3)
int(368)
int(9999)
int(9)

PHP7.2.5 以下的某个版本 以前,intval() 函数无法正确解析字符 Ee,于是 intval 在解析到 Ee 时立即停止,这导致科学计数法实际并未生效。

[WUSTCTF 2020]朴实无华

可能性分析

2020 年举办的 CTF 比赛 WUSTCTF 中存在一道名为 朴实无华WEB 题,其中存在这么一段代码:

if(intval($num) < 2020 && intval($num + 1) > 2021){
	echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.
"
; }else{ die("金钱解决不了穷人的本质问题"); }

我们需要传递一个 字符串 作为 $num 的值使得判断语句成立以 使得程序继续进行,而不是因为 die() 函数 的执行而停止。

从逻辑上来说,没有一个数值(字符串形式)可以使得 $num 满足判断语句。但请不要忘记,intval() 在某些 PHP 版本中 对使用科学计数法的数值(字符串)无法正常解析,这也就产生了绕过的可能。

绕过

$num 在第二个 intval() 函数中以 表达式 的形式出现,字符串在与数值进行运算时,PHP 会将字符串转化为数值使用科学计数法的数值(字符串) 在某些版本中无法被 intval() 正确解析,但 PHP 是认得它的,在与数值 1 进行加法运算时,$num 将被 PHP 正确解析。于是,我们尝试将 $num="1e4"。对此,请参考如下示例:




$num = '1e4';

if(intval($num) < 2020 && intval($num + 1) > 2021){
    echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.
"
; }else{ die("金钱解决不了穷人的本质问题"); } # 测试 print("\n"); var_dump(intval($num)); var_dump(intval($num + 1));

执行效果

尝试在 PHP7.0.0 版本下执行上述代码,得到如下结果:

我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>
int(1)
int(10001)

你可能感兴趣的:(安全,php,版本差异,intval,函数,Bypass,CTF,科学计数法,intval,的解析规则)