本关与之前关卡的考查重点有所不同,特此记录。
提醒:不要直接访问默认链接http://127.0.0.1/sqlilabs/Less-29,因为这关默认的页面是index.php,实际上index.php并没有涉及到WAF的问题,因此访问链接http://127.0.0.1/sqlilabs/Less-29/login.php,考察的在这个页面里面。
渗透之前先来正常访问页面。

上面可以看到,正常访问是没有任何问题的。接下来尝试一下是否存在注入漏洞。

可以发现,无论输入 ' " 或者其他任何符号,都会弹出hacked.php的页面(页面中图片的链接在github上,我这里并没有加载出来,如果加载出来了,效果会更明显)。到这里我已经懵掉了,所以直接看源码。
<?php
// 包含MySQL数据库连接参数配置文件
// 该文件通常包含数据库主机、用户名、密码和数据库名等信息
include("../sql-connections/sql-connect.php");
// 禁用错误报告,不在页面上显示任何错误信息
// 这在生产环境中常见,但可能隐藏潜在问题
error_reporting(0);
// 检查是否通过GET请求传递了id参数
if(isset($_GET['id']))
{
// 获取完整的查询字符串(如?id=1&name=test)
$qs = $_SERVER['QUERY_STRING'];
$hint = $qs; // 这个变量未被使用,可能是调试遗留
// 调用java_implimentation函数处理查询字符串,返回处理后的id值
$id1 = java_implimentation($qs);
// 直接获取GET参数中的id值
$id = $_GET['id'];
// 调用白名单验证函数,检查$id1是否符合要求
whitelist($id1);
// 将获取到的id值记录到日志文件result.txt中
$fp = fopen('result.txt', 'a'); // 以追加模式打开文件
fwrite($fp, 'ID:' . $id . "\n"); // 写入ID信息
fclose($fp); // 关闭文件句柄
// 数据库查询操作:根据id查询users表中的记录
$sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result = mysql_query($sql); // 执行查询(注意:mysql_*函数已过时,存在安全隐患)
$row = mysql_fetch_array($result); // 获取查询结果的第一行
// 如果查询到结果,则显示用户名和密码
if($row)
{
echo "<font size='5' color= '#99FF00'>";
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' . $row['password'];
echo "</font>";
}
// 如果查询失败,显示数据库错误信息
else
{
echo '<font color= "#FFFF00">';
print_r(mysql_error()); // 输出MySQL错误信息
echo "</font>";
}
}
// 如果没有传递id参数,提示用户输入数字ID
else {
echo "Please input the ID as parameter with numeric value";
}
// 白名单验证函数:仅允许输入为纯数字
// 这是一种WAF(Web应用防火墙)实现方式
function whitelist($input)
{
// 使用正则表达式检查输入是否只包含数字
// ^表示开始,\d+表示一个或多个数字,$表示结束
$match = preg_match("/^\d+$/", $input);
if($match)
{
// 输入合法,不做任何处理(原注释说明但未实现返回值)
//echo "you are good";
//return $match;
}
else
{
// 输入不合法,重定向到hacked.php页面
header('Location: hacked.php');
//echo "you are bad";
}
}
// 该函数模拟了HTTP参数污染(HTTP Parameter Pollution, HPP)情况下的参数处理行为
// HPP指在请求中包含多个同名参数时的处理方式
function java_implimentation($query_string)
{
$q_s = $query_string; // 接收查询字符串参数
// 将查询字符串按"&"符号拆分,得到参数数组
// 例如"id=1&name=test&id=2"会拆分为["id=1", "name=test", "id=2"]
$qs_array = explode("&", $q_s);
// 遍历参数数组
foreach($qs_array as $key => $value)
{
// 截取每个参数的前2个字符
$val = substr($value, 0, 2);
// 检查前2个字符是否为"id"(即匹配id参数)
if($val == "id")
{
// 从第4个字符开始(索引3)截取最多30个字符作为id值
// 这里假设参数格式为"id=xxx",所以跳过"id="这3个字符
$id_value = substr($value, 3, 30);
// 返回获取到的id值,函数执行终止
return $id_value;
// 以下代码永远不会执行,因为前面已经return了
echo "<br>";
break;
}
}
}
上面是AI给的详细代码注释,感兴趣的可以看,下面我对主要部分进行讲解。
$qs变量获得的是你输入的全部内容,例如你输入了 id=1&username='aaa',那么$qs的内容就是它,接下来$qs变量被传递给了 java_implementation()函数,这个函数做了什么事情呢?就是把你输入的内容按&分开,按照上面的例子就是分成['id=1','username='aaa'']。然后 java_implementation()函数依次访问列表中每个字符串,判断其前两个字符是否是id,如果是,就截取id的内容返回。找到一个符合条件的就会停止。第一个字符串是 id=1,前两个字符是id,满足条件,然后用substr(),从第四个字符开始截取到后面的所有内容(有长度限制)。那正好截取到参数1。然后会调用whitelist()函数,对刚才截取到的内容进行过滤,whitelist()函数就是一个正则表达式匹配,他要求输入的字符串必须全是整数,那么刚才输入的 1 肯定满足条件,这就是一次正常的查询。如果你拼接了 除数字以外的其他任何字符,都会跳转到hacked.php。程序到这里的时候还是很不错的,但是问题在于,他接下来又获取了一次 id参数,而这次对id并没有进行WAF,而直接带入了sql进行查询。漏洞就在这里。我们可以利用http的参数污染,连续写两个id,类似id=1&id=2,这样他截取的第一个id我们正常数字,就会绕过WAF,而第二个id可以构造我们的SQL语句。下面可能有人会有疑问,为什么第二次get的id就一定会是我们构造的第二个id呢?因为对于绝大多数解析器,例如apache,当收到多个同名参数时,会默认处理最后一个。原理解析了,接下来就非常简单了。
http://127.0.0.1/sqlilabs/Less-29/login.php/?id=1&id=1' and updatexml(1,concat(0x7e,database(),0x7e),1)--+

其他的同理,都做到这了,应该也都知道了。30关闭合方式是 " ,31关闭合方式是 "),其余部分完全一样。
1797

被折叠的 条评论
为什么被折叠?



