
\a to generate unpleasant "beeps" from system unit speakers. This was especially inconvenient if you wanted to generate more complex sound sequences like 8-bit music. Therefore, Jonathan Nightingale wrote the program beep . It was a short and very simple program that allowed fine-tuning the sound from the speaker.beep to work, the user had to either be the superuser or own the current tty. That is, beep will always work for a root user or for any local user, but will not work for a non-root remote user. At the same time, any terminal (for example, xterm) connected to the X-server is considered “remote”, and therefore beep will not work.SUID bit. This is a special bit, if you set it for a binary, the file is executed with the owner's rights (in this case, root), and not the ordinary user (yours).poweroff to work, root privileges are needed (only a root user can shut down the computer), but for a personal computer this would be too much. Imagine that you are a sysadmin, and all users in the company ask you to turn off their computers. On the other hand, if one attacker can turn off a server with a large number of users, this is a serious security breach.SUID are potential gaps. Take the same bash, a free root shell. Therefore, such programs are very carefully analyzed by the community.beep , consisting of only 375 lines of code, scanned by a bunch of people, can be set up safely, despite the SUID , right?beep , it lies here: https://github.com/johnath/beep/blob/master/beep.c .play_beep() . int main(int argc, char **argv) { /* ... */ signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); parse_command_line(argc, argv, parms); while(parms) { beep_parms_t *next = parms->next; if(parms->stdin_beep) { /* ... */ } else { play_beep(*parms); } /* Junk each parms struct after playing it */ free(parms); parms = next; } if(console_device) free(console_device); return EXIT_SUCCESS; } play_beep() opens the target device, searches for its types and calls do_beep() for each repeat. void play_beep(beep_parms_t parms) { /* ... */ /* try to snag the console */ if(console_device) console_fd = open(console_device, O_WRONLY); else if((console_fd = open("/dev/tty0", O_WRONLY)) == -1) console_fd = open("/dev/vc/0", O_WRONLY); if(console_fd == -1) { /* ... */ } if (ioctl(console_fd, EVIOCGSND(0)) != -1) console_type = BEEP_TYPE_EVDEV; else console_type = BEEP_TYPE_CONSOLE; /* Beep */ for (i = 0; i < parms.reps; i++) { /* start beep */ do_beep(parms.freq); usleep(1000*parms.length); /* wait... */ do_beep(0); /* stop beep */ if(parms.end_delay || (i+1 < parms.reps)) usleep(1000*parms.delay); /* wait... */ } /* repeat. */ close(console_fd); } do_beep() simply calls the desired function to generate a signal depending on the target device: void do_beep(int freq) { int period = (freq != 0 ? (int)(CLOCK_TICK_RATE/freq) : freq); if(console_type == BEEP_TYPE_CONSOLE) { if(ioctl(console_fd, KIOCSOUND, period) < 0) { putchar('\a'); perror("ioctl"); } } else { /* BEEP_TYPE_EVDEV */ struct input_event e; e.type = EV_SND; e.code = SND_TONE; e.value = freq; if(write(console_fd, &e, sizeof(struct input_event)) < 0) { putchar('\a'); /* See above */ perror("write"); } } } char * ), and if it worked, it interrupts the sound by calling do_beep(0) . /* If we get interrupted, it would be nice to not leave the speaker beeping in perpetuity. */ void handle_signal(int signum) { if(console_device) free(console_device); switch(signum) { case SIGINT: case SIGTERM: if(console_fd >= 0) { /* Kill the sound, quit gracefully */ do_beep(0); close(console_fd); exit(signum); } else { /* Just quit gracefully */ exit(signum); } } } SIGINT and SIGTERM sent at the same time, it is likely to call free() twice. But I do not see any other useful applications besides the crash of the program, since after that the console_device will not be used anywhere.write() function in do_beep() looks appropriate. It would be great to use it to write to an intermediate file!console_type , which should be BEEP_TYPE_EVDEV .console_type is set in play_beep() depending on the ioctl() return value. That is, ioctl() must allow BEEP_TYPE_EVDEV .ioctl() lie. If the file does not belong to the device, ioctl() fails, device_type will not be BEEP_TYPE_EVDEV , and do_beep() will not call write() (instead, it uses ioctl() , which, to my knowledge, is safe in this context).do_beep() . If at this point in console_fd and console_type we have the correct values, then we will be able to write to the target file.play_beep() ? Here is the code: void play_beep(beep_parms_t parms) { /* ... */ /* try to snag the console */ if(console_device) console_fd = open(console_device, O_WRONLY); else if((console_fd = open("/dev/tty0", O_WRONLY)) == -1) console_fd = open("/dev/vc/0", O_WRONLY); if(console_fd == -1) { /* ... */ } if (ioctl(console_fd, EVIOCGSND(0)) != -1) console_type = BEEP_TYPE_EVDEV; else console_type = BEEP_TYPE_CONSOLE; /* Beep */ for (i = 0; i < parms.reps; i++) { /* start beep */ do_beep(parms.freq); usleep(1000*parms.length); /* wait... */ do_beep(0); /* stop beep */ if(parms.end_delay || (i+1 < parms.reps)) usleep(1000*parms.delay); /* wait... */ } /* repeat. */ close(console_fd); } beep . If the previous call is successful, console_fd and console_type will still have their old values.console_fd has a new value, and console_type still has the old value.console_device ). But you can make a symlink, first leading to the correct device, and then to the target file. struct input_event e; e.type = EV_SND; e.code = SND_TONE; e.value = freq; if(write(console_fd, &e, sizeof(struct input_event)) < 0) { putchar('\a'); /* See above */ perror("write"); } struct input_event defined in linux/input.h : struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; struct timeval { __kernel_time_t tv_sec; /* seconds */ __kernel_suseconds_t tv_usec; /* microseconds */ }; // On my system, sizeof(struct timeval) is 16. time element is not assigned to the beep source code, and this is the first element of the structure, so its value will be the first bytes of the target file after the attack.-l parameter will be stored there, and after that - \0 . The value is integer, which gives us 4 bytes./*/x . In a shell script, this will execute the program (pre-made) /tmp/x ./etc/profile or /etc/bash/bashrc , then we will achieve complete success with any logged in user./dev/input/event0 , starts beep , waits a bit, reassigns the link, waits again, and then generates a signal. $ echo 'echo PWND $(whoami)' > /tmp/x $ ./exploit.py /etc/bash/bashrc # Or any shell script Backup made at '/etc/bash/bashrc.bak' Done! $ su PWND root Source: https://habr.com/ru/post/354130/
All Articles