Для устранения [[http://www.opennet.me/opennews/art.shtml?num=32872 уязвимости]] CVE-2012-0056 в /proc/pid/mem без обновления ядра Linux можно использовать systemtap для ограничения системного вызова. Systemtap из коробки доступен в RHEL/CentOS и может быть установлен из репозитория в Debian/Ubuntu. Без systemtap обходным путем решения проблемы является блокирования доступа пользователя на запуск setuid-программ.Проверим, уязвима ли наша система. Для этого загружаем, собираем и запускаем специальную проверочную утилиту:
$ wget http://grsecurity.net/~spender/correct_proc_mem_reproducer.c
$ make correct_proc_mem_reproducer
$ ./correct_proc_mem_reproducer
vulnerable
Рассмотрим процесс установки и использования systemtap в Ubuntu и Debian.
Установим systemtap и пакет для отладки ядра (занимает около 2 Гб)
Debian:
sudo apt-get install -y systemtap linux-image-$(uname -r)-dbg
Ubuntu (требуется подключение отладочного репозитория):
sudo apt-get install -y lsb-release
echo "deb http://ddebs.ubuntu.com/ $(lsb_release -cs) main restricted universe multiverse" | \
tee -a /etc/apt/sources.list.d/ddebs.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01
sudo apt-get update
sudo apt-get install -y systemtap linux-image-$(uname -r)-dbgsym
Если в процессе выполнения "apt-get update" выведена ошибка, загружаем и устанавливаем отладочные пакеты с ядром вручную:
sudo apt-get install -y systemtap dpkg-dev
wget http://ddebs.ubuntu.com/pool/main/l/linux/$(dpkg -l linux-image-$(uname -r) | \
grep ^ii | awk '{print $2 "-dbgsym_" $3}' | tail -n1)_$(dpkg-architecture -qDEB_HOST_ARCH).ddeb
sudo dpkg -i linux-image-$(uname -r)-dbgsym.ddeb
Создаём systemtap-скрипт proc-pid-mem.stp для блокирования уязвимости (в функции mem_write изменяем на ноль значение аргумента count, определяющего размер записываемых данных):
probe kernel.function("mem_write@fs/proc/base.c").call {
$count = 0
}
Запускаем скрипт:
sudo stap -Fg proc-pid-mem.stp
Убедимся, что уязвимость теперь не проявляется:
$ ./correct_proc_mem_reproducer
not vulnerable
Чтобы каждый раз не компилировать модуль systemtap, можно собрать его загодя:
stap -g -m cve_2012_0056 proc-pid-mem.stp -p4
(опция "-g" включает режим гуру, при котором допускается изменение параметров функций, опция -m cve_2012_0056 задаёт имя модуля, -p4 указывает на необходимость остановиться на 4 шаге и не загружать модуль)
Копируем созданный модуль в область с модулями ядра:
mkdir /lib/modules/$(uname -r)/systemtap
cp cve_2012_0056.ko /lib/modules/$(uname -r)/systemtap
Запускаем модуль:
staprun -L cve_2012_0056
Достоинство использования готового модуля в том, что для его запуска нет необходимости в отладочных пакетах (для сборки они нужны), т.е. его можно скопировать на другие системы с той же версией ядра Linux.
По аналогии можно вклиниваться в работу других системных вызовов. Некоторые
[[http://sourceware.org/systemtap/examples/keyword-index.html примеры]]:
Запрет на создание файлов с определённым именем:
#!/usr/bin/env stap
# badname.stp
# Prevent the creation of files with undesirable names.
# Source: http://blog.cuviper.com/2009/04/08/hacking-linux-filenames/
# return non-zero if the filename should be blocked
function filter:long (name:string)
{
return isinstr(name, "XXXbadnameXXX")
}
global squash_inode_permission
probe kernel.function("may_create@fs/namei.c")
{
# screen out the conditions which may_create will fail anyway
if ($child->d_inode || $dir->i_flags & 16) next
# check that the new file meets our naming rules
if (filter(kernel_string($child->d_name->name)))
squash_inode_permission[tid()] = 1
}
probe kernel.function("inode_permission@fs/namei.c").return !,
kernel.function("permission@fs/namei.c").return
{
if (!$return && squash_inode_permission[tid()])
$return = -13 # -EACCES (Permission denied)
delete squash_inode_permission[tid()]
}
Блокирование работы ptrace: http://sourceware.org/systemtap/examples/process/noptrace.stp
Вывод в лог дополнительных данных о всех TCP-соединениях:
#! /usr/bin/env stap
probe begin {
printf("%6s %16s %6s %6s %16s\n",
"UID", "CMD", "PID", "PORT", "IP_SOURCE")
}
probe kernel.function("tcp_accept").return?,
kernel.function("inet_csk_accept").return? {
sock = $return
if (sock != 0)
printf("%6d %16s %6d %6d %16s\n", uid(), execname(), pid(),
inet_get_local_port(sock), inet_get_ip_source(sock))
}
Вывод деталей о входе и выходе из функции ядра:
#! /usr/bin/env stap
# Simple probe to detect when a process is waiting for more socket send
# buffer memory. Usually means the process is doing writes larger than the
# socket send buffer size or there is a slow receiver at the other side.
# Increasing the socket's send buffer size might help decrease application
# latencies, but it might also make it worse, so buyer beware.
#
# Typical output: timestamp in microseconds: procname(pid) event
#
# 1218230114875167: python(17631) blocked on full send buffer
# 1218230114876196: python(17631) recovered from full send buffer
# 1218230114876271: python(17631) blocked on full send buffer
# 1218230114876479: python(17631) recovered from full send buffer
probe kernel.function("sk_stream_wait_memory")
{
printf("%u: %s(%d) blocked on full send buffer\n",
gettimeofday_us(), execname(), pid())
}
probe kernel.function("sk_stream_wait_memory").return
{
printf("%u: %s(%d) recovered from full send buffer\n",
gettimeofday_us(), execname(), pid())
}
URL: http://www.outflux.net/blog/archives/2012/01/22/fixing-vulne.../
Обсуждается: http://www.opennet.me/tips/info/2663.shtml