Skip to content

Commit

Permalink
Fix issue of listen before chmod on Unix sockets (CVE-2023-45145) (#1…
Browse files Browse the repository at this point in the history
…2671)

Before this commit, Unix socket setup performed chmod(2) on the socket
file after calling listen(2). Depending on what umask is used, this
could leave the file with the wrong permissions for a short period of
time. As a result, another process could exploit this race condition and
establish a connection that would otherwise not be possible.

We now make sure the socket permissions are set up prior to calling
listen(2).

(cherry picked from commit 1119eca)

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
  • Loading branch information
oranagra and yossigo committed Oct 18, 2023
1 parent 3c734b8 commit 03345dd
Showing 1 changed file with 6 additions and 5 deletions.
11 changes: 6 additions & 5 deletions src/anet.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,16 @@ int anetUnixGenericConnect(char *err, const char *path, int flags)
return s;
}

static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog, mode_t perm) {
if (bind(s,sa,len) == -1) {
anetSetError(err, "bind: %s", strerror(errno));
close(s);
return ANET_ERR;
}

if (sa->sa_family == AF_LOCAL && perm)
chmod(((struct sockaddr_un *) sa)->sun_path, perm);

if (listen(s, backlog) == -1) {
anetSetError(err, "listen: %s", strerror(errno));
close(s);
Expand Down Expand Up @@ -467,7 +470,7 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl

if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) s = ANET_ERR;
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog,0) == ANET_ERR) s = ANET_ERR;
goto end;
}
if (p == NULL) {
Expand Down Expand Up @@ -508,10 +511,8 @@ int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
memset(&sa,0,sizeof(sa));
sa.sun_family = AF_LOCAL;
redis_strlcpy(sa.sun_path,path,sizeof(sa.sun_path));
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog,perm) == ANET_ERR)
return ANET_ERR;
if (perm)
chmod(sa.sun_path, perm);
return s;
}

Expand Down

2 comments on commit 03345dd

@PsychoPunkSage
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,
I'm currently developing a microservice application that utilizes multiple instance of Redis images. Specifically, I'm focusing on the area of intrusion attacks. Could you please provide guidance on how I can simulate and recreate such exploits locally?

@sundb
Copy link
Collaborator

@sundb sundb commented on 03345dd Jun 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PsychoPunkSage steps:

  1. use the code before the fix.
  2. change the code to:
    if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
        return ANET_ERR;
    usleep(10 * 1000 * 1000);
    if (perm)
        chmod(sa.sun_path, perm);
  1. enable the config in the redis.conf
unixsocket /run/redis.sock
unixsocketperm 700
  1. run Redis with root
  2. run a redis-cli with non-root user before the end of usleep
redis-cli -s unixsocket /run/redis.sock
  1. if you don't see permission denied, you success.

Please sign in to comment.