Happy New Year!, 2021에는..

안녕하세요, 블로그를 방치한지 수개월에서 이제는 1년이 더 되어가던 찰나에 아무래도 뭐라도 쓰는 것이 좋겠다는 생각이 들어 편집기를 열었습니다. 2020년 한 해는 코로나를 포함해 여러 이슈가 있었던 탓에 너무 빠르게 지나가버린 것 같네요. 나이를 한 살 더 먹을 수록 시간이 빠르게 간다고 하는데, 정말 맞는 이야기인 것 같습니다. 내 자신을 되돌아보는 회고글을 작성하려고 생각해봤는데 아무래도 보통 고난과 시련도 별 탈 없이 넘기는 편이라 별로 도움이 되지 못할 것 같아 그냥 제가 하고 싶은 이야기를 적으려고 합니다.

첫 번째는 이 블로그의 시작과 앞으로의 방향을 떠올려봤습니다. 제가 공부하거나 생각하고 있는 것들을 나누려고 처음 시작했었는데 제 부족한 글쓰기 실력과 선 뜻 내 생각을 말하는 것에 두려움과 학생에서 직장 생활을 하다보니 없어지는 시간등의 이유로 잘 지켜지지 못한 것 같네요. 회사를 다니면서 블로그 글을 꾸준히 작성하시는 분들을 보면 참 신기하기도 하고 부러울 따름입니다. 이 블로그는 2018년에 처음 생성했습니다. “github pages, 네이버, 티스토리, 구글 블로그등 말고 왜 워드프레스일까?” 라고 핟다면 http://bpak.org의 영향도 크지만 무엇보다 제일 완성도 높다고 생각했고 굳이 서버 관리를 할 필요가 없다고 느꼈고 비용적인 측면에서도 일년에 $100정도면 굳이 매달 $10을 쓰는 lightsail 보다 훨씬 나을 것이라고 생각했었습니다. 뭐, 하여튼 첫 해년도는 “건강한 보안 생태계 발전을 위해”이라는 글이 젤 많은 뷰를 기록했습니다. 사실 그때에 비해 많은 생각의 변화가 있었는데 시간과 기회가 된다면 그 내용도 한번 써보려고 합니다. 이 글을 제외한 기술 관련 글들은 예전 블로그에서 임포트해온 것이라 많은 뷰가 있지는 않았습니다. 2019년은 깃헙 스타를 누르면 커피를 준다는 마케팅으로 비난을 받았던 metatron-discovery를 해커톤 처럼 빠르게 회사 동료분과 리뷰한 결과를 올린 “metatron-discovery security review speed run”입니다. 이슈에 별다른 디스커션이 생기지는 않아서 어떻게 패치되었는지는 모르겠지만 다른 오픈 소스들 처럼 “너가 찾았으니 너가 패치하렴 …”라는 태도는 아니여서 다행이었습니다. 뭐 암튼! 그래도 나름 기술 관련 게시글이 많은 뷰를 기록했으니 다행이라고 생각합니다. 그리고 올해(2020년)는 해커도 해킹을 당한다! “CTF 문제 풀다 해킹당한 이야기”가 히트를 기록했었습니다. 나름 재밌게 쓴다고는 써봤는데 어땟을지 모르겠습니다. 이러한 글의 성향으로 봐선 아무래도 “자료를 찾기 위해 들어오는 블로그”보단 “임준오의 일기를 보기 위한 블로그”가 되면 좋겠다는 제 생각이 어느정도 드러나지는 않았나 싶습니다. 그래서! 2021년에는 좀 더 가볍고 신기한 주제로 최소, 한달에 한번은 주제를 정해 작성하려고 합니다. 이게 익숙해지면 1주일에 하나씩은 작성하게 되겠죠 하하..

두번째이면서 마지막인, 20201년 계획과 목표입니다.

  1. 첫번 째는 역시 건강입니다. 육체적인 것을 포함해 정신적인 것도 모두 포함하는데요, 2021년에는 보다 나 자신에게 너그러워지고 건강한 식습관 / 업무!를 통해 주변 사람들도 잘 챙기려고 합니다. 요새 배달 음식 보다 직접 요리해먹고 있는데 즐겁습니다~!
  2. 한달에 한 번 재미난 주제를 정해 블로그 글을 써보려고 합니다.
  3. 외국어를 열심히 공부하자! 입니다. 미국분과 함께 일할 때 소통의 문제로 제 자신이 답답한게 많았는데 이런 부분들을 개선하는 의미도 있고 진행한 연구나 프로젝트의 관심을 더 받기 위함도 있습니다. 정면 돌파 하고자 1월 말일쯤에 홍콩에서 진행하는 온라인 세미나 발표가 하나 잡혀있긴 한데 아직 준비가 안되어 있어서 어떻게 될지 모르겠네요 ㅜ. 아무래도 Script를 작성해 읽는 것으로 진행해야 할 것 같습니다.
  4. 는 좀더 생각해보고 작성해보도록 하겠습니다.

올해는 위 세가지 목표를 꼭 이루었으면 좋겠습니다. 그리고 코로나가 정상화 되어서 여행도 자주 다녔으면 좋겠네요. 하하, 여러분들도 모두 행복하시고 새해 복 많이 받으세요!

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명, 임준오님 포함, 웃고 있음, 서 있는 사람들
이랬던 사람들이,ㅡ,

openssl은 가만히 놔두자.

어느 순간 서버에 있는 모든 cron과 bot들이 작동을 멈추었다. 이유를 찾아보니 ASAN 어쩌구 하면서 에러를 뱉으며 죽었는데, 저번달에 segfault 나는 crash 분석하기 귀찮아서 asan 붙인다고 빌드하다가 실수한걸로 결론 땅땅.

openssl configure할 때 prefix를 /usr/local로 줘버려서 기존에 있는 친구들이 모두 asan이 적용된 library로 바뀌었다. 처음엔 이걸 모르고 apt로 re-install 해보고 symlink 버전 다른 것들 다 맞추고 별 쌩쇼를 다 했는데 그냥 build하고 install하니깐 고쳐졋다. ㅎ..ㅎ

# download *.tar.gz from https://www.openssl.org/source/
./Configure linux-x86_64 shared  no-md2 no-mdc2 no-rc5 no-rc4  --prefix=/usr/local
make depend && make && make install

대충 터졌던 시드를 공유하자면 요렇게 생김.

printf "ca\nreq\nreq --help" | LD_LIBRARY_PATH=/usr/local/lib openssl -> heap uaf -> can change pc directly
printf "rsa" | LD_LIBRARY_PATH=/usr/local/lib openssl -> memory leak 128bytes
bomb zz lol

애초에 openssl이 한번의 execution으로 multi command를 처리하려고 만들어진 것도 아니긴 한데 시간나면 fuzzing좀 돌려서 CTF에 내봐야 겠다 ㅋㅋ (어차피 코드 양이 별로 안커서 manual audit해도 상관 없을 듯)

요즘 너무 바빠서 일말고 암것도 못하고 있다 ㅜㅡㅜ 으쌰으쌰 파이팅

CTF 문제 풀다 해킹당한 이야기

tl;dr

  1. CTF 문제 풀기 위해 redis bind를 0.0.0.0으로 설정했는데 풀고 난 후 까먹고 돌려놓지 않음.
  2. module upload하는 exploit으로 서버 털림
  3. 리얼 월드  Incident Response/Investigation을 내 서버에 해봄 ㅋㅋ

발단

연휴에 아마존 형님으로 부터 한 통의 메일을 받았다. 대략 내용은 네가 갖고 있는 서버가 다른 서버를 공격했다, 네가 한게 아니라면 해킹 당했을 확률이 높으니 확인해봐라.. CTF 문제 Exploit들이나 PoC 테스트할 때 AWS 인스턴스에서 보낸적이 많으니 대수롭지 않게 넘어갔다.

asdf

하지만,, 집착의 아이콘 베조스 형님 답게 ** SECOND NOTIFICATION ** 알림과 24시간 내에 답장할 것을 요구하고 있다.

전개

인스턴스의 Public IP를 자세히 보니 내 개인 서버인 app.imjuno.com 주소인 것을 확인했다. 해당 서버는 ssh public-key 인증을 사용하고 별다른 서비스를 열어둔게 없어 오탐이겠지 생각하고 대충 티켓을 마무리 지으려고 했다.

대충 공격 벡터는 ssh login 밖에 없을 것이라 생각해 private key가 털린 것 같다고 중요한 파일들은 모두 백업하고 다른 key-pair를 이용해 인스턴스를 만들겠다고 답장을 보냈다.

그랬더니 앞으로의 다짐을 메일로 써서 보내라고 한다. ㅠㅜ

3줄 이상 써야할 것 같아서 열심히 private-key 보안을 철저히 하고, ubuntu 계정으로 sudo 커맨드를 사용할 때 password를 사용하게 변경할 것을 약속했다.

근데 여기서 궁금증이 생긴게 어떻게 내 서버에서 공격 패킷이 나갔냐는 것이다. 우선 어떤 공격이 진행 됐는지 확인하기 위해 메일 하단에 첨부된 로그를 살펴 보았다.

payload_class를 보면 exploit:gen/docker_unauth_rce 이런 내용이었다. payload_data를 base64 디코딩 해보면, docker remote API의 접근제어가 걸려있지 않은 서버를 대상으로 특정 entrypoint를 실행시키는 exploit인 것을 확인할 수 있다.

d.sh 를 받고 실행 하는데 어떤 행위를 하는 쉘 스크립트일까? (https://gist.github.com/junorouse/453435135782e4d1c4a4f83b95cadc03)

kinsing이라는 바이너리를 특정 서버 or bitbucket(해당 repo는 지금 삭제됨) 에서 다운로드 후 실행을 하는 스크립트다.

사실 이때까지만 해도 AWS가 오탐했다고 생각했다. 내 서버는 절대 털릴일이 없을 것이라는 무언의 자신감 때문이었을까? (ㅎ.ㅎ)

위기

해당 바이너리를 분석하기 시작했다. golang으로 만들어진 바이너리 였고 스트립 되어있었다. golang 특성상 심볼을 모두 복구할 수 있기 때문에 대충 어떤 프로그램인지 알아낼 수 있었다. 그러던 중 C&C 서버로 보이는 URL을 찾아냈다. 혹시 몰라 해당 URL을 구글 검색하니 매우 귀중한 자료를 찾을 수 있었는데,

C&C 서버로 보이는 URL

https://www.alibabacloud.com/blog/new-outbreak-of-h2miner-worms-exploiting-redis-rce-detected_595743 // h2miner worm의 C&C고 redis exploit을 사용한다고 나와있다. redis exploit 뿐만 아니라 다른 공격 방법도 사용한다고 분석 됐는데 그 중 Docker Remote API Unauthorized RCE 라는 공격이 AWS가 내게 리포트 해준 exploit이다.

흠… 설마 설마 하고 redis 로그를 확인 했다.. 해당 공격 방법은 다음 블로그 참고. (https://blog.wooeong.kr/2020/05/ssrf-to-redis.html)

절정

이모티콘] 네이버 라인 이모티콘 모음 - 문페이스편 : 네이버 블로그
11980:S 23 Mar 04:38:19.877 * Module ‘system’ loaded from /tmp/exp_lin.so
11980:S 22 Mar 14:19:02.050 * Module ‘system’ loaded from /tmp/exp_lin.so
11980:S 22 Mar 02:32:03.184 * Module ‘system’ loaded from /tmp/exp_lin.so
11980:S 20 Mar 08:09:21.899 * Module ‘system’ loaded from /tmp/exp_lin.so
11980:S 18 Mar 12:53:46.233 * Module ‘system’ loaded from /tmp/exp_lin.so
11980:S 16 Mar 22:55:49.485 * Module ‘system’ loaded from /tmp/exp_lin.so
11980:S 16 Mar 01:30:13.683 * Module ‘system’ loaded from /tmp/exp_lin.so
11980:S 14 Mar 20:26:21.484 * Module ‘system’ loaded from /tmp/exp_lin.so
11980:S 13 Mar 11:11:22.432 * Module ‘system’ loaded from ./red2.so
11980:S 13 Mar 02:06:53.651 * Module ‘system’ loaded from /tmp/exp_lin.so
좀 많이 털림..

ㅋㅋㅋㅋㅋㅋㅋ A&D CTF 마냥 42번이나 털린 것을 확인했다. redis가 0.0.0.0으로 bind 되어있어야 해당 공격을 수행할 수 있는데 진짜 0.0.0.0으로 바인드 되었나 의심스러워 내 서버를 포렌식 하기 시작했다.

03월 07일 22:44 (KST)에 변경되었다!

자, 파일 내용을 확인해보면 … bind 0.0.0.0설정이 적용된 말도 안되는 상황을 맞닥뜨렸다. 띠용 띠용 띠용 상태라 왜 내가 저 설정으로 해놨는지 생각하기 시작했다.


추가로 아래에 있는 protection mode는 0.0.0.0 바인딩 되었을 때 뭔가를 보호해주는 것이 아니라 아무런 bind 설정을 하지 않으면 *:6379로 바인딩 되는데 해당 상태 때만 리모트 인터페이스에서 접근하지 못하게 하는 친구다.

$ ps aux | grep redis-server
redis 16416 0.0 0.3 51660 3756 ? Ssl 07:52 0:00 /usr/bin/redis-server *:6379
——
$ nc 13.124.191.129 6379 -v
Connection to 13.124.191.129 port 6379 [tcp/*] succeeded!
-DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command ‘CONFIG SET protected-mode no’ from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to ‘no’, and then restarting the server. 3) If you started the server manually just for testing, restart it with the ‘–protected-mode no’ option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.

해당 시점은 dreamhack 강의의 실습 문제를 만들고 있었을 때랑 상당히 겹친다. 03월 05일 쯤 Redis에 Protection Mode라는게 생겼다고 신기해 하고 있는 모습이다. (ㅋㅋ)

다만 실습 문제를 다 만들고 binding을 다시 해제 하지 않을 만큼 멍청한 나 자신이 아니기 때문에 믿지 않았다. 문득 그러다 CTF 문제를 풀기 위해 0.0.0.0으로 바인딩 해놨을 것 같아,, 해당 시점에 열렸던 CTF 목록을 찾아보기로 했다.

그렇다. zer0pts를 참여했는데 redis 관련 문제가 나왔던 것을 기억해 대회 중 대화했던 메시지 로그를 찾아보았다. app.imjuno.com 6379에 migrate를 해 flag를 뽑아낸 exploit 코드를 확인할 수 있다…..

이모티콘] 네이버 라인 이모티콘 모음 - 문페이스편 : 네이버 블로그

털린건 확실하고,,, 그럼 이제 어떤 자료들이 털렸나 확인을 해봐야 한다. 우선 홈 폴더인 /home/ubuntu는 755 퍼미션으로 설정되어 있고 몇 개의 중요 자료들이 들어있다. (exploits / poc / private keys 등)

다행히 systemd service들은 namespace를 이용한 sandboxing을 지원한다. redis service 파일을 확인해 보면 여러가지 Protect* directive가 true로 설정되어 있는데,

systemd 사랑해요

실제로 mountinfo를 확인해보면 /home/ 은 접근할 수 없는 폴더로 마운트 되어있다.

이모티콘 중 가장 불신의 이모티콘.jpg - 스퀘어 카테고리

휴휴, 그럼 이제 /var/www/html 웹 폴더인데 아쉽게도 systemd는 해당 폴더를 분리해놓지 않았다. mountinfo로 확인해볼 수 있지만 github에 있는 익스플로잇 코드를 통해 실제 interactive shell을 획득해 ls -al 명령어를 입력해보았다. 예상대로 /home 은 접근할 수 없지만 /var/www/는 목록 뿐만 아니라 내부의 파일 또한 대부분 755로 설정되어 있어 모두 열람이 가능했다. ㅠㅜㅠㅠㅜㅠㅜㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

해당 폴더에는 몇 개의 발표 자료와 CTF 문제 풀이를 위한 exploit들이 들어 있다. ㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅡㅡㅜㅜㅜㅜㅜㅜㅜㅜㅠㅜ

systemd가 걸어둔 restrictions 때문에 LPE 벡터도 매우 적고 WORM 바이러스라 내부에 존재하는 파일들을 빼갈 것 같지는 않지만 혹시 모르니 서버에 존재하는 모든 private key(대부분 git repo의 release용 read-only key)를 유출되었다고 가정하고 revoke 과정을 거쳤다. 문제는 해당 서버에 집에 있는 PC를 키기 위한 WOL 스크립트가 있는데 집 공유기의 password가 평문으로 저장되어 있다. revoke 시키던가 펌웨어 wipe하고 재설치 하는 과정이 필요할 것 같다.

결말

실제 redis log파일은 https://drive.google.com/file/d/1ryiVDOwSSQWqyn2H5UIO1kYXd9ro3efX/view?usp=sharing 에 올려두었다. 아래 스크립트를 통해 로그 파일은 분석해보면 65번의 공격이 있었고 47번 성공했고 파일 경로를 잘못 올리는 등의 문제로 18번은 실패했다. 공격이 들어온 IP를 분석해보면 대부분 클라우드를 사용했고 DNS Resolve가 되는 IP들이 있는걸 보아하니 2차 공격을 행한 서버도 있는 것 같다.

교훈

  1. CTF 문제 풀 때는 새로운 인스턴스를 생성하고 하자.
  2. AWS 메일은 열심히 읽어보자.
  3. 꺼진불도 다시보자…
  4. https://dreamhack.io/learn/1/15#54
네이버 라인 스티커 (천송이 이모티콘) 다운 : 네이버 블로그
files = [
    'redis-server.log',
    'redis-server.log.1',
    'redis-server.log.2',
    'redis-server.log.3',
    'redis-server.log.4',
    'redis-server.log.5',
    'redis-server.log.6',
    'redis-server.log.7',
][::-1]

attacker_ips = []

def analyze(f):
    data = ''
    with open(f) as _:
        d = _.read()

    flag = False
    ips = []

    for x in d.strip().split('\n'):
        if 'SLAVE OF' in x:
            try:
                z = (x.split('SLAVE OF ')[1].split(':')[0], x.split('addr=')[1].split(':')[0])
            except:
                z = None

            if z is None:
                continue

            ips.append(z)

            for k in z:
                attacker_ips.append(k)

            flag = True

        if 'Module' in x and ('failed to load' in x or 'loaded from' in x or 'not loaded' in x):
            assert(flag == True)
            ips = set(ips)
            print ips
            print x
            print '--->',
            print 'Success' if 'loaded from' in x else 'Fail'
            flag = False
            # assert(len(ips) == 1)
            ips = []
            print '---------------------------------------\n'




if __name__ == '__main__':
    for f in files:
        print '************* %s *************' % f
        analyze(f)
    
    attacker_ips = set(attacker_ips)

    from pprint import pprint

    pprint(attacker_ips)