那天我在BUUCTF平台闲逛时,发现一个名为Ezsql的靶场。页面就是个普通登录框,但职业敏感让我立刻想到SQL注入测试。随手输入经典的'OR'1'='1万能密码,没想到直接跳出了"登录成功"的提示——这就像用万能钥匙打开了防盗门,暴露出严重的SQL注入漏洞。
这种漏洞的根源在于代码直接拼接用户输入。比如原代码中的危险写法:
php复制$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
当攻击者输入admin'--作为用户名时,实际执行的SQL就变成了:
sql复制SELECT * FROM users WHERE username = 'admin'--' AND password = ''
注释符--直接截断了密码验证部分。更可怕的是万能密码场景,构造'OR'1'='1使WHERE条件永远为真,相当于绕过了整个认证体系。
首先用Burp Suite拦截登录请求,发现参数通过GET传递:
code复制GET /login.php?username=admin&password=123
修改为注入payload:
code复制GET /login.php?username='OR'1'='1'--&password=任意值
服务器返回了所有用户数据,甚至包含管理员会话令牌。这验证了存在基于UNION的注入漏洞,可以进一步获取数据库信息。
根据题目提示,使用给定凭证通过SSH连接靶机:
bash复制ssh ctf@target_ip -p 27133
# 密码:123456
在/var/www/html目录找到漏洞源码。关键问题在于:
$_GET参数做任何过滤query()执行动态SQL初级方案是添加过滤函数:
php复制$username = mysqli_real_escape_string($conn, $_GET['username']);
但这种方法存在缺陷:
最终采用参数化查询改造代码:
php复制$stmt = $mysqli->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param("ss", $username, $password);
实测中,同样的注入payload现在会返回"用户名或密码错误",证明防御生效。参数化查询的优势在于:
使用sqlmap验证加固效果:
bash复制sqlmap -u "http://target/login.php" --data="username=test&password=test" --risk=3 --level=5
加固前会检测出注入点,加固后显示"未发现可注入参数"。
在类似题目中,建议:
'、"、1=1--+等注释符测试截断记得最后通过SSH检查/flag文件获取flag,这种Web+SSH的混合题型在CTF中很常见。通过这次实战,我深刻体会到参数化查询不是可选项,而是Web开发的必选项。下次遇到SQL处理时,记得先准备好你的预处理语句!