avatar
oxasploits
one zero day at a time
  • HOME
  • SERVICES
  • SPONSORS
  • CATEGORIES
  • PROJECTS
  • EXPLOITS
  • WORDLISTS
  • UPTIME
  • GITHUB
  • PRIVACY
  • ABOUT
  • PREVIOUS ENEAVORS
  • ARCHIVES
Home A shadow-utils BoF whitepaper
Post
Large Logo

A shadow-utils BoF whitepaper

By Marshall Whittaker
Posted Sep 14, 2021 Updated Sep 17, 2022 4 min read
shadow chfn chsh userdel passwd buffer-overflow exploit 0day
If you enjoy my work, sponsor or hire me! I work hard keeping oxasploits running!
Bitcoin Address:
bc1qclqhff9dlvmmuqgu4907gh6gxy8wy8yqk596yp

Thank you so much and happy hacking!

Background

A while back an old friend had asked me if I had a chfn bug. I could see why he wanted one, I mean, a suid 0 binary on every system? Wow yeah, but sadly no, at the time I did not have one. A couple of months went by, and later I was playing around with chsh (a related binary, from the same package, that changes the user’s default shell).

Part 1: chsh

I had about given up, as I wasn’t using a fuzzer and was trying it all by hand; as a last-ditch effort I decided to try adding a user test1 and doing:

sudo chsh -s $(perl -e 'print "A"x2048;') test1

I had about given up, as I wasn’t using a fuzzer and was trying it all by hand; as a last-ditch effort I decided to try adding a user test1 and doing:

which edit’s it’s /etc/passwd reference to show a line like:

test1:x:1001:1001::/home/test1:AAAAAAAA

Ok. It had more A’s than that. A lot more. Well damn, that didn’t work, it just gracefully warned me that the file doesn’t even exist… Let’s try something else. That night I gave up.

Part 2: chfn

About a day later I moved on, with the same virtual machine, and decided to play with a different binary, from the same suite of utilities, also setuid root, chsh. First I wondered if it even would allow me to use non-numeric characters in a phone number field, specified by -h, so, like a lazy ass, I used sudo from my user, thus:

sudo chfn -h 000000000 test1

Would do. I got the response: chfn: user ‘test1’ does not exist in /etc/passwd …Wait what? I must have removed the user the other day and forgot about it, I thought. So I ran:

useradd test1

Also an unexpected: useradd: user 'test1' already exists

Now, what the hell. I now check on /etc/passwd to figure out what the deal is, and to my surprise, test1 does still exist after all, and still with that extremely long shell field from the other day! Wow, that’s strange, it’s bugging out with a buffer overflow without crashing. So I thought, I better try this again from the start since I hadn’t put it all together yet.

Part 3: userdel

I run userdel on the account, and get back a similar message:

userdel: user 'test1' does not exist

I run userdel on the account, and get back a similar message:

userdel: user ‘test1’ does not exist

I think here was when I realized it was all connected because forgive me, I can be a bit dense at times. After looking the utility up on Github, I realize they are indeed all from the same package…

Killing 3 birds with one stone

…and all call to the same function pw_locate which in practice looks like:

   pw = pw_locate
 (user);
	if (NULL == pw
) {
		fprintf (stderr,
		         _("%s: user '%s' does not exist in %s\n"),
		         Prog, user, pw_dbname ());
		fail_exit (E_NOPERM);
	}

Which is using:

/*@observer@*/ /*@null@*/const void *commonio_locate (
struct commonio_db *db, const char *name)
{
	struct commonio_entry *p;

	if (!db->
isopen) {
		errno = EINVAL;
		return NULL;
	}
	p = find_entry_by_name (db, name);
	if (NULL == p
) {
		errno = ENOENT;
		return NULL;
	}
	db->cursor = p
;
	return p->eptr;

}

There is:

return (void *) sgetpwent (line);

In the code, called by passwd_parse, but I don’t see that ever being called by what is making the function fail.

Also:

static char
 pwdbuf[1024];

From sgetpwent.c returns 0 on failure, and could be root cause for NULL being returned later.

From sgetpwent.c returns 0 on failure and could be the root cause for NULL being returned later.

Conclusion

That’s returning NULL instead of p->eptr when it should not, but I don’t see it ever call passwd_parse or otherwise return (void *) sgetpwent (line); so the error still exists with too long of a string. It may be that returning 0 will always tell it to return NULL when it should not, and this could be the reason a classical buffer overflow with a segmentation fault is prevented. The other major caveat to this vulnerability, and why I haven’t released a PoC for it, is that to trigger it, you need to already be root, to be able to edit the /etc/passwd beyond constraints set by chsh and chfn themselves for a user.

bugs
This post is licensed under CC BY 4.0 by the author.
Share
Recently Updated
  • I Hacked a Bank and Got Arrested in 2012
  • Bluetooth HCI HID Controller abuse RCE exploit
  • Enumerating SUID files targeted for priv esc
  • Writing the shortest valid C quine
  • Advanced Fuzzing Techniques in ansvif
Trending Tags
exploit vulnerabilities PoC 0day code-injection config perl RCE walkthrough bitcoin


  

Further Reading

Jun 5, 2021

The time I enumerated every GitHub admin

Finding the design flaw While playing with the GitHub API querying different things, I had a light bulb go off. If you can query any GitHub user via API, and see their administrator access level, ...

Dec 8, 2021

Simple x86_64 buffer overflow in gdb

Background We will be debugging a C buffer overflow in gdb to attain higher privileges. The basic idea behind a C buffer overflow is pretty simple. You have a buffer, a chunk of memory reserved...

Feb 8, 2022

CVE-2019-15947 Bitcoin Core crash dumps contain wallets

What is stored in crash dumps? The basic idea behind a crash dump is on abnormal program failure (a fault, or kill signal) the operating system will sometimes (depending on settings) dump core o...

Networks with OpenVPN and Suricata

Bash wildcard expansion globbing abuse

© 2023 Marshall Whittaker. Some rights reserved.

| Home | Services | About | Wordlists | GitHub | Projects |
| Exploits | Services | Privacy| Endeavors | Status |
| Franklin | SPaste |