Sudo bug with formatting string

Думал что багов с printf, как и gets больше никогда ни в чём важном не увижу. Но как оказалось нет. Сегодня ещё утром прочитал новость на опеннете, что в sudo нашли баг с форматированием строки. Казалось бы это уже такая избитая ошибка, что он ней и в википедии прочитать можно, и ещё куча статей. Сразу как прочёл, полез к ним в mercurial искать исправление. Приведу пример как было и как стало.

Вот багнутая функция:

1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
void
sudo_debug(int level, const char *fmt, ...)
{
    va_list ap;
    char *fmt2;
 
    if (level > debug_level)
	return;
 
    /* Backet fmt with program name and a newline to make it a single write */
    easprintf(&fmt2, "%s: %s\n", getprogname(), fmt);
    va_start(ap, fmt);
    vfprintf(stderr, fmt2, ap);
    va_end(ap);
    efree(fmt2);
}

А вот исправленный вариант:

1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
void
sudo_debug(int level, const char *fmt, ...)
{
    va_list ap;
    char *buf;
 
    if (level > debug_level)
	return;
 
    /* Bracket fmt with program name and a newline to make it a single write */
    va_start(ap, fmt);
    evasprintf(&buf, fmt, ap);
    va_end(ap);
    fprintf(stderr, "%s: %s\n", getprogname(), buf);
    efree(buf);
}

Для понимания также приведу ещё пару функций, которые не изменились:

22
23
int	 easprintf(char **, const char *, ...) __printflike(2, 3);
int	 evasprintf(char **, const char *, va_list) __printflike(2, 0);
37
38
39
40
41
42
43
44
/* For catching format string mismatches */
#ifndef __printflike
# if defined(__GNUC__) && (__GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7)
#  define __printflike(f, v) 	__attribute__((__format__ (__printf__, f, v)))
# else
#  define __printflike(f, v)
# endif
#endif

Код getprogname приводить не буду, итак всё понятно из названия :) .
Я конечно понимаю все мы люди, все ошибаемся, но как такое попало в стейбл. И ведь баги с printf форматированием раньше часто возникали(ещё помню как его нашли в php, а это было аж несколько раз).
Может что-бы в впредь не возникало такой ситуации надо поступить так как это сделала Microsoft заменив printf на безопасный printf_s. Пока для начала можно в glibc добавить printf_s, а потом может сделать printf линком на printf_s.

Помню когда там нашли ошибку в проверки uid пользователя, который его запускал (не совсем там). Кто не знает о ней так создайте файл lib.c с таким содержанием:

int getuid(void){ return 0; }
int geteuid(void){ return 0; }  
int getgid(void){ return 0; } 
int getegid(void){ return 0; }

И откомпилируйте его:
$ gcc -shared -o lib.so lib.c
Теперь делаем так:
$ LD_PRELOAD="./lib.so" bash
И теперь вам может показаться что вы получили рута но это не так. Всякие системные утилиты, вроде whoami, будут говорить вам что вы всё таки root, но это не правда. Для того что-бы убедиться попробуйте сделать то что может только root.
Например:
# cat /etc/shadow
Системные программы, такие как whoami, показывают что вы root, потому что функции getuid и другие возвращают 0, а это id рута. Эти функции заменили старые функции в glibc. Так вот раньше был баг из-за которого можно было обмануть sudo и сделать так что-бы он решил что вы уже root, и в результате он бы запустил процесс уже от настоящего root. Там было всё не совсем так просто так как setuid программа обрабатывает только те либы из LD_PRELOAD которые имеют setuid бит и находятся в стандартном каталоге, но из за ошибок в ld.so это можно было обойти.

Вернёмся к нашему printf, если использовать систему с патчами для защита типа PaX (как например в Gentoo Hardened), тогда баги типа этой не будут вам страшны.

Запись опубликована в рубрике Безопаснось, Программирование, С/C++, Юмор с метками , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>