Skip to content

Jenkins analysis#226

Merged
aarontp merged 8 commits into
masterfrom
jenkins-analysis
Sep 18, 2018
Merged

Jenkins analysis#226
aarontp merged 8 commits into
masterfrom
jenkins-analysis

Conversation

@berggren

@berggren berggren commented Aug 10, 2018

Copy link
Copy Markdown
Collaborator

Add analysis plugin for Jenkins. The plugin will:

  • Extract Jenkins config files from artifact definition and image_export
  • Extract version and credentials from Jenkins config files
  • Try to find weak passwords by bruteforcing with John the Ripper

Example output:

Jenkins analysis found potential issues.

Jenkins version: 2.121.2
1 weak password(s) found:

  • User "admin2" with password "123456"

@berggren berggren requested a review from aarontp August 29, 2018 14:26
@aarontp

aarontp commented Aug 30, 2018

Copy link
Copy Markdown
Member

Thanks! I didn't get a chance to look today, but I'll review this tomorrow.

@aarontp aarontp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Partial review for now, and I'll try to finish up tomorrow. Feel free to ignore these comments until I finish if that's easier for you.


from __future__ import unicode_literals

from turbinia.evidence import RawDisk

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We probably want the other cloud disk types here as well, right?

Comment thread turbinia/lib/utils.py Outdated
Comment thread turbinia/workers/analysis/jenkins.py Outdated
return credentials

@staticmethod
def analyse_jenkins(version, credentials):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this should be s/analyse/analyze/

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Heh, english! :) fixed.

Comment thread turbinia/lib/utils.py Outdated
Comment thread turbinia/lib/utils.py Outdated
"""Bruteforce password hashes using John the Ripper.

Args:
password_hashes (list): List of password hashes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Tiny nit, but maybe change it like s/hashes./hashes as strings./ just to disambiguate from the many other formats that hashes sometimes come in?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

Comment thread turbinia/lib/utils.py Outdated
return collected_file_paths


def bruteforce_password_hashes(password_hashes, timeout=90):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just curious about the timeout here. That seems pretty quick for most password cracking, but I guess we just want to get the super easily cracked passwords? Given how long all the other disk processing tasks take to run, I would consider raising this number a bit.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, this is taken from the cluedo stuff. It is meant to crack really easy passwords. I'll raise it a bit though. Good catch.


findings = []
credentials_registry = {hash: username for username, hash in credentials}
weak_passwords = bruteforce_password_hashes(credentials_registry.keys())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you add a TODO here to set a timeout method parameter here that we can override with "dynamic task configuration" once it's available? That's part of the "Recipes" design that we talked about.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I just filed #244 that you can reference here as well.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done.

Comment thread turbinia/lib/utils.py
raise RuntimeError('john the ripper failed.')

result = []
pot_file = os.path.expanduser('~/.john/john.pot')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can't set the path to this as a command line flag? It seems really strange to me that this is the best way to retrieve the results. Maybe there's an alternate output format we can use?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I know this is not optimal but it is how John works. The only way to get the result is in the .pot file, and there is no way to control where that file is created. It always end up in ~/.john/ and there is no way to change that, i.e. no reason to add it as a flag.

Comment thread turbinia/lib/utils.py
Comment thread turbinia/lib/utils.py
@berggren

Copy link
Copy Markdown
Collaborator Author

Codefactor don't let me add TODOs.

@aarontp aarontp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LG, Just a couple more comments. Thanks!

Comment thread turbinia/lib/utils.py Outdated
Comment thread turbinia/lib/utils.py

# Add the resulting evidence to the result object.
result.add_evidence(output_evidence, evidence.config)
result.close(self, success=True)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we add a 'status' here with a summary of what was discovered? Maybe we could use the first line of the analysis report (possibly with some small changes)?

str: The version of Jenkins.
"""
version = None
version_re = re.compile('<version>(.*)</version>')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just curious, what is the benefit to compiling this here?

username_match = re.search(username_re, config)

if username_match and password_hash_match:
username = username_match.group(1)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems like as it is this method will only ever return one tuple of user/hash. Is it possible that there are multiple usernames/hashes in this file? If so, can we change this to extract all of them, and if not, should we change the method to just return a tuple?

Comment thread turbinia/lib/utils.py
# Cancel the timer if the process is done before the timer.
if timer.is_alive():
timer.cancel()
except OSError:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If there are potentially multiple users that can be extracted from the config (see my comments on _extract_jenkins_credentials()), then I guess we'd want to check for changes to the .pot file (or something similar) since john might have only cracked a subset of the users.

@aarontp

aarontp commented Sep 18, 2018

Copy link
Copy Markdown
Member

Ping. Can you take a look at the latest comments? I think the only thing I was hoping to address was the status, and I had a question about whether we could see more than one user in these files. Thanks!

@aarontp

aarontp commented Sep 18, 2018

Copy link
Copy Markdown
Member

Actually, since I think you might be busy today, I'll ping you about just merging this, and then I can make a small change to update the status (and we can address the user issue later).

@aarontp aarontp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. A couple small things, but I'll fix those in a second pass.

@aarontp aarontp merged commit 3f02a61 into master Sep 18, 2018
@aarontp aarontp deleted the jenkins-analysis branch September 18, 2018 14:03
Onager pushed a commit to Onager/turbinia that referenced this pull request Sep 21, 2018
* Scaffolding for Jenkins analysis

* Add analysis and tests

* Linter happy

* Allow more input evidence

* Add import of JenkinsAnalysisJob
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants