靶机:http://challenge01.root-me.org/web-serveur/ch17/
解题思路:
本题考的是PHP变量覆盖漏洞。那么首先来了解下PHP的变量覆盖。
变量覆盖:
变量覆盖指的是用我们自定义的参数值去替换程序原有的变量值,一般变量覆盖漏洞需要结合程序的其它功能来实现完整的攻击。经常导致变量覆盖漏洞场景有:$$,extract()函数,parse_str()函数,import_request_variables()使用不当,开启了全局变量注册等。
全局变量覆盖:
register_globals的意思就是注册为全局变量,所以当On的时候,传递过来的值会被直接的注册为全局变量直接使用,而Off的时候,我们需要到特定的数组里去得到它。
<form action='' method='get'>
<input type='text' name='username' value='alex' >
<input type='submit' name='sub' value='sub'>
</form>
<?php
echo 'username::',$username;
echo '<br>sub::',$sub;
echo '<br>GET::';
print_r($_GET);
?>
#当register_globals = On的时候,程序运行提交输出结果为:
username::alex
sub::sub
array ( [username] => alex [sub] => sub )
#当register_globals = Off的时候,程序运行提交输出结果为:
username::
sub::
array ( [username] => alex [sub] => sub )
extract()变量覆盖:
int extract ( array &$array
[, int $flags
= EXTR_OVERWRITE [, string $prefix
= NULL
]] )
从数组中将变量导入到当前的符号表,此函数会将键名当作变量名,值作为变量的值。
<?php
$auth = '0';
extract($_GET);
if($auth==1){
echo "private!";
}else{
echo "public!";
}
?>
如果GET传入auth=1,这时就会把原有的$auth='0' 覆盖成$auth=1。
当然还有其他类型的变量覆盖,以后碰到在一一讲解。
根据提示,开发人员留下了备份文件:
那么随手测试.bak的备份文件后缀名下载index.php源码。
http://challenge01.root-me.org/web-serveur/ch17/index.php.bak
<?php
function auth($password, $hidden_password){
$res=0;
if (isset($password) && $password!=""){
if ( $password == $hidden_password ){
$res=1;
}
}
$_SESSION["logged"]=$res;
return $res;
}
function display($res){
$aff= '
<html>
<head>
</head>
<body>
<h1>Authentication v 0.05</h1>
<form action="" method="POST">
Password <br/>
<input type="password" name="password" /><br/><br/>
<br/><br/>
<input type="submit" value="connect" /><br/><br/>
</form>
<h3>'.htmlentities($res).'</h3>
</body>
</html>';
return $aff;
}
session_start();
if ( ! isset($_SESSION["logged"]) )
$_SESSION["logged"]=0;
$aff="";
include("config.inc.php");
if (isset($_POST["password"]))
$password = $_POST["password"];
if (!ini_get('register_globals')) {
$superglobals = array($_SERVER, $_ENV,$_FILES, $_COOKIE, $_POST, $_GET);
if (isset($_SESSION)) {
array_unshift($superglobals, $_SESSION);
}
foreach ($superglobals as $superglobal) {
extract($superglobal, 0 );
}
}
if (( isset ($password) && $password!="" && auth($password,$hidden_password)==1) || (is_array($_SESSION) && $_SESSION["logged"]==1 ) ){
$aff=display("well done, you can validate with the password : $hidden_password");
} else {
$aff=display("try again");
}
echo $aff;
?>
可看到这里进行了全局变量注册:如果未开启全局变量注册,就用extract函数进行接受各种HTTP方法传参进行注册全局变量。
接下来,看判断密码的条件,输入的密码要和$hidden_password变量的值相同才行,那么我们传入一个$hidden_password覆盖原有的$hidden_password变量值即可。
可通过GET、POST、cookie等等方式传递。
通过之后,为当前session进行logged=1设置。当然,也可以直接传入_SESSION[logged]=1;。
那么再次刷新网页,因为已满足这个session条件,就会显示flag。