在Chrome中使用WebRTC ICE服務進行端口掃描

22.png

WebRTC,名稱源自網頁即時通信(英語:Web Real-Time Communication)的縮寫,是一個支持網頁瀏覽器進行實時語音對話或視頻對話的API。它於2011年6月1日開源並在Google、Mozilla、Opera支持下被納入萬維網聯盟的W3C推薦標準。——百度百科

使用瀏覽器掃描局域網並不是一種新想法,目前已有許多利用XHR請求websockets存粹的HTML代碼來發現和識別局域網設備的例子。但在這篇博客中,我將介紹一種使用WebRTC ICE服務進行掃描的技術。該技術掃描速度較快,並且與其他方法不同的是,它可以繞過[blocked ports list](https://chromium.googlesource.com/chromium/src.git/+/refs/heads/master/net/ba se/port_util.cc)。但不幸的是,它只能在Chrome上才能生效。

你可以跳過我的解釋,直接進入代碼演示頁面。如果想詳細瞭解,先讓我們從PoC視頻開始,主要是掃描我的192.168.88.0/24網絡。

33.png

視頻地址:https://youtu.be/M6lBVhkzUmM

什麼是ICE Server?

如前所述,掃描技術使用WebRTC ICE服務。ICE服務是WebRTC RTCPeerConnection用於自我發現、NAT遍歷和中繼的STUNTURN服務,通過將服務器列表傳遞到RTCPeerConnection的構造來實現。下面是一個和谷歌公共STUN服務器有關的構造器之一:

var rtc = new RTCPeerConnection({
    iceServers:[{“urls”:”stun:stun.l.google.com:19302”}]
});

當上述RTCPeerConnection進入ICE收集狀態時,它將嘗試連接到所提供的服務器。

協議

ICE服務可以綁定到UDP或TCP端口。但是,除非特別設定,Chrome似乎只嘗試通過UDP進行通信。下面是一個Wireshark截圖,顯示了Chrome發送數據到一個不存在的TURN服務器,一切都基於UDP。

44.png

如果你知道一些關於ICE服務器的URL,可以強迫Chrome通過TCP進行連接。傳遞給RTCPeerConnection構造器的URL必須符合RFC 7064(STUN)或RFC 7065(TURN)。TURN URI的方案如下:

55.png

對於掃描來說最重要的是“?transport=”字段。它可以通過“?transport=TCP”強制ICE使用TCP。

現在,我們有了一種方法來向我們選擇的任何IP和端口發出TCP連接。但是,由於我們要掃描的所有主機幾乎都和TURN服務無關,那麼如何確定主機是否處於活動狀態呢?

確定目標是否存活

爲了找到192.168.[0-255].1範圍內的活動地址,下面的JSFiddle會生成256個TURN URI。

var brute_array = [];
for (i = 0; i < 256; i++) {
  brute_address = "turn:192.168." + i + ".1:445?transport=tcp";
  brute_array.push({
    urls: brute_address,
    credential: "lobster",
    username: "albino"
  });
}

var rtc_brute = new RTCPeerConnection({
  iceServers: brute_array,
  iceCandidatePoolSize: 0
});
rtc_brute.createDataChannel('', {
  reliable: false
});

rtc_brute.onicecandidateerror = function(e) {
  if (e.url == null) {
    return;
  }

  url_split = e.url.split(":");
  host_div = document.createElement('div');
  host_div.id = url_split[1];
  host_div.innerHTML = url_split[1];
  document.getElementById('hosts').appendChild(host_div);
}

// trigger the gathering of ICE candidates
rtc_brute.createOffer(function(offerDesc) {
  rtc_brute.setLocalDesc ription(offerDesc);
}, function(e) {
  console.log("Create offer failed callback.");
});

icecandidateerror事件生成時,這個地址就被確定爲“活動的”。如果主機以某種形式拒絕連接,Chrome就會將生成錯誤事件。理想情況下,在Chrome發送初始信息後,會立刻有RST回覆或一個快速拒絕。雖然服務可能只是保持連接打開,但錯誤事件將需要大約30秒來生成。

這就是爲什麼JSFiddle使用端口445進行掃描。我實現的SMB完成了TCP握手,然後在Chrome的非SMB通信之後關閉連接。445端口的另一個理想之處在於它和Windows關係緊密。

如果Chrome沒有響應,則不會生成事件。這可能是因爲防火牆進行了處理,也可能是不存在可用主機。

我遇到的唯一一個極端情況回覆中存在一個ICMP響應。這導致Chrome生成一個icecandidateerror,形成了一定程度的干擾。

端口掃描

JSFiddle會掃描192.168.88.1上的21、22、23、25、53、80、443、445、5900和8080端口。

var ports = [21, 22, 23, 25, 53, 80, 443, 445, 5900, 8080];
var target = "192.168.88.1";

address_div = document.createElement('div');
address_div.id = target;
address_div.innerHTML = target;
document.getElementById("hosts").appendChild(address_div);

var scan_array = [];
for (i = 0; i < ports.length; i++) {
  probe_address = "turn:" + target + ":" + ports[i] + "?transport=tcp";
  scan_array.push({
urls: probe_address,
credential: "lobster",
username: "albino"
  });

  port_div = document.createElement('div');
  port_div.id = ports[i]
  port_div.innerHTML = "&nbsp;&nbsp;&nbsp;-> Port " + ports[i] + " - ?"
  document.getElementById(target).appendChild(port_div);
}

var port_scan = new RTCPeerConnection({
  iceServers: scan_array,
  iceCandidatePoolSize: 0
});
port_scan.createDataChannel('', {
  reliable: false
});

port_scan.onicecandidateerror = function(e) {
  if (e.url == null) {
return;
  }

  url_split = e.url.split(":");
  port_split = url_split[2].split("?");

  if (e.hostCandidate != "0.0.0.x:0") {
    document.getElementById(port_split[0]).innerHTML = "&nbsp;&nbsp;&nbsp;-> Port " + port_split[0] + " - <b><i>Open</i><b>"
  } else {
    document.getElementById(port_split[0]).innerHTML = "&nbsp;&nbsp;&nbsp;-> Port " + port_split[0] + " - Closed"
  }
}

setTimeout(function() {
  if (port_scan.iceGatheringState === "gathering") {
    port_scan.close();
  }
}, 60000);

port_scan.onicegatheringstatechange = function(e) {
  if (port_scan.iceGatheringState == "complete") {
    port_scan.close();
  }
}

port_scan.createOffer(function(offerDesc) {
    port_scan.setLocalDesc ription(offerDesc);
  },
  function(e) {
    console.log("Create offer failed callback.");
  });

以下本地網絡掃描結果:

77.png

基於Chrome生成的icecandidateerror事件,腳本能夠將端口分類爲“打開”或“關閉”。每個icecandidateerror都有一個hostCandidate變量。任何完成TCP三次握手的ICE服務器都將在hostCandidate中列出本地IP和端口(例如192.168.88.x:51688)。無法訪問的ICE服務器以“0.0.0.x:0”的形式生成hostCandidates。因此,判斷一個端口是否打開很簡單。

88.png

只適用於Chrome?

目前我無法在任何其他瀏覽器中重現掃描,其他瀏覽器似乎沒有實現onicecandidateerror。這個特性在Chrome中存在的時間也不長,因爲MDN顯示“不支持”:

99.png

其他瀏覽器似乎對RTCPeerConnection的使用也不太靈活。雖然Chrome很樂意接受255個不同的ICE服務器,但Firefox就不行。

100.png

關於PoC代碼

Chrome最近已修復因WebRTC而泄露的本地地址這一問題。當“Experimental”功能的“Anonymize local IPs exposed by WebRTC”標誌被啓用時,Chrome將嘗試使用mDNS.local主機名,而不是本地IP。

110.png

我覺得這真是一個很好的安全加固,肯定能阻止不少潛在的攻擊者。

不過我在PoC也考慮到了IP無法獲取這一點,此時它將嘗試搜索192.168.[0–255].1上的某個活動IP。

這是一個弱點嗎?

起初,我覺得這是一個弱點。攻擊者(有爭議地)繞過Chrome的受限端口列表,能夠搜索受害者的局域網。但谷歌似乎認爲這是一個“隱私”問題,而不是安全漏洞。

120.png

不過,現在有各種各樣的插件可以禁用WebRTC,你也可以選擇其他瀏覽器。

本文由白帽彙整理並翻譯,不代表白帽匯任何觀點和立場:https://nosec.org/home/detail/3595.html
來源:https://medium.com/tenable-techblog/using-webrtc-ice-servers-for-port-scanning-in-chrome-ce17b19dd474

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