A shadow-utils BoF whitepaper
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:
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
reference to show a line like:1
/etc/passwd
1
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:
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:
Also an unexpected: 1
useradd: user 'test1' already exists
Now, what the hell. I now check on
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.1
/etc/passwd
Part 3: userdel
I run
on the account, and get back a similar message:1
userdel
1
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
which in practice looks like:1
pw_locate
Which is using:
There is:
In the code, called by passwd_parse, but I don’t see that ever being called by what is making the function fail.
Also:
From
returns 0 on failure, and could be root cause for 1
sgetpwent.c
being returned later.1
NULL
From
returns 0 on failure and could be the root cause for NULL being returned later.1
sgetpwent.c
Conclusion
That’s returning NULL instead of
when it should not, but I don’t see it ever call 1
p->eptr
or otherwise 1
passwd_parse
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 1
return (void *) sgetpwent (line);
beyond constraints set by 1
/etc/passwd
and 1
chsh
themselves for a user.1
chfn
If you enjoy my work, sponsor or hire me! I work hard keeping oxasploits running!
Bitcoin Address:
bc1qq7vvwfe7760s3dm8uq28seck465h3tqp3fjq4l
Thank you so much and happy hacking!