Black-Hole's Blog

In love

Web安全概览

Posted at — Oct 17, 2019

前言

介绍一下 Web 端相关的攻击手法及原理,包括但不限于 前端 后端 运维

本文后端语言将使用PHP,因为经过对比,PHP作为后端代码演示,是最显而易见的。

以及本文介绍的所有攻击手法都将提供Docker 靶场,方便后续大家自行研究、测试。

此篇文章是由我之前写的安全文章进行了部分汇总、优化及添加。如果大家比较感兴趣,可以去Freebuf Black-Hole看我之前写的文章。

前端

XSS

XSS本质就是在别人的浏览器里执行你的JavaScript代码,其他都是一种辅助手段。

DOM XSS

利用JavaScript操作DOM的特性,来做到攻击

经常出现的情况:前后端分离架构

很多开发者,都知道不到万不得已不要使用eval函数,不使用的原因其中一点就是不安全,但是难道不使用eval就不会有安全问题么?答案是否定的。

见下面的代码:

<script>
  window.open(new URL(location.hash.slice(1)).href)
</script>

为了更好的说明,我这里简单的把作用说明一下:

假设当前url为 http://baidu.com/#http://360.cn,那么location.hash.slice(1)的结果就是http://360.cn

new URL(url).href则是会对url进行解析,并取出解析后的url。如果你的url不满足它内部的判断,则会报错。

是不是觉得这段代码没什么问题。毕竟有new URL()这种浏览器内置函数帮我们做了过滤。

OK,那我们就要去攻破这个函数的判断。下图为URL接口规范中的列表:

我们可以看到hello:world是符合规范的。而且hello:正好又是JavaScript里的标记,类似于C语言中的goto。那我们更改url为: http://baidu.com/#javascript:alert(1)。则会成功触发漏洞。

Docker 靶场

后端

反射XSS

DOM XSS也属于反射型 XSS,一般而言统称都叫反射 XSS,只有当需要划分比较细时,才会分开来。

因为前/后端没有做好相对应的过滤,导致的问题

经常出现的情况:MVC架构

因为像MVC这种架构,其实是在后端语言中编写前端代码,在访问url的时候,会先由后端根据请求去构造前端页面,再返回前端源码到浏览器端,而这个过程中,如果出现了XSS漏洞,又因为后端参与其中,所以我们一般认为这是反射型XSS

见下面的 PHP 代码:

<?php
  // 关闭浏览器的XSS检测机制
  header("X-XSS-Protection: 0;");

  $bg = $_GET['bg'];
  if (empty($bg)) {
    $bg = '999';
  }
  echo "<div style='width: 120px; height: 120px; background-color:#$bg'>我是一只小方块</div>";
?>

可以看到,这段代码将会获取url参数中的bg来获取背景颜色。乍一看,好像没什么问题。但是要知道其中$bg是可控的。那我们只需要把里面的标签闭合掉就行。见下面:

http://127.0.0.1:8082/?bg=123' onclick='alert(1)

上面的代码放在bg参数里,那代码就变成了

<div style=‘width: 120px; height: 120px; background-color:#123' onclick='alert(1)‘>我是一只小方块<\/div>

Docker 靶场

存储型XSS

在反射XSS的基础上,做了一步 入库的操作

见下面的代码:

// 获取客户端的IP地址
// 此段代码来之:https://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php
$ipaddress = 'UNKNOWN';
$keys = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR');
foreach($keys as $k) {
  if (isset($_SERVER[$k]) && !empty($_SERVER[$k])) {
    $ipaddress = $_SERVER[$k];
    break;
  }
}

// 获取内容,并对内容做编码过滤
$content = htmlspecialchars($_POST['content'], ENT_QUOTES);

$sql = "INSERT INTO xss.message (content, ip) VALUES ('$content', '$ipaddress')";
$conn->query($sql);

这段代码看起来是没有任何问题的,因为我们已经对content参数做了进一步的编码过滤。

htmlspecialchars(string,ENT_QUOTES) 此方法的编码规则为:

& 编码为 &amp;

" 编码为 &quot;

' 编码为 &#039;

< 编码为 &lt;

> 编码为 &gt;

那即使我们写入了HTML标签等代码,也会被转码,假如我们提交的内容为<script>alert(1)</script>那在数据库的里表现就会变成: &lt;script&gt;alert(1)&lt;/script&gt;

那难道没有办法绕过去么?答案当然是可以。

可以看到,这段代码不止把内容入库了,还把用户的IP也入库了。那问题就出现了,CLIENT-IPX_FORWARDED_FOR是用户可控的。只需要安装 ModHeader 这个浏览器插件即可如图:

Imgur

修改后,再去重新提交一次,就会发现已经成功入库了,如图:

Imgur

Docker 靶场

CSRF

CSRF可以理解为是一种借刀杀人的手法,一般出现在表单提交里。最常见的是出现的开源项目里,比如各类CMS。

<form method="post" action="addAdminUser.php">
  <div class="input-group">
    <label for="username">要添加的管理员账号: </label>
    <input type="input" name="username">
  </div>
  <div class="input-group">
    <label for="password">要添加的管理员密码: </label>
    <input type="password" name="password">
  </div>
  <div class="input-group">
    <button type="submit">添加</button>
  </div>
</form>

见上面代码,此代码存在于管理员后台里。用于添加管理员账号。并且 addAdminUser.php 里是有对当前用户的 Cookies 做了校验,非管理员账户不能添加。

但是这个表单里没有 验证码Token ,以及 addAdminUser.php 里没有做 referer 来源校验。

就会出现 CSRF 漏洞。

因为浏览器发现你请求了某个资源后,会自动把你未过期的 Cookies 加入到请求头里。也就是说,即使是在其他网站里,发送了添加管理员的请求,会自动把你登陆后的 Cookies 加入到请求里,服务端会认为是你本人触发的(因为 Cookies 校验成功)

至于为什么这里 Cors 没有起到作用,是因为这种方式,请求后是拿不到任何返回信息的,所以 Cors 并不会拦截。如果你使用 Ajax来请求,是肯定会被拦截掉,因为 Ajax 请求是可以拿到返回信息,所有会被 Cors 拦截掉。

那除了 form 标签,还有什么方法可以进行攻击呢?在 w3c cors 规范中,有这么一句话:

A simple cross-origin request has been defined as congruent with those which may be generated by currently deployed user agents that do not conform to this specification. Simple cross-origin requests generated outside this specification (such as cross-origin form submissions using GET or POST or cross-origin GET requests resulting from script elements) typically include user credentials, so resources conforming to this specification must always be prepared to expect simple cross-origin requests with credentials.

—-详情可见 w3c cors

这里没有说的特别细,我补充了一下,大致意思是说,使用 GETPOST 进行表单提交时,亦或者使用一些 HTML 标签引起的 GET 请求,如:aimg 等,基本上都会携带用户凭据(Cookies)

Docker 靶场

SSRF

SSRF 本质其实和 CSRF 相差不大。只是 CSRF 面向的是客户端的用户,而 SSRF面向的是服务端本身。

这种漏洞一般出现于:会访问并返回用户可控的资源的功能上。

例如:在线查看网站源码获取用户发链接的title在线翻译网页

<script>
  // 根据url动态拼接url
  function seeCode() {
    const url = document.getElementsByTagName('input')[0].value;
    location.href = location.origin + location.pathname + '?url=' + url
  }
</script>

<input type="text" placeholder="请输入要查看源代码的网站 URL"/>
<button onclick="seeCode()">查看</button>
<?php
  $url = $_GET['url'];
  if ("" == trim($url)) {
    return;
  }

  // 获取内容
  $websiteCode = file_get_contents($url);

  // 转码后存入 textarea 标签里
  echo "<textarea>".htmlspecialchars($websiteCode, ENT_QUOTES)."</textarea>";
?>

可以看到,这里没有对用户的输入做任何的过滤,导致用户可以直接输入http://192.168.1.2等url,来访问内网资源,因为访问资源的是服务端本身,而服务端本身也是可以访问同局域网的资源的。

Docker 靶场

JSON Hijacking

JSON Hijacking 其实本质和 CSRF 原理一样

这里的原理指的是,漏洞产生的原理一样,还是因为标签产生的请求,会携带用户凭证

这个攻击手法和 CSRF 最大的不同,就是 JSON HijackingCSRF 的基础上,又利用了 JSONP 的特性

<?php
  require 'utils.php';

  // 用户认证、并获取用户信息
  $result = getUserInfo();
  if (count($result) != 3) {
    echo "";
    exit();
  }

  $fnName = $_GET['callback'];
  if ("" == trim($fnName)) {
    echo "";
    exit();
  }

	// 输出为: getInfo({"name": name, "balance": balance });
  echo "$fnName({name: '$result[0]', balance: '$result[2]'})";
?>
<script>
  function getInfo(data) {
    console.log(data); // {"name": name, "balance": balance}
  }
</script>
<script src="./json.php?callback=getInfo"></script>

如果有恶意攻击者,写了一个页面,并且也在网页里加入了<script src="http://xxx/json.php?callback=getInfo"></script>

根据上文提到的 w3c cors 规范中,如果用户事先已经登陆过了,再打开恶意攻击者发的链接,那么攻击者就可以获取一些本不应该获取的敏感信息。

Docker 靶场