문제 개요

smcauth는 Rust로 작성된 garbled circuit 구현체의 취약점을 찾아 공격하는 문제였습니다. Crypto 카테고리로 출제되었으며 대회 종료까지 총 6팀이 해결했습니다. Garbled circuit, oblivious transfer, Rust 바이너리 리버싱, 패킷 로깅 스크립트 작성, 위장 RPC 클라이언트 작성 모두 이번 문제에서 처음으로 배우고 시도한 것들이었습니다. 다양한 지식을 익히고 시도하느라 정신 없었지만, 풀면서 굉장히 즐거운 문제였습니다. 대회 종료 15분을 남기고 아슬아슬하게 해결했는데, 팀원에게 “대회 때마다 항상 아쉽게 막타를 못 치더니 성장했다”라는 평을 들었습니다(…)

Garbled circuit은 두 사람이 서로의 입력값을 모르는 상태로, 신뢰할 수 있는 제삼자(trusted 3rd-party)의 존재 없이 부울 회로 형태로 작성된 함수의 결과를 계산하는 프로토콜입니다.

Garbled circuit 프로토콜의 개략적인 동작 순서는 다음과 같습니다. 위키피디아에 좀 더 자세하게 설명되어 있으니, write-up을 읽기 전 해당 프로토콜의 동작을 이해하고 오시는 것을 추천합니다.

  1. Garbler는 회로의 모든 와이어 w_i에 대해 라벨 x_{i,0}, x_{i,1} \in X을 랜덤하게 생성합니다.
  2. Garbler는 회로의 각 게이트의 진리표를 대칭키 암호 등을 이용해 암호화해, evaluator가 진리표의 한 행만을 복호화 할 수 있도록 합니다. 예를 들어, 와이어 w_iw_j에서 입력을 받아 와이어 w_k에 출력하는 XOR 게이트가 있을 때 이 게이트는 [Enc_{x_{i,0}, x_{j,0}}(x_{k,0}), Enc_{x_{i,0}, x_{j,1}}(x_{k,1}), Enc_{x_{i,1}, x_{j,0}}(x_{k,1}), Enc_{x_{i,1}, x_{j,1}}(x_{k,0})]로 암호화 됩니다. 이를 garbling이라 부르며, 암호화된 회로를 garbled circuit이라 부릅니다.
  3. Garbler는 암호화된 회로 정보와 자신의 입력값에 해당하는 라벨을 evaluator에게 전송합니다. Evaluator는 1-2 oblivious transfer를 이용해 자신의 입력값에 해당하는 라벨을 garbler에게 요청합니다. Garbler가 가진 두 개의 라벨 중 evaluator는 단 하나의 값만을 획득할 수 있으며, garbler는 evaluator가 어떤 값을 획득했는지를 알 수 없는 전송 방식입니다.
  4. Evaluator는 자신과 garbler의 입력에 해당하는 라벨들을 이용해 garbled circuit 계산을 수행합니다. 이를 통해 회로의 최종 출력값에 대응되는 라벨(들)을 얻습니다. 마지막으로, evaluator와 garbler는 출력값의 라벨 정보를 공유해 회로의 실제 출력 결과를 알아냅니다.

Garbler는 oblivious transfer의 특성 때문에 evaluator의 입력값을 알 수 없습니다. Evaluator는 garbler의 입력에 해당하는 라벨을 가지고 있지만, 해당 라벨이 어느 값에 대응되는지를 알 수 없기 때문에 원래 입력값을 알 수 없습니다.

문제에서는 smcauth ELF 바이너리 파일 하나와 smcauth_syn.v 회로 파일 하나가 주어졌습니다. 바이너리는 verify와 auth 두 가지 모드로 동작하며, verify = garbler = server이며 auth = evaluator = client입니다. Garbled Circuit 프로토콜 자체가 안전함은 수학적으로 증명되어 있고, Rust 구현체에 문제가 있어 이를 공격해 서버 측의 비밀 키(회로 입력값)를 알아내는 문제라고 예상했습니다.

바이너리 실행 커맨드 예제는 다음과 같습니다.

./smcauth verify --netlist smcauth_syn.v --secret aaaaaaaabbbbbbbbccccccccdddddddd

바이너리는 Verilog 회로 파일 하나, 32자의 시크릿 키 하나를 입력으로 받으며 auth 모드에서는 --verifier 옵션으로 서버의 주소를 추가로 입력받습니다.

1. 입출력 관찰

로컬 환경 테스트를 통해, verify와 auth의 시크릿 키를 동일하게 입력할 경우 Jun 07 11:58:28.937 INFO authentication successful처럼 성공 메시지가 출력되며, 다르게 입력할 경우 Jun 07 11:58:40.923 WARN authentication failed처럼 실패 메시지가 출력되는 것을 확인했습니다.

모든 입력값을 OR하는 회로와 AND하는 회로 등 smcauth_syn.v 이외의 회로 파일을 시도해 보면서, netlist 옵션으로 입력하는 회로는 256 비트의 e_inputg_input을 입력으로 받아 1 비트의 output을 출력해야 한다는 것을 확인했습니다. 또한, 회로의 output 비트가 1인 경우 “authentication successful” 메시지가 출력되는 것을 통해, smcauth_syn.v는 두 입력 값이 같은 경우 1을 출력하는 회로일 것이라 추측했습니다.

2. 패킷 분석

다음으로 수행한 것은 바이너리의 패킷 분석입니다. 먼저 Wireshark를 이용해 패킷에 TLS 등의 추가 암호화가 이루어지지 않음을 확인한 이후, Verify 프로세스와 auth 프로세스가 주고 받는 패킷을 전송과 수신으로 나누어 저장하는 Python 스크립트를 작성했습니다. 패킷 캡처 라이브러리인 pcap 등의 의존성 없이, strace 커맨드의 결과값을 파싱하는 방식으로 간단하게 작성했습니다.

dumper.py

바이너리에 포함된 문자열을 분석해 해당 바이너리가 RPC 프레임워크로 tarpc를 사용하고 있으며, 검색을 통해 tarpc는 serdebincode를 기본 직렬화 포맷으로 사용하고 있음을 알 수 있었습니다. 회로와 시크릿 값을 바꾸어 가며 수집한 패킷들을 비교 분석하며 휴먼러닝해 서버와 클라이언트가 주고 받는 패킷의 순서와 의미가 다음과 같음을 알아냈습니다.

  1. (전송 1) 세션 초기화 요청
  2. (수신 1) Proof of work 질의
  3. (전송 2) Proof of work 결과 전송
  4. (수신 2) Garbler 입력 라벨 정보
  5. (수신 2) Garbled circuit 정보
  6. (수신 2) Oblivious transfer를 위한 RSA 키
  7. (수신 2) Oblivious transfer를 위한 랜덤값
  8. (전송 3) Oblivious transfer를 이용한 evaluator 라벨 질의
  9. (수신 3) Evaluator 라벨 정보
  10. (전송 4) 결과 라벨 전송
  11. (수신 4) 라벨에 해당하는 결과값 수신

패킷을 분석하면서 라벨 생성이 시크릿 키에 의존하며 서로 다른 세션에서도 변하지 않는다는 것을 확인했지만, 이를 직접 익스플로잇에 이용하지는 않았습니다. RPISEC이나 upbhack 등 다른 팀은 이 특성을 이용해 디버거를 붙여 입력을 브루트포싱하는 방식으로 시크릿 키를 알아낸 것으로 보입니다.

저는 evaluator의 라벨 정보를 가져오는 RPC 프로시저를 두 번 호출해 evaluator의 모든 라벨을 알아내는 방식으로 접근했습니다. Evaluator의 모든 라벨을 알고 있다면 garbled circuit의 한 행만이 아니라 여러 행을 복호화 할 수 있고, 이를 반복해 필요한 모든 와이어의 상태를 복구할 수 있습니다. 이를 통해 출력 와이어를 원하는 결과로 만드는 입력값을 SMT solver를 이용해 역연산 하는 것을 목표로 삼았습니다.

3. 위장 RPC 클라이언트 작성

패킷 분석의 다음 단계는 RPC 프로시저를 두 번 호출하는 위장 RPC 클라이언트를 작성하고, 라벨과 회로 정보를 SMT solver가 취급하기 쉬운 형태로 출력하는 스크립트를 작성하는 것이었습니다. 패킷 분석을 통해 정보가 어떤 순서로 오고 가는지는 파악하고 있었으나, garbled circuit의 계산 및 oblivious transfer이 실제로 어떻게 이루어지는지는 패킷 분석만으로 알아낼 수 없기 때문에 바이너리를 리버스 엔지니어링 해야 했습니다.

삽질과 시행착오를 통해, 7A760이 oblivious transfer 관련 로직이며 2EE70이 garbled circuit 계산 관련 로직임을 알아냈습니다. 해당 함수를 분석해 다음 정보들을 알아냈습니다.

  • RSA-based oblivious transfer는 Udacity의 Applied Cryptography 과목의 영상에 설명된 것과 동일하게 동작하는 것을 확인했습니다.
  • Garbled circuit의 계산은 두 입력 라벨을 XOR한 결과를 AES-256의 키로 사용해, ECB + PKCS#7 모드로 블록을 복호화하고, 복호화된 블록의 길이가 32 바이트인 것을 체크한 뒤 해당 결과를 출력 라벨로 취급되는 것을 확인했습니다.

작성된 최종 스크립트는 다음과 같습니다.

client.py

첫 번째 통신인 proof of work 계산까지는 클라이언트와 서버 사이의 프록시로 작동하며 클라이언트의 입력값을 그대로 서버에 전달합니다(43~70행). 이를 통해 proof of work 리버싱을 건너뛸 수 있었으며, RPC 프로토콜에 사용되는 클라이언트 ID를 수집합니다(62행).

(수신 2)부터는 클라이언트에 의존하지 않고, 분석한 정보에 따라 패킷 역직렬화를 주도적으로 수행합니다(72~116행). 그 다음으로는 해당 스크립트의 핵심이라 할 수 있는 oblivious transfer를 두 번 호출하는 부분이 이어집니다(118~154행).

획득한 라벨을 이용해 garbled circuit 계산을 수행하고(188~208행), 이를 SMT solver가 다시 파싱하기 쉬운 형태로 출력합니다(210~229행). 이를 통해 출력된 SMT 정보는 다음과 같습니다.

SMT

해당 파일에서 e_input을 제외하고, 모든 와이어의 0과 1은 실제 입력값과는 상관 없이 임의로 붙인 변환값입니다. 이 변환을 통해 SMT solver를 호출하는 단에서는 라벨을 이용한 계산을 부울 함수 형태로 취급할 수 있습니다.

4. SMT solver

마지막 단계는 SMT solver를 이용해 회로의 출력을 1로 만드는 시크릿 키를 찾는 것입니다. z3의 Python 바인딩을 이용했습니다. 와이어 output의 0과 1 중 어느 것이 원래 회로의 1에 대응되는지 모르기 때문에, 두 가능성을 모두 시도해 보아야 합니다(44행).

smt_solver.py

OOO{m4by3_7ru57_1sn7_4lw4y5_b4d}

오랜만의 블로그 포스트가 알고리즘 대회 후기가 될 것이라고 예상하지 못했는데, 마지막 글이 올라온지도 꽤 됐고 이번 대회에서 하고 싶은 얘기도 있어서 포스팅을 하기로 했다. 현재 나는 작년 ICPC 이후로 공식적으로는 경쟁 프로그래밍을 은퇴한 상태다. 평소에 공부하고 시간 쓰는건 완전히 멈췄고, 대신 거기 쓰던 시간을 워게임 등 해킹 문제를 풀거나 졸업 후 진로 준비에 쓰고 있다. 이렇게 말하니까 알고리즘 공부에 계속 시간을 많이 쓰고 노력했던 것 같지만 은퇴 선언 이전에도 실질적으로 손을 놓은지는 꽤 됐다. 지금은 티셔츠나 상금 등 부상 주는 대회를 가끔 부담 없이 나가는 걸 목표로 하고 있다.

원래 이번 코드잼은 Rust로 알고리즘 라이브러리를 짜고 그걸 써서 대회를 치고, 대회와 대회 사이에 라이브러리를 보강한 뒤 대회 이후 프로젝트를 다듬어서 공개한다는 원대한 계획과 함께하고 있었다. 하지만 코드잼 플랫폼이 바뀌고 Rust가 지원 언어 목록에 없어서 라이브러리 작성에 대한 관심이 급격하게 식었고, 다익스트라 알고리즘 정도만 겨우 구현된 Rust 알고리즘 라이브러리는 지금까지 존재했던 나만 알고 있다가 비트의 저편으로 사라졌던 많은 개인 프로젝트와 마찬가지로 GitHub 개인 저장소에 조금 더 머무르다 영영 사라질 예정이다. Cargo 배포용으로 이름도 지어줬는데 불쌍한 친구…

올해 코드잼 연습 세션을 치고 나서 느꼈던 건 생각하는 능력 자체는 예상보다 덜 줄었고, 전통적인 테크닉을 떠올리거나 코드를 빠르게 짜는 능력은 예상보다 많이 줄었다는 거였다. 여기서 “생각하는 능력”은 연습 세션 3번 Steed 2 Cruise Control에서 여러 속도로 달리는 말들 사이의 불변조건을 구하는 능력을 말하고 “전통적인 테크닉”은 R1A 3번 Edgy Baking을 보고 냅색이니까 DP로 풀면 뚝딱 할 수 있겠다는 걸 떠올리는 능력을 말한다. 연습 세션부터 이번 R1A까지 예전에 비해 같은 코드를 짜는데 시간이 훨씬 오래 걸린다는게 느껴졌다.

이번 대회를 치면서 좀 놀랐던 점은 대회 중 오랜만에 초심을 되찾은 기분을 느꼈던 것이다. 내 풀이에서 놓친 점을 찾았을 때 예전 같았으면 쉬운 문제에서 실수했다고 자책했겠지만 이번 대회에서는 웬일로 풀이의 빠진 구멍을 채워 나가는 느낌이 즐겁게 느껴졌다. 알고리즘 대회에서 어려운 문제에 도전하는 지적 만족감을 느낀 적은 많았지만, 문제를 푸는 것이 아니라 시간 제한을 두고 대회를 치는 것을 즐겁다고 생각해 본지는 꽤 오래됐다. 평소에 연습을 많이 안 하니 예전만큼 잘하지 못하는 건 당연하다고 생각하면서도 무의식 속에서는 그래도 내가 경력이 얼마인데 못해도 어느 정도는 해야 한다는 부담을 느끼고 있었다고 생각한다. 나도 내가 무의식에서 어떤 생각을 하는지 완전히 모르지만, 공식적으로 은퇴했다고 선언했던게 그런 부담을 낮춰준 게 아닌가 추측하고 있다. 바뀐 플랫폼에서 스코어보드가 문제 페이지에 보이지 않는 것도 도움이 됐다.

원래 대회 후기는 문제 풀이도 함께 올리는게 전통이므로 간단한 풀이도 함께 첨부한다.

  • A: 전체 초콜릿 개수를 세고, \text{(number of H piece)}\times \text{(number of V piece)}로 나누어 떨어지는지 확인한다. 가로 분할선과 세로 분할선의 위치는 초콜릿 개수를 통해 독립적으로 판단 가능하며, 분할선들의 위치가 정해지면 분할된 각 칸에 초콜릿이 균등하게 들어가 있는지 확인한다.
  • B: 계산이 특정 시간 안에 끝날 수 있는지를 판단하는 판별 함수를 작성하고 파라메트릭 바이너리 서치를 수행한다. 판별 함수는 각 계산원이 그 시간까지 처리 가능한 비트의 수를 계산한 뒤 정렬해 큰 순서대로 R개를 뽑아 B 이상이면 처리 가능한 것이다.
  • C: 0-1 냅색. 2 \cdot w_i h_i를 미리 P에서 빼고, 물건의 크기가 min(w, h) 점수는 \sqrt{w^2 + h^2}인 냅색을 수행한다.

레주메는 보통 MS Word 등의 워드프로세서나 LaTeX로 작성하는 것이 일반적입니다. 하지만 둘 모두 문서 작성에 초점이 맞춰져 상호작용 기능이 부족했고, 여기에 제 힙스터 기질이 더해져 웹 기반 레주메를 만들어보자는 생각이 들었습니다. 그래서 구글의 머티리얼 디자인을 채용해 웹 기반 레주메를 만들었었습니다. CSS 전처리기로는 Less를 사용했고, Font Awesome 아이콘도 활용해 각 프로젝트의 GitHub 페이지 링크를 걸고, 아이콘을 누르면 프로젝트 스크린샷을 보여주는 기능 등을 넣었습니다.

그런데…

여기까지 작업은 작년에 마치고, 학교 후원 인턴 프로그램인 SES에 지원할 때 사용했습니다. 별도로 문서 파일로 된 버전이 없었기 때문에 이력서 제출할 때는 연락처와 레주메 링크를 txt 파일로 저장하고 이 txt 파일을 제출했습니다. 지금 생각해보면 정말 패기있는 결정이었네요. 제가 지원했던 회사는 졸업 예정 학생만 뽑는다고 해서 결국 인턴십은 하지 않았지만 그래도 연락이 온 걸 보면 썩 나쁘지는 않았나 봅니다.

…라고 착각하고 있었습니다. 내년 1학기 인턴을 준비중이라 업데이트를 위해 GitHub에 들어갔더니 예전에 만들어 놓은 레주메에 고칠 점이 너무 많이 보였습니다. 1년 학교를 더 다니는 동안 성장한 증거라고 생각하고 있습니다. 큰 문제점은 두 가지였습니다.

  1. 지나치게 많은 정보를 담고 있었습니다. 지원하는 회사는 수많은 지원서를 봐야 하는데, 작은 취미 프로젝트들까지 전부 써 놓았더니 지원서가 너무 길어 보였습니다. 레주메 보다는 포트폴리오에 가까운 구성이었습니다.
  2. 파일로 존재하는 실체가 없었습니다. 레주메 링크를 txt로 첨부하는 것은 상황에 따라 무례해 보일 수도 있고, 추가 접속을 필요로 한다는 점에서 좋은 UX가 아니었습니다.

그래서 기존에 만들어 놓은 웹 기반 레주메의 내용을 다듬으며, 브라우저에서 보이는 웹 문서 파일 뿐 아니라 PDF 출력 파일까지 같은 소스코드에서 뽑는 것을 목표로 리팩토링을 시작했습니다.

서론이 길었네요. 그래서 이번 게시글에서는 기존 포트폴리오 스타일 레주메를 리팩토링하면서 어떤 내용과 디자인을 다듬는데 주력했는지, 그리고 웹 기반 PDF 문서 파일을 만들 때 사용한 CSS 기술 소개와, 화면 기반 컨텐츠와 어떤 차별점을 두었는지를 공유해보려고 합니다.

내용 다듬기


리팩토링 전 레주메 페이지와 프린트 미리보기입니다. 장황하고, 한 눈에 잘 안 들어오고, 두서가 없습니다. 프린트 미리보기에서는 페이지가 잘려 있는 모습도 확인할 수 있습니다. 또 하나의 문제점은 오래된 내용을 위쪽에 배치하고, 새로운 내용을 아래쪽에 배치한 것입니다. 기존 구성은 Education과 Works 두 파트로 나뉘어 Education에서는 학업 진행상황과 관련 연구 및 프로젝트를 혼합해 서술했고, Works 문단에서는 수상 내역과 진행한 프로젝트를 혼합해 서술했습니다.

이러한 구성이 전체적인 내용 파악을 어렵게 한다고 생각되어 새로운 레주메 페이지는 Education, Researches and Projects, Awards의 세 부분으로 나누고 최근 진행한 프로젝트가 위쪽에 오도록 내용을 다듬으며, 각 프로젝트에 대한 설명을 핵심만 짧게 포함하도록 다듬었습니다. 또한, 페이지 마지막에 최종 수정 날짜를 추가해 얼마나 최신 변경사항까지 다루고 있는지를 표기했습니다.

디자인 다듬기

폰트 사이즈 축소

기존에는 구글의 머티리얼 디자인 타이포그라피 가이드를 최대한 지키려고 노력하며 작업했습니다. 폰트 사이즈와 줄 간격 등을 개발자 도구로 뽑아서 그 값을 그대로 사용하려고 했습니다. 하지만 해당 가이드는 앱이나 웹 가독성에 최적화 되어 있었고, 레주메에 사용하기에는 줄 간격 등의 여백이 조금 넓어 보였습니다. 그래서 이번 리팩토링에서 머티리얼 디자인의 느낌은 유지하면서, 전체적으로 여백과 폰트 사이즈를 조금씩 줄였습니다.

Chip 삭제

기존 디자인은 머티리얼 디자인의 chip을 사용해 다양한 정보를 표시했습니다.

하지만 내용 리팩토링을 하면서 연구나 프로젝트를 제외한 소규모 취미 프로젝트들 관련 내용을 대부분 삭제했기 때문에 chip을 사용할만한 자리가 많이 남지 않았고, 어설프게 사용할 경우 실제 중요한 내용보다 chip에 시선이 머무르는 것처럼 느껴져 대부분의 chip을 삭제했습니다.

페이지 우측 공간 활용

위쪽 스크린샷에서 확인하시는 바와 같이, 기존 디자인에서는 프로젝트 제목 바로 옆에 GitHub 링크나 소개 스크린샷, 사용 기술 등의 정보가 포함되어 있었습니다. 새로운 디자인에서는 연도 정보를 제외한 다른 추가 정보들을 float: right CSS를 활용해 페이지 우측으로 옮겼고, 시선을 덜 차지하도록 변경했습니다. 아래는 변경된 디자인입니다.

여기까지 변경 사항을 적용해 레주메 웹 페이지는 다음과 같이 변했습니다. 이제 PDF 페이지를 디자인할 차례입니다.

문서 파일로 내보내도 예쁜 웹페이지 만들기

웹페이지를 인쇄할 때 PDF 내보내기 옵션을 사용하면 PDF 파일로 출력이 가능합니다. 하지만 별도로 신경써서 만들지 않는 경우 세로로 무한히 스크롤이 가능한 웹 페이지와, 페이지 단위로 내용이 잘리는 문서 파일의 차이로 인해 그렇게 예쁜 레이아웃이 나오지는 않습니다. 여기서 CSS의 기능을 활용하면 문서 파일에서도 예뻐 보이는 페이지를 디자인할 수 있습니다. 제가 사용한 CSS 기능은 두 가지입니다. 첫 번째는 CSS의 media 쿼리, 두 번째는 page-break-before 스타일입니다. 각 요소에 대한 자세한 설명은 MDN의 미디어 쿼리 페이지와 page-break-before 페이지를 참고하시고, 이 포스트에서는 간단한 사용법만 다룰 예정입니다.

  • CSS 미디어 쿼리는 어떤 미디어에서 보고 있냐에 따라 다른 CSS를 적용할 수 있게 해 줍니다. @media screen {} 내부에 정의한 내용은 화면으로 볼 때만 적용되며, @media print {} 내부에 정의한 내용은 프린트 할 때만 적용됩니다. 화면 크기 등의 제한 조건을 넣어 반응형 웹사이트를 만들 때에도 CSS 미디어 쿼리가 사용됩니다.
  • page-break-before은 문서를 프린트할 때 HTML 요소 앞에서 페이지가 어떻게 넘겨질지를 결정합니다. always로 설정할 경우 Word 등에서 Ctrl+Enter로 페이지를 넘기는 것처럼 페이지가 인쇄되도록 할 수 있습니다.

이 두 가지 기능을 활용해 웹 기반 레주메 페이지를 문서 파일로 볼 때에도 예쁜 디자인을 유지하도록 추가 작업에 들어갔습니다.

  1. 문서 파일 처음에 있는 마진을 제거했습니다. 웹 페이지에서는 머티리얼 디자인의 특징인 ‘실제 종이를 들고 읽는 듯한 느낌’을 중시했기에 페이지 상단에 배경이 보이도록 여백을 넣었습니다. 문서 파일에서는 페이지 상단에 어색한 공백을 만들기 때문에 이를 제거했습니다.
  2. 동작하지 않는 인터랙션 기능을 제거했습니다. 웹 페이지에서 해당 아이콘을 클릭하는 경우 라이트박스를 이용해 스크린샷과 영상 자료 등을 띄워주도록 구성되어 있습니다. PDF 파일 내에 링크는 넣을 수 있기 때문에 GitHub 링크는 남기고, JavaScript가 필요한 버튼들은 제거합니다.
  3. 1번과 마찬가지 이유로 머티리얼 디자인의 그림자는 제거했습니다.
  4. 문서 파일에서는 페이지 개념이 있는 관계로, 적절한 곳에 page-break를 삽입합니다. 페이지가 나뉘는 경우 page-break-before 전에 프린트 미디어에서만 보이는 hr 태그를 하나 더 삽입해 문서의 각 섹션이 하나의 페이지 안에 담기도록 합니다.
  5. 웹 페이지에서는 hr 태그가 가로를 전부 채우지만, 실제 문서 파일에서는 문서 양쪽에 공백을 두는 것이 일반적이므로 추가 패딩을 적용합니다.
  6. 최종 수정 날짜 옆에 온라인 레주메 링크를 삽입합니다. 이를 통해 예전 버전의 문서 파일을 가지고 있을 때 최신 버전의 레주메를 쉽게 찾을 수 있습니다.

이렇게 완성된 레주메는 제 GitHub Page에서 확인하실 수 있습니다. 출력 파일은 Letter, 여백 없음을 기준으로 작업했으니 PDF 버전도 Ctrl + P로 확인해보세요!

올해는 hacking4soju, 그레이해시, 삼성 소프트웨어 센터와 함께 연합해 hacking4danbi 팀으로 대회에 출전했다. 작년에는 그레이해시와 PLUS의 연합 팀인 playhash로 진출해 아쉽게 예선 통과에 실패했었는데, 올해는 성공적으로 본선에 나갈 수 있었다. 특히 해외 팀과의 연합으로 예선 때 사이클이 잘 맞아서 우리가 잘 때 hacking4soju에서 문제를 풀어주거나, 그 쪽 새벽 시간대에 우리가 활발하게 문제를 풀었다.

대회는 라스베가스에서 3일간 열리지만, 올해는 대회가 legitBS 팀의 커스텀 아키텍처인 cLEMENCy 아키텍처를 기반으로 문제가 출제될 것으로 예고되어 있었고 아키텍처 정보가 대회 24시간 전에 공개되어 사실상 4일 대회라 봐야 했다. 대회 방식은 Attack & Defense로 첫 두 날은 8시간 정도, 마지막날은 4시간으로 짧게 끊는 스케줄이었다.

대회 전에 PLUS 멤버들은 ndh 아키텍처를 기반으로 IDA 로더, ROP gadget, binary diff 툴 등의 설계를 연습해 갔다. 나는 x64 아키텍처로 컨버팅 하는 부분을 준비했다. 하지만 cLEMENCy 아키텍처가 9bit, middle-endian으로 작정하고 더럽게 나와서 컨버팅을 제대로 처리할 수가 없었다. 아키텍처가 공개되었을 때 9bit aligned instruction은 처리할 수 있었지만 middle-endian 메모리 액세스를 효율적으로 처리할 수 없고, 작업량이 많아 대회 전에 끝낼 수 없을 것 같아 컨버팅은 포기하고 다른 툴링에 집중했다. 우리는 어셈블러, 디스어셈블러, ROP gadget, 웹 기반 CFG 분석 도구를 준비했고 꽤 잘 준비한 편일거라 생각했지만 다른 팀들이 준비한 수준도 굉장히 높아 깜짝 놀랐다.

대회장 안에 자리는 팀별로 총 8자리가 준비되어 있었고, PLUS 학부생 멤버는 그 중 두 자리를 할당 받았다. 나는 세 날 모두 들어갔고 다른 멤버로 첫 두 날에는 ebmoon이, 마지막 날에는 flamehaze가 참가했다. 처음에는 대회장에 들어가는게 exploit 경험이 많은 멤버가 들어가야 한다고 생각했으나, 대회를 마치고 나서 대회장 내에는 모니터링 및 커뮤니케이션 담당을 두고 실력이 좋은 멤버들이 편한 환경에서 작업할 수 있게 하는게 중요하다는 것을 깨달았다. PPP 박세준님의 후기에도 비슷한 내용이 있었다.

처음에 나는 하루나 하루 반 정도 대회장에 들어가보고 다른 멤버들을 들여보내려 했었다. 하지만 첫 날의 대회 결과에 대해 논의하는 과정에서 패치를 한 군데서 관리할 필요성이 제기되었고, 내가 패치 및 모니터링을 담당하게 되어 대회 끝날때까지 대회장에서 공격 및 수비 모니터링과 대회장 내외 커뮤니케이션 역할을 맡았다. Attack & Defense 방식과 망분리 환경에 익숙하지 않아 따로 모니터링 도구가 필요할 거라고 생각하지 않아서 관련 도구 개발을 대회 둘째 날부터 시작했는데 이를 미리 준비했더라면 첫 날에 더 나은 성적을 냈을 거라 생각한다. 팀이 흩어져 있어 정보가 잘 공유되지 않다보니 현재 공격/수비 상황을 알지 못해 팀원들끼리 전략적으로 문제에 집중하지 못했던 점이 아쉽다. 올해는 중하위권인 11위로 마무리했는데 다음해 또 나올 기회가 된다면 더 잘할 수 있을 것이라 생각한다.

이번 대회를 거치며 다양한 분들에게 도움을 받았다. 왕복 항공료는 삼성 소프트웨어 센터에서 지원을 받았고, 숙소는 POSTECH 학생지원팀의 지원을 받아 개인 부담이 크지 않게 국제 대회에 다녀올 수 있었다. 그레이해시 측에서도 함께 대회를 하기 좋은 큰 방을 잡아 주시고 대회에 필요한 장비를 준비해 주셔서 좀 더 쾌적한 환경에서 대회를 진행할 수 있었다. 금전적인 지원 뿐 아니라 같이 대회를 나가며 어깨 너머로 배운 지식들도 추후 동아리 활동을 하면서 큰 도움이 될 것이라 생각한다.

이번 참가에서 가장 인상 깊었던 부분 중 하나는 커뮤니티가 주도하는 대회와 컨퍼런스 방식이었다. 맥주를 마시며 농담을 섞어 가며 컨퍼런스 결과를 발표하고, 직접 그 대회를 준비한 사람들에게 찬사가 돌아가고 격식없이 서로를 대하는 모습이 대회와 전혀 상관 없는 의전 행사만 한 시간이 넘는 국내 대회들과 비교되었다. 해외 대회들이 국내 대회를 의식해 선수 대우를 더 강화했듯이, 국내 대회들도 해외 대회의 좋은 점을 보며 서로 발전했으면 좋겠다고 생각한다.

이번 DEFCON 본선에 참가하며 국제대회 Qualification을 통과한 것도 처음, 동아리 이외의 사람들과 함께 해킹을 해본 것도 처음, 세계 유명 팀들과 얼굴을 보며 경쟁을 해 본 것도 처음, 여러모로 처음이 많은 대회였다. 대학에 입학하고 2년동안 방학마다 합숙에 참가하며 짧다면 짧고 길다면 길게 해킹을 공부해 왔는데, 다시 초심으로 돌아가 해킹이라는 분야에 대해 한 번 더 생각해 보는 계기가 되었다.

많은 것을 보고 듣는 시간이었고, 세상이 넓다는 것을 다시 한 번 느꼈다.