在复现第八届封神台CTF时碰到了一个这样的漏洞
这一题的描述如下
我可以很嚣张的告诉你:用户名是admin,后端用的是nodejs,我还用上了数据库,flag就放在后台 不需要任何扫描或爆破,请不要扫描和爆破,这没有意义
部分代码:
var mysql = require("mysql");
if (username && password) {
pool.query(
"SELECT * FROM accounts WHERE username = ? AND password = ?",
这里使用了参数化查询(?作为占位符),这是防止SQL注入的好做法
但是
我们现在将改变password
参数到password[password]
使参数为Object
而不是String
.
我们就可以访问管理员帐户
或者也可以将数据作为 JSON 传递并绕过身份验证

这是由于某些类型(特别是对象类型)quoted identifiers在被转义函数转义时包含引用的标识符。引用的标识符用于指示数据库、表格、列或此类。有了这个,我们可以引用查询中的其他表或列。
将password =
obj_key_1
更改为password =
password``
自引用的标识符password
被视为一列,它最终会成为password = password
现在,如果我们改变obj_val_1
为1,它最终将成为(1=1)=1
最终返回1。
因此,当密码参数传递为{'password': 1}
它最终会转化为``password=1
最后绕过了身份验证逻辑。
所以在面对nodejs和mysql的登陆时我们可以
username[username]=1&password[password]=1
来登录

或者
username=admin&password[password]=1
登录特定用户如admin

防御方法
解决方法 1:在创建连接时添加 stringifyObjects 选项
添加"stringifyObjects":true
调用时的选项mysql.createConnection
最终会阻止所有意想不到的行为,当Object
在参数中传递。
但是,这可能会影响项目中所有现有的查询,并且当某些查询实际通过时可能会引入另一个问题。Object
参数。您可能还想查看变通方法2作为替代方案。
解决方法 2:添加类型检查
解决方法1可能是解决这个问题的最有效和最有效的方法。
然而,前一种解决方法只会阻止任何意想不到的行为。Object
独家。其他类型如Array
, 数组 ofArray
,Boolean
仍然可能导致意想不到的问题,因为它仍然根据值类型以不同的方式逃逸。前一种解决方法在许多罕见情况下仍然会引入其他意想不到的行为。
因此,最好添加类型检查代码,使您的代码更加严格。这种解决方法的缺点是,添加类型检查代码并对项目进行维护可能需要花费大量时间和成本。此外,在编写代码时,您可能会错过类型检查。