Pikachu靶场通关笔记(2) — 验证码绕过(服务端逻辑缺陷,验证码复用)

33次阅读
没有评论

1. 漏洞原理

通常,合格的验证码机制应该遵循  “一次一验,验完即毁”  的原则。

  • 漏洞逻辑(本关):用户提交表单 -> 后端校验验证码 -> 校验通过 / 失败 -> 后端没有销毁旧验证码 -> 攻击者可以一直拿着这个旧的验证码去跑字典。
  • 正常逻辑 :用户提交表单 -> 后端校验验证码 ->  无论校验成功与否,立即销毁当前 Session 中的验证码 -> 生成新的验证码。

2. 测试过程

第一步:抓包分析
输入正确的验证码,随意的用户名 test 和密码 test,抓包。

第二步:验证漏洞是否存在 (关键步骤)
不要急着去爆破。先右键发送到  Repeater (重发器)

  1. 保持验证码和 Cookie 不变。
  2. 修改密码参数,点击 Send。
  3. 再次修改密码参数,点击 Send。
  4. 观察 :两次(甚至多次)请求都返回了“username or password is not exists”(说明验证码校验通过了,只是账号密码不对),而不是提示“验证码错误”, 说明验证码可以被重复使用
Pikachu 靶场通关笔记(2) — 验证码绕过(服务端逻辑缺陷,验证码复用)

第三步:爆破 (Intruder)

  1. 发送到 Intruder。
  2. 重点 :把  username、password  设置为变量(§), 验证码参数保持抓包时的那个正确值不变
  3. 载入字典,开始攻击。
  4. 根据响应长度找到正确密码。

3. 代码审计

if (empty($_POST['vcode'])) {$html .= "<p class='notice'>验证码不能为空哦!</p>";
            } else {
//              验证验证码是否正确
                if (strtolower($_POST['vcode']) != strtolower($_SESSION['vcode'])) {$html .= "<p class='notice'>验证码输入错误哦!</p>";
                    // 应该在验证完成后, 销毁该 $_SESSION['vcode']
                    // unset($_SESSION['vcode']); 
                }else{$username = $_POST['username'];
                    $password = $_POST['password'];
                    $vcode = $_POST['vcode'];
                    $sql = "select * from users where username=? and password=md5(?)";
                    $line_pre = $link->prepare($sql);
                    $line_pre->bind_param('ss',$username,$password);
                    if($line_pre->execute()){$line_pre->store_result();
                        // 虽然前面做了为空判断, 但最后, 却没有验证验证码!!!
                        if($line_pre->num_rows()==1){$html.='<p> login success</p>';
                        }else{$html.= '<p> username or password is not exists~</p>';
                        }
                    }else{$html.= '<p>执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'</p>';
                    }
                }
            }

分析
代码中缺少了  unset($_SESSION[‘vcode’]);  这一行。导致只要 Session 不过期,或者不刷新页面,这个  $_SESSION[‘vcode’]  的值一直不变,前端发过来的包只要匹配这个值就能一直通过校验。

4. 修复方案 (开发视角)

在验证码校验逻辑完成后(无论成功还是失败),必须强制更新或销毁 Session 中的验证码。

if($_SESSION['vcode'] == strtolower($_POST['vcode'])) {
    // 校验后立即销毁,防止复用
    unset($_SESSION['vcode']); 
    
    // 或者重新生成一个新的
    // $_SESSION['vcode'] = generate_new_code();

    // ... 具体的登录逻辑 ...
}
正文完
 0
评论(没有评论)