euid, ruid, suid
Reading time: 7 minutes
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
사용자 식별 변수
ruid: 실제 사용자 ID는 프로세스를 시작한 사용자를 나타냅니다.euid: 유효 사용자 ID로 알려져 있으며, 시스템이 프로세스 권한을 확인하는 데 사용하는 사용자 신원을 나타냅니다. 일반적으로euid는ruid와 일치하지만, SetUID 바이너리 실행과 같은 경우에는euid가 파일 소유자의 신원을 취하여 특정 작업 권한을 부여합니다.suid: 이 저장된 사용자 ID는 높은 권한의 프로세스(일반적으로 root로 실행)가 특정 작업을 수행하기 위해 일시적으로 권한을 포기해야 할 때 중요하며, 이후 다시 초기 상승된 상태를 회복합니다.
중요 참고 사항
root로 실행되지 않는 프로세스는 현재 ruid, euid 또는 suid와 일치하도록 euid를 수정할 수 있습니다.
set*uid 함수 이해하기
setuid: 초기 가정과는 달리,setuid는 주로ruid가 아닌euid를 수정합니다. 특히, 권한이 있는 프로세스의 경우, 지정된 사용자(종종 root)와 함께ruid,euid,suid를 정렬하여 이러한 ID를 강화합니다. 자세한 내용은 setuid man page에서 확인할 수 있습니다.setreuid및setresuid: 이러한 함수는ruid,euid,suid의 미세 조정을 허용합니다. 그러나 그 기능은 프로세스의 권한 수준에 따라 달라집니다. 비-root 프로세스의 경우, 수정은 현재ruid,euid,suid의 값으로 제한됩니다. 반면, root 프로세스나CAP_SETUID권한이 있는 프로세스는 이러한 ID에 임의의 값을 할당할 수 있습니다. 더 많은 정보는 setresuid man page와 setreuid man page에서 확인할 수 있습니다.
이러한 기능은 보안 메커니즘이 아니라 프로그램이 다른 사용자의 신원을 채택하기 위해 유효 사용자 ID를 변경하는 것과 같은 의도된 작업 흐름을 촉진하기 위해 설계되었습니다.
특히, setuid는 root로의 권한 상승을 위한 일반적인 방법일 수 있지만(모든 ID를 root로 정렬하므로), 이러한 함수 간의 차이를 이해하고 다양한 시나리오에서 사용자 ID 동작을 조작하는 것이 중요합니다.
리눅스에서 프로그램 실행 메커니즘
execve 시스템 호출
- 기능:
execve는 첫 번째 인수에 의해 결정된 프로그램을 시작합니다. 두 개의 배열 인수, 인수용argv와 환경용envp를 사용합니다. - 동작: 호출자의 메모리 공간을 유지하지만 스택, 힙 및 데이터 세그먼트를 새로 고칩니다. 프로그램의 코드는 새 프로그램으로 대체됩니다.
- 사용자 ID 보존:
ruid,euid및 추가 그룹 ID는 변경되지 않습니다.- 새 프로그램에 SetUID 비트가 설정된 경우
euid에 미세한 변화가 있을 수 있습니다. - 실행 후
suid는euid에서 업데이트됩니다. - 문서: 자세한 정보는
execveman page에서 확인할 수 있습니다.
system 함수
- 기능:
execve와 달리system은fork를 사용하여 자식 프로세스를 생성하고 해당 자식 프로세스 내에서 명령을 실행합니다. - 명령 실행:
sh를 통해 명령을 실행하며,execl("/bin/sh", "sh", "-c", command, (char *) NULL);를 사용합니다. - 동작:
execl은execve의 한 형태로, 새로운 자식 프로세스의 맥락에서 유사하게 작동합니다. - 문서: 추가 정보는
systemman page에서 확인할 수 있습니다.
SUID와 함께하는 bash 및 sh의 동작
bash:euid와ruid의 처리 방식에 영향을 미치는-p옵션이 있습니다.-p가 없으면,bash는euid가ruid와 다를 경우euid를ruid로 설정합니다.-p가 있으면, 초기euid가 보존됩니다.- 더 많은 세부정보는
bashman page에서 확인할 수 있습니다. sh:bash의-p와 유사한 메커니즘이 없습니다.- 사용자 ID와 관련된 동작은 명시적으로 언급되지 않으며,
-i옵션 하에서euid와ruid의 동등성을 강조합니다. - 추가 정보는
shman page에서 확인할 수 있습니다.
이러한 메커니즘은 작동 방식이 다르며, 프로그램을 실행하고 전환하는 데 다양한 옵션을 제공하며, 사용자 ID가 관리되고 보존되는 방식에 특정한 뉘앙스가 있습니다.
실행에서 사용자 ID 동작 테스트
예제는 https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail에서 가져왔으며, 추가 정보를 확인하세요.
사례 1: system과 함께 setuid 사용
목표: system과 bash를 sh로 조합했을 때 setuid의 효과를 이해합니다.
C 코드:
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setuid(1000);
system("id");
return 0;
}
컴파일 및 권한:
oxdf@hacky$ gcc a.c -o /mnt/nfsshare/a;
oxdf@hacky$ chmod 4755 /mnt/nfsshare/a
bash-4.2$ $ ./a
uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
분석:
ruid와euid는 각각 99 (nobody)와 1000 (frank)로 시작합니다.setuid는 둘 다 1000으로 맞춥니다.system은 sh에서 bash로의 심볼릭 링크로 인해/bin/bash -c id를 실행합니다.bash는-p없이euid를ruid와 일치시키므로 둘 다 99 (nobody)가 됩니다.
케이스 2: system과 함께 setreuid 사용
C 코드:
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setreuid(1000, 1000);
system("id");
return 0;
}
컴파일 및 권한:
oxdf@hacky$ gcc b.c -o /mnt/nfsshare/b; chmod 4755 /mnt/nfsshare/b
실행 및 결과:
bash-4.2$ $ ./b
uid=1000(frank) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
분석:
setreuid는 ruid와 euid를 모두 1000으로 설정합니다.system은 bash를 호출하며, 사용자 ID의 동등성으로 인해 이를 유지하여 사실상 frank로 작동합니다.
사례 3: execve와 함께 setuid 사용
목표: setuid와 execve 간의 상호작용 탐색.
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setuid(1000);
execve("/usr/bin/id", NULL, NULL);
return 0;
}
실행 및 결과:
bash-4.2$ $ ./c
uid=99(nobody) gid=99(nobody) euid=1000(frank) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
분석:
ruid는 99로 유지되지만, euid는 setuid의 효과에 따라 1000으로 설정됩니다.
C 코드 예제 2 (Bash 호출):
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setuid(1000);
execve("/bin/bash", NULL, NULL);
return 0;
}
실행 및 결과:
bash-4.2$ $ ./d
bash-4.2$ $ id
uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
분석:
euid가setuid에 의해 1000으로 설정되었지만,bash는-p가 없기 때문에ruid(99)로euid를 재설정합니다.
C 코드 예제 3 (bash -p 사용):
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
char *const paramList[10] = {"/bin/bash", "-p", NULL};
setuid(1000);
execve(paramList[0], paramList, NULL);
return 0;
}
실행 및 결과:
bash-4.2$ $ ./e
bash-4.2$ $ id
uid=99(nobody) gid=99(nobody) euid=100
References
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks