CTF-Web14(涉及PHP代码审计——不走寻常路的绕过)
14.程序逻辑问题
分析:
查看源代码:
初了解(可略):
1.mysql_fetch_array() 中可选的第二个参数 result_type 是一个常量,可以接受以下值:MYSQL_ASSOC,MYSQL_NUM 和 MYSQL_BOTH。本特性是 PHP 3.0.7 起新加的。
2.本参数的默认值是 MYSQL_BOTH。如果用了 MYSQL_BOTH,将得到一个同时包含关联和数字索引的数组。用 MYSQL_ASSOC 只得到关联索引(如同 mysql_fetch_assoc() 那样),用 MYSQL_NUM 只得到数字索引(如同mysql_fetch_row() 那样)。它仅仅返回关联数组。
3.strcasecmp($pass, $row[pw])二进制安全比较字符串(不区分大小写)。本题要求也就是$pass==$row[pw]
根据源码可以看到两处特别需要重视的地方,我已标红,很明显该sq|语句存在注入漏洞,但是密码栏不能通过一般的注入来绕过,但是可以发现,只要满足了( $row[pw]) && (strcasecmp($pass, $row[pw])就可以拿到flag ,也就是说,我们输入的$pass与从数据库取出来的pw一致就行,但是貌似不知道数据库中pw和user的值,不过我们却可以控制$psas的值,也就是说,我们可以不需要访问题里的数据库,只要我们修改了$sql的值,此题解决。我们可以直接用union select "某一个经过md5加密后的字符串" #来自己随意设定密码,注意这里一定是经过md5加密,不然会出错。
构造语句: ' and 0=1 union select MD5(1) # 或者: ' and 0=1 union select "c4ca4238a0b923820dcc509a6f75849b"#
密码: 1
就拿到flag了。.
(备注:1的md5值是:c4ca4238a0b923820dcc509a6f75849b)
解释:
1. 最前面的单引号:闭合原文的where user='
2. AND 0=1:为了使前面的表达式返回值为空.
3. 接着我们使用UNION SELECT MD5(1),直接把MD5值作为返回值retuen给$sql,这样在查询的时候$query就会有值.
4. 最后的#用来注释掉后面没用的东西
可以本地做个测试,更清楚的说明这个问题:
这里我输入:
Uesrname==>' and 0=1 union select "c4ca4238a0b923820dcc509a6f75849b"#
Password==>1
补充:
<?php
if($_POST[user] && $_POST[pass]) {
$conn = mysql_connect("********, "*****", "********");
mysql_select_db("phpformysql") or die("Could not select database");
if ($conn->connect_error) {
die("Connection failed: " . mysql_error($conn));
}
$user = $_POST[user];
$pass = md5($_POST[pass]);
$sql = "select pw from php where user='$user'";
$query = mysql_query($sql);
if (!$query) {
printf("Error: %s\n", mysql_error($conn));
exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];
if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
//strcasecmp — 二进制安全比较字符串(不区分大小写)
//和strcmp不同,这里没法通过php弱类型绕过
echo "<p>Logged in! Key:************** </p>";
}
else {
echo("<p>Log in failure!</p>");
}
?>
下一篇: Godot基础教程04:实例-控制大炮