《起源游戲引擎的漏洞挖掘與利用.pdf》由會員分享,可在線閱讀,更多相關《起源游戲引擎的漏洞挖掘與利用.pdf(42頁珍藏版)》請在三個皮匠報告上搜索。
1、為何研究游戲引擎游戲用戶基數巨大游戲公司普遍不夠重視安全JUST FOR FUN何為游戲引擎何為游戲引擎游戲引擎相當于游戲的內核,是一個游戲的核心組件提供了現成的渲染引擎、物理引擎、碰撞檢測系統、音效、腳本引擎、電腦動畫、人工智能、網絡引擎以及場景管理等等功能開發者可以利用現成的游戲引擎快速的進行游戲開發游戲腳本聲音AI物理引擎圖形繪制內存管理網絡通信何為游戲引擎游戲引擎提供了實現不同功能的API接口開發者可以使用官方提供的SDK來調用接口,實現高度可定制化的游戲開發APIGame AGame B常見的游戲引擎寒霜引擎(FROSTBITE):戰地系列,FIFA系列虛幻引擎(UNREAL):質量
2、效應,戰爭機器起源引擎(SOURCE):CS,軍團要塞,DOTA2CRY ENGINE,UNITY3D漏洞?由于性能等方面的原因,大部分的游戲引擎使用C/C+開發而在游戲引擎中的漏洞,往往能影響同引擎開發的所有游戲APIGame AGame B起源引擎起源引擎起源引擎最初為1998年為半條命開發的游戲引擎作為開放授權引擎,使用范圍很廣引擎構架與功能音頻播放建模/動畫效果渲染系統Steam 集成物理引擎網絡起源引擎多人游戲網絡構架采用客戶端-服務端網絡模型服務端負責游戲規則,玩家動作同步,建立世界等客戶端連接至服務端并且”遵守”規則網絡流量通過UDP/IP發送起源引擎使用UDP以降低網絡延時起源
3、引擎在UPD的基礎上重新定義了“TCP”壓縮與解壓縮加密各種底層的網絡處理多人游戲網絡構架起源引擎客戶端與服務器同步數據的基本時間間隔:TICK每一次TICK中,服務器會將游戲的快照發送給客戶端快照中包含著在此TICK中所有已經改變的實體客戶端只負責將用戶的輸入發給服務器多人游戲網絡構架快照 tick(角色移動,死亡)輸入 tick(w,a,s,d,鼠標點擊等)起源引擎起源引擎中,傳輸信息的基本單位根據發送者分成三大類server messageclient messagenetwork message(客戶端與服務端都會發送)Message起源引擎 net_NOP net_Disconnec
4、t net_File net_LastControlMessage net_SplitScreenUser net_Tick net_StringCmd net_SetConVar net_SignonStateNetwork Message客戶端和服務端都可以發送的消息,以net_開頭起源引擎 clc_ClientInfo clc_Move clc_VoiceData clc_BaselineAck clc_ListenEvents clc_RespondCvarValue clc_FileCRCCheck clc_LoadingProgress clc_SplitPlayerConnect
5、 clc_ClientMessage clc_CmdKeyValuesClient Message由客戶端發送,以clc_開頭起源引擎 svc_ServerInfo svc_SendTable svc_CreateStringTable svc_UpdateStringTable svc_UserMessage svc_EntityMessage svc_GameEvent svc_PacketEntities svc_TempEntities svc_GameEventList svc_GetCvarValue svc_CmdKeyValues.Server Message由服務端發送,以s
6、vc_開頭起源引擎Server Messagesvc_UserMessage是游戲相關的一種特殊的message。由服務器發送,用來通知各種各樣的游戲事件。有著數量眾多的子消息類型,每個游戲都有區別。數量眾多,但是漏洞通常沒有辦法其他游戲中存在E.g:CS_UM_ShowMenu/TF_UM_ShowMenu CS_UM_SayText.svc_UserMessageShowMenu.起源引擎Network Entities實體(Entities)在客戶端和服務端同時存在起源引擎將幾乎所有的東西都抽象為實體。服務器會保證所有客戶端的實體保持同步只有和客戶端有關的實體才會被創建或者更新(可見的/
7、可以聽到的)當客戶端收到svc_PacketEntities消息時,會自動創建不存在的實體。起源引擎最多可以同時持有2048個實體。實體通常是在snapshots中發送的。ABDC起源引擎ConVars and ConCommandsConsole Variables 儲存了服務端和客戶端的各種配置參數,類型linux中的環境變量有一些Console Variables會在客戶端和服務端上同步Console Variables有各種屬性,其中有一些可以被遠端讀取和設置e.g:bot_dont_shoot:設置后,bots將不會開火host_map:當前地圖名字(只讀,不可設置)Console
8、Commands是一些命令用來實現各種附加功能本質上是一些擁有特殊屬性的ConVarse.g:retry:重新連接上一次連接的服務器 gods:所有玩家無敵起源引擎ConVars and ConCommandssvc_GetCvarValue:服務端可以通過發送這個消息獲取客戶端的Cvarclc_RespondCvarValue:客戶端通過這個消息給服務端返回Cvarnet_StringCmd:客戶端和服務端都可以發送這個消息讓對方執行cmdGetCvarValueRespondCvarValueStringCmd起源引擎Message格式起源引擎默認使用bitstreams來傳輸messag
9、e消息每個游戲可以自定義消息的傳輸格式例如:CSGO使用google protocol代替bitstreams來傳輸消息但是某些特殊的消息(例如svc_PacketEntities)依然使用bitsteams起源引擎漏洞挖掘起源引擎漏洞挖掘攻擊面有哪些?Message:直接接受并處理網絡數據,非常容易出問題engine.dll:引擎的核心,包括net_X 和svc_X 消息的處理函數client.dll:絕大部分游戲相關代碼,所有svc_UserMessage子消息的處理函數Cvar:有數千個Cvar,許多可以由服務端來設置地圖解析:起源引擎的地圖格式為BSP,客戶端會自動的從服務端下載地圖并
10、加載音頻解析起源引擎TF2中的整形溢出服務端可以發送svc_CreateStringTable消息以創建一個字符串列表。這個字符串列表記錄了服務端經常發送的一些字符串通過這個列表,服務端只需要發送一個index地址來代替字符串起源引擎TF2中的整形溢出*this,SVC_CreateStringTable*creatstrtab)char CBaseClient:ProcessCreateStringTable(void.int dataBits;/edx2 void*dest;/edi8 char*source;/esi8.int destLen;/esp+30h ebp-Ch8 int s
11、ourceLen;/esp+38h ebp-4h6.if(creatstrtab-isCompressed)/#1/#2dataIn=&creatstrtab-m_DataIn;.destLen=READ32(dataIn);sourceLen=READ32(dataIn);.dest=operator new(destLen+3)&0 xFFFFFFFC);source=operator new(sourceLen+3)&0 xFFFFFFFC);bf_read:ReadBits(dataIn,source,8*sourceLen);NET_BufferToBufferDecompress(
12、dest,&destLen,source,sourceLen);.operator delete(dest);operator delete(source);.CS:GO中不存在此bug起源引擎CSGO中的數組越界寫UM_ProcessSpottedEntityUpdate函數用以處理CS_UM_ProcessSpottedEntityUpdate消息至于具體的作用,不用關心起源引擎CSGO中的數組越界寫*data)int ProcessSpottedEntityUpdate(_BYTE*this,ProcessSpottedEntityUpdate_t.for(i=0;idx numEnti
13、ties;i=idx)entitiesArray=data-entitiesArray;entName=0;update=entitiesArrayidx;ent_idx=update-ent_idx;.objidx=0 x1E0*&this_objidx*&this_objidx*&this_objidx*&this_objidx*&this_objidx*&this_objidx*ent_idx;-16=4*update-origin_z;-24=4*update-origin_x;-20=4*update-origin_z;-12=0;-8=update-angle_y;4=0;.TF2
14、中不存在此bug.signed int newEntity;/edx1.IClientNetworkable*ent;/edi3.newifent ifreturn Host_Error(CL_CopyNewEntity:invalid class index(%d).n,iClass);.if(ent)v8=ent-vtbl-someMethod(ent);.起源引擎CL_CopyNewEntity中的符號整形溢出服務器會在每個tick中發送svc_PacketEntities消息以更新和創建那些已經發生改變的實體Entity=u-m_nNewEntity;(newEntity=2048)r
15、eturn Host_Error(CL_CopyNewEntity:m_nNewEntity=MAX_EDICTS);=entitylist-vtbl-GetClientNetworkable(newEntity);(iClass=gClassMaxIndex)IClientNetworkable*GetClientNetworkable(CClientEntityList*this,int index)return(&this-m_EntityCacheInfo)2*index;起源引擎只要偽造一個object,越界讀取回我們偽造的object,調用類虛方法的時候就可以控制eip漏洞出現在起
16、源引擎內核中,影響所有游戲CL_CopyNewEntity中的符號整形溢出起源引擎我們需要讓entitylist.m_EntityCacheInfoidx指向我們偽造的結構體讓我們看看GetClientNetworkable 的匯編實現GetClientNetworkable proc nearindexpush mov mov mov pop retn=dword ptr8 ebpebp,espeax,ebp+index eax,ecx+eax*8+28h ebp4GetClientNetworkable endp負數的index可以繞過長度檢查。因為index會乘以8,所以可以構造任意in
17、dex值現在我們需要在.data 段構造數據整形溢出利用起源引擎怎么在.data段存儲數據?讓我們看看 UserMessagesUM_ShowMenu 消息用來在客戶端顯示菜單此消息會儲存數據在client.dll 的數據段中所以,現在的思路是發送UM_ShowMenu 消息來偽造C+類。然后觸發svc_PacketEntities 中的漏洞整形溢出利用svc_UserMessage (UM_ShowMenu)svc_PacketEntit ies(Bugger)client.dllentitylist.m_EntityCacheInfofake_obj=0 x41414141,0 x4242
18、4242,0 x43434343,.起源引擎由于aslr的關系,還需要考慮leak讓我們再看看CL_CopyNewEntity()函數整形溢出利用intcdecl CL_CopyNewEntity(CEntityReadInfo*u,int iClass,int iSerialNum).ent=CL_CreateDLLEntity(u-m_nNewEntity,iClass,iSerialNum);if(!ent).return Host_Error(CL_ParsePacketEntities:Error creatingentity”);.ent=(entitylist-vtbl-GetC
19、lientNetworkable)(u-m_nNewEntity);if(!ent).return Host_Error(CL_ParseDelta:invalid recv table for ent%d.n,u-m_nNewEntity);.起源引擎當實體不存在時(GetClientNetworkable 函數返回NULL),CreateDLLEntity函數會自動創建一個新的實體最后由AddEntityAtSlot函數添加實體可以看到,最后實體被添加到的EntityArray數組中整形溢出利用int*CBaseEntityList:AddEntityAtSlot(unsigned int
20、*EntityArray,IClientNetworkable*ent_object,int index,int serial_num)unsigned int*object_ptr;/eax1.object_ptr=&EntityArray4*index+1;*object_ptr=ent_object;if(serial_num!=-1)EntityArray4*index+2=(unsigned int)serial_num;.起源引擎有兩個數組m_EntityArray 和m_EntityCacheInfo有一個index只要m_EntityCacheInfo中的值為NULL,就可以在
21、m_EntityArray中創建一個C+對象整形溢出利用0 x00000000.C+object.m_EntityCacheInfom_EntityArrayidx起源引擎現在我們有了一個C+對象的指針如果我們將它作為一個字符串讀取回來,就可以leak出vtable從而繞過aslr所以,怎么讀?答案是Cvars 有許多的Cvar儲存在clinet.dll 的.data段中,可以被覆蓋Cvar有一個char*str_value 字段。只要將它替換為C+對象指針。然后用svc_GetCvarValue 來讀取整形溢出利用起源引擎所以,現在的攻擊思路是這樣的整形溢出利用svc_GetCvarValu
22、e(corrupted_ cvar)svc_PacketEntities (corrupt_cvar)svc_ResponCvarValue(corrupt ed_cvar)svc_UserMessage(Sh owMenuMessage)svc_PacketEntities (pwn)起源引擎現在還有一個問題由于整形溢出,之后的函數檢查將會失敗。Host_Error 函數將會斷開客戶端和服務端的連接需要讓客戶端重新連接服務器整形溢出利用int cdecl CL_CopyNewEntity(CEntityReadInfo*u,int iClass,int iSerialNum).ent=ent
23、itylist-vtbl-GetClientNetworkable(u-m_nNewEntity);if(!ent).return Host_Error(CL_ParseDelta:invalid recv table for ent%d.n,u-m_nNewEntity);.起源引擎還記得ConCommands么?:)ConCommands中正好有這么一個命令 retry:重新連接上一次連接的服務器整形溢出利用雖然沒有辦法給一個已經斷開連接的客戶端發送命令。但是可以將command 和 svc_PacketEntities放在同一個數據包中發送由于command會延時執行的原因,retry命
24、令可以在服務器斷開連接之后執行起源引擎所以,改進之后的攻擊思路是這樣的。整形溢出利用svc_GetCvarValue(corrupted_ cvar)svc_PacketEntities(corrupt_cvar)+net_StringCmd(retry)svc_ResponCvarValue(corrupt ed_cvar)svc_UserMessage(Sh owMenuMessage)svc_PacketEntities (pwn)Wait for client reconnect.攻擊演示TODOTODO游戲引擎的安全性很低不要連接陌生的游戲服務器Fuzzing 游戲引擎的某些模塊,比如地圖解析謝謝