2020 사이버 포카전 Retrospective

mspaint로 만듬.

올해도 역시 사이버 이공계 학생 교류전의 해킹 분야 문제 출제 / 운영을 LeaveCat에서 진행을 했다. 포항에서 하면 카-포전 대전에서 하면 포-카전이라고 하는데 코로나 19 여파로 인해 모든게 다 온라인으로 진행되면서 갖게 된 새로운 명칭이다. 바쁜 업무 + 며칠전 부터 있던 몸살 기운으로 문제 출제를 아예 못하게 될 줄 알았지만 운이 좋게도 대회 당일 조금 몸이 괜찮아져서 몇 문제를 낼 수 있게 되었다. 그 중 한 문제는 며칠전 공개된 CVE-2020-1472 (NetLogon aka ZeroLogon) 를 오마주 해서 출제하게 되었다. 대회 중간에 야식 이벤트를 진행했는데 의도치 않게 테트리스 고인물을 만나는 신기한 경험도 했다.

작년 문제는 github을 통해 모두 archiving 해 아래 링크에서 만나볼 수 있다. 올해 문제는 어떻게 할지 아직 모름.

https://github.com/LeaveCat/poka-sciencewar-2019


우리가 운영하기 시작한 첫 해와 두번째 해는 빙고 컨셉의 대회 운영 사이트를 만들어 문제를 해결할 경우 빙고판을 채워넣고 빙고!를 만들면 추가 점수를 획득할 수 있는 방식을 사용했다. 해가 지나갈수록 팀원 대부분이 취업을 하거나 고등교육을 받으러 떠나 개발할 시간이 충분하지 않았기 때문에 CTFd를 사용했다. 작년까지만 해도 vultr / digital ocean을 사용해 한 사람이 모두 dockerizing 된 문제들을 deploy하는 방식이었다. 올해는 팀원 중 한 사람의 aws 계정에 iam (ec2 full access)를 만들고 해당 계정에서 문제를 관리하는 방식을 사용하려고 했지만, 역-시나 대부분이 다른 서버에 deploy 하는 바람에 관리가 조금 힘들었다. 아직 해커들은 클라우드에 친숙하지 않나보다.. 😭

올해 Mic Check, Real Hacker, 0ero Trust Login (0TL) 총 3가지의 문제를 출제했다. 앞에 2개는 어렵지 않게 만들 수 있고 푸는 입장에서도 쉽게 풀 수 있는 문제다. 마지막 0TL 문제는 CVE-2020-1472를 오마주한 문제이다.

Mic Check는 이번 코드게이트 본선에 출제되었던 Mic Check에서 감명 받아 만든 문제이다. CRC32 결과를 주고 flag를 찾아내라는 문제였다. 당연히 4byte 이상일 것이라고 생각한 우리는 4byte 부터 brute-force를 하였지만 답은 안나왔고 결국 MIC, 3글자가 정답이었다. ㅎ.ㅎ MIC 대신 m1c를 사용하였고 flag format을 알려주어 9자리지만 3자리만 brute-force하면 되는 재밌는 문제를 만들었다. shasum 결과까지 제공해 쉽게 풀 수 있도록 하였다.

-C0deG4te-

Real Hacker는 말그대로 진정한 해커가 되자는 의미에서, 공책에 flag를 적은 후 종이를 구긴 다음 찢어서 사진을 찍었다. 작년에는 flag가 중간에 적힌 소설을 프린트한 후 파쇄기에 넣은 결과물을 오프라인에서 제공해주었다. 올해는 온라인으로 진행되어 전달해줄 수 없어 사진으로 제공했다. 정답 풀이는 사진 편집 프로그램을 적절히 사용해.. flag를 조합하면 된다. (실제로 몇천 장의 종이를 파쇄기에 집어넣은 후 결과를 복구하는 대회도 있다.) 잘 복구가 되지 않는 부분은 local 환경에서 brute-force할 수 있게 flag의 hash도 제공했다.

0TL은 Microsoft의 DC 서버에서 발견된 CVSS Score 10점 만점짜리인 CVE-2020-1472의 취약점을 재활용 했다. Root Cuase는 크립토 취약점인데 난이도도 높지 않고 취약점 자체가 cool해서 문제로 바로 만들면 좋겠다는 생각이 들어 제작했다. 해당 CVE에 대한 자세한 설명은 https://www.secura.com/blog/zero-logon 에서 확인할 수 있다. CVE를 토대로 출제된 문제에서 사용하는 Key Exchange Algorithm은 아래와 같다. 원래는 Client에서 IV를 선택할 수 있어 취약점을 손쉽게 트리거할 수 있지만 대회 문제다 보니 의도치 않게 풀리는 것을 방지하기 위해 Server에서 Random IV를 반환해주고 해당 IV를 모두 \x00 * 16로 초기화 할 수 있는 취약점을 넣어두었다.

Key Exchange Algorithm

CFB16을 간단하게 설명하자면 아래와 같다. feed back operation인데 iv와 plain을 합친 후 첫 16바이트 data[:16] 의 ecb encryption의 결과 값의 첫 바이트와 data[16]을 xor한 후 다시 data[1:17]을 ecb encryption 한 후 data[17]과 xor을 반복적으로 해 총 16바이트가 모두 xor 된 다음 결과를 반환한다. (Microsoft는 CFB8을 사용한다.)

cfb16 operation

이 때 발생할 수 있는 취약점은 만약 IV가 모두 \x00고 Plain 또한 모두 \x00일 경우 1/256의 확률로 계속해서 aes_ecb(\x00 * 16)을 하는 경우가 생길 수 있다. (특정 key에 따라 \x00 * 16의 암호화 된 결과의 첫 바이트가 다시 \x00이 반환된다면..) IV를 모두 \x00로 만들기 위해 취약점을 2가지 만들어 두었다. 하나는 session key를 만드는 함수에서 인자로 받은 iv를 strncpy를 이용해 복사하기 때문에 첫 바이트가 \x00라면 realIv는 모두 \x00가 된다. 이를 이용해 결과 값도 모두 \x00가 되는 경우를 만들 수 있다. 하지만 서버에서 IV를 랜덤하게 만들어 반환하기 때문에 Random IV의 첫 바이트가 \x00가 되는 확률과 ecb encrypt된 결과의 첫 바이트가 \x00이 되는 확률을 곱해야 하기 때문에 매우 많은 brute-force가 필요하다. 서버를 대상으로 하는 공격이라 문제를 solve하는데 너무 많은 시간이 들 것 같아서 추가적인 취약점을 만들어뒀다.

IV를 생성한 후 username을 입력 받는데 스택 프레임을 보면 username 뒤에 iv가 있어 “admin” + “\x00” * 11을 입력으로 주게 되면 off-by-one으로 인해 IV의 첫 바이트가 \x00로 덮이게 된다. 이를 통해 2바이트의 확률을 1바이트로 줄일 수 있게 된다.

/*
-0000000000000208 username        xmmword ?
-00000000000001F8 iv              xmmword ?
*/

// function: main

/*
lea     r12, [rsp+2E8h+username]
call    sub_1190
lea     rsi, aUsername  ; "username >> "
mov     edi, 1
xor     eax, eax
call    printf
mov     rsi, r12
lea     rdi, a16s       ; "%16s"
xor     eax, eax
call    scanf
*/

// function: make Session Key
strncpy_0(&realIv, iv, 16LL);

아쉽게도 이를 해결한 KAIST는 서버에 2바이트 brute-force하는 방식으로 문제를 해결했다. 중간에 Rate-Limit을 추가하려고 했으나 졸려서 그냥 잤다. ㅋ

서버 아파요 ㅜㅜㅜ
자고 일어났더니 풀려있어 기분이 좋았다 ㅎ.ㅎ

Solver는 https://gist.github.com/junorouse/831e5b8774104922705972d80d676ee4 여기에 올려두었다.


총 4번의 운영을 통해 배운점도 많고 어린 나이에 대회를 운영해 볼 수 있는 경험을 주었다는 것은 매우 행운이었다. 이제는 이런 경험을 성장하고 있는 다른 팀들도 겪어볼 수 있게 이제는 다른 팀에게 운영을 넘겨줄 때가 되지 않았나 싶다. (Dreamhack ?)

이미지: 사람 6명, 임준오님 포함, 웃고 있음, 서 있는 사람들
이랬던 사람들이,ㅡ,