Skip to content

A Basic SSTI Expr

v1ll4n edited this page Jun 14, 2023 · 3 revisions
cases = [
    {"boundary": ["", ""], "note": "no-boundary", "note_zh": "无边界", "usedby": []},
    {"boundary": ["{{", "}}"], "note": "basic:{{...}}", "note_zh": "基础模版: {{...}}", "usedby": ["twig", "flask/jinja2", "django"]},
    {"boundary": ["${", "}"], "note": "basic:${...}", "note_zh": "基础模版:${...}", "usedby": ["java", ]},
    {"boundary": ["{", "}"], "note": "basic:{...}", "note_zh": "基础模版:{...}", "usedby": []},
    {"boundary": ["<%=", "%>"], "note": "ruby", "note_zh": "Ruby 模版"},
    {"boundary": ["{php}", "{/php}"], "note": "smarty: {php}...{/php}", "usedby": ["smarty"]},
    {"boundary": ["{php} echo ", "; {/php}"], "note": "smarty: {php}...{/php}", "usedby": ["smarty"]},
    {"boundary": ["$eval('", "')"], "note": "AngularJS: $eval('...')", "usedby": ["Angulary"]},
    {"boundary": ["{%", "%}"], "note": "Tornado: {%...%}", "usedby": ["Tornado", "django"]},
]

yakit.AutoInitYakit()

sstiDesc = `服务器端模板注入(Server-Side Template Injection,简称 SSTI)是一种安全漏洞,它发生在服务器端的模板引擎中。模板引擎通常用于将动态数据嵌入到静态 HTML 页面中,以生成最终的网页。当攻击者能够向模板引擎提供恶意输入并成功执行任意代码时,就发生了服务器端模板注入。

SSTI 的风险因素包括:

任意代码执行:攻击者可能利用模板注入来执行任意代码,从而控制服务器或访问敏感数据。
数据泄露:攻击者可能利用模板注入来访问服务器上的敏感数据,例如数据库中的用户凭据或其他重要信息。
拒绝服务:攻击者可能利用模板注入来导致服务器崩溃,导致服务不可用。`

solution = `为了防止服务器端模板注入,可以采取以下措施:

输入验证:对用户输入进行严格的验证,确保只接受预期的数据类型和格式。可以使用白名单方法,仅允许已知安全的输入。
输出编码:在将用户输入插入模板之前,对其进行适当的编码,以防止恶意代码执行。
最小权限原则:确保服务器端应用程序以最小权限运行,以减少潜在的损害。
使用安全的模板引擎:选择已知具有良好安全记录的模板引擎,并确保使用最新版本。
通过采取这些措施,可以大大降低服务器端模板注入的风险。
`

checkCase = (instance, https, reqBytes) => {
    prefix, suffix = instance.boundary
    yakit.Info("开始测试 SSTI:%v ... %v", prefix, suffix)

    var params = fuzz.HTTPRequest(reqBytes, fuzz.https(true))~.GetCommonParams()
    for param in params {
        checked = 0
        failed = false
        var lastResponse
        var lastPayload

        baseResponse, _ = poc.HTTP(reqBytes, poc.https(https))~

        for count in 6 {
            index = count + 1
            if index - 3 >= checked {
                failed = true
                break
            }
            try {
                exprDetails = fuzz.FuzzCalcExpr()
                var result = exprDetails.result
                var expr = exprDetails.expr

                generateExprCount = 0
                for str.Contains(string(baseResponse), expr) && generateExprCount < 100 {
                    generateExprCount ++
                    exprDetails = fuzz.FuzzCalcExpr()
                    result = exprDetails.result
                    expr = exprDetails.expr
                }

                payload = prefix + expr + suffix
                freq = param.Fuzz(payload)
                // freq.Show()
                rsp, err = freq.ExecFirst()
                if err {
                    yakit.Info("请求失败: %v", err)
                    continue
                }
                if (result in string(rsp.ResponseRaw)) && !str.MatchAnyOfRegexp(string(rsp.ResponseRaw), result + `\d{2}`, `\d{2}` + result) {
                    yakit.Info("SSTI 表达式执行成功:复核次数: " + string(index))
                    checked ++
                    lastResponse = rsp
                    lastPayload = payload
                }
            } catch err {
                die(err)
            }
        }
        if !failed {
            yakit.Info("表达式注入成功检测:参数:%v", str.TrimSpace(param.String()))
            url = lastResponse.Url
            risk.NewRisk(
               lastResponse.Url, 
               risk.titleVerbose(f"SSTI 表达式注入(参数:${param.Name()}): ${url} "),
               risk.title(f"SSTI Expr Injection (Param:${param.Name()}): ${url}"),
               risk.request(lastResponse.RequestRaw),
               risk.response(lastResponse.ResponseRaw),
               risk.payload(lastPayload),
               risk.severity("high"),
               risk.type("SSTI"),
               risk.typeVerbose("模版注入"),
               risk.description(sstiDesc),
               risk.solution(solution),
               risk.details({
                   "reason": "本漏洞的检测方法是通过数值计算来实现的,我们通过输入一个类似 2012-10-02 的表达式,如果它的结果是 2000,那么我们认为 SSTI Expr 成功,这种计算方法非常简单明了",
                   "note": "为了保证计算有效,我们会多次计算这个值确保绝大多数计算都是成功的",
                   "url": url,
               })
            )
        }
    }
}

checkReq = (ishttps, req) => {
    for instance in cases {
        try {
            checkCase(instance, ishttps, req)
        } catch err {
            yakit.Error(err)
        }
    }
}

mirrorNewWebsitePathParams = (isHttps, url, req, rsp, body) => {
    checkReq(ishttps, req)
}

// if YAK_MAIN {
//     testurl = [
//         "https://127.0.0.1:8787/expr/injection?b={%22a%22:%201}",
//         `https://127.0.0.1:8787/expr/injection?a=1`,
//         `https://127.0.0.1:8787/expr/injection?c=abc`,
//     ]
//     for u in testurl {
//         https, bytes, _ = poc.ParseUrlToHTTPRequestRaw("GET", u)
//         checkReq(https, bytes)
//     }
// }
Clone this wiki locally