在內網環境使用WPAD/PAC和JS攻擊win10

Editor發表於2018-03-01


引子


從20/20事後來看,許多廣泛部署的技術似乎是一種奇怪或不必要的冒極大風險的想法。IT工程決策往往是由資訊不完善和時間壓力造成的,而IT系統中的一些古怪問題最好用“當時看來是個好主意”來解釋。在這篇文章的作者個人觀點中,WPAD(“WebProxy Auto Discovery Protocol” --更具體地說是“Proxy Auto-Config”)是這些怪異之一。


在網際網路早些時候 - 1996年以前,Netscape的工程師認為JavaScript是編寫配置檔案的好語言。這就產生了PAC--一種配置檔案格式,其工作原理如下:瀏覽器連線到預先配置的伺服器,下載PAC檔案,並執行特定的JavaScript函式以確定完整的代理配置。為什麼不呢?它確實比XML(比方說)更具表現力和詳細,而且似乎是向許多客戶提供配置的合理方式。


PAC本身伴隨了一個名為WPAD的協議 --一個協議,使得瀏覽器不需要預先配置的伺服器來連線。相反,WPAD允許計算機查詢本地網路以確定載入PAC檔案的伺服器。


不知何故,這項技術最終成為1999年到期的IETF草案,現在,在2017年,每臺Windows機器都會詢問本地網路:“嘿,我在哪裡可以找到一個Javascript檔案來執行?”。這可以通過多種機制來現:DNS,WINS,但也許最有趣的是DHCP。


近年來,瀏覽器的攻擊已經從主要面向DOM的方向發展到直接面向Javascript引擎,所以只要我們可以在沒有瀏覽器的情況下通過網路來執行Javascript執行就是激動人心的。最初調查顯示,JS引擎負責執行這些配置檔案是JSCRIPT.DLL - JS引擎也支援IE7和IE8(如果使用適當的指令碼屬性,仍然可以在IE11在IE7 / 8相容模式中使用)。這有好有壞 -- 一方面,這意味著並不是每一個Chakra bug自動成為本地網路的遠端攻擊,但另一方面,這意味著一些很老的程式碼將負責執行我們的JavaScript。


安全研究人員之前曾警告WPAD的危險。 但就我們所知,這是第一次發現對WPAD的攻擊,結果導致使用WPAD使用者機器的完全破壞。

Windows並不是實現WPAD的唯一系統,其他作業系統和應用程式也是如此。例如Google Chrome也有一個WPAD實現,但在Chrome中,評估來自PAC檔案的JavaScript程式碼發生在一個沙箱內。而其他支援WPAD的作業系統預設不啟用它。這就是為什麼Windows是目前這種攻擊最有趣的目標。


Web Proxy Auto-Discovery

如上所述,WPAD將查詢DHCP和DNS(按此順序)以獲取要連線的URL -- 如果沒有可用的DNS響應,顯然LLMNR和Netbios也可以使用。WPAD-over-DNS的一些特性使得攻擊向量能夠出人意料地發揮作用。


攻擊場景:通過DHCP的本地網路


在最常見的情況下,一臺機器將使用選項碼252查詢本地DHCP伺服器.DHCP伺服器回覆一個字串-- 比如“http://server.domain/proxyconfig.pac”,它指定一個配置檔案的URL。 然後客戶端繼續獲取這個檔案,並將內容作為Javascript執行。


在本地網路中,攻擊者可以簡單地模仿DHCP伺服器 -- 可以通過ARP遊戲或通過合法DHCP進行競爭。然後,攻擊者可以提供一個惡意JavaScript檔案所在的URL。


攻擊場景:通過特權地位和DNS進行遠端攻擊


除了本地網路攻擊場景,WPAD查詢也可能通過DNS發生的事實會產生一個輔助攻擊場景。許多使用者將他們的計算機配置為針對公共的,全域性可見的DNS伺服器(如8.8.8.8, 8.8.4.4,  208.67.222.222和208.67.220.220)來執行DNS查詢。 在這種情況下,一臺機器會傳送DNS查詢(如wpad.local)到位於本地網路之外的伺服器。 處於網路特權位置的攻擊者(例如閘道器或任何其他上游主機)可以監視DNS查詢並偽造應答,指示客戶端下載並執行惡意的JavaScript檔案。


像這些設定似乎是常見的 - 根據這個維基百科條目,DNS根伺服器流量的一小部分是本地請求。


攻擊場景:通過惡意的wpad.tld遠端攻擊


WPAD的特殊之處在於遞迴地遍歷本地機器名稱以查詢要查詢的域。如果一臺機器被稱為“laptop01.us.division.company.com”,則按照以下方式查詢以下域名:

1.wpad.us.division.company.com

2.wpad.division.company.com

3.wpad.company.com

4.wpad.com


這(根據這個維基百科條目)導致人們註冊wpad.co.uk並將流量重定向到線上拍賣網站。進一步引用該條目:

通過WPAD檔案,攻擊者可以將使用者的瀏覽器指向自己的代理,攔截並修改所有的http流量。儘管2005年對WindowsWPAD處理進行了簡單的修復,但它只解決了.com域的問題。在Kiwicon的一次演講顯示,世界其他地區仍然受到這個安全漏洞的嚴重威脅,為測試,我們在紐西蘭註冊一個樣本域,接收來自全國各地的代理請求,幾秒鐘就接受到大量的請求。一些wpad.tld域名(包括COM,NET,ORG和US)現今會指向客戶端回送地址,以防止此漏洞,儘管有些名稱仍然是註冊的(wpad.co.uk)。


因此,管理員應確保使用者可以信任網路中中的所有DHCP伺服器,並且網路中所有可能的wpad域都受到控制。此外,如果沒有為網路配置wpad域,則使用者將轉到外部位置的具有域層次結構中下一個wpad站點,並將其用於配置。這就允許攻擊者在某個特定的國家註冊wpad子域名,通過將自己設定為所有流量感興趣站點的代理,對該國的大部分網際網路流量進行中間人攻擊。


另一方面,IETF草案明確要求客戶只允許“典型域”(例如非頂級域名)。我們還沒有調查客戶在什麼程度上實施這個,或者二級域名(如.co.uk)是否是流量重定向案例的罪魁禍首。


無論哪種方式:如果某個特定組織的頂級域名(TLD)註冊了$ TLD,只要該頂級域名(TLD)未被客戶端明確列入黑名單,就可以通過網際網路遠端利用Javascript引擎中的bug。 鑑於1999年的IETF草案提到了1994年的TLD列表(RFC1591),客戶不太可能已經更新了最新的TLD。


Bugs


我們花了一些時間採用手動分析和fuzz尋找jscript.dll中的bug。 Jscript存在著一些挑戰,因為很多用於觸發JavaScript引擎中的bug的“特性”不能在JScript中使用,只是由於它太舊而無法支援它們。例如:

沒有多個陣列型別(int陣列,float陣列等)。因此混淆一個陣列型別是不可能的。


沒有像更新,更快的JavaScript引擎那樣的優化(“fast path”)。這些fast path往往是bug的來源。


在通用JavaScript物件上定義getter / setter是不可能的。可以呼叫defineProperty,但只能呼叫不適合我們的DOM物件,因為在WPAD過程中不會有DOM。即使存在,當呼叫一個帶有“JScript object expected”訊息的DOM物件時,很多JScript函式也會失敗。


不可能改變已經建立物件的原型(即沒有“__proto__”屬性)。


但是,JScript確實有很多的“老派”漏洞,比如UAF。 JScript的垃圾回收器在這個老的MSDN文章中有描述。 JScript使用非世代的標記和清理垃圾收集器。從本質上講,無論何時垃圾收集被觸發,它都會標記所有的JScript物件。然後從一組“根”物件(有時也稱為“清道夫”)開始掃描它們,並從所遇到的所有物件中清除標記。所有仍被標記的物件都被刪除。一個反覆出現的問題是預設情況下,堆疊上的區域性變數不會被新增到根物件列表中,這意味著程式設計師需要記住將它們新增到垃圾回收器的根列表中,特別是如果這些變數引用可以在函式生命週期中被刪除。


其他可能的漏洞型別包括緩衝區溢位,未初始化的變數等。


為了fuzzing,我們使用了基於語法的Domato fuzz引擎,並專門為JScript寫了一個新的語法。我們通過檢視各種JScript物件的EnsureBuiltin方法來識別有趣的內建屬性和函式,以新增到語法中。 JScript語法已經被新增到這裡的Domato倉庫中。


通過fuzz和手動分析,我們確定了七個安全漏洞。它們總結在下表中:

在內網環境使用WPAD/PAC和JS攻擊win10


在這篇博文釋出之前,所有以上的漏洞都已經唄微軟所修復。


該表中的漏洞通過觸發它們所需的類和相容模式來觸發。WPAD中的JScript相當於在IE7相容模式下執行指令碼,這意味著儘管我們發現了7個漏洞,但WPAD中只能觸發5個。但是,其他漏洞仍然可以在Internet ExplorerIE8相容模式下被惡意網頁使用(包括IE11)。


利用


理解Jscript的變數和字串


在這篇博文的其餘部分中,我們將討論JScript VAR和Strings,在深入探討漏洞的工作方式之前,先描述這些內容是非常有用的。

JScript VAR是一個24位元組(在64位版本上)的結構,表示一個JavaScript變數,並且與MSDN文章中描述的VARIANT資料結構基本相同。在大多數情況下(足以跟蹤漏洞)其記憶體佈局如下所示:


在內網環境使用WPAD/PAC和JS攻擊win10


例如,我們可以通過將5寫入前2個位元組(表示雙重型別)的VAR表示雙精度數,後在偏移8跟一個實際的雙精度值.最後8個位元組將是未使用的,但它們如果從這個VAR複製另一個VAR的值,將被複制。


JScript字串是一種型別為8,偏移量8處為指標的VAR。指標指向這裡描述的BSTR結構。在64位構建BSTR佈局看起來像這樣:

在內網環境使用WPAD/PAC和JS攻擊win10


一個字串VAR直接指向字元陣列,這意味著,要獲得一個字串的長度,指標需要減少4,並從那裡讀取長度。請注意,BSTR由OleAut32.dll處理,並分配在一個單獨的堆上(即與其他JScript物件不同的堆)。


BSTR的釋放也不同於大多數物件,並不是直接釋放BSTR,而是在呼叫SysFreeString時,它首先將一個字串放入由OleAut32.dll控制的快取中。 這個機制在JavaScript的堆風水(原文HeapFeng Shui)中有詳細的介紹。


階段1:資訊洩露


infoleak的目的是獲得我們完全控制的字串的記憶體地址。此時我們不會洩露任何可執行的模組地址,這將在稍後進行。相反,其目標是挫敗高熵堆的隨機化,使第二階段的利用可靠,而不必使用堆噴射。


對於infoleak,我們要在RegExp.lastParen中使用這個bug。為了理解這個bug,我們先來仔細看看jscript!RegExpFncObj的記憶體佈局,它對應於JScript RegExp物件。在偏移量0xAC處,RegExpFncObj包含20個整數的緩衝區。實際上這些是10對整數:第一個元素是輸入字串的起始索引,第二個元素是結束索引。只要RegExp.test,RegExp.exec 或帶有RegExp引數的String.search遇到捕獲組(RegExp語法中的圓括號),匹配到的開始和結束索引就會儲存在這裡。顯然在緩衝區中只有10個匹配的空間,所以只有前10個匹配組被儲存在這個緩衝區中。


但是,如果RegExp.lastParen被呼叫,並且有超過10個捕獲組,RegExpFncObj :: LastParen會很高興地使用捕獲組的數量作為索引進入緩衝區,導致越界讀取。這是一個PoC:

  var r= new RegExp(Array(100).join('()'));

  ''.search(r);

  alert(RegExp.lastParen);


這兩個索引(我們稱之為start_index和end_index)是在緩衝區邊界之外讀取的,因此可以任意大。假設這第一次越界訪問不會導致崩潰,如果這些索引中的值大於輸入字串的長度,那麼將發生第二次越界訪問,這允許我們讀取輸入字串的邊界之外的資料。像這樣越界讀取的字串內容會返回給呼叫者一個可被檢查的字串變數。


我們要使用的是第二次越界讀取,但首先我們需要弄清楚如何將受控資料寫入start_index和end_index。幸運的是,檢視RegExpFncObj的佈局,在索引緩衝區結束後就是我們控制的資料:RegExp.input值。通過將RegExp.input設定為整數值並使用由41組空括號組成的RegExp,當呼叫RegExp.lastParen時,start_index將為0,end_index將為我們寫入RegExp.input的任何值。


如果我們把一個輸入字串與一個被釋放的字串相鄰,那麼通過在輸入字串的邊界之後讀取,我們可以獲得堆的後設資料,例如指向其他空閒堆段的指標(在紅黑色中的Left,Right和Parent節點堆的樹塊,請參閱Windows10 Segment堆內部瞭解更多資訊)。圖1顯示了infoleak時的相關物件。


在內網環境使用WPAD/PAC和JS攻擊win10


我們使用20000位元組長的字串作為輸入,以便它們不會分配到低碎片堆(LFH只能用於16K位元組或更小的分配),因為LFH堆的後設資料是不同的,並且不包括Windows 10 Segment堆中有用的指標。此外,LFH引入了隨機性,這會干擾我們將輸入字串放在釋放字串旁邊的操作。


通過從返回字串讀取堆中的後設資料,我們可以獲得一個釋放字串的地址。那麼,如果我們分配一個與被釋放字串大小相同的字串,它就可以被放置在這個地址。我們實現了我們的目標,那就是我們知道一個內容由我們控制的字串的記憶體地址。


整個infoleak過程如下所示:


分配1000個10000字元的字串(注意:10000個字元== 20000位元組)。


釋放2,4,6字串……


觸發資訊洩漏錯誤。使用剩餘的字串之一作為輸入字串並讀取20080位元組。


分析洩漏的字串並獲取指向一個釋放字串的指標。


分配與釋放的字串(10000個字元)長度相同的特定內容的500個字串。


特製字串內容在這個階段並不重要,但在下一個階段是重要的,所以在這裡不會描述。另外請注意,通過檢查堆後設資料,我們可以輕鬆地確定程式正在使用的堆(Segment Heap vs NT Heap)。


圖2和圖3顯示了在infoleak時使用Heap History Viewer建立的堆視覺化影像。綠色條紋表示分配的塊(被字串佔據),灰色條紋表示分配的塊被釋放,然後再次被分配(字串釋放並在觸發infoleak bug後被重新分配)白色條紋表示從未分配的資料(保護頁)。你可以看到隨著時間的推移字串是如何分配的,一半被釋放(灰色的),一段時間後又被分配(條紋變成綠色)。


我們可以看到每隔3次分配這個大小後就會有一個保護頁。我們的漏洞從來沒有實際觸及任何這些保護頁(它讀取的字串末尾的資料太少,以至於不會發生這種情況),但在這種情況下,在輸入字串之後不會有空閒字串infoleak,所以預期的堆後設資料將會丟失。但是,我們可以很容易地檢測到這種情況,並使用另一個輸入字串觸發infoleak bug,或者悄悄地中止exploit(注意:到目前為止我們沒有觸發任何記憶體損壞)。


在內網環境使用WPAD/PAC和JS攻擊win10

Image 2: Heap Diagram: Showing the evolution ofthe heap over time


在內網環境使用WPAD/PAC和JS攻擊win10

Image 3: Step-by-step illustration ofleaking a pointer to a string.


階段2:溢位


在攻擊的第二階段,我們將使用Array.sort中的堆溢位bug。如果輸入陣列中的元素數量大於Array.length / 2,則JsArrayStringHeapSort(如果未指定比較函式,則由Array.sort呼叫)將分配一個相同大小的臨時緩衝區(注意:可以小於array.lenght)。然後嘗試從0到Array.length索引檢索相應的元素,如果該元素存在,則將其新增到緩衝區並轉換為字串。如果陣列在JsArrayStringHeapSort的生命週期中沒有改變,這將工作正常。但是,JsArrayStringHeapSort將陣列元素轉換為可以觸發toString()回撥的字串。如果在其中一個toString()回撥元素被新增到未定義的陣列中,將發生溢位。


為了更好地理解這個bug及其可利用性,我們來仔細看看溢位的緩衝區的結構。已經提到過,陣列的大小與當前在輸入陣列中的元素的數量相同(確切地說,它將是元素的數量+1)。陣列中的每個元素的大小都是48位元組(64位版本),結構如下:


在內網環境使用WPAD/PAC和JS攻擊win10


在JsArrayStringHeapSort期間,index

1.陣列元素被讀入偏移量為16的VAR

2.原始的VAR被轉換成一個字串VAR。指向字串VAR的指標寫在偏移量0處。

3.在偏移8處,寫入陣列中當前元素的索引

4.根據原始的VAR型別,在偏移量40寫0或1


觀察臨時緩衝區的結構,我們不能直接控制很多細節。如果一個陣列成員是一個字串,那麼在偏移量為0和24時,我們將會有一個指標,當它被取消引用時,偏移量為8的指標包含另一個指向由我們控制的資料的指標。然而,這是一個間接的在大多數情況下對我們有用的大方向。


然而,如果陣列的成員是一個雙精度數,則在偏移24(對應於偏移量8原始VAR),該數的值將直接被寫入,並且是我們的控制之下。如果我們建立具有相同代表double的如在階段1中獲得的指標的數,那麼我們可以溢位覆蓋指向緩衝區之外的指標為指向我們直接控制記憶體中。


現在問題變成了,我們該按照這樣的方式覆蓋什麼指標來利用。如果我們仔細研究一下物件如何在JScript中工作那麼就會發現一個可能的答案。


每個物件(更具體地說,一個NameList的JScript物件)將有一個指向雜湊表的指標。這個雜湊表是一個指標陣列。當一個物件的成員元素被訪問時,先計算元素名稱的hash。然後取消對應於雜湊最低位偏移處的指標。這個指標指向一個物件元素的連結串列,然後這個連結串列被遍歷,直到我們到達一個與請求元素具有相同名字的元素。這在圖4中顯示。


在內網環境使用WPAD/PAC和JS攻擊win10

Image 4: JScript 內部物件元素


請注意,當元素的名稱少於4個位元組時,它將儲存在與VAR(元素值)相同的結構中。否則,將會有一個指向元素名稱的指標。名稱長度<= 4對於我們來說已經足夠了,所以我們不需要深入瞭解這個細節。


一個物件雜湊表是一個很好的覆蓋候選,因為:

我們可以通過訪問相應的物件成員來控制哪些元素被取笑引用。我們用不受控制的資料覆蓋的元素將永遠不會被訪問。

通過控制相應物件的成員個數,我們對雜湊表大小有著有限控制。例如,雜湊表以1024位元組開始,但如果我們向物件新增超過512個元素,雜湊表將被重新分配為8192個位元組。


通過用指向控制資料的指標覆蓋雜湊表指標,我們可以在控制資料中建立假的JScript變數,並通過訪問相應的物件成員來訪問它們。


要可靠地進行覆蓋,我們執行以下操作:

分配和釋放大量大小為8192的記憶體塊.這將開啟Low Fragmentation Heap使得分配大小為8192。這將確保我們溢位的緩衝區,以及我們溢位的雜湊表將被分配LFH。這一點很重要,因為這意味著附近不會有其他大小的分配來破壞漏洞攻擊(因為LFH只能包含特定大小的分配)。這反過來確保我們將高度可靠地覆蓋我們想要的東西。


建立2000個物件,每個物件包含512個成員。在這種狀態下,每個物件都有一個1024位元組的雜湊表。但是,向其中一個物件新增一個元素將導致其雜湊表增長到8192個位元組。


將513元素新增到前1000個物件中,導致1000個8192位元組雜湊表的分配。使用長度為300和170個元素的陣列觸發Array.sort。這將分配一個大小為(170 + 1)* 48 = 8208位元組的緩衝區。由於LFH的粒度,這個物件將被分配在與8192位元組雜湊表相同的LFH中。


立即(在第一個陣列元素的toString()方法中)向第二個1000物件新增第513個元素。這使得我們非常確定現在排序緩衝區是相鄰的雜湊表之一。在相同的toString()方法中也向陣列中新增更多的元素,這會導致它超出邊界。


圖5顯示了排序緩衝區地址時的堆視覺化(紅線)。您可以看到排序緩衝區被大小相近的分配所包圍,這些分配都與物件雜湊表相對應。你也可以觀察到LFH的隨機性,因為隨後的分配不一定在隨後的地址上,然而這對我們的利用沒有任何影響。


在內網環境使用WPAD/PAC和JS攻擊win10

Image5: Heap visualization around the overflow buffer


正如前面提到的,我們以這樣一種方式造成了溢位:一個不幸的JScript物件的hashtable指標會被覆蓋為指向到我們控制的資料的指標。現在終於輪到我們的資料來起作用:我們以這樣一種方式建立一個包含5個(假的)JavaScript變數:

變數1只包含數字1337。


變數2是特殊型別的0x400C。 這個型別基本上告訴JavaScript,實際的VAR是由偏移量為8的指標指向的,在讀取或寫入這個變數之前,這個指標應該被解除引用。在我們的例子中,這個指標指向變數1之前的16個位元組。這基本上意味著變數2的最後8位元組的qword和變數1的第一個8位元組的qword重疊。


變數3,變數4和變數5是簡單的整數。它們的特殊之處在於它們分別在其最後8個位元組中包含數字5,8和0x400C。


在內網環境使用WPAD/PAC和JS攻擊win10

圖6中顯示了溢位之後被破壞的物件的狀態。


圖6:溢位後的物件狀態紅色區域表示溢位發生的地方。 底行中的每個框(除了那些標記為“...”的)對應於8個位元組。 為清楚起見,“...”框中的資料被省略

我們只需訪問正確索引處(我們稱之為index1)的已損壞物件就可以訪問變數1,對於變數2-5也是如此。實際上,我們可以通過訪問所有物件的index1來檢測哪個物件被破壞,並檢視哪個物件現在具有值1337。


重疊變數1和變數2的作用是可以將變數1的型別(第一個WORD)更改為5(double),8(string)或0x400C(pointer)。我們通過讀取變數2,3或4然後將讀取的值寫入變數2來實現。例如,語句

corruptedobject [index2] = corruptedobject[index4];


具有將變數1的型別更改為String(8)的效果,而變數1的所有其他欄位將保持不變。


這種佈局給了我們幾個非常強大的利用許可權:

如果我們將一個包含一個指標的變數寫入變數1,我們可以通過將變數1的型別改為double(5)並讀出它來洩露這個指標的值

我們可以通過在該地址偽造一個字串來在任意地址上洩露(讀取)記憶體。我們可以通過首先將與我們想要讀取的地址對應的double值寫入變數1,然後將變數1的型別更改為String(8)來完成此操作。


首先將對應地址的數值寫入變數1,然後將變數1的型別更改為0x400C(指標),最後將一些資料寫入變數1,從而寫入任意地址。

有了這些利用許可權,通常獲得程式碼執行將非常簡單,但是由於我們正在利用Windows 10,我們首先需要繞過Control Flow Guard(CFG)。


階段3 繞過CFG


這裡可能有已經公開的方法,但事實證明,jscript.dll有一些非常方便的繞過方法(一旦攻擊者有一個讀/寫許可權)。我們將利用以下事實:

返回地址不受CFG保護

一些Jscript物件有指向本地堆疊的指標


具體來說,每個NameTbl物件(在Jscript中,所有的JavaScript物件都繼承自NameTbl),在偏移量為24的位置持有一個指向CSession物件的指標。CSession物件在偏移量為80的地方儲存一個指向本地堆疊頂部附近的指標。


因此,通過任意讀取,跟隨來自任何JScript物件的指標鏈,可以檢索指向本地堆疊的指標。然後,通過任意寫入,可以覆蓋返回地址,繞過CFG。


階段4 在本地服務實現任意程式碼執行


利用所有的漏洞元素,我們現在可以開始編寫poc。 我們正在這樣做:


1.從任何JScript物件的vtable讀取jscript.dll的地址

2.通過閱讀jscript.dll的匯入表讀取kernel32.dll的地址

3.通過讀取kernel32.dll的匯入表來讀取kernelbase.dll的地址

4.在kernel32.dll掃描我們需要的rop gadget

5.從kernel32.dll的匯出表中獲取WinExec的地址

6.如上一節所述,洩漏堆疊地址


7.準備ROP鏈並將其寫入堆疊,從最接近我們洩漏堆疊地址的返回地址開始。


我們使用的ROP鏈如下所示:


[addressof RET]  //needed to align the stack to16 bytes

[address of POPRCX; RET] //loads the first parameter into rcx

[address ofcommand to execute]

[address of POPRDX; RET] //loads the second parameter into rdx

1

[address ofWinExec]


通過執行這個ROP鏈,我們用指定的命令呼叫WinExec。例如,如果我們執行命令“cmd”,我們將看到一個命令提示符被生成,以本地服務執行(執行WPAD服務的相同使用者)。


不幸的是,從作為本地服務執行的子程式中,我們不能與網路通訊,但是我們可以做的是將提權載荷從記憶體中放入到磁碟中。本地服務可以從那裡寫入並執行它。


階段5:提權


雖然本地服務帳戶是服務帳戶,但它不具有管理許可權。這意味著攻擊者在系統上可以訪問和修改的東西是非常有限的,尤其是在利用之後或系統重啟之後維持訪問。雖然在Windows中總有可能存在未固定的特權升級,但我們不需要找到新的漏洞來升級我們的特權。相反,我們可以濫用內建功能從本地服務升級到SYSTEM帳戶。我們來看看已授予WPAD服務帳戶的許可權:


在內網環境使用WPAD/PAC和JS攻擊win10

Image7: Service Access Token’s Privileges showing Impersonate Privilege


我們只有三個許可權,但突出顯示的許可權SeImpersonatePrivilege非常重要。此許可權允許服務模擬本地系統上的其他使用者。服務具有模擬特權的原因是它接受來自本地系統上所有使用者的請求,可能需要代表他們執行操作。但是,只要我們能夠獲取我們想要模擬的帳戶的訪問令牌,我們就可以獲得令牌帳戶的完全訪問許可權,包括SYSTEM,這會使我們獲得本地系統上的管理員許可權。


濫用模擬是Windows安全模型的一個已知問題(您可以通過搜尋令牌綁架找到更多詳細資訊)。微軟已經試圖讓獲得特權使用者訪問令牌變得更加困難,但幾乎不可能關閉所有可能門路。例如,James在Windows的DCOM實現中發現了一個漏洞,允許任何使用者訪問SYSTEM訪問令牌。雖然微軟修復了直接特權提升漏洞,但是他們沒有,也許不能修復令牌綁架問題。我們可以利用這個功能來捕獲SYSTEM令牌,冒充令牌,然後完全破壞系統,比如安裝特權服務。

有一個通過DCOM(RottenPotato)令牌綁架的現有實現,但是實現被設計為與Metasploit框架的getsystem命令一起使用,但我們並沒有使用msf。因此,我們在C ++中實現了我們自己的更簡單的版本,它使用CreateProcessWithToken API直接生成一個帶有SYSTEM標記的任意程式。我們能夠將它編譯成11KiB的可執行檔案,比RottenPotato小得多,這使得它更容易放入磁碟並從ROP載荷執行。


將它們聯絡在一起


當WPAD服務查詢PAC檔案時,我們提供利用WPAD服務的漏洞檔案並執行WinExec來釋放和執行許可權提升的二進位制檔案。這個二進位制然後執行一個系統命令(在我們的情況下,硬編碼'CMD')。


這個漏洞在我們的實驗中非常可靠地工作,但是有趣的是,不需要100%可靠的漏洞 - 如果漏洞使WPAD服務崩潰,當客戶端發出另一個來自WPAD的請求時,將會產生一個新的例項服務,所以攻擊者可以再試一次。雖然視窗錯誤報告可能會發生崩潰並將其報告給Microsoft,但使用者沒有禁用它,UI中將沒有任何跡象表明WPAD服務已經崩潰。


事實上,我們的利用沒有很好的回收措施,一旦執行載荷就會使WPAD服務崩潰,所以如果我們在服務被利用後繼續提供PAC檔案,它將會被再次利用。你可以在圖7中看到這個效果,這個效果是在漏洞利用伺服器執行了幾分鐘並且在受害者機器上發出大量的HTTP請求之後得到的。


在內網環境使用WPAD/PAC和JS攻擊win10


稍後我們會將漏洞利用原始碼釋出在issuehacker中


總結


執行不受信任的JavaScript程式碼是危險的,並且在非沙箱環境中執行它更是如此。即使它是由相對強健的JavaScript引擎(如jscript.dll)也是如此。我們確定了7個安全漏洞,併成功地演示了在安裝了Fall Creator Update的Windows 10 64位完全打補丁(撰寫本文)時從本地網路(以及其他地方)執行任意程式碼。

由於錯誤現在已經修復,這是否意味著我們已經完成並可以休息?不太可能。儘管我們花費了相當多的時間,精力和計算能力來查詢jscript.dll錯誤,但是我們沒有聲稱我們發現了所有這些錯誤。事實上,有第7個bug,就可能會有第8個。所以如果有什麼東西沒有改變的話,我們很有可能會看到這樣的一個鏈條,就像有一天在野外一樣(當然,樂觀地認為攻擊者已經沒有這個能力了)。


那麼,微軟可以做些什麼來使今後的攻擊變得更加困難:

預設情況下禁用WPAD。實際上,在其他作業系統支援WPAD的情況下,Windows是唯一一個預設啟用的。


在WPAD服務內部提供JScript直譯器。由於直譯器需要執行一個定義好輸入的JavaScript函式並返回輸出字串,因此沙盒應該非常簡單。考慮到輸入輸出模型的簡單性,如果微軟在 seccomp-strict 中引入了一個類似限制性的沙箱,那將是非常好的:有些程式真的不需要比“接收一點資料” “執行一些計算“,”返回一點資料“更多的特權。


如果您想自行採取措施,唯一能夠使用新的,防止此類攻擊以及目前未知漏洞的方法似乎是完全禁用WinHttpAutoProxySvc服務。有時候,由於其他服務依賴於WPAD,所以在“服務”UI中無法完成(“啟動型別”控制元件將變灰),但可以通過相應的登錄檔項來完成。


在“HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinHttpAutoProxySvc”下,將“Start的值從3(手動)更改為4(禁用)。


以下是在搜尋“禁用WPAD”時通常在網上找到的一些建議,這些建議在我們的實驗中無法防止攻擊:

在控制皮膚中關閉“自動檢測設定”

設定“WpadOverride”登錄檔項

將“255.255.255.255 wpad”放在主機檔案中(這將停止DNS變體,但可能不是DHCP變體)


本文由看雪翻譯小組 wangrin 編譯 轉載請註明來自看雪社群

相關文章