php分段考试,PHP代码审计分段讲解(8)

本文探讨了一种PHP代码中的逻辑漏洞,通过利用十六进制编码和正则表达式的弱点,绕过了字符限制并获取了flag。学习者将理解如何构造payload来避开检测,以及如何在实际场景中应用这些技巧。

20 十六进制与数字比较

源代码为:

error_reporting(0);

function noother_says_correct($temp)

{

$flag = 'flag{test}';

$one = ord('1'); //ord — 返回字符的 ASCII 码值

$nine = ord('9'); //ord — 返回字符的 ASCII 码值

$number = '3735929054';

// Check all the input characters!

for ($i = 0; $i < strlen($number); $i++)

{

// Disallow all the digits!

$digit = ord($temp{$i});

if ( ($digit >= $one) && ($digit <= $nine) )

{

// Aha, digit not allowed!

return "flase";

}

}

if($number == $temp)

return $flag;

}

$temp = $_GET['password'];

echo noother_says_correct($temp);

?>

GET方式传入password的值,输出经过noother_says_correct函数处理之后的值。

noother_says_correct函数处理的逻辑为:先对password中的每一位进行判断,若在1--9之间,则直接返回false,若都满足,并且$number = '3735929054',$number==$password,则返回flag。

我们很容易能够注意到:

$number == $temp

这里两个等号,是弱类型比较,即会先将比较的两者类型进行转化后再比较,在计算机中0x开头的表示为十六进制,所以我们可以使用十六进制来绕过前面的for循环,同时满足等于temp

3735929054转换成十六进制后为:deadc0de

加上0x后为0xdeadc0de,可以看出可以逃逸出for循环中的限制,所以最后的payload为:

?password=0xdeadc0de

21 数字验证正则绕过

error_reporting(0);

$flag = 'flag{test}';

if ("POST" == $_SERVER['REQUEST_METHOD'])

{

$password = $_POST['password'];

if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配

{

echo 'Wrong Format';

exit;

}

while (TRUE)

{

$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';

if (6 > preg_match_all($reg, $password, $arr))

break;

$c = 0;

$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母

foreach ($ps as $pt)

{

if (preg_match("/[[:$pt:]]+/", $password))

$c += 1;

}

if ($c < 3) break;

//>=3,必须包含四种类型三种与三种以上

if ("42" == $password) echo $flag;

else echo 'Wrong password';

exit;

}

}

?>

这里用到了PHP正则表达式,简单列举一下字符串类的正则表达式:

[[:alpha:]] :匹配任何字母

[[:digit:]] :匹配任何数字

[[:alnum:]] :匹配任何字母和数字

[[:space:]] :匹配任何空白字符

[[:upper:]] :匹配任何大写字母

[[:lower:]] :匹配任何小写字母

[[:punct:]] :匹配任何标点符号

[[:xdigit:]] :匹配任何16进制数字,相当于[0-9a-fA-F]

[[:blank:]] :匹配空格和Tab,等价于[\t]

[[:cntrl:]] :匹配所有ASCII 0到31之间的控制符

[[:graph:]] :匹配所有的可打印字符,等价于[^ \t\n\r\f\v]

[[:print:]] :匹配所有的可打印字符和空格,等价于[^\t\n\r\f\v]

使用POST方式传输password

if ("POST" == $_SERVER['REQUEST_METHOD'])

{

$password = $_POST['password'];

第一个if中正则表达式匹配所有的可打印字符,并且需要大于等于12个

if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配

{

echo 'Wrong Format';

exit;

}

然后是一个while循环

第二个 if 里面,使用 preg_match_all

$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';

按照这个规则(标点符号;数字;大写字母;小写字母),将password至少分为6段

例如:abc123+a1? 就可以分为:abc 123 + a 1 ? 这6段

第三个 if 中,使用 for 循环判断 password 中是否存在 标点符号,数字,大写字母,小写字母中的至少三种

$c = 0;

$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母

foreach ($ps as $pt)

{

if (preg_match("/[[:$pt:]]+/", $password))

$c += 1;

}

if ($c < 3) break;

最后想要获得 flag ,还需要令传入的password=="42"

if ("42" == $password) echo $flag;

从最后的相等出发,因为是弱类型比较,我们首先想到的是十六进制或者科学计数法。

其中

var_dump(0 == "a"); // 0 == 0 -> true

var_dump("1" == "01"); // 1 == 1 -> true

var_dump("10" == "1e1"); // 10 == 10 -> true

var_dump(100 == "1e2"); // 100 == 100 -> true

所以我们可以有 4.2e1==42,也可以有420e-1==42,当然还可以继续如0.42e2==42

为了多一个减号,我们采用420e-1,再变形为:420.0e-1,满足password分成六段的要求,接着满足长度大于等于12的要求,变形为:420.00000e-1

POST传输:

6f2c4b712486cb871be66aab1426e15f.png

参考链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值