nuclei-templates/.github/scripts/wordpress-plugins-update.py

186 lines
6.3 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
'''
This script reads the URL https://wordpress.org/plugins/browse/popular/ until page 10, extract each plugin name and namespace,
then in http://plugins.svn.wordpress.org/ website, looks for the "Stable tag" inside the readme.txt and extract the last version
number from trunk branch. Finally generates a template and a payload file with last version number to be used during scan that
compares the detect version with the payload version.
The generated template also includes the tags top-100 and top-200 allowing filtering.
e.g.
nuclei -t http/technologies/wordpress/plugins -tags top-100 -u https://www.example.com
'''
__author__ = "ricardomaia"
from time import sleep
from bs4 import BeautifulSoup
import requests
import re
from markdown import markdown
import os
from termcolor import colored, cprint
# Regex to extract the name of th plugin from the URL
regex = r"https://wordpress.org/plugins/(\w.+)/"
ranking = 1
# Top 200 Wordpress Plugins
for page_number in range(1, 11):
html = requests.get(url=f"https://wordpress.org/plugins/browse/popular/page/{page_number}", headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Cache-Control": "max-age=0",
"Pragma": "no-cache",
}).content
# Parse HTML
soup = BeautifulSoup(html, 'html.parser')
results = soup.find(id="main")
articles = results.find_all("article", class_="plugin-card")
# Setting the top tag
top_tag = "top-100,top-200" if page_number <= 5 else "top-200"
# Get each plugin in the page
for article in articles:
full_title = article.find("h3", class_="entry-title").get_text()
regex_remove_quotes = r"[\"`:]"
subst_remove_quotes = "'"
title = re.sub(regex_remove_quotes, subst_remove_quotes, full_title)
link = article.find("a").get("href")
name = re.search(regex, link).group(1)
cprint(f"Title: {title}", "cyan")
cprint(f"Link: {link}", "yellow")
cprint(f"Name: {name} - Ranking: {ranking}", "green")
print(f"Page Number: {page_number}")
print(f"Top Tag: {top_tag}")
print(f"http://plugins.svn.wordpress.org/{name}/trunk/readme.txt")
ranking += 1
sleep(0.2)
# Get the readme.txt file from SVN
readme = requests.get(
url=f"http://plugins.svn.wordpress.org/{name}/trunk/readme.txt",
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Host": "plugins.svn.wordpress.org",
"Pragma": "no-cache",
"Upgrade-Insecure-Requests": "1",
"Referer": "http://plugins.svn.wordpress.org/{name}/trunk/"}).content
# Extract the plugin version
try:
version = re.search(r"(?i)Stable.tag:\s+([\w.]+)",
readme.decode("utf-8")).group(1)
except:
version = "N/A"
# Extract the plugin description
try:
description_markdown = re.search(
r"(?i)==.Description.==\W+\n?(.*)", readme.decode("utf-8")).group(1)
html = markdown(description_markdown)
full_description = BeautifulSoup(html, 'html.parser').get_text()
regex_max_length = r"(\b.{80}\b)"
subst_max_lenght = "\\g<1>\\n "
description = re.sub(
regex_max_length, subst_max_lenght, full_description, 0, re.MULTILINE)
except:
description = "N/A"
print(f"Version: {version}")
print(f"Description: {description}")
# Write the plugin template to file
template = f'''id: wordpress-{name}
info:
name: {title} Detection
author: ricardomaia
severity: info
reference:
- https://wordpress.org/plugins/{name}/
metadata:
plugin_namespace: {name}
wpscan: https://wpscan.com/plugin/{name}
tags: tech,wordpress,wp-plugin,{top_tag}
http:
- method: GET
path:
- "{{{{BaseURL}}}}/wp-content/plugins/{name}/readme.txt"
payloads:
last_version: helpers/wordpress/plugins/{name}.txt
extractors:
- type: regex
part: body
internal: true
name: internal_detected_version
group: 1
regex:
- '(?i)Stable.tag:\s?([\w.]+)'
- type: regex
part: body
name: detected_version
group: 1
regex:
- '(?i)Stable.tag:\s?([\w.]+)'
matchers-condition: or
matchers:
- type: dsl
name: "outdated_version"
dsl:
- compare_versions(internal_detected_version, concat("< ", last_version))
- type: regex
part: body
regex:
- '(?i)Stable.tag:\s?([\w.]+)'
'''
work_dir = os.getcwd()
print(f"Current working directory: {work_dir}")
helper_dir = f"{work_dir}/helpers/wordpress/plugins"
template_dir = f"{work_dir}/http/technologies/wordpress/plugins"
if not os.path.exists(helper_dir):
os.makedirs(helper_dir)
if not os.path.exists(template_dir):
os.makedirs(template_dir)
helper_path = f"helpers/wordpress/plugins/{name}.txt"
version_file = open(helper_path, "w")
version_file.write(version)
version_file.close()
template_path = f"http/technologies/wordpress/plugins/{name}.yaml"
template_file = open(template_path, "w") # Dev environment
template_file.write(template)
template_file.close()
print("--------------------------------------------")
print("\n")