go_ssti风险
老铁233 发表于 广东 WEB安全 862浏览 · 2024-09-08 14:52

go_ssti风险

简介

​ 这篇文章中,主要讲解Go场景下SSTI风险

环境记录

​ 详见下文具体代码

从SSTI开始

​ SSTI(Server-Side Template Injection,服务端模板注入)是一种Web应用漏洞,发生在应用程序使用不安全的模板引擎进行服务器端模板渲染时。攻击者通过在用户输入中注入恶意模板代码,服务器在渲染模板时执行了这些代码,从而导致执行任意代码、窃取数据或执行其他恶意操作。

​ 其中:常见的模板引擎如Jinja2(Python)、Thymeleaf(Java)、Smarty(PHP)等如果没有对用户输入进行适当的过滤和转义,就有可能导致SSTI漏洞。

攻击流程

  • 发现模板渲染位置:攻击者通过各种输入字段,尝试注入模板表达式,观察输出中的变化,以确定是否存在模板注入点。

  • 模板注入:一旦发现模板渲染,攻击者可以通过注入模板引擎的特殊语法执行恶意代码。例如,在Jinja2中注入{{ 7*7 }},如果返回49,则表示可以注入模板代码。

  • 代码执行:通过进一步构造模板表达式,攻击者可能最终执行任意代码,获取敏感数据,甚至完全控制服务器。

预防措施

  • 避免直接使用用户输入进行模板渲染:不信任用户的输入,尽量使用安全的模板引擎或避免用户控制的输入直接渲染到模板中。

  • 对用户输入进行严格过滤:可以使用白名单来限制允许的输入。

  • 限制模板引擎功能:有些模板引擎提供了沙盒模式,限制了模板中能够执行的操作,以防止恶意代码的执行。

  • 安全审计和测试:通过静态代码分析、渗透测试和自动化扫描工具检测可能的SSTI漏洞。

Go语言中的SSTI

​ 在 Go 语言中,SSTI(服务器端模板注入)问题通常出现在使用不安全的模板引擎进行用户输入渲染时。Go 标准库中提供的 html/templatetext/template 两种模板引擎,前者是为防范 XSS(跨站脚本攻击)设计的,默认会对 HTML 进行自动转义,因此较为安全。而后者则没有自动转义功能,如果使用不当,则可能导致 SSTI 或其他代码注入风险

  • 一个go语言的demo
package main

import (
    "os"
    "text/template"
)

func main() {
    // 构建模板
    tmpl, err := template.New("").Parse("你好,{{ . }}")
    if err != nil {
        panic(err)
    }

    err = tmpl.Execute(os.Stdout, "冰镇")
    if err != nil {
        panic(err)
    }
}

>>>
[root@localhost jsonparser-demo]# go run go_ssti_one.go 
你好冰镇

自定义一个靶场练习

package main

import (
    "fmt"
    "net/http"
    "text/template"
)


type User struct {
    Id     int
    Name   string
    Passwd string
}

func sstiHandler(w http.ResponseWriter, r *http.Request) {
    // 解析用户输入
    userInput := r.URL.Query().Get("input")
    user := &User{1, "admin", "laotie233"}

    // 使用text/template不安全地渲染用户输入
    tmpl, err := template.New("ssti").Parse(userInput)
    if err != nil {
        http.Error(w, "Error parsing template", http.StatusInternalServerError)
        return
    }

    // 执行模板并将结果输出
    err = tmpl.Execute(w, user)
    if err != nil {
        http.Error(w, "Error executing template", http.StatusInternalServerError)
        return
    }
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    homeHTML := `
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>SSTI Vulnerable Web App</title>
        </head>
        <body>
            <h1>SSTI Vulnerable Web App</h1>
            <p>Enter a template expression to test SSTI:</p>
            <form action="/ssti" method="get">
                <input type="text" name="input" placeholder="Enter your template" />
                <button type="submit">Submit</button>
            </form>
            <p>Try submitting something to test the vulnerability.</p>
        </body>
        </html>
    `

    fmt.Fprint(w, homeHTML)
}

func main() {
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/ssti", sstiHandler)

    // 启动服务器
    fmt.Println("Server is running on http://xxxxx:8080")
    http.ListenAndServe(":8080", nil)
}
  • 访问主页如下

获取admin账号密码(敏感信息泄露)

payload: {{.passwd}}

payload: {{.}}

XSS

payload: <script>alert('xss')</script>

命令执行&文件读取

  • 利用前提,代码中有调用相关指令点

新增如下演示代码

"exec": func(command string) string {
            // 执行系统命令,模拟RCE漏洞
            out, err := exec.Command("sh", "-c", command).Output()
            if err != nil {
                return err.Error()
            }
            return string(out)
        },

完整代码如下

package main

import (
    "fmt"
    "net/http"
    "os"
    "os/exec"
    "text/template"
)


type User struct {
    Id     int
    Name   string
    Passwd string
}

func sstiHandler(w http.ResponseWriter, r *http.Request) {
    // 解析用户输入
    userInput := r.URL.Query().Get("input")
    user := &User{1, "admin", "laotie233"}

    // 使用text/template不安全地渲染用户输入
    tmpl, err := template.New("ssti").Funcs(template.FuncMap{
        "exec": func(command string) string {
            // 执行系统命令,模拟RCE漏洞
            out, err := exec.Command("sh", "-c", command).Output()
            if err != nil {
                return err.Error()
            }
            return string(out)
        },
        "readFile": func(filePath string) string {
            // 读取文件,模拟文件读取漏洞
            content, err := os.ReadFile(filePath)
            if err != nil {
                return err.Error()
            }
            return string(content)
        },
    }).Parse(userInput)
    if err != nil {
        http.Error(w, "Error parsing template", http.StatusInternalServerError)
        return
    }

    // 执行模板并将结果输出
    err = tmpl.Execute(w, user)
    if err != nil {
        http.Error(w, "Error executing template", http.StatusInternalServerError)
        return
    }
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    homeHTML := `
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>SSTI Vulnerable Web App</title>
        </head>
        <body>
            <h1>SSTI Vulnerable Web App</h1>
            <p>Enter a template expression to test SSTI:</p>
            <form action="/ssti" method="get">
                <input type="text" name="input" placeholder="Enter your template" />
                <button type="submit">Submit</button>
            </form>
            <p>Try submitting something to test the vulnerability.</p>
        </body>
        </html>
    `

    fmt.Fprint(w, homeHTML)
}

func main() {
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/ssti", sstiHandler)

    // 启动服务器
    fmt.Println("Server is running on http://xxxxx:8080")
    http.ListenAndServe(":8080", nil)
}
  • 命令执行
payload: {{ exec "ls" }}

  • 文件读取
payload: {{ readFile "/etc/passwd" }}

写在最后

​ Go SSTI杀伤力整理来说没有jinja2大,不过模板注入作为一种攻击方式对各种语言的模板使用与规则进行研究还是必要的。

参考

https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection

https://www.onsecurity.io/blog/go-ssti-method-research/

0 条评论
某人
表情
可输入 255