函数特性 | 绕过的空白字符
前言
最近在整理php弱类型,发现函数缺陷在弱类型中还是挺有意思的,遂找了is_numeric函数来试试手。挺有收货的,不断补全自己的知识盲区。
使用的空格符替换
控制码
“\0” “%00” (ASCII 0 (0x00)),空字节符。
制表符
“\t” (ASCII 9 (0x09)),水平制表符。
空白字符:
“\n” (ASCII 10 (0x0A)),换行符。
“\v” “\x0b” (ASCII 11 (0x0B)),垂直制表符。
“\f” “%0c” 换页符
“\r” “%0d”(ASCII 13 (0x0D)),回车符。
空格:
“ “ “%20” (ASCII 32 (0x20)),普通空格符。
源码查找方法
现在ext找到方法在文件中的定义名,然后在Zend文件中查找
php-5.6.30/ext/standard/string.c
$ sudo grep -rn "PHP_FUNCTION(is_numeric)"
->standard/type.c:314:PHP_FUNCTION(is_numeric)
->standard/php_type.h:35:PHP_FUNCTION(is_numeric);
php-5.6.30/Zend/zend_operators.c
$ sudo grep -rn "is_numeric_string"
Is_numeric源码分析
Is_numeric只是判别是否为数,而不是修改数。这是大前提,通过源码我们可以得知。
static inline zend_uchar is_numeric_string_ex(const char *str, int length, long *lval, double *dval, int allow_errors, int *oflow_info)
{
const char *ptr;
int base = 10, digits = 0, dp_or_e = 0;
double local_dval = 0.0;
zend_uchar type;
if (!length) {
return 0;
}
if (oflow_info != NULL) {
*oflow_info = 0;
}
/* Skip any whitespace
* This is much faster than the isspace() function */
while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') {
str++;
length--;
}
ptr = str;
//判断正负
if (*ptr == '-' || *ptr == '+') {
ptr++;
}
if (ZEND_IS_DIGIT(*ptr)) {
/* Handle hex numbers
* str is used instead of ptr to disallow signs and keep old behavior */
//判断十六进制
if (length > 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) {
base = 16;
ptr += 2;
}
/* Skip any leading 0s */
while (*ptr == '0') {
ptr++;
}
/* Count the number of digits. If a decimal point/exponent is found,
* it's a double. Otherwise, if there's a dval or no need to check for
* a full match, stop when there are too many digits for a long */
for (type = IS_LONG; !(digits >= MAX_LENGTH_OF_LONG && (dval || allow_errors == 1)); digits++, ptr++) {
check_digits:
if (ZEND_IS_DIGIT(*ptr) || (base == 16 && ZEND_IS_XDIGIT(*ptr))) {...............
分析源代码:
Is_numerci()代码执行流程
- 处理空格字符串
- 判断传参正负
- 十六进制识别
- 识别小数和科学计数法
is_numeric while缺陷利用
利用在ctf中展示
## 空格字符处理
Is_numeric() 在执行前会跳过所有空格字符,保留原来空格字符长度增大。这样处理对数值判断是没有影响。
验证Demo:
='0' && ($_GET['num']) <= '9'){ echo "True"; } else{ echo "False"; } ?>数值判断方法
#define ZEND_IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
如果没有绕过while对字符串的处理那么函数is_numeric返回为FALSE
CTF
<?php
$info = "";
$req = [];
$flag="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
ini_set("display_error", false); //为一个配置选项设置值
error_reporting(0); //关闭所有PHP错误报告
if(!isset($_GET['number'])){
header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txt
die("have a fun!!"); //die — 等同于 exit()
}
foreach([$_GET, $_POST] as $global_var) { //foreach 语法结构提供了遍历数组的简单方式
foreach($global_var as $key => $value) {
$value = trim($value); //trim — 去除字符串首尾处的空格字符(或者其他字符)
is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串
} //存入数组req中value
}
function is_palindrome_number($number) {
$number = strval($number); //strval — 获取变量的字符串值
$i = 0;
$j = strlen($number) - 1; //strlen — 获取字符串长度
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}
if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串
{
$info="sorry, you cann't input a number!";
}
elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值.这里要做的是使得elseif False。空白字符绕过
{
$info = "number must be equal to it's integer!! ";
}
else
{
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"]));//strrev 反转字符串
if($value1!=$value2){
$info="no, this is not a palindrome number!";
}
else
{
if(is_palindrome_number($req["number"])){
$info = "nice! {$value1} is a palindrome number!";
}
else
{
$info=$flag;
}
}
}
echo $info;
分析代码
- 循环遍历数组foreach去除字符串首尾空格,转义字符串。
- 方法 is_palindrome_number()判断是否为回文数。
- 之后的代码才是重点通过矛盾的if/else语句得到flag
我们的目标是得到$info=$flag
最后一个else语句
我首先想到是科学计数法(我也不知为什么),但发现不可以,看完之前对is_numeric源码分析,发现判断无关整数的类型。
题目要求回文整数
1. 传入number=191
sorry, you cann't input a number!
2. 分析is_numeric
使得while为False必须绕过’ ’,’\t’,‘\r’,’\v’,’f’
而条件$req['number']!=strval(intval($req['number']
因为弱类型会忽略空白格符号自动转换为整形比较数值
通过fuzz可得,可传入参数%00191
nice! 191 is a palindrome number!
//不需要考虑$value1!=$value2影响,因为他们被intval修饰!!!
3. 但是我们需要的是$info=$flag;需要绕过is_palindrome_number($req[“number”]
根据trim源代码分析少去掉的空格符号有\n \r \t \v \0
发现少了个\f
url编码为%0c
4. 传入参数?number=%00%0c191
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
成功得到flag
参考链接
Url编码规范:https://www.oschina.net/translate/what-every-web-developer-must-know-about-url-encoding
几期『三个白帽』小竞赛的writeup:https://www.leavesongs.com/PENETRATION/some-sangebaimao-ctf-writeups.html
PHP代码审计分段讲解:https://github.com/bowu678/php_bugs
总结
1.学习了怎么通过分析函数源代码了解函数缺陷
2.第一次分析条件矛盾的ctf,刚开始确实很晕但是理清楚函数之间的特异性。就能理清他为什么要设置这样的矛盾
3.学习了url编码
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!