원문 캡처 · yossarian.net
원문 캡처 · yossarian.net

wordexp(3)은 C에서 셸 방식의 확장을 수행하는 POSIX API다. ~/foo를 /home/me/foo처럼 바꾸는 물결표 확장에 쓰라고 권하는 경우가 있지만, 기본 설정에서는 명령 치환을 포함한 모든 확장 기능이 활성화된다.

예를 들어 입력에 $(whoami)/foo나 $(cat /etc/passwd)/foo가 포함되면 명령이 실행될 수 있다. 플래그 인수에 WRDE_NOCMD를 전달해 명령 치환을 막을 수 있지만, 일부 libc 구현에서는 이 방법도 안전을 보장하지 못한다.

  • musl은 시스템 셸에 처리를 맡기기 전에 명령 치환을 간단히 검사하지만, GNU Bash 같은 셸이 유효한 명령 치환으로 받아들이는 일부 패턴은 잡아내지 못한다는 설명이다.
  • macOS의 libc도 /usr/lib/system/wordexp-helper에 처리를 맡기는 비슷한 방식이다. Julian Squires에 따르면 2023년에도 musl과 같은 우회가 가능했지만, 기본 셸이 Bash에서 zsh로 바뀐 뒤에도 여전히 유효한지는 확인하지 않았다.

따라서 이 글은 WRDE_NOCMD를 설정하더라도 신뢰할 수 없는 입력에 wordexp(3)를 사용하지 말라고 권한다. macOS의 wordexp(3)는 과거 Perl을 실행했으며, wordexp-helper는 그 뒤의 ‘현대적’ 대안으로 보인다고 덧붙인다.

부록: 원문 예시

text
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wordexp.h>

int expand_tilde(const char *input, char **output) {
 wordexp_t p;
 int ret = wordexp(input, &p, 0);
 if (ret == 0) {
 *output = strdup(p.we_wordv[0]);
 wordfree(&p);
 }
 return ret;
}

int main(void) {
 char *expanded = NULL;
 expand_tilde("~/foo", &expanded);
 printf("expanded: %s\n", expanded);
 return 0;
}
text
expand_tilde("$(whoami)/foo", &expanded);
text
expand_tilde("$(cat /etc/passwd)/foo", &expanded);