Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Tue, 24 Aug 2021 20:14:02 -0300
From: Jean Diogo <j@....com.br>
To: oss-security@...ts.openwall.com
Subject: Possible memory leak on getspnam / getspnam_r

Hi,

The function getspnam() and it's reentrant sister getspnam_r() do not clean
the content of allocated memory before returning to the user, resulting in
the leak of /etc/shadow content. In some cases this might be an issue.

>From my tests, it doesn't matter whether the user calls getspnam_r (which
buffer is controlled by the user) rather than getspnam, both functions
malloc the buffer on itself (apparently the heap pointer is stored on
<respbuf> in libc by _nss_files_getspnam_r from libnss) and does not zero
it before returning.

I understand that this may be a desired behavior, caching, however there
might be some situations where that's not desired and the user has no
control over this buffer.

Let me put ProFTPd as an example: it's daemon starts as root, when it
receives a connection it forks, opens all the files that it'll need, then
it calls getspnam with the provided FTP user to validate the provided
password. Later on it setreuid(nobody) abandoning root privileges [1]. Here
it doesn't matter whether it calls getspnam or getspnam_r, the malloced
buffer will remain on heap memory (and it's pointer in libc <buffer> and
<respbuf>, I suppose). So now the child process is running on a low
privileged user and has a copy of /etc/shadow on it's heap memory.
The vulnerability CVE-2020-9273 [2] (an use-after-free on heap) allowed me
to get RCE on ProFTPd, in an exploit I created last year. Additionally,
thanks to getspnam caching it is possible to read the root cryptogram (and
other users).

I'm not suggesting that ProFTPd architecture is correct [3]. The problem is
that even if ProFTPd calls getspnam_r it has no mechanism to zero the cache
before forking, since internal pointers are not known to the user (read
developer).

Thus, although caching is mostly required for performance, maybe this
(caching) should happen only on getspnam function but not on getspnam_r.
That's because on getspnam_r the user wants to have control over this
buffering, then the user has a way to clean up it's memory when this
caching is not desired.

Thanks guys, and sorry if I slipped into any concept.
dukpt.

References:
[1] - ProFTPd opens root-writable-only files while running as root. I
understand that the permissions are validated during opening, so read /
write operations to duplicated file descriptors after fork are guaranteed
(thanks to Rick Altherr for pointing me out);
[2] - https://nvd.nist.gov/vuln/detail/CVE-2020-9273 ;
[3] - It is a good programming practice to exec right after fork.

N.B.: Perhaps another option could be creating a function similar to
endspent but that not only close() /etc/shadow but also bzero() internal
allocated memory. Since endspent does not wipe memory it's very likely that
[gs]etpent family also have the same behavior (I didn't test all functions
from the manual, so there might be others in similar situations). Also,
from the tests I did I think getspent and getspnam share the same buffer.

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.