mirror of
https://github.com/assafdori/bypass-mdm.git
synced 2026-04-19 10:34:57 +00:00
feat: v2.0 release
this version should help those who faced issues with the legacy script
This commit is contained in:
401
bypass-mdm-v2.sh
401
bypass-mdm-v2.sh
@@ -9,17 +9,183 @@ PUR='\033[1;35m'
|
|||||||
CYAN='\033[1;36m'
|
CYAN='\033[1;36m'
|
||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
# Function to get the system volume name
|
# Error handling function
|
||||||
get_system_volume() {
|
error_exit() {
|
||||||
system_volume=$(diskutil info / | grep "Device Node" | awk -F': ' '{print $2}' | xargs diskutil info | grep "Volume Name" | awk -F': ' '{print $2}' | tr -d ' ')
|
echo -e "${RED}ERROR: $1${NC}" >&2
|
||||||
echo "$system_volume"
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get the system volume name
|
# Warning function
|
||||||
system_volume=$(get_system_volume)
|
warn() {
|
||||||
|
echo -e "${YEL}WARNING: $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Success function
|
||||||
|
success() {
|
||||||
|
echo -e "${GRN}✓ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Info function
|
||||||
|
info() {
|
||||||
|
echo -e "${BLU}ℹ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validation function for username
|
||||||
|
validate_username() {
|
||||||
|
local username="$1"
|
||||||
|
|
||||||
|
# Check if username is empty
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
echo "Username cannot be empty"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check length (1-31 characters for macOS)
|
||||||
|
if [ ${#username} -gt 31 ]; then
|
||||||
|
echo "Username too long (max 31 characters)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for valid characters (alphanumeric, underscore, hyphen)
|
||||||
|
if ! [[ "$username" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
||||||
|
echo "Username can only contain letters, numbers, underscore, and hyphen"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if starts with letter or underscore
|
||||||
|
if ! [[ "$username" =~ ^[a-zA-Z_] ]]; then
|
||||||
|
echo "Username must start with a letter or underscore"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validation function for password
|
||||||
|
validate_password() {
|
||||||
|
local password="$1"
|
||||||
|
|
||||||
|
# Check if password is empty
|
||||||
|
if [ -z "$password" ]; then
|
||||||
|
echo "Password cannot be empty"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check minimum length (macOS allows any length, but recommend 4+)
|
||||||
|
if [ ${#password} -lt 4 ]; then
|
||||||
|
echo "Password too short (minimum 4 characters recommended)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if user already exists
|
||||||
|
check_user_exists() {
|
||||||
|
local dscl_path="$1"
|
||||||
|
local username="$2"
|
||||||
|
|
||||||
|
if dscl -f "$dscl_path" localhost -read "/Local/Default/Users/$username" 2>/dev/null; then
|
||||||
|
return 0 # User exists
|
||||||
|
else
|
||||||
|
return 1 # User doesn't exist
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find available UID
|
||||||
|
find_available_uid() {
|
||||||
|
local dscl_path="$1"
|
||||||
|
local uid=501
|
||||||
|
|
||||||
|
# Check UIDs from 501-599
|
||||||
|
while [ $uid -lt 600 ]; do
|
||||||
|
if ! dscl -f "$dscl_path" localhost -search /Local/Default/Users UniqueID $uid 2>/dev/null | grep -q "UniqueID"; then
|
||||||
|
echo $uid
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
uid=$((uid + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "501" # Default fallback
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to detect system volumes with multiple fallback strategies
|
||||||
|
detect_volumes() {
|
||||||
|
local system_vol=""
|
||||||
|
local data_vol=""
|
||||||
|
|
||||||
|
info "Detecting system volumes..." >&2
|
||||||
|
|
||||||
|
# Strategy 1: Look for common macOS APFS volume patterns
|
||||||
|
# List all volumes and look for system volume (ends with or contains common names)
|
||||||
|
for vol in /Volumes/*; do
|
||||||
|
if [ -d "$vol" ]; then
|
||||||
|
vol_name=$(basename "$vol")
|
||||||
|
|
||||||
|
# Check if this looks like a system volume (not Data, not recovery)
|
||||||
|
if [[ ! "$vol_name" =~ "Data"$ ]] && [[ ! "$vol_name" =~ "Recovery" ]] && [ -d "$vol/System" ]; then
|
||||||
|
system_vol="$vol_name"
|
||||||
|
info "Found system volume: $system_vol" >&2
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Strategy 2: If no system volume found, try looking for any volume with /System directory
|
||||||
|
if [ -z "$system_vol" ]; then
|
||||||
|
for vol in /Volumes/*; do
|
||||||
|
if [ -d "$vol/System" ]; then
|
||||||
|
system_vol=$(basename "$vol")
|
||||||
|
warn "Using volume with /System directory: $system_vol" >&2
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Strategy 3: Check for Data volume
|
||||||
|
if [ -d "/Volumes/Data" ]; then
|
||||||
|
data_vol="Data"
|
||||||
|
info "Found data volume: $data_vol" >&2
|
||||||
|
elif [ -n "$system_vol" ] && [ -d "/Volumes/$system_vol - Data" ]; then
|
||||||
|
data_vol="$system_vol - Data"
|
||||||
|
info "Found data volume: $data_vol" >&2
|
||||||
|
else
|
||||||
|
# Look for any volume ending with "Data"
|
||||||
|
for vol in /Volumes/*Data; do
|
||||||
|
if [ -d "$vol" ]; then
|
||||||
|
data_vol=$(basename "$vol")
|
||||||
|
warn "Found data volume: $data_vol" >&2
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate findings
|
||||||
|
if [ -z "$system_vol" ]; then
|
||||||
|
error_exit "Could not detect system volume. Please ensure you're running this in Recovery mode with a macOS installation present."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$data_vol" ]; then
|
||||||
|
error_exit "Could not detect data volume. Please ensure you're running this in Recovery mode with a macOS installation present."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$system_vol|$data_vol"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect volumes at startup
|
||||||
|
volume_info=$(detect_volumes)
|
||||||
|
system_volume=$(echo "$volume_info" | cut -d'|' -f1)
|
||||||
|
data_volume=$(echo "$volume_info" | cut -d'|' -f2)
|
||||||
|
|
||||||
# Display header
|
# Display header
|
||||||
echo -e "${CYAN}Bypass MDM By Assaf Dori (assafdori.com)${NC}"
|
echo ""
|
||||||
|
echo -e "${CYAN}╔═══════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${CYAN}║ Bypass MDM By Assaf Dori (assafdori.com) ║${NC}"
|
||||||
|
echo -e "${CYAN}╚═══════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
success "System Volume: $system_volume"
|
||||||
|
success "Data Volume: $data_volume"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Prompt user for choice
|
# Prompt user for choice
|
||||||
@@ -28,57 +194,216 @@ options=("Bypass MDM from Recovery" "Reboot & Exit")
|
|||||||
select opt in "${options[@]}"; do
|
select opt in "${options[@]}"; do
|
||||||
case $opt in
|
case $opt in
|
||||||
"Bypass MDM from Recovery")
|
"Bypass MDM from Recovery")
|
||||||
# Bypass MDM from Recovery
|
echo ""
|
||||||
echo -e "${YEL}Bypass MDM from Recovery"
|
echo -e "${YEL}═══════════════════════════════════════${NC}"
|
||||||
if [ -d "/Volumes/$system_volume - Data" ]; then
|
echo -e "${YEL} Starting MDM Bypass Process${NC}"
|
||||||
diskutil rename "$system_volume - Data" "Data"
|
echo -e "${YEL}═══════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Normalize data volume name if needed
|
||||||
|
if [ "$data_volume" != "Data" ]; then
|
||||||
|
info "Renaming data volume to 'Data' for consistency..."
|
||||||
|
if diskutil rename "$data_volume" "Data" 2>/dev/null; then
|
||||||
|
success "Data volume renamed successfully"
|
||||||
|
data_volume="Data"
|
||||||
|
else
|
||||||
|
warn "Could not rename data volume, continuing with: $data_volume"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Validate critical paths
|
||||||
|
info "Validating system paths..."
|
||||||
|
|
||||||
|
system_path="/Volumes/$system_volume"
|
||||||
|
data_path="/Volumes/$data_volume"
|
||||||
|
|
||||||
|
if [ ! -d "$system_path" ]; then
|
||||||
|
error_exit "System volume path does not exist: $system_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$data_path" ]; then
|
||||||
|
error_exit "Data volume path does not exist: $data_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dscl_path="$data_path/private/var/db/dslocal/nodes/Default"
|
||||||
|
if [ ! -d "$dscl_path" ]; then
|
||||||
|
error_exit "Directory Services path does not exist: $dscl_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "All system paths validated"
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Create Temporary User
|
# Create Temporary User
|
||||||
echo -e "${NC}Create a Temporary User"
|
echo -e "${CYAN}Creating Temporary Admin User${NC}"
|
||||||
|
echo -e "${NC}Press Enter to use defaults (recommended)${NC}"
|
||||||
|
|
||||||
|
# Get and validate real name
|
||||||
read -p "Enter Temporary Fullname (Default is 'Apple'): " realName
|
read -p "Enter Temporary Fullname (Default is 'Apple'): " realName
|
||||||
realName="${realName:=Apple}"
|
realName="${realName:=Apple}"
|
||||||
|
|
||||||
|
# Get and validate username
|
||||||
|
while true; do
|
||||||
read -p "Enter Temporary Username (Default is 'Apple'): " username
|
read -p "Enter Temporary Username (Default is 'Apple'): " username
|
||||||
username="${username:=Apple}"
|
username="${username:=Apple}"
|
||||||
|
|
||||||
|
if validation_msg=$(validate_username "$username"); then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
warn "$validation_msg"
|
||||||
|
echo -e "${YEL}Please try again or press Ctrl+C to exit${NC}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if user already exists
|
||||||
|
if check_user_exists "$dscl_path" "$username"; then
|
||||||
|
warn "User '$username' already exists in the system"
|
||||||
|
read -p "Do you want to use a different username? (y/n): " response
|
||||||
|
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||||
|
while true; do
|
||||||
|
read -p "Enter a different username: " username
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
warn "Username cannot be empty"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if validation_msg=$(validate_username "$username"); then
|
||||||
|
if ! check_user_exists "$dscl_path" "$username"; then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
warn "User '$username' also exists. Try another name."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "$validation_msg"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
warn "Continuing with existing user '$username' (may cause conflicts)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get and validate password
|
||||||
|
while true; do
|
||||||
read -p "Enter Temporary Password (Default is '1234'): " passw
|
read -p "Enter Temporary Password (Default is '1234'): " passw
|
||||||
passw="${passw:=1234}"
|
passw="${passw:=1234}"
|
||||||
|
|
||||||
# Create User
|
if validation_msg=$(validate_password "$passw"); then
|
||||||
dscl_path='/Volumes/Data/private/var/db/dslocal/nodes/Default'
|
break
|
||||||
echo -e "${GREEN}Creating Temporary User"
|
else
|
||||||
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username"
|
warn "$validation_msg"
|
||||||
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" UserShell "/bin/zsh"
|
echo -e "${YEL}Please try again or press Ctrl+C to exit${NC}"
|
||||||
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" RealName "$realName"
|
fi
|
||||||
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" UniqueID "501"
|
done
|
||||||
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" PrimaryGroupID "20"
|
|
||||||
mkdir "/Volumes/Data/Users/$username"
|
echo ""
|
||||||
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" NFSHomeDirectory "/Users/$username"
|
|
||||||
dscl -f "$dscl_path" localhost -passwd "/Local/Default/Users/$username" "$passw"
|
# Find available UID
|
||||||
dscl -f "$dscl_path" localhost -append "/Local/Default/Groups/admin" GroupMembership $username
|
info "Checking for available UID..."
|
||||||
|
available_uid=$(find_available_uid "$dscl_path")
|
||||||
|
if [ $? -eq 0 ] && [ "$available_uid" != "501" ]; then
|
||||||
|
info "UID 501 is in use, using UID $available_uid instead"
|
||||||
|
else
|
||||||
|
available_uid="501"
|
||||||
|
fi
|
||||||
|
success "Using UID: $available_uid"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create User with error handling
|
||||||
|
info "Creating user account: $username"
|
||||||
|
|
||||||
|
if ! dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" 2>/dev/null; then
|
||||||
|
error_exit "Failed to create user account"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" UserShell "/bin/zsh" || warn "Failed to set user shell"
|
||||||
|
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" RealName "$realName" || warn "Failed to set real name"
|
||||||
|
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" UniqueID "$available_uid" || warn "Failed to set UID"
|
||||||
|
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" PrimaryGroupID "20" || warn "Failed to set GID"
|
||||||
|
|
||||||
|
user_home="$data_path/Users/$username"
|
||||||
|
if [ ! -d "$user_home" ]; then
|
||||||
|
if mkdir -p "$user_home" 2>/dev/null; then
|
||||||
|
success "Created user home directory"
|
||||||
|
else
|
||||||
|
error_exit "Failed to create user home directory: $user_home"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "User home directory already exists: $user_home"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dscl -f "$dscl_path" localhost -create "/Local/Default/Users/$username" NFSHomeDirectory "/Users/$username" || warn "Failed to set home directory"
|
||||||
|
|
||||||
|
if ! dscl -f "$dscl_path" localhost -passwd "/Local/Default/Users/$username" "$passw" 2>/dev/null; then
|
||||||
|
error_exit "Failed to set user password"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! dscl -f "$dscl_path" localhost -append "/Local/Default/Groups/admin" GroupMembership "$username" 2>/dev/null; then
|
||||||
|
error_exit "Failed to add user to admin group"
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "User account created successfully"
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Block MDM domains
|
# Block MDM domains
|
||||||
echo "0.0.0.0 deviceenrollment.apple.com" >>/Volumes/"$system_volume"/etc/hosts
|
info "Blocking MDM enrollment domains..."
|
||||||
echo "0.0.0.0 mdmenrollment.apple.com" >>/Volumes/"$system_volume"/etc/hosts
|
|
||||||
echo "0.0.0.0 iprofiles.apple.com" >>/Volumes/"$system_volume"/etc/hosts
|
hosts_file="$system_path/etc/hosts"
|
||||||
echo -e "${GRN}Successfully blocked MDM & Profile Domains"
|
if [ ! -f "$hosts_file" ]; then
|
||||||
|
warn "Hosts file does not exist, creating it"
|
||||||
|
touch "$hosts_file" || error_exit "Failed to create hosts file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if entries already exist to avoid duplicates
|
||||||
|
grep -q "deviceenrollment.apple.com" "$hosts_file" 2>/dev/null || echo "0.0.0.0 deviceenrollment.apple.com" >>"$hosts_file"
|
||||||
|
grep -q "mdmenrollment.apple.com" "$hosts_file" 2>/dev/null || echo "0.0.0.0 mdmenrollment.apple.com" >>"$hosts_file"
|
||||||
|
grep -q "iprofiles.apple.com" "$hosts_file" 2>/dev/null || echo "0.0.0.0 iprofiles.apple.com" >>"$hosts_file"
|
||||||
|
|
||||||
|
success "MDM domains blocked in hosts file"
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Remove configuration profiles
|
# Remove configuration profiles
|
||||||
touch /Volumes/Data/private/var/db/.AppleSetupDone
|
info "Configuring MDM bypass settings..."
|
||||||
rm -rf /Volumes/"$system_volume"/var/db/ConfigurationProfiles/Settings/.cloudConfigHasActivationRecord
|
|
||||||
rm -rf /Volumes/"$system_volume"/var/db/ConfigurationProfiles/Settings/.cloudConfigRecordFound
|
|
||||||
touch /Volumes/"$system_volume"/var/db/ConfigurationProfiles/Settings/.cloudConfigProfileInstalled
|
|
||||||
touch /Volumes/"$system_volume"/var/db/ConfigurationProfiles/Settings/.cloudConfigRecordNotFound
|
|
||||||
|
|
||||||
echo -e "${GRN}MDM enrollment has been bypassed!${NC}"
|
config_path="$system_path/var/db/ConfigurationProfiles/Settings"
|
||||||
echo -e "${NC}Exit terminal and reboot your Mac.${NC}"
|
|
||||||
|
# Create config directory if it doesn't exist
|
||||||
|
if [ ! -d "$config_path" ]; then
|
||||||
|
if mkdir -p "$config_path" 2>/dev/null; then
|
||||||
|
success "Created configuration directory"
|
||||||
|
else
|
||||||
|
warn "Could not create configuration directory"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Mark setup as done
|
||||||
|
touch "$data_path/private/var/db/.AppleSetupDone" 2>/dev/null && success "Marked setup as complete" || warn "Could not mark setup as complete"
|
||||||
|
|
||||||
|
# Remove activation records
|
||||||
|
rm -rf "$config_path/.cloudConfigHasActivationRecord" 2>/dev/null && success "Removed activation record" || info "No activation record to remove"
|
||||||
|
rm -rf "$config_path/.cloudConfigRecordFound" 2>/dev/null && success "Removed cloud config record" || info "No cloud config record to remove"
|
||||||
|
|
||||||
|
# Create bypass markers
|
||||||
|
touch "$config_path/.cloudConfigProfileInstalled" 2>/dev/null && success "Created profile installed marker" || warn "Could not create profile marker"
|
||||||
|
touch "$config_path/.cloudConfigRecordNotFound" 2>/dev/null && success "Created record not found marker" || warn "Could not create not found marker"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GRN}╔═══════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${GRN}║ MDM Bypass Completed Successfully! ║${NC}"
|
||||||
|
echo -e "${GRN}╚═══════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Next steps:${NC}"
|
||||||
|
echo -e " 1. Close this terminal window"
|
||||||
|
echo -e " 2. Reboot your Mac"
|
||||||
|
echo -e " 3. Login with username: ${YEL}$username${NC} and password: ${YEL}$passw${NC}"
|
||||||
|
echo ""
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
"Reboot & Exit")
|
"Reboot & Exit")
|
||||||
# Reboot & Exit
|
echo ""
|
||||||
echo "Rebooting..."
|
info "Rebooting system..."
|
||||||
reboot
|
reboot
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
*) echo "Invalid option $REPLY" ;;
|
*)
|
||||||
|
echo -e "${RED}Invalid option $REPLY${NC}"
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|||||||
Reference in New Issue
Block a user