Speaking of this topic, we have to introduce two things first:
**What is suid escalation **
Why can nmap use suid to escalate rights
**Generally speaking, Linux runs a program using the permissions of the user currently running the program, which is of course reasonable. But there are some special programs, such as our commonly used ping command. **
**0 x00 suid escalation **
Ping needs to send ICMP packets, and this operation needs to send Raw Socket. Before the introduction of CAPABILITIES in Linux 2.2, the use of Raw Socket requires root privileges (of course it does not mean that the introduction of CAPABILITIES does not require permissions, but can be solved by other methods, which will be discussed later), so if you ls in some old systems -al $(which ping), you can find that its permission is -rwsr-xr-x, which has an s bit, this is:
suid:root@linux:~# ls -al /bin/ping-rwsr-xr-x 1 root root 44168 May 72014/bin/ping
The full name of suid is Set owner User ID up on execution. This is an attribute of Linux to executable files. In the above cases, the reason why ordinary users can also use the ping command is that we have set suid permissions for the executable file of ping.
When the program with the s bit is running, its Effective UID will be set as the owner of the program. For example, the owner of the program /bin/ping
is 0 (root), and it sets the s bit, so when ordinary users run ping, their Effective UID is 0, which is equivalent to having root privileges.
Here introduces a new concept Effective UID. Linux processes have three UIDs when they are running:
Under normal circumstances, Effective UID and Real UID are equal, so ordinary users cannot write /etc/passwd
which can only be written with UID=0; when a program with suid is started, Effective UID is equal to the owner of the binary file. At that time, Real UID may not be equal to Effective UID.
Some students said that as long as a certain program has suid permission, it can raise its rights. This statement is actually inaccurate. Only if the owner of this program is No. 0 or other super user, and has suid permission, can the privilege be raised.
Students who use nmap know that if you want to scan UDP or TCP SYN, you need root permissions:
$ nmap -sU targetYou requested a scan type which requires root privileges.QUITTING!$ nmap -sS 127.0.0.1You requested a scan type which requires root privileges.QUITTING!
The reason is that these operations will use Raw Socket.
Sometimes you have to use sudo to execute nmap, but sudo requires a tty when the script calls nmap, and you may also need to enter a password. This restriction will cause unnecessary trouble in many cases.
Therefore, some administrators will add suid permissions to nmap so that ordinary users can run nmap at will.
Of course, nmap with the added s bit is insecure, and we can use nmap to increase rights. Before nmap 5.20, there was an interactive interactive mode, we can use this mode to increase the privilege:
Zero safety
After nmap 5.20, you can execute commands by loading a custom script:
One supplement, --interactive should be an option provided by the older version of nmap. This option is not available on recent nmap, but you can write an nse script with the content
os.execute('/bin/sh')
, Thennmap --script=shell.nse
to escalate rights
It is indeed a very timely addition, because most of the nmap now has no interactive interactive mode.
But after testing, we found that the shell started by this method still seems to belong to the current user, and there is no privilege escalation we imagined.
I have used the interactive mode to escalate the privileges successfully, but because the nmap version is too old and does not have script support, it is impossible to test the script's privilege escalation method; similarly, the new nmap supports script but does not have an interactive mode and cannot be compared directly. I We can only guess the reason for the failure to promote the right:
There are too many variables in these conjectures, so I need to control it. First I read the source code of the old version of nmap, and found that in fact, !sh
executes a very simple system('sh')
, and there is no need to discard the effective UID permission operation:
} elseif(*myargv[0]=='!'){ cptr =strchr(command,'!');system(cptr +1);}
Then we abstract this process into such a C program suid.c
:
int main(int argc, char* argv[]){returnsystem(argv[1]);}
Compile and give it suid permission:
root@linux:/tmp# gcc suid.c -o suidroot@linux:/tmp# chmod +s suid
Then I tried to run ./suid id
as user www-data in different systems:
Linux release | output final version |
---|---|
Ubuntu 14.04 | uid=33(www-data) gid=33(www-data) euid=0(root) egid=0(root) groups=0(root),33(www-data) |
Ubuntu 16.04 | uid=33(www-data) gid=33(www-data) groups=33(www-data) |
Ubuntu 18.04 | uid=33(www-data) gid=33(www-data) groups=33(www-data) |
CentOS 6 | uid=33(www-data) gid=33(www-data) groups=33(www-data) |
CentOS 8 | uid=33(www-data) gid=33(www-data) groups=33(www-data) |
Debian 6 | uid=33(www-data) gid=33(www-data) euid=0(root) egid=0(root) groups=0(root),33(www-data) |
Debian 8 | uid=33(www-data) gid=33(www-data) euid=0(root) egid=0(root) groups=0(root),33(www-data) |
Kali 2019 | uid=33(www-data) gid=33(www-data) groups=33(www-data) |
It can be seen that some systems have root authority, and some systems still have original user authority. Then the above reasons for the failure of nmap to promote rights can be ruled out.
Similarly, CentOS 6 and Debian 6 are both older releases, but the performance of CentOS 6 is similar to that of the new version of Ubuntu. After inquiries and documents on the Internet, I got such a description in bash:
If the shell is started with the effective user (group) id not equal to the real user (group) id, and the -p option is not supplied, no startup files are read, shell functions are not inherited from the environment, the SHELLOPTS, BASHOPTS, CDPATH, and GLOBIGNORE variables, if they appear in the environment, are ignored, and the effective user id is set to the real user id. If the -p option is supplied at invocation, the startup behavior is the same, but the effective user id is not reset.
If the Effective UID is different from the Real UID when bash is started, and the -p parameter is not used, bash will restore the Effective UID to Real UID.
We know that the Linux system()
function is actually executed by /bin/sh -c
, while the CentOS /bin/sh
points to /bin/bash
:
[ root@localhost tmp]# ls -al /bin/shlrwxrwxrwx.1 root root 4 Apr 102017/bin/sh -> bash
This explains why the result obtained by executing the id of the suid program in CentOS is still www-data. Suppose we modify sh to dash at this time and see what the result is:
[ root@localhost tmp]# su -s /bin/bash nobodybash-4.1$ ls -al /bin/shlrwxrwxrwx.1 root root 9 Feb 1900:21/bin/sh ->/bin/dashbash-4.1$ ./suid iduid=99(nobody) gid=99(nobody) euid=0(root) egid=0(root) groups=0(root),99(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
dash does not limit the Effective UID. Here you can see that you have successfully obtained root permissions.
However, let's take a look at Ubuntu 16.04, where /bin/sh also points to dash:
$ ls -al /bin/shlrwxrwxrwx 1 root root 49 182016/bin/sh -> dash$ ls -al /bin/dash-rwxr-xr-x 1 root root 1540722July 172016/bin/dash
Why is there still a situation where rights cannot be elevated?
At this time we need to understand another knowledge. Generally speaking, distributions like Ubuntu will modify some programs. For example, when we check the PHP version, we often see this banner: PHP 7.0.33-0ubuntu0.16.04.11
, in the official Some version numbers of Ubuntu will be brought after the version number of. This is because the Ubuntu distribution adds some of its own code when packaging these software.
Then we can take a look at the dash directory in the Ubuntu 16.04 source:
Download the dash_0.5.8.orig.tar.gz
and dash_0.5.8-2.1ubuntu2.diff.gz
and decompress them respectively. We can see the original code of dash 0.5.8 and the patch made by Ubuntu. .
After we patch the original code, we will find an extra setprivileged
function:
voidsetprivileged(int on){static int is_privileged =1;if(is_privileged == on)return;
is_privileged = on;/* * To limit bogus system(3) or popen(3) calls in setuid binaries, require * -p flag to work in this situation. */if(!on &&(uid !=geteuid()|| gid !=getegid())){setuid(uid);setgid(gid);/* PS1 might need to be changed accordingly. */choose_ps1();}}
The value of on
depends on whether the user passes the -p
parameter, and uid and gid are the Real UID (GID) of the current process. It can be seen that when on is false and Real UID is not equal to Effective UID, the UID of the process is reset here:
setuid(uid)
The setuid function is used to set the Effective UID of the current process. If the current process has root privileges or has the CAP_SETUID
privilege, the Real UID and Saved UID will be set together.
Therefore, it can be seen that the Ubuntu release officially modified dash: **When dash runs with suid permission and the -p
option is not specified, the suid permission will be discarded and the current user permission will be restored. **
In this way, dash has the same performance as bash in suid, which explains why after Ubuntu 16.04, we cannot directly use SUID+system()
to raise privileges.
Similarly, if you download the latest dash of Debian 10, you can also see similar codes. So, why did the major distributions add this limit to sh?
We can understand it as a kind of containment by Linux for escalation of suid rights. Because in general, many command injection vulnerabilities occur in the system()
and popen()
functions, and these functions depend on the system's /bin/sh. Compared with CentOS, the sh in Ubuntu and Debian has always been dash, and has been affected by the suid privilege escalation vulnerability.
Once there is a command injection vulnerability in a program with suid or the function of executing commands, there is a risk of local privilege escalation. If this restriction is added to sh, the hidden danger of privilege escalation can be greatly curbed.
So, if we just want to leave a shell with suid as a backdoor, what should we do?
Modify the previous suid.c
as follows:
int main(int argc, char* argv[]){setuid(0);system(argv[1]);}
Compile and execute, we can find that the uid output by the id command is 0:
The reason is that we have also modified the Real UID of the current process to 0, the Real UID and Effective UID are equal, and will not be demoted after entering dash.
Another way, we can add the -p option to dash or bash, so that it does not lower the authority of the shell. But it should be noted here that we can no longer use the system function, because the internal execution of system()
is /bin/sh -c
, we can only control the parameter value of -c, and cannot add the -p option to sh .
Here we can use execl
or other exec series functions:
int main(int argc, char* argv[]){returnexecl("/bin/sh","sh","-p","-c", argv[1],(char *)0);}
The output at this time is similar to Ubuntu 14..The result in 04, because I added sh-p parameter:
Back to our original question: So how can we escalate the privileges of nmap with suid privileges in Ubuntu 18.04 or similar systems?
Because the lua language is used in nmap script, and there seems to be no way to directly start the process in the lua library, it all depends on the system shell, so we may not be able to directly raise the privilege by executing the shell. But because nmap is already rooted at this time, we can add a new super user by modifying /etc/passwd
:
local file = io.open("/etc/passwd","a")file:write("root2::0:0::/root:/bin/bash\n")file:close()
Successful escalation:
Of course, we need to thank the developers of Linux kernel and Ubuntu and Debian distributions. They are also slowly helping us to continuously improve the security and stability of the system, but similar to powerful software like nmap, we can’t expect to be secure. By Default, so we must learn some more interesting knowledge.
After Linux 2.2, the concept of capabilities has been added, which can be understood as the separation of horizontal permissions. In the past, if a certain function of a certain program required privileges, we could only use root to execute or add SUID permissions to it. Once this happens, we are equivalent to granting all the privileges of this program, which does not meet the requirements of minimum permissions Yes; after the introduction of capabilities, root's permissions are divided into many sub-permissions, which avoids the problem of abuse of privileges. We can see the description of these privileges in capabilities(7)-Linux manual page.
Programs like ping and nmap actually only need network-related privileges. So, if you look at the capabilities of the ping command under Kali, you will see a cap_net_raw
:
$ ls -al /bin/ping-rwxr-xr-x 1 root root 73496 Oct 522:34/bin/ping$ getcap /bin/ping/bin/ping = cap_net_raw+ep
This is why Kali's ping command does not need to set setuid permissions, but it can still be run as a normal user.
Similarly, we can also add similar capabilities to nmap:
sudo setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip /usr/bin/nmapnmap --privileged -sS 192.168.1.1
When using TCP SYN scanning again, there will be no permission errors: