概述

这道题目的解法并不唯一,但我个人感觉这种方法感觉是最触及核心的,也是见多识广的人应该了解的方法。

开始实践,细细琢磨

首先看到的就是这个验证码,
substr(md5(captcha), 0, 3)=9ef

关键在于他真的很’智能’真的是每次都变,不是闹着玩的
大概的意思是他会把captcha这个数据md5,然后取前3位,和等于号后面的值比较,相等就行了,然后好奇capcha到底怎么获得,看了一下元素,发现他是真的有这个参数的
image

不管那么多只能写一个脚本爆破md5的值,毕竟不能逆推只能比对,脚本如下:

1
2
3
4
import hashlib
for i in xrange(100000):
if hashlib.md5(str(i)).hexdigest().startswith('efd'):
print i

开始注入:

首先我们尝试最一般的 username=admin&password=admin&captcha=8029
报 password error
说明似乎用户名被蒙对了
换一个随便的用户名 username=a&password=admin&captcha=9612
报 username error
说明登录验证是分两步进行的,首先是检验用户名是否正确,如果用户名正确那么验证密码正确与否,密码正确那么登陆成功,密码错误登录失败。但是用户名不正确,就不会检验密码,直接登录失败。

补充1

php常见的登录验证方式就是两种

1.select from foobar where username=’’ and password=’’
查寻语句为真就登陆成功否则登录失败
2.select password from foobar where username=’’(这个username就是我们输
入的值)
如果找不到那么用户名错误
否则
如果查到的密码和输入的password/或者是输入的经过md5加密后的值相等那么登陆成功
否则输出密码错误
我们可以去验证一下是不是属于第二种情况(依据是他是否只select password还是select

image
image
image

这里面会报两个password error 因为union select @@version 也在查询到的数据中,他是对查询到的数据一条一条和密码去匹配,结果发现我们的两条数据和输入的密码都不一样,所以会报两个password error
我们来本机测试一下
image

两条数据清晰可见,union select 很多时候就是用来填充或者引入别的数据到同一个表里
那么我们只要用union select 去构造一个数据在存放查询得到的password 的那个表中,也就是上图的5.6.17的位置,再输入我们设定好的password,那样依赖他循环遍历对比的特点,就可以绕过密码登陆成功,我们试一下:
image
image
image

咦,怎么回事呢,因为他验证用的是我上面说的另一种方法,把我们输入的值得md5的结果和数据库中的对比,也就是说数据库中存储的全都是md5的值,因此我们修改一下我们的payload
image
image
image

完全符合我们的猜想,第一个用户名admin的真实密码无法和我们构造的假密码去匹配,所以第一个是错的,然后第二个恰恰使我们构造的假密码,绕过成功!!

补充2

登录验证类似于这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
$servername = "localhost";
$username = "php";
$password = "##############";
$database = "php";

if($_POST[user] && $_POST[pass]) {
    $conn = mysqli_connect($servername$username$password$database);
    if ($conn->connect_error) {
        die("Connection failed: " . mysqli_error($conn));

$user = $_POST[user];
$pass = md5($_POST[pass]);

$sql = "select pw from php where user='$user'";
$query = mysqli_query($conn,$sql);
if (!$query) {
    printf("Error: %s\n", mysqli_error($conn));
    exit();
}
$row = mysqli_fetch_array($query);
//echo $row["pw"];
if (($row[pw]) && (!strcasecmp($pass$row[pw]))) {
    echo "<p>Logged in! Key: ################################ </p>";
}
else {
    echo("<p>Log in failure!</p>");

  }
}

?>

总结

这种类型的绕过应该算是比较常见的绕过方式的一种,平时应该多积累,这样才能有所突破,运用起来也会游刃有余。