예전에 학교 C 수업에서 모든 파일의 탭 사용을 금지하면서 제출물에 Makefile을 반드시 포함하라고 했던 일이 떠오름. 규칙을 글자 그대로 따르면 Makefile에도 탭을 쓸 수 없어 사실상 아무것도 하기 어려웠지만, 탭이 있는 Makefile을 탭 없는 Makefile로 바꾸는 변환기를 만들어 우회했음.
예를 들어 all: 아래에서 gcc a.c -o a를 실행하는 규칙을, FOO := $(shell echo 'all:\n\tgcc a.c -o a' > GNUmakefile; $(MAKE) >&2)와 아무 작업도 하지 않는 all: 규칙으로 바꾸는 방식임.
즉시 평가되는 더미 변수를 이용해 실제 빌드 내용을 별도 파일에 쓰고, 그 파일을 대상으로 make를 재귀 호출함. GNU make가 GNUmakefile, makefile, Makefile 순서로 파일을 찾는다는 점을 활용했으며, 당시에는 모두 GNU make를 사용했음. 가산점은 받지 못했지만 재미있는 일화는 남겼음.
세미콜론을 써도 가능함. all:;gcc -o a a.c로 작성하면 됨.
탭을 금지한 이유가 잘 이해되지 않는데, 혹시 이유를 물어봤는지?
그래서 학교에서는 이 시도를 보고 뭐라고 했는지? 그 뒷이야기가 빠져 있음!
임의의 코드에 시스템 호출이 들어가지 못하도록 막기는 어려워 보임. 학교가 실행 환경을 통제한다면, 금지된 시스템 호출을 차단하도록 strace 를 설정해 프로그램을 실행하는 편이 훨씬 쉬울 것 같음.
코드를 Wasm으로 컴파일하고, 허용된 표준 라이브러리 함수만 가져올 수 있도록 제한하면 대응 가능해 보임.
이런 간이 샌드박스를 두고 벌이는 숨바꼭질이 재미있음. dlsym(RTLD_NEXT, "execve") 도 작동할 것 같음.
dlsym이 허용된 심볼일 때만 가능함.
인라인 어셈블리로 syscall을 직접 실행하는 방식도 시도해 봤는지? argv를 {"/bin/sh", NULL}, envp를 {NULL}로 두고, x86-64에서 시스템 호출 번호 59L인 SYS_execve와 인자를 레지스터에 넣어 호출하는 방식임.
방어하려면 샌드박스에 seccomp 를 적용할 수 있음. 이상적으로는 execve를 제외하고 필요한 일부 시스템 호출만 허용해야 함.