forked from p15670423/monkey
Add Azure password stealing to the report.
This commit is contained in:
parent
21abdb5cef
commit
93fee0d2c5
|
@ -126,5 +126,7 @@ class InfoCollector(object):
|
||||||
# we might be losing passwords in case of multiple reset attempts on same username
|
# we might be losing passwords in case of multiple reset attempts on same username
|
||||||
# or in case another collector already filled in a password for this user
|
# or in case another collector already filled in a password for this user
|
||||||
self.info["credentials"][username]['Password'] = password
|
self.info["credentials"][username]['Password'] = password
|
||||||
|
self.info["credentials"][username]['Azure'] = True
|
||||||
|
|
||||||
if len(azure_creds) != 0:
|
if len(azure_creds) != 0:
|
||||||
self.info["Azure"] = True
|
self.info["Azure"] = True
|
||||||
|
|
|
@ -33,6 +33,7 @@ class ReportService:
|
||||||
SAMBACRY = 3
|
SAMBACRY = 3
|
||||||
SHELLSHOCK = 4
|
SHELLSHOCK = 4
|
||||||
CONFICKER = 5
|
CONFICKER = 5
|
||||||
|
AZURE = 6
|
||||||
|
|
||||||
class WARNINGS_DICT(Enum):
|
class WARNINGS_DICT(Enum):
|
||||||
CROSS_SEGMENT = 0
|
CROSS_SEGMENT = 0
|
||||||
|
@ -71,6 +72,19 @@ class ReportService:
|
||||||
}
|
}
|
||||||
for tunnel in mongo.db.monkey.find({'tunnel': {'$exists': True}}, {'tunnel': 1})]
|
for tunnel in mongo.db.monkey.find({'tunnel': {'$exists': True}}, {'tunnel': 1})]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_azure_issues():
|
||||||
|
creds = ReportService.get_azure_creds()
|
||||||
|
machines = set([instance['origin'] for instance in creds])
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'type': 'azure_password',
|
||||||
|
'machine': machine,
|
||||||
|
'users': set([instance['username'] for instance in creds if instance['origin']==machine])
|
||||||
|
}
|
||||||
|
for machine in machines]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_scanned():
|
def get_scanned():
|
||||||
nodes = \
|
nodes = \
|
||||||
|
@ -135,6 +149,26 @@ class ReportService:
|
||||||
)
|
)
|
||||||
return creds
|
return creds
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_azure_creds():
|
||||||
|
"""
|
||||||
|
Recover all credentials marked as being from an Azure machine
|
||||||
|
:return: List of credentials.
|
||||||
|
"""
|
||||||
|
creds = []
|
||||||
|
for telem in mongo.db.telemetry.find(
|
||||||
|
{'telem_type': 'system_info_collection', 'data.Azure': {'$exists': True}},
|
||||||
|
{'data.credentials': 1, 'monkey_guid': 1}
|
||||||
|
):
|
||||||
|
monkey_creds = telem['data']['credentials']
|
||||||
|
if len(monkey_creds) == 0:
|
||||||
|
continue
|
||||||
|
origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname']
|
||||||
|
new_creds = [{'username': user.replace(',', '.'), 'type': 'Clear Password',
|
||||||
|
'origin': origin} for user in monkey_creds if 'Azure' in user]
|
||||||
|
creds.extend(new_creds)
|
||||||
|
return creds
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_general_exploit(exploit):
|
def process_general_exploit(exploit):
|
||||||
ip_addr = exploit['data']['machine']['ip_addr']
|
ip_addr = exploit['data']['machine']['ip_addr']
|
||||||
|
@ -277,7 +311,7 @@ class ReportService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_issues():
|
def get_issues():
|
||||||
issues = ReportService.get_exploits() + ReportService.get_tunnels() + ReportService.get_cross_segment_issues()
|
issues = ReportService.get_exploits() + ReportService.get_tunnels() + ReportService.get_cross_segment_issues() + ReportService.get_azure_issues()
|
||||||
issues_dict = {}
|
issues_dict = {}
|
||||||
for issue in issues:
|
for issue in issues:
|
||||||
machine = issue['machine']
|
machine = issue['machine']
|
||||||
|
@ -337,6 +371,8 @@ class ReportService:
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.SHELLSHOCK.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.SHELLSHOCK.value] = True
|
||||||
elif issue['type'] == 'conficker':
|
elif issue['type'] == 'conficker':
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.CONFICKER.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.CONFICKER.value] = True
|
||||||
|
elif issue['type'] == 'azure_password':
|
||||||
|
issues_byte_array[ReportService.ISSUES_DICT.AZURE.value] = True
|
||||||
elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \
|
elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \
|
||||||
issue['username'] in config_users:
|
issue['username'] in config_users:
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True
|
||||||
|
@ -397,7 +433,8 @@ class ReportService:
|
||||||
{
|
{
|
||||||
'scanned': ReportService.get_scanned(),
|
'scanned': ReportService.get_scanned(),
|
||||||
'exploited': ReportService.get_exploited(),
|
'exploited': ReportService.get_exploited(),
|
||||||
'stolen_creds': ReportService.get_stolen_creds()
|
'stolen_creds': ReportService.get_stolen_creds(),
|
||||||
|
'azure_passwords': ReportService.get_azure_creds(),
|
||||||
},
|
},
|
||||||
'recommendations':
|
'recommendations':
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,7 +21,8 @@ class ReportPageComponent extends AuthComponent {
|
||||||
ELASTIC: 2,
|
ELASTIC: 2,
|
||||||
SAMBACRY: 3,
|
SAMBACRY: 3,
|
||||||
SHELLSHOCK: 4,
|
SHELLSHOCK: 4,
|
||||||
CONFICKER: 5
|
CONFICKER: 5,
|
||||||
|
AZURE: 6
|
||||||
};
|
};
|
||||||
|
|
||||||
Warning =
|
Warning =
|
||||||
|
@ -313,6 +314,11 @@ class ReportPageComponent extends AuthComponent {
|
||||||
{this.state.report.overview.issues[this.Issue.WEAK_PASSWORD] ?
|
{this.state.report.overview.issues[this.Issue.WEAK_PASSWORD] ?
|
||||||
<li>Machines are accessible using passwords supplied by the user during the Monkey’s
|
<li>Machines are accessible using passwords supplied by the user during the Monkey’s
|
||||||
configuration.</li> : null}
|
configuration.</li> : null}
|
||||||
|
{this.state.report.overview.issues[this.Issue.AZURE] ?
|
||||||
|
<li>Machines contained plain text passwords accessible to attackers. For more info see <a
|
||||||
|
href="https://www.guardicore.com/2018/03/recovering-plaintext-passwords-azure/"
|
||||||
|
>Harvesting Azure Passwords</a>.</li> : null}
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
|
@ -587,6 +593,21 @@ class ReportPageComponent extends AuthComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateAzureIssue(issue) {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
Azure VM Access configuration files should be deleted after use to avoid credential leakage.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
VM Access plugin configuration files were left on the machine. Credentials could be stolen from <span
|
||||||
|
className="label label-primary">{issue.machine}</span> for the following users<span
|
||||||
|
className="label label-primary">{issue.users}</span>. Read more about the security issue and remediation <a
|
||||||
|
href="https://www.guardicore.com/2018/03/recovering-plaintext-passwords-azure/"
|
||||||
|
>here</a>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
generateConfickerIssue(issue) {
|
generateConfickerIssue(issue) {
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
|
@ -631,6 +652,8 @@ class ReportPageComponent extends AuthComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
generateIssue = (issue) => {
|
generateIssue = (issue) => {
|
||||||
let data;
|
let data;
|
||||||
switch (issue.type) {
|
switch (issue.type) {
|
||||||
|
@ -670,6 +693,9 @@ class ReportPageComponent extends AuthComponent {
|
||||||
case 'tunnel':
|
case 'tunnel':
|
||||||
data = this.generateTunnelIssue(issue);
|
data = this.generateTunnelIssue(issue);
|
||||||
break;
|
break;
|
||||||
|
case 'azure_password':
|
||||||
|
data = this.generateAzureIssue(issue);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue