PHP程式碼審計——Day2-Twig

smile_2233發表於2024-04-02

前置知識 - Twig

Twig是一個PHP模板引擎,它提供了一種更簡潔和可擴充套件的方法來建立PHP應用程式的檢視層。Twig模板引擎旨在將設計與業務邏輯分離,併為開發人員提供一種更加清晰和易於維護的方式來構建網頁。

使用前提:在PHP專案中安裝,然後引入Twig的自動載入檔案

composer require twig/twig

require_once 'vendor/autoload.php';

基本配置示例:

$loader = new \Twig\Loader\FilesystemLoader('path/to/templates');
$twig = new \Twig\Environment($loader, [
    'cache' => 'path/to/cache',
    'debug' => true,
]);

使用FilesystemLoader來載入模板檔案。將path/to/templates替換為你實際的模板目錄。配置中的cache引數指定Twig編譯後的快取目錄,debug引數設定為true可以使Twig在有錯誤時輸出更詳細的錯誤資訊。

漏洞解析

// composer require "twig/twig"
require 'vendor/autoload.php';

class Template {
  private $twig;

  // 初始化 Twig 環境並載入模板
  public function __construct() { 
    $indexTemplate = '<img ' .
      'src="https://loremflickr.com/320/240">' .
      '<a href="{{link|escape}}">Next slide &raquo;</a>';

    // Default twig setup, simulate loading
    // index.html file from disk
    $loader = new Twig\Loader\ArrayLoader([
      'index.html' => $indexTemplate
    ]);
    $this->twig = new Twig\Environment($loader);
  }

  // 從 $_GET 中獲取名為 nextSlide 的引數,並透過 filter_var 函式驗證為一個有效的 URL,並返回該URL
  public function getNexSlideUrl() {
    $nextSlide = $_GET['nextSlide'];
    return filter_var($nextSlide, FILTER_VALIDATE_URL);
  }

  // 渲染模板,將下一張幻燈片的連結作為引數傳遞給 Twig 模板引擎,然後輸出渲染後的 HTML 內容
  public function render() {
    echo $this->twig->render(
      'index.html',
      ['link' => $this->getNexSlideUrl()]
    );
  }
}

(new Template())->render();

考察點:xss漏洞

程式碼中使用了escapefilter_var兩個過濾方法,但還是可以被繞過,程式使用 Twig 模板引擎定義的 escape 過濾器來過濾link,而實際上這裡的 escape 過濾器,是用PHP內建函式 htmlspecialchars 來實現的

{{link|escape}}
這是twig自帶的過濾器,在twig的文件中我們可以知道它是透過PHP的htmlspecialchars來實現的,他會將特殊字元轉換為 HTML 實體。

htmlspecialchars函式:(PHP 4, PHP 5, PHP 7) 將特殊字元轉換為 HTML 實體

定義 :string htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string$encoding = ini_get("default_charset") [, bool $double_encode = TRUE ]]] )

& (& 符號)  ===============  &amp;
" (雙引號)  ===============  &quot;
' (單引號)  ===============  &apos;
< (小於號)  ===============  &lt;
> (大於號)  ===============  &gt;

filter_var: (PHP 5 >= 5.2.0, PHP 7) 使用特定的過濾器過濾一個變數

定義 :mixed filter_var ( mixed $variable [, int $filter = FILTER_DEFAULT [, mixed $options ]] )

過濾函式,有一些常用的過濾器型別及對應的預定義變數:

  • FILTER_VALIDATE_EMAIL:驗證是否為有效的電子郵件地址。
  • FILTER_VALIDATE_URL:驗證是否為有效的URL。
  • FILTER_VALIDATE_IP:驗證是否為有效的IP地址。
  • FILTER_SANITIZE_STRING:去除字串中的 HTML 和 PHP 標記。
  • FILTER_SANITIZE_NUMBER_INT:去除字串中的所有字元,除了數字和符號。

繞過方法

使用JavaScript偽協議繞過

<a href="javascript:;">這個標籤中的javascript是啥意思?</a>

// href 屬性的值可以是任何有效文件的相對或絕對 URL,包括片段識別符號和 JavaScript 程式碼段。
// 這裡的href=”javascript:;”,其中javascript:是偽協議,它可以讓我們透過一個連結來呼叫javascript函式。
// javascript:是表示在觸發<a>預設動作時,執行一段JavaScript程式碼

demo測試

<?php
  $url = filter_var($_GET['url'], FILTER_VALIDATE_URL);
  var_dump($url);
  $url = htmlspecialchars($url);
  var_dump($url);
  echo "<a href='$url'>Next slide~~</a>";
?>

構造payload:?url=javascript://comment%250aalert(1);

  • 這裡的 // 在JavaScript中表示單行註釋,所以後面的內容均為註釋
  • 字元 %0a 為換行符,所以 alert 語句與註釋符 // 就不在同一行,就能執行
  • 這裡我們要對 % 百分號編碼成 %25 ,因為程式將瀏覽器發來的payload:javascript://comment%250aalert(1) 先解碼成: javascript://comment%0aalert(1) 儲存在變數 $url 中,然後使用者點選a標籤連結就會觸發 alert 函式。

修復建議

對於XSS漏洞,最好過濾關鍵詞,將特殊字元進行HTML實體編碼替換

參考文章

https://xz.aliyun.com/t/2457?time__1311=n4%2BxnieDqQqWwqCwx0v%2Bb%2BoitrGQkQetKz4D&alichlgref=https%3A%2F%2Fxz.aliyun.com%2Fu%2F10394%3Fpage%3D2
https://lanvnal.com/2020/02/19/rips-php-security-calendar-2017-xue-xi-ji-lu/#toc-heading-2

相關文章