pickle을 loads할 때 신뢰할 수 없는 데이터는 절대로 언피클하지 말라고 되어 있습니다. 왜 그럴까요? 문서의 이 부분에서도 잠깐 언급되는 부분이지만, os.system과 같은 함수들을 수행되게 할 수 있기 때문입니다. 문서에 나온 코드를 조금 응용해서 실습해 보겠습니다.

 


 먼저, b"cos\nsystem\n(S'rm -r *.txt'\ntR."이 pickle.loads의 인자로 들어왔습니다.

 

 

 해당 python 파일을 실행시켜 보겠습니다. 실행 전과 후를 비교해 보면, txt 파일이 죄다 사라졌음을 볼 수 있습니다. 이것만 봐도 그냥 깡으로 필터링 없이 처리하면 얼마나 위험한지 알 수 있습니다.

 


 pickletools의 dis를 이용하면, 해당 byte가 어떻게 해석되는지를 보여줍니다.

 

 요렇게 해석이 되는데요. 여기서 중요한 부분만 보도록 하겠습니다. 문서를 같이 보시면 도움이 될 듯 합니다. 0번째 줄에 GLOBAL 'os system'이라고 되어 있어요. 이 과정에서 os.system이 import 되게 됩니다.

 

 그리고 global object가 스택에 push 됩니다. 다음에 MARK, STRING, TUPLE이 차례대로 나옵니다. 먼저, MARK를 만나면, mark object를 스택에 push 합니다. string을 만나면 또 스택에 push를 해요. 이 때 까지 상황을 보면 요래 됩니다.

 

 

 다음에 tuple을 만나면 MARK arg1 arg2 ... 형태로 되어 있던 것이 (arg1, arg2 ...) 형태의 tuple로 바뀌게 됩니다.

 

 

 MARK object가 있었고, string "rm -f *.txt"가 있었습니다. 이것이 그냥 tuple로 바뀌게 됩니다.

 

 

 이렇게 스택이 변하게 됩니다. 다음에 reduce가 수행되면서, 스택에는 아래와 같이 바뀌게 됩니다.

 

 여기까지만 보면 별 문제가 없어 보입니다. os.system("rm -r *.txt")라는 callable한 객체가 스택에 있는 건 위험한 상황은 아닙니다. 그런데, 문서에 적혀있는 reduce 함수에 대한 설명을 자세히 봅시다.

 


 callable_object는 object를 만들기 위해 호출되는 개체입니다. 위의 경우에는 os.system인데요. 개체가 만들어진다? 개체를 만드는 과정도 없었는데요. 문서의 맨 윗 부분을 보면 object에서 byte stream 등으로 바꾸는 것을 pickle, 역방향을 unpickle이라고 하고 있어요.

 

 

 즉, Unpickle을 하는 과정은 byte stream에서 object를 생성하는 것이라 볼 수 있어요. byte stream으로부터 object를 만든다는 행위는 unpickle을 하는 행위와 같다고 볼 수 있어요. 이제 위의 상황을 다시 정리해 봅시다. unpickle을 하는 과정에서, 객체를 만들기 위해, callable object가 호출된다고 했는데요.

 

 

 callable_object는 os.system이였습니다. 인자는 "rm -f *.txt"였고요. 객체를 만드는 과정에서 이 함수가 호출 되기 때문에, byte를 pickle로 load하는 과정에서 모든 텍스트 파일이 삭제되게 됩니다. 신뢰할 수 없는 데이터를 함부로 unpickle 하면, 매우 위험한 함수가 호출될 수 있습니다. 따라서, 신뢰할 수 없는 데이터는 절대로 unpickle하면 안 됩니다.