Skip to content

Instantly share code, notes, and snippets.

@VSharapov
Created June 6, 2025 21:42
Show Gist options
  • Select an option

  • Save VSharapov/e85dabfdc948084fcc2ab198e669d7ed to your computer and use it in GitHub Desktop.

Select an option

Save VSharapov/e85dabfdc948084fcc2ab198e669d7ed to your computer and use it in GitHub Desktop.
If there's no `passwd` or `chpasswd` try this jank
#!/bin/sh
set -eu
# DEBUG=true
DEBUG=false
if $DEBUG; then
trap 'echo "# $BASH_COMMAND";read' DEBUG
fi
PAGER=${PAGER:-more}
for cmd in sed openssl diff $PAGER; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Missing required command: $cmd" >&2
exit 1
fi
done
if [ "$(id -u)" -ne 0 ]; then
echo "You must run this as root." >&2
exit 1
fi
if ! cat /etc/shadow > /dev/null; then
echo "Cannot read /etc/shadow" >&2
exit 1
fi
printf "What user are we messing with? " >&2
read -r target_user
shadow_line=$(grep "^$target_user:" /etc/shadow || true)
if [ -z "$shadow_line" ]; then
echo "User '$target_user' not found in /etc/shadow" >&2
exit 1
fi
IFS='$' read -r _ algo salt current_hash <<EOF
$(echo "$shadow_line" | cut -d: -f2)
EOF
if ! [ -n "$algo" ] && [ -n "$salt" ] && [ -n "$current_hash" ]; then
echo "Failed to extract hash components." >&2
echo "shadow_line: ${shadow_line:-"unset"}"
echo "algo: ${algo:-"unset"}"
echo "salt: ${salt:-"unset"}"
echo "current_hash: ${current_hash:-"unset"}"
exit 1
fi
echo "Sanity check: if you know the current password, enter it now."
echo "Or press Enter to skip:"
read -r current_pw
if [ -n "$current_pw" ]; then
computed_hash=$(openssl passwd -$algo -salt "$salt" "$current_pw")
expected_output="\$$algo\$$salt\$$current_hash"
if [ "$computed_hash" != "$expected_output" ]; then
echo "Password does not match!" >&2
echo "Computed from your input:" >&2
echo "$computed_hash" >&2
echo "What appears in /etc/shadow:" >&2
echo "$expected_output" >&2
echo "The full line in /etc/shadow:" >&2
echo "$shadow_line" >&2
exit 1
else
echo "Password verified."
fi
else
echo "You have FOOLISHLY chosen to skip password verification." >&2
fi
printf "Enter new password: " >&2
read -r new_pw
# openssl will output the dollar signs, algorithm, salt, and hash.
new_full_hash=$(openssl passwd -$algo -salt "$salt" "$new_pw")
# Assigning to a variable seems to turn newlines into spaces, I'm sure it could be fixed, but this works fine.
cat /etc/shadow | sed "s#^$target_user:\$$algo\$$salt\$$current_hash:#$target_user:$new_full_hash:#" > /etc/shadow.new && \
echo "Wrote /etc/shadow.new" >&2
echo "Here is a diff of the changes:" >&2
echo "---" >&2
diff /etc/shadow /etc/shadow.new | $PAGER
echo "---" >&2
printf "Type 'lgtm' to apply changes (a backup will be made): " >&2
read -r confirm
if [ "$confirm" != "lgtm" ]; then
echo "Deleting /etc/shadow.new and aborting." >&2
rm /etc/shadow.new
exit 1
fi
echo "Applying changes..." >&2
cp /etc/shadow /etc/shadow.bak
cat /etc/shadow.new > /etc/shadow
rm /etc/shadow.new
cat <<EOF
!!!!!!!!!!!!!!!!!!!!!!!!!!!
/!\ /etc/shadow updated /!\
!!!!!!!!!!!!!!!!!!!!!!!!!!!
Test login now. If it fails, run:
mv /etc/shadow.bak /etc/shadow
If everything looks good, run:
rm /etc/shadow.bak
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment