本文主要是從工業控制網絡必備的組件 PLC (可編程控制器)出發,闡明了一種新型后門的實現。本文主要內容是來自 BLACK HAT 2015 上柏林自由大學 scadacs 團隊發表的演講,這是他們的paper原文Internet-facing PLCs - A New Back Orifice。我會濾掉他們論文湊字數的部分,并在他們給出的核心思路上增加一些實現方面的具體技巧和資料。
不了解工控安全的哥們兒可以先去這篇文章上補一下基礎知識 工控安全入門分析 。
下面幾個部分都是一些背景和基礎知識,如果已經對plc工控安全非常了解,可以直接跳到攻擊描述(0x03)部分。
本文中,我們研究攻擊者如何通過公網plc訪問到深層工業網絡。
我們采取的方法是將plc變成網關(本文采用西門子系列plc相關技術和特性),這種方法在缺乏適當權限認證手段的plc上是可行的。經驗豐富的攻擊者擁有某plc的訪問權限時,可以往上面上傳或者下載代碼,只要代碼是由MC7字節碼組成,這是plc的原生代碼形式。我們研究了運行時環境中的plc,并發現可以通過上傳mc7代碼來實現很多網絡服務。特別是,我們實現了
并且他們的實現完全只依靠編譯為MC7字節碼的STL語言代碼。我們的掃描器和代理可以部署在plc中,并且不會中斷plc中原有程序的運行,這可以使運維很難意識到plc已被感染。為了說明和分析深層工業網絡入侵,我們開發了一個概念性證明工具,PLCinject(附上github項目地址: SCADACS/PLCinject)。根據我們的概念性證明,xxxxxxxx(這段太tmd復雜,我實在沒法準確翻譯,主要意思就是講運行在plc上的惡意軟件會使其原有代碼擴展增加,如果我們定時觀測原有代碼和感染惡意代碼后的程序,在統計學上這兩者的運行效果有明顯的差異,然而其對生產過程的影響微乎其微,除非運營者主動監控從PLC中發出的惡意訪問的流量,否則很難在生產過程中發現)。此外,攻擊者可以利用我們的方法,通過工業控制網絡來攻擊企業的業務網絡。這意味著網絡管理必須警惕從業務網絡正面和背面發起的雙向攻擊。
我會在文章最后補充一下針對施耐德plc的編碼。
上圖展現了典型的使用自動化系統的公司結構。工業控制系統由這么幾層構成。在頂部是企業資源規劃(ERP)系統,其保存著當前可用資源和生產能力的相關數據。制造執行系統(MES)能夠管理多個工廠或平臺,并且從ERP系統接受任務。在MES下的系統位于工廠內部,監督、控制和數據采集(SCADA)系統控制生產線。他們提供關于目前生產狀態的數據,并且他們提供干預手段。存儲著有關生產過程的邏輯的設備稱為可編程邏輯控制器(PLC)。人機交互界面(HMI)顯示當前的進度,并且允許運營者與生產過程相互作用。
本文將著眼于針對 PLC 的攻擊。
PLC 原本僅僅是為自動化控制而開發,在其開發之初,其應用場景是極其封閉的,幾乎不能與工業內網外的任何第三方設備有所接觸,但是近幾年互聯網的迅猛發展,和物聯網、智能硬件的出現,開始逐漸有工業 PLC 暴露在公網之中,大家可以去seebug和shodan上搜索schneider或者siemens等廠商型號來發現公網上的plc設備。盡管如此,目前PLC的安全性是十分十分差的。首先來說,plc的固件迭代更新緩慢,雖然廠商可能進行維護和更新,但是給工業控制網絡中的正在運行的線上plc更新固件,代價是異常巨大的,一次關機可能就是整個工廠的停止運行。其次,目前的plc已經有了一些比較低級的訪問控制手段,但是很少有人會主動開啟,因為它會降低plc的運行效率和穩定性。 因此,一般來說,如果某個plc面向公網開放,我們可以向其加載任意代碼。
除了在權限控制上的嚴重問題,攻擊者有可能利用plc作為一個進入生產網絡甚至公司內網的網關。在本文中,我們分析和討論這一威脅載體,并且,我們將證明,這種利用方式是真實可行的。出于演示的目的,我們開發一個運行在plc上的端口掃描器和一個socks代理。這個掃描器和代理使用plc的原生編程語言Statement List(STL)編寫。
硬件
PLC由一個 CPU (一般帶有通訊模塊,如工業以太網、modbus、profinet等等,和一些服務的接口,如ftp、web、telnet等等),和其外部附加的數字量和模擬量輸入輸出模塊共同組成(有時外部還會附加專用的通訊模塊)。本文使用西門子 S7-314C-2 PN/DP。
執行環境
這部分如果學過計算機組成原理會比較容易看懂,這部分主要講plc的代碼執行流程,跟后面我們的攻擊方式的隱蔽性可行性和代理編寫時的通信穩定性密切相關。
西門子PLC運行著實時操作系統,他初始化周期性時間監視。隨后操作系統周期性執行四個步驟,如下圖:
在第一步中,CPU復制過程鏡像的輸出值來輸出模塊的狀態。第二步,CPU讀取輸入模塊的狀態,并且更新過程映像的輸入值。第三步,用戶程序在時間片中執行1毫秒的持續時間。每個時間片被分割成三個部分,依次執行:操作系統,用戶程序和通信。時間片的個數取決于當前的用戶程序。默認情況下,時間應該不長于150毫秒,工程師可以配置不同的值。如果規定的時間用盡,中斷例程被調用,在通常情況下CPU返回到周期的開始狀態,并重新開始循環時間監視。
我們使用 STEP 7 為plc進行編程,我使用的版本是v5.5。
參考這篇文章進行安裝 step7 v5.5 cn 軟件下載、仿真器安裝、授權
注意,仿真器是需要額外安裝的,在上文中有。
工程師可以使用梯形圖(LAD),功能塊圖(FBD),結構化控制語言(SCL)和語句表(STL)來為PLC編程。與基于文本的SCL和類似匯編的STL,LAD和FBD語言是圖形化的。PLC程序被分成組織塊(OB),功能(FC),功能塊(FB),數據塊(DB),系統功能(SFC),系統功能塊(SFB)和系統數據塊(SDB)這幾個單元。OB,FC和FB包含著實際的代碼,而DB存儲著數據結構,SDB存儲PLC的當前配置。帶有前綴M的內存地址被用于內部數據存儲尋址。
一個PLC程序至少由一個組織塊(稱為OB 1)組成,這就相當于C程序中的main函數。它將由操作系統調用。存在更多的用于特定用途的組織塊,比如,OB 100。這個塊在PLC啟動時被調用一次,并通常用于初始化系統。
關于各種編程語言的語法,再次不再贅述,請自行查閱相關資料。
這一部分盡管原文中針對通訊過程做了較為詳細的闡述,但是我不會做太多解釋,因為本文主要側重惡意代碼的編寫。
但是還是要多說一句,這些嵌入式設備通常都是使用裁剪過的vxworks等系統,我個人認為,目前工業控制系統的滲透攻擊和漏洞挖掘,在固件分析還沒有特別深入的現狀下,針對工控網絡的通訊協議進行攻擊是最為高效的手段。我大體翻了下wooyun上的工控漏洞,其實還是web滲透那一套,其實對于工業網絡,有時候可用性甚至比保密性更加重要(比如伊朗核設施爆炸)。
西門子plc使用其自身的S7Comm協議來傳輸塊。這是一種基于TCP/IP和基于TCP的ISO傳輸服務的遠程過程調用(RPC)協議。包封裝如下圖:
協議提供了以下功能:
被傳輸的塊被結構化,其由頭部,數據部分和尾部構成。
詳細的傳輸過程請查看原文。這里僅給出一些已知字節的結構:
尾部包含用于調用功能的參數信息。并不是所有頭部和尾部的字節都被我們所知曉,但是我們已經確定了用來理解其內容的必要區域。
這部分主要是為了解釋進行惡意代碼注入的方式,其實完全可以使用step 7 進行代碼注入,甚至可以說該協議的所有操作基本都可以使用 step 7 完成,但是如果我們弄懂了協議的結構內容和作用,就可以編寫利用腳本,自動化攻擊。
西門子plc提供了一個系統庫,該庫包含了可以建立任意TCP/UDP連接的功能。攻擊者可以使用完整的TCP/UDP支持來掃描公網plc背后的本地生產網絡。 其實根據我的了解,應該只有profinet上的通訊可以完成完整的TCP/UDP請求,這就要求我們使用的西門子plc的型號中,必須帶有 PN
,比如原文中使用的 S7-314C-2 PN/DP,除此之外還有s7-319-3 PN/DP等等。
我們首先下載plc的OB1
塊,之后添加一條CALL
指令調用任意一個我們可控制的函數,在我們給出的樣例中,該函數叫做FC 666
。之后,patche后的OB1,也就是FC 666
和其他塊(這里可能包含很多的塊,比如我們自己編寫的FC塊,背景數據塊,共享數據塊等等),將被上傳到PLC。下圖展示了代碼注入過程:
在下個執行周期時,新上傳的包含攻擊代碼的程序就會被執行,并且不會造成任何服務中斷(這里我認為并不一定,在RUN
模式下還是會有中斷發生,在RUN-P
模式下就會自動在下一個周期執行而不會發生中斷)。這個過程使得攻擊者能夠在plc上運行任意惡意代碼。我們隨這篇文章發布了一款名叫PLCinject的工具,他可以自動化完成這個過程。有了這種技術,攻擊者可以執行如下圖所示的攻擊流程:
在第一步中,攻擊者注入一個SNMP掃描器,它會與plc上的正常代碼一同運行。在完成一個針對本地網絡的完整SNMP掃描(第二步)之后,攻擊者可以從plc中下載掃描結果(第三步)。攻擊者現在擁有了公網plc背后內網的一張縮略圖。之后他溢出SNMP掃描器并注入一個socks代理(第四步)。這使得攻擊者可以通過充當代理的plc到達本地生產網絡的所有plc。在之后的兩節中我們將闡明SNMP掃描器和SOCKS代理的實現。我們不會詳細解釋每個操作和系統調用的細節。對于這些的詳細描述我們參考了S7-300 Instruction list S7-300 CPUs and ET 200 CPUs和Siemens. (2006) System Software for S7-300/400 System and Standard Functions Volume 1/2。
本文用到的幾個系統調用為: SFC 51 "RDSYSST", FB 65
"TCON", FB 63 "TSEND" ,FB 64 "TRCV", FB 67 "TU
SEND", FB 68 "TURCV", UDT 65 "TCON_PAR".
請在上面給出的那篇Siemens. (2006) System Software for S7-300/400 System and Standard Functions Volume 1/2查找其參數和輸入輸出。
西門子plc不能作為一個TCP端口掃描器來使用,因為TCP連接函數TCON
直到該函數成功建立連接之前,都無法被終止。此外,最多只能在西門子S7-300并發地運行8個TCP連接。因此,該PLC只能在不會發生8個連接同時失敗的情況下作為TCP掃描器(如果8個連接同時失敗,那么這8個連接函數就無法斷開,那么掃描就無法繼續進行了)。此限制并不適用于無狀態的UDP連接。這就是我們要使用基于UDP的簡單網絡管理協議(SNMP)的原因。SNMP v1.0 在 RFC 1157[23]中被定義,并且它是為監視和控制網絡設備而開發的。大量網絡設備和大部分西門子 Simatic PLC 有默認支持SNMP。西門子plc在開啟SNMP功能的情況下是非常活躍的。通過使用OID 1.3.6.1.2.1.1.1
讀取SNMP系統基本信息(sysDesc)對象,西門子plc將發送其產品類型,產品型號,硬件和固件版本,形如以下SNMP響應:
#!bash
Siemens, SIMATIC S7, CPU314C-2 PN/DP, 6ES7 314-6EH04-0AB0 , HW: 4, FW: V3.3.10.
該系統描述可以用來在漏洞和exp庫中匹配已發現的plc。plc的固件不會經常打補丁。主要有兩種原因:一方面,plc的固件升級將中斷生產過程,這將造成虧損;另一方面,plc的固件補丁能夠導致某些產品質量問題,這對客戶來說是不能容忍的。這就是為什么找到一個擁有已知漏洞的西門子plc設備的可能性非常之高。該SNMP掃描器可以被分解為以下步驟:
plc編程與使用C語言在X86系統上正常編程完全不同,如果學過verilog這種針對硬件的編程語言的話就比較容易理解。每個PLC程序周期性執行,所以他需要在每步之后將程序狀態存儲到狀態變量中。在此我們只解釋SNMP掃描步驟1到3。 下圖顯示第一步的一段代碼片段,調用RDSYSST函數。
RDSYSST函數讀取內部系統狀態表(SSL)來獲得plc本地IP,SSL請求通常是用于診斷。
這里給出siemens官網上的一個實例:讀取本地ip
14和15行將在RDSYSST功能繁忙的情況下停止該函數。 下圖展示了程序如何計算本地網絡的第一個ip地址和ip總量。
這是通過將plc的本地ip和子網掩碼進行按位AND
運算來完成的,這將返回本地網絡的起始地址(24-30行)。現在SNMP掃描器需要知道子網的ip總量。因此,我們將子網掩碼與0xFFFFFFFF
進行XOR
運算(35-39行)。結果即為子網的ip總量。 下圖展示了如何使用STL語言建立一個UDP連接。首先我們需要調用TCON
方法,并使用我們TCON_PAR_SCAN
這個DB作為該函數的背景數據塊。
在UDP
協議的情況下,TCON
函數不能建立連接,他只能在TCP協議下完成,因為與UDP
相反,他的連接是定向的。 但是只調用一次TCON
是不夠的,當#connect
變量在兩次調用之間從0上升到1時,連接函數才開始工作。這就是為什么我們在連接函數首次出現之后編寫了一個切換功能(10-11行)。這將在一個周期里,TCON
首次被調用之后改變#connect
的值,使之從False變為True。TCON
函數在下一個周期被調用時,檢測到上升沿信號,之后開始執行。下一步操作是,基于UDP
協議的SNMP數據包,并且接收響應。這將通過調用TUSEND
和TURCV
函數來完成。之后,SNMP掃描完成,所有數據被存儲到可以被攻擊者下載的數據塊中(step 3)。
給出一個實例udp協議交換
關于 TCON
這個函數,其他參數沒什么問題,關鍵在于CONNECT
這個參數,這個參數需要一個指針實參,指向以UDT 65
為模版建立的DB塊。關于UDT 65
的細節可以在上文中的手冊中找到,但是我們要手動建好這么個數據結構真是非常蛋疼,所以我找到了一個專門的用來建立TCON這個連接參數的工具 Open Communication Wizard_V2.3.3
一旦攻擊者已經發現所有的SNMP設備,包括本地plc,下一步就是要連接到他們。這可以通過使用可訪問的plc作為進入內網的網關來實現。為了做到這一點,我們選擇在plc上實現一個socks5代理。這有兩個主要原因,首先,SOCKS協議是輕量級的,非常容易實現。另外所有應用都可以使用此類型的代理,要么應用是原生支持socks協議的,要么可以使用所謂的proxifier來為任意程序添加對socks的支持。socks5協議在RFC 1928[24]中被定義。通過代理無差錯地TCP連接到目標需要以下幾步:
我們的實現提供了最小化的必要功能,他不支持身份驗證,所以我們跳過了第三步。我們也不支持錯誤處理。并且,只支持連接IPV4地址。一旦客戶端連接,我們期望該信息會經過以下幾個步驟:
如前面提到的,plc程序周期性執行。這就是為什么我們使用一個簡單的狀態機來處理SOCKS協議。因此,我們將每個狀態編號,并且使用一個跳轉表來執行相應的代碼塊。如下圖:
狀態轉換是由存儲在一個數據塊中的狀態碼的遞增來實現的。 每個狀態和其動作描述如下:
綁定監聽: 第一次啟動程序需要綁定和監聽SOCKS端口1080,這是通過系統調用TCON
在被動模式下實現的。我們保持在這個狀態直到有人連接到這個端口。 協商: 我們等待客戶端發送任何消息。這通過TRCV
函數實現,該函數需要EN_R
參數使之執行。見下圖:
認證: 第一條消息后,我們發送回復,指明客戶端是無認證的。為了這個目的,我們使用TSEND
系統調用。與TRCV
相反,這個函數是邊沿控制的,這意味著REQ
參數必須在連續的調用之間從False變為True,這樣來激活發送。如下圖所示,我們切換標志并且在REQ的上升沿兩次調用TSEND
。
連接請求: 然后,我們期望客戶端發送一個包含有目標IP和端口號的連接設置信息,該信息將為下一個狀態而儲存。 連接: 我們使用TCON
建立到目標的連接。
連接驗證: 當到目標的連接已經建立,我們向客戶端發送驗證信息。
代理: 現在我們只需要在客戶端和目標之間打開連接隧道。所有使用TRCV
從客戶端收到的數據被存儲到一個緩沖區中,并且TSEND函數也從中取出數據發送給客戶端。同樣的原則也適用于相反的方向,但是我們必須考慮到,發送消息可能需要幾個周期,因此,第二個緩沖區被用于確保沒有消息被混合或者丟失。TRCV
的錯誤標志被作為連接斷開的信號。當該信號發生時,我們將發送最后收到的數據,然后跳轉到下一狀態。
復位: 在這種狀態下,我們使用TDISCON
關閉所有連接,并且重置所有標志位到其初始狀態。
該代理的最大傳輸速率大約是40kb/s。如果socks代理單獨運行在plc上,速率可以高達730KB/s。所有的網絡設備以100Mbit/s的以太網直連plc。最后我們在實驗室中測試了所描述的攻擊周期。除了常規的通信,我們驗證了可以通過使用tsocks
庫的socks隧道實施對DOS漏洞CVE-2015-2177的利用。利用代碼通過socks隧道成功執行。
我們的攻擊有一定的局限性。為了確保plc總是可以應答請求,主程序執行時間需要被監視,當執行時間過長時主程序會被結束。我們上傳的snmp掃描器或者代理代碼,連同原有程序,不應超過最大總執行時間,150ms。注入一個掃描器或者代理不太可能觸發超時,因為代理運行時的附加執行時間為1.35ms,這遠遠小于150ms。另外,超時可通過在注入程序執行完成后重置時間計數器來避免,這要使用系統調用RE_TRIGR。針對上文中的攻擊,最簡單的防護方式是保持plc脫機,或者使用VPN來代替公網訪問。如果這是不可能的,應當激活西門子plc的第三級保護級別。這使得plc的讀取基于口令,并且有寫保護。沒有正確密碼的攻擊者不能修改plc程序。根據我們的研究,這個功能在實際中很少用到。另一個應用保護機制是使用防火墻過濾可疑數據包,比如試圖重新編程plc的惡意訪問。
查閱施耐德 UNITY PRO 手冊得到以下技術細節:
施耐德設備的tcp通訊是依靠TCP_OPEN
庫完成的,但是這個庫有很大的限制。 首先Unity Pro默認的庫中是沒有TCP_OPEN這個功能塊庫的,TCP OPEN的庫是需要額外訂購的,訂貨號為TLXCDTCP50M,安裝后才能使用,且只有Unity Premium才支持這個功能塊庫。并且我發現,可能只有 TSX ETY 1100WS 和 TSX ETY 5103 提供了TCP_OPEN
這個庫。
這就是說,可能在實際環境中只有非常非常非常少的施耐德設備可以完成完整的TCP通訊。這也是這種攻擊的局限性,這在總結部分也會提到。
這里只說一下可能用到的幾個函數塊,具體API調用請查閱unity pro手冊的 EF/EFB/DFB庫 -> TCP Open庫 -> 高級 部分。
其實這篇文章思路雖然新穎,但是并不是非常猥瑣,還是大家都可以想到的點,雖然思路簡單,但是經過我親自的編程實現,發現坑點實在是太多,原本一周時間就完成本文的復現,但是最后發現有點心有余而力不足的感腳,畢竟我作為一只web狗,研究自動化的時間還太短。
之后,個人認為,這種攻擊手段的局限性非常大,據我調查,西門子PN型號的CPU,數量相比之下還算較多,但是其他廠商的cpu基本不具有完整的tcp/udp通訊功能,比如上一部分提到的施耐德。
下面是我自己寫的幾個文中出現的小demo,放到一個工程里了,雖然實現上不完整并且問題很大,但是還是放出來給初學者摸一摸,更是想讓高手指點一下。希望大家能一起交流一下。
直接用這個zip歸檔在step7里恢復一下就可以了,不需要解壓。
有任何問題或者姿勢指導,請發送郵件到 ronnyschiatto#gmail.com