Skip to content
KitploitKITPLOIT
ИнструментыБлог
Log in
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2012-0056 — linux 提权 | Kitploit
Инструменты/GitHubGitHub/pythonone/cve-2012-0056
Повышение привилегийАнализ уязвимостейЭксплуатацияШелл-кодОбучение и ОбразованиеЭксплуатация Бинарных Файлов
GitHubpythonone/cve-2012-0056

CVE-2012-0056

linux 提权

Репозиторий
161310 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Linux Local Privilege Escalation via SUID /proc/pid/mem Write

Mempodipper

Представляем Mempodipper, эксплойт для CVE-2012-0056. /proc/pid/mem — это интерфейс для прямого чтения и записи памяти процесса путем перемещения по тем же адресам, что и виртуальное адресное пространство процесса. В версии 2.6.39 защита от несанкционированного доступа к /proc/pid/mem была признана достаточной, и предыдущая директива #ifdef, запрещавшая запись в произвольную память процесса, была удалена. Любой, имеющий соответствующие разрешения, мог записывать в память процесса. Оказалось, конечно, что проверка разрешений была выполнена плохо. Это означает, что все ядра Linux >=2.6.39 уязвимы, вплоть до коммита с исправлением, выпущенного пару дней назад. Давайте шаг за шагом рассмотрим старый код ядра и разберемся, в чем проблема.

Когда /proc/pid/mem открывается, вызывается этот код ядра:

static int mem_open(struct inode* inode, struct file* file)
{
	file->private_data = (void*)((long)current->self_exec_id);
	/* OK to pass negative loff_t, we can catch out-of-range */
	file->f_mode |= FMODE_UNSIGNED_OFFSET;
	return 0;
}

При открытии нет никаких ограничений; любой может открыть файловый дескриптор /proc/pid/mem для любого процесса (с учетом обычных ограничений VFS). Он просто запоминает исходный self_exec_id процесса, с которым был открыт, и сохраняет его для последующей проверки при чтении и записи.

Однако запись (и чтение) имеют ограничения проверки разрешений. Давайте взглянем на функцию записи:

static ssize_t mem_write(struct file * file, const char __user *buf,
			 size_t count, loff_t *ppos)
{

/* unimportant code removed for blog post */	

	struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
	
/* unimportant code removed for blog post */

	mm = check_mem_permission(task);
	copied = PTR_ERR(mm);
	if (IS_ERR(mm))
		goto out_free;

/* unimportant code removed for blog post */	

	if (file->private_data != (void *)((long)current->self_exec_id))
		goto out_mm;

/* unimportant code removed for blog post
 * (the function here goes onto write the buffer into the memory)
 */	

Итак, на месте две соответствующие проверки для предотвращения несанкционированных записей: check_mem_permission и self_exec_id. Сначала разберем первую, затем вторую.

Код check_mem_permission просто вызывает __check_mem_permission, так что вот код последней:

static struct mm_struct *__check_mem_permission(struct task_struct *task)
{
	struct mm_struct *mm;

	mm = get_task_mm(task);
	if (!mm)
		return ERR_PTR(-EINVAL);

	/*
	 * A task can always look at itself, in case it chooses
	 * to use system calls instead of load instructions.
	 */
	if (task == current)
		return mm;

	/*
	 * If current is actively ptrace'ing, and would also be
	 * permitted to freshly attach with ptrace now, permit it.
	 */
	if (task_is_stopped_or_traced(task)) {
		int match;
		rcu_read_lock();
		match = (ptrace_parent(task) == current);
		rcu_read_unlock();
		if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH))
			return mm;
	}

	/*
	 * No one else is allowed.
	 */
	mmput(mm);
	return ERR_PTR(-EPERM);
}

Есть два способа авторизовать запись в память. Либо task == current, то есть процесс, в который идет запись, является процессом, который пишет, либо current (пишущий процесс) имеет эзотерические разрешения ptrace-уровня для работы с task (процессом, в который идет запись). Может быть, вы думаете, что можно обмануть код ptrace? Это заманчиво. Но я не знаю. Вместо этого давайте разберемся, как заставить процесс записывать произвольные данные в свою собственную память, чтобы task == current.

Естественно, мы хотим записывать в память suid-процессов, чтобы получить root. Взгляните на это:

$ su "yeeeee haw I am a cowboy"
Unknown id: yeeeee haw I am a cowboy

su выводит любой ваш текст в stderr с префиксом "Unknown id:". Таким образом, мы можем открыть файловый дескриптор к /proc/self/mem, выполнить lseek в нужное место памяти для записи (об этом позже), использовать dup2 для связывания stderr и mem-дескриптора, а затем exec в su $shellcode, чтобы записать шелл-спавнер в память процесса, после чего мы получим root. Правда? Не так просто.

Здесь вступает в силу другое ограничение. После прохождения проверки task == current он проверяет, совпадает ли текущий self_exec_id с тем self_exec_id, с которым был открыт файловый дескриптор. Что же такое self_exec_id? Он упоминается лишь в нескольких местах в ядре. Самое важное из них находится внутри exec:

void setup_new_exec(struct linux_binprm * bprm)
{
/* massive amounts of code trimmed for the purpose of this blog post */

	/* An exec changes our domain. We are no longer part of the thread
	   group */

	current->self_exec_id++;
			
	flush_signal_handlers(current, 0);
	flush_old_files(current->files);
}
EXPORT_SYMBOL(setup_new_exec);

self_exec_id увеличивается каждый раз, когда процесс выполняет exec. Таким образом, это работает так: вы не можете открыть файловый дескриптор в не-suid процессе, выполнить dup2, а затем exec в suid-процесс... именно то, что мы пытались сделать выше. Довольно хитрый способ предотвратить нашу атаку, не так ли?

Вот как это обойти. Мы форкаем дочерний процесс, и внутри этого дочернего процесса выполняем exec в новый процесс. У форкнутого дочернего процесса изначально self_exec_id равен родительскому. Когда мы выполняем exec в новый процесс, self_exec_id увеличивается на единицу. Тем временем сам родитель занят выполнением exec в наш процесс su, который пишет шелл-код, поэтому его self_exec_id также увеличивается до того же значения. Итак, мы делаем следующее: форкаем дочерний процесс и выполняем в нем exec в новый процесс, и уже внутри этого нового процесса открываем файловый дескриптор к /proc/parent-pid/mem, используя PID родительского процесса, а не свой собственный (как было раньше). Мы можем открыть файловый дескриптор, потому что для простого открытия нет проверки разрешений. При открытии его self_exec_id уже увеличился до того значения, каким станет self_exec_id родителя, когда мы выполним exec в su. Наконец, мы передаем открытый файловый дескриптор от дочернего процесса обратно родительскому (используя немного очень темной магии с доменными сокетами Unix), выполняем dup2 и exec в su с шелл-кодом.

Скачать инструмент