The vulnerability lies in the __init__ method of the RunningCommand class in sh.py. This method is responsible for setting up and executing a new process. When a user specifies the _uid special keyword argument to run a command as a different user, sh attempts to drop privileges. However, the implementation of this privilege drop was flawed in versions prior to 2.2.4.
An analysis of the commits between versions 2.2.2 and 2.2.4 reveals the evolution of the vulnerability and its fix.
-
Prior to version 2.2.3: The code was missing a call to os.initgroups(). This meant that when dropping privileges, the child process would inherit the supplementary groups of the privileged parent process, which is a security risk.
-
Version 2.2.3 (commit 722d90bf02ed10d4107fdda355ddd758807c114a): A fix was introduced to address the supplementary groups issue by adding os.initgroups(target_name, target_gid). However, this commit also removed the existing os.setgid(target_gid) call. This created a regression where the primary group ID of the process was not being correctly set.
-
Version 2.2.4 (commit 3d855daba91f87a089b490c0d1cf1df3faace2f1): The regression was fixed by re-introducing the os.setgid(target_gid) call after os.initgroups(). This ensures that both the supplementary groups and the primary group ID are correctly set for the new user, thus completing the privilege drop correctly.
Therefore, the RunningCommand.__init__ function is the locus of the vulnerability. Any execution of sh with the _uid parameter on a vulnerable version would pass through this vulnerable function.