兩個月前,首次披露了W32.Stuxnet針對工業(yè)生產(chǎn)控制系統(tǒng)(ICS) 進(jìn)行攻擊,如應(yīng)用于管道和核動力工廠的控制系統(tǒng)。讀者可參見賽門鐵克2010年7月19日的博客– “W32.Stuxnet 攻擊零日漏洞利用USB設(shè)備大肆傳播”。
2010年9月29日,我們還將在Virus Bulletin 會議上發(fā)布一篇包含W32.Stuxnet詳盡技術(shù)細(xì)節(jié)的論文。同時我們也注意到,最近非常多的人開始對Stuxnet感染系統(tǒng)且不易檢測的事情表示關(guān)注。
由于Stuxnet針對某個特定的工業(yè)生產(chǎn)控制系統(tǒng)進(jìn)行攻擊,而這些行為不會在測試環(huán)境中出現(xiàn),因此在測試環(huán)境下觀察到的行為不全面,很可能產(chǎn)生誤導(dǎo)。事實(shí)上,運(yùn)行后,Stuxnet會立即嘗試進(jìn)入一個可編程邏輯控制器(PLC) 的數(shù)據(jù)塊—DB890。這個數(shù)據(jù)塊其實(shí)是Stuxnet自己加的,并不屬于目標(biāo)系統(tǒng)本身。Stuxnet 會監(jiān)測并向這個里寫入數(shù)據(jù),以根據(jù)情況和需求實(shí)時改變PLC的流程。
在這篇博客里,我們會深入探討Stuxnet的PLC感染方式和Rootkit功能,特別是以下幾個方面:
它如何選擇作為攻擊目標(biāo)的工業(yè)生產(chǎn)控制系統(tǒng);
感染PLC代碼塊的方法;
注入PLC的;
在被感染W(wǎng)indows機(jī)器中的PLC Rootkit代碼。
這四點(diǎn)我們會分開講,因?yàn)橛脕韺?shí)現(xiàn)這些目的的代碼差異很大。
Stuxnet的目的是通過修改PLC來改變工業(yè)生產(chǎn)控制系統(tǒng)的行為,包括攔截發(fā)送給PLC的讀/寫請求,以此判斷系統(tǒng)是否為潛在的攻擊目標(biāo);修改現(xiàn)有的PLC代碼塊,并往PLC中寫入新的代碼塊;利用Rootkit功能隱藏PLC感染,躲避PLC管理員或程序員的檢測。這些任務(wù)之間差別很大,比如,在被感染的Windows 機(jī)器中隱藏感染代碼使用的是標(biāo)準(zhǔn)的C/C++ 代碼,而Stuxnet 試圖在工業(yè)生產(chǎn)控制系統(tǒng)及PLC中執(zhí)行的惡意代碼則是用MC7字節(jié)碼寫的。MC7 是PLC 環(huán)境中運(yùn)行的一種匯編語言,并常用STL 進(jìn)行編寫。
在討論Stuxnet攻擊PLC的技術(shù)之前,讓我們先來看看PLC是如何訪問和編寫的。![]()
要進(jìn)入PLC, 首先需要安裝特殊的軟件;Stuxnet 會專門針對編寫PLC某些模塊的WinCC/Step 7軟件進(jìn)行攻擊。安裝這些軟件后,程序員可以通過數(shù)據(jù)線連接PLC,以訪問其中的內(nèi)容,重新配置PLC,下載程序至PLC,或調(diào)試之前加載的代碼。一旦PLC被配置和編譯后,Windows機(jī)器就可以斷開和PLC的聯(lián)系了,PLC會自行運(yùn)行。為了使您有一個更直觀的感受,下圖顯示了在實(shí)際操作中,實(shí)驗(yàn)室里一些基本的設(shè)備配置:![]()
下面的截圖顯示了Step7 STL編譯器中Stuxnet惡意代碼的一部分。其中,編寫Stuxnet功能代碼塊的MC7代碼的開始部分是可視的;下面顯示的代碼來自于反匯編后的FC1873模塊。
![]()
Step 7 軟件使用庫文件s7otbxdx.dll 來和PLC。當(dāng)Step7 程序準(zhǔn)備進(jìn)入PLC時,它會調(diào)用該DLL文件中不同的例程。例如,如果一個代碼塊需要用Step 7從PLC中讀出,那么,例程s7blk_read就會被調(diào)用到。s7otbxdx.dll中的代碼會進(jìn)入PLC, 讀出其中的代碼,并把它傳回Step 7程序,如下圖所示:
![]()
現(xiàn)在讓我們看看當(dāng)Stuxnet是如何進(jìn)入PLC的。運(yùn)行后,Stuxnet會將原始的s7otbxdx.dll文件重命名為s7otbxsx.dll。然后,它將用自身取代原始的DLL文件。現(xiàn)在,Stuxnet就可以攔截任何來自其他軟件的訪問PLC的命令。
![]()
被Stuxnet修改后的s7otbxdx.dll 文件保留了原來的導(dǎo)出表,導(dǎo)出函數(shù)為109個,這就令Stuxnet可以應(yīng)付所有相同的請求。大部分導(dǎo)出命令會轉(zhuǎn)發(fā)給真正的DLL,即重命名后的s7otbxsx.dll,并不會出現(xiàn)什么難對付的狀況;事實(shí)上,109種導(dǎo)出形式中的93種都會照這樣處理。然而,真正的“詭計(jì)”使用在剩下的16種導(dǎo)出命令中。這16種導(dǎo)出不會被簡單的轉(zhuǎn)發(fā),而是被改動后的DLL 攔截了。被攔截的導(dǎo)出命令為在PLC中讀、寫、定位代碼塊的例程。通過攔截這些請求,Stuxnet 可以在PLC 管理員沒有察覺的情況下,修改發(fā)送至PLC 或從PLC返回的數(shù)據(jù)。同時,通過利用這些例程,Stuxnet 可以將惡意代碼隱藏在PLC 中。
為了更好的了解Stuxnet 如何進(jìn)入和感染PLC,我們先來看看各種類型的數(shù)據(jù)。PLC 會處理由管理員加載到PLC的代碼和數(shù)據(jù)。這里,我們將簡要介紹一下最常見的模塊和他們的功能:
數(shù)據(jù)模塊(DB)包含了程序相關(guān)的數(shù)據(jù),比如數(shù)字,結(jié)構(gòu)等。
系統(tǒng)數(shù)據(jù)模塊(SDB) 包含了PLC 的配置信息; 它們是根據(jù)連接到PLC 的硬件模塊的數(shù)量/種類設(shè)立的。
組織模塊(OB) 是程序的入口。他們由CPU 循環(huán)執(zhí)行。針對Stuxnet, 有兩個特別需要的OB:
OB1 是PLC 程序的入口。它沒有特別的時間要求,總是循環(huán)執(zhí)行。
OB35 是一個標(biāo)準(zhǔn)的“看門狗”模塊,系統(tǒng)會每100ms執(zhí)行一次。這個功能可能包含了所有用于監(jiān)控緊要輸入的邏輯,以達(dá)到立即響應(yīng),執(zhí)行功能的目的。
功能模塊(FC)都是標(biāo)準(zhǔn)的代碼快。它們包含了會被PLC 執(zhí)行的代碼。一般說來,OB1模塊會引用至少一個FC 模塊。
下面的部分會詳細(xì)講述之前提到的威脅的四大方面。
1. 如何選擇需要感染的PLC。
Stuxnet會根據(jù)目標(biāo)系統(tǒng)的特點(diǎn),使用不同的代碼來感染PLC。
一個感染的序列包括了許多PLC 模塊(代碼模塊和數(shù)據(jù)模塊),用以注入PLC來改變目標(biāo)PLC 的行為。這個威脅包括了三個感染序列。其中兩個非常相似,功能也相同,我們將其命名為序列A和B。第三個序列我們命名為序列C。Stuxnet通過驗(yàn)證“指紋”來判斷系統(tǒng)是否為計(jì)劃攻擊的目標(biāo)。它會檢查:
PLC種類/家族:只有CPU 6ES7-417 和6ES7--2 會被感染。
系統(tǒng)數(shù)據(jù)模塊:SDB 會被解析;根據(jù)他們包含的數(shù)據(jù),感染進(jìn)程會選擇A,B或其它感染方式開始行動。當(dāng)解析SDB 時,代碼會搜索這兩個值是否存在-- 7050h and 9500h;然后根據(jù)這兩個數(shù)值的出現(xiàn)次數(shù),選擇序列A 或B 中的一種來感染PLC。
代碼還會在SDB 模塊的50h 子集索字節(jié)序2C CB 00 01, 這個字節(jié)序反映了通信CP 342-5 (用作Profibus-DP) 是否存在。
而選擇序列C進(jìn)行感染的條件則由其他因素構(gòu)成。
2. 感染方法
Stuxnet使用“代碼插入”的感染方式。當(dāng)Stuxnet 感染OB1時,它會執(zhí)行以下行為:
增加原始模塊的大小;
在模塊開頭寫入惡意代碼;
在惡意代碼后插入原始的OB1 代碼。
![]()
Stuxnet也會用類似于感染OB1的方式感染OB35。它會用自身來取代標(biāo)準(zhǔn)的協(xié)同處理器DP_RECV 代碼塊,然后在Profibus (一個標(biāo)準(zhǔn)的用作分布式I/O的工業(yè)網(wǎng)絡(luò)總線) 中掛鉤網(wǎng)絡(luò)通信。
利用A/B方法的感染步驟如下:
檢查PLC 類型;該類型必須為S7/315-2;
檢查SDB 模塊,判斷應(yīng)該寫入序列A 或B 中的哪一個;
找到DP_RECV,將其復(fù)制到FC1869,并用Stuxnet嵌入的一個惡意拷貝將其取代;
在序列中寫入惡意模塊(總共20個),由Stuxnet 嵌入;
感染OB1,令惡意代碼可以在新的周期開始時執(zhí)行;
感染OB35, 它將扮演“看門狗”的角色。
3. 感染代碼
被注入OB1 功能的代碼是用來感染序列A 和B的。這些序列包含了以下模塊:
代碼塊:FC1865 至FC1874, FC1876 至FC1880 (注意:FC1869并非Stuxnet的一部分,而是PLC的DP_RECV模塊的一個拷貝);
數(shù)據(jù)模塊:DB888 至DB891。
序列A 和B 用DP_RECV 掛鉤模塊來攔截Profibus 中的數(shù)據(jù)包,并根據(jù)在這些模塊中找到的數(shù)值,來構(gòu)造其他的數(shù)據(jù)包并發(fā)送出去。這由一個復(fù)雜的狀態(tài)機(jī)控制(狀態(tài)機(jī)被建立在上面提到的FC 模塊中)。這個狀態(tài)機(jī)可部分受控于數(shù)據(jù)塊DB890 中的DLL。
在某些條件下,序列C會被寫入一個PLC。這個序列比A和B包含更多的模塊:
FC6055 至FC6084;
DB8062, DB8063;
DB8061, DB8064 至DB8070 (在運(yùn)行中產(chǎn)生)。
序列C主要為了將I/O信息讀寫入PLC的內(nèi)存文件映射的I/O 區(qū)域,以及外圍設(shè)備的I/O。
程序A/B 的控制流如下圖所示,在之前的Step7 編輯器的截圖中也有部分顯示(數(shù)據(jù)模塊FC1873):
![]()
而序列C 的程序流則更加復(fù)雜,可以從下面的圖表中看到:
![]()
4. Rootkit
Stuxnet PLC rootkit代碼全部藏身于假冒的s7otbxdx.dll中。為了不被PLC所檢測到,它至少需要應(yīng)付以下情況:
對自己的惡意數(shù)據(jù)模塊的讀請求;
對受感染模塊(OB1 , OB35, DP_RECV) 的讀請求;
可能覆蓋Stuxnet自身代碼的寫請求。
Stuxnet包含了監(jiān)測和攔截這些請求的代碼,它會修改這些請求以保證Stuxnet 的PLC 代碼不會被發(fā)現(xiàn)或被破壞。下面列出了幾個Stuxnet用被掛鉤的導(dǎo)出命令來應(yīng)付這些情況的例子:
s7blk_read: 監(jiān)測讀請求,而后Stuxnet 會返回:
真實(shí)請求的DP_RECV (保存為FV1869);
錯誤信息,如果讀請求會涉及到它的惡意模塊;
OB1或OB35的干凈版本的拷貝
s7blk_write: 監(jiān)測關(guān)于OB1/OB35的寫請求,以保證他們的新版本也會被感染。
s7blk_findfirst / s7blk_findnext: 這些例程被用于枚舉PLC中的模塊。惡意模塊會被自動跳過。
s7blk_delete: 監(jiān)測對模塊的“刪除”操作。
如上文所述,Stuxnet 是一個非常復(fù)雜的威脅,而其中的PLC 感染代碼令問題更加難以解決。僅僅關(guān)于注入的MC7代碼(我們于幾個月前通過逆向工程獲得)就可以討論很久。若想了解更多關(guān)于PLC 感染例程和Stuxnet的總體情況,請務(wù)必關(guān)注我們即將于Virus Bulletin會議中發(fā)布的白皮書。