웹 악성코드 탐지 플랫폼 만들기 - 1편 개요

오랫만에 외장하드를 뒤져보면서 8년전에 만들었던 악성코드 탐지 로직이 생각나 만들어 보려고한다.

 

이전에 코드를 되돌아보는 포스팅을 했었는데 너무 조잡해서 귀찮으니 그냥 한번더 만들어보기로 결정했다.

 

사전 정보 수집

 

악성코드 탐지 로직을 만들었을 당시 악성코드 유포의 주요 키포인트는 아래와 같다.

1. 대부분 웹 사이트 페이지가 시작되는 랜딩페이지에 심어진다는 것

2017년 10월 10일 네이버 피싱 악성링크 탐지 기록 중

 

2. js파일이나 iframe또는 리다이렉션으로 외부링크에 연결된다는 것

2017년 7월 23일 특정 홈페이지에 악성링크 확인
2017년 7월 24일 경유지 확인, js파일의 내용은 counter였다.

 

3. 호스팅 보안이나 기타 외적인 수단을 피하기 위해 인코딩 또는 난독화를 한다는 것

스페이스 탭, 웹하드 사이트에 자주 출몰하였다 (미*디스크, 케* 디스크 등등...)

 

4. 특수한 경우이나 특정 페이지에 들어가야만 악성링크가 로드되는 것 (sub web page)

애석하게도 남겨놓은 자료가 없다...

 

5. 놀랍게도 호스팅 전체에 영향이 가는수도 있다. (퍼미션 미설정)

국내 호스팅 나야*를 통한 대규모 유포

 

악성코드 유포는 아래와 같은 모습을 보여준다

유포지 (선량한 웹사이트) -> 경유지 (해커가 심어놓은 악성링크) -> 최종지 (exploit 또는 피싱 사이트로 연결)

네이버 로그인 피싱 사이트

CK Exploit Kit (Gongda Pack 변조)

CK Exploit Kit 5.29 (변수명이 유명 자동차 브랜드이름이다)

악성코드 유포가 지금은 트렌드가 어떻게 되는지 정확히 모르지만.. 이전에는 counter링크를 유포예정지에 하루 또는 한달까지 심어놓고 규모를 조사하는 치밀함도 보였다.

2018년 7월 20일 카운터 링크 삽입 모습

 

특이 사항 정리

 

위와 같은 과거 동향을 살펴본 결과를 간단히 정리하면 아래와 같다

  • 국내에서 사용하지 않을법한 링크들이 있다 (카운터 링크)
  • 랜딩페이지에서 해당 웹사이트 도메인과 전혀 관련 없는 외부링크가 걸려있다
    (외부링크는 jquery와 같은곳도 있지만..)
  • 난독화 또는 인코딩을 한다
  • Exploit kit에는 보통 웹페이지에서 사용하지 않는 특정 패턴이 있다. (마세라티... 페라리...부가티...?)

 

추가로 알아두면 좋은건 악성코드를 넣는 해커들의 특징이 몇 가지 있다.

  • 유포했던 악성링크를 재사용 한다 (1년전에 사용한 링크를 다시 사용하는 경우도...)
  • 최종지에서 Drive-by-download하는 payload는 파일 해시가 변경될 수 있다 (백신 탐지를 피하기 위함)
  • 위에서 보여준 난독화 또는 인코딩 방식에서 크게 벗어나진 않는다.

 

대충 로직은 어떤식으로 짜야할지 감이 잡혔으니 하드웨어를 준비해보자

 

하드웨어 준비

 

사용할 스크립트 서버 사양은 아래와 같다

출처 : https://core-electronics.com.au/guides/raspberry-pi-5-vs-raspberry-pi-4-model-b-comparison-and-benchmarking/

필자는 라즈베리파이 4 8GB 모델을 선택하였다.

좋은 퍼포먼스를 내고싶다면 좋은 사양의 하드웨어를 선택하는 것 또한 나쁘지 않다.

 

추가로 external drive 500gb를 따로 탑재하였고, 해당 외장 하드에 공간은 데이터베이스에 데이터 미러링용으로 사용할 것이다.

(SD카드의 용량이 적다보니 스케줄링을 통해 데이터베이스를 수시로 비워줘야할 것 같다)

 

구지 외장하드에 저장을 하지않는 이유는 파일의 I/O가 혹여나 느릴까 노파심에 주기적으로 스케줄러를 돌려서 로우데이터와 디비덤프를 백업하고 로컬위치에서 로우데이터와 디비를 비워주는 식으로 환경을 생각하고있다.

 

실제 쓰기 속도를 기준으로 보면 USB 3.0보다 SD카드의 쓰기 속도가 월등한 것을 볼 수 있어, 읽기속도도 차이가 날 것이라 생각한다.

 

root@rsrv-testlab:/var/tmp# dd if=/dev/zero of=./test.file count=1024 bs=1M
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 2.03182 s, 528 MB/s

root@rsrv-testlab:/var/tmp# dd if=/dev/zero of=/mnt/ext_disk/test2.file count=1024 bs=1M
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 3.79874 s, 283 MB/s

 

소프트웨어 준비

 

OS는 라즈비안을 기준으로 root권한의 환경에서 스크립트를 짤 것이고, bash 스크립트로 진행할 것이다.

의존 패키지는 아래와 같으며, 다른언어를 사용하겠다고 하면 다른언어를 사용해도 좋다. 그 만큼 본 스크립의 로직은 굉장히 범용하게 만들 것 이고 모듈로 나누어 각 기능을 로드하는 형태로 구현할 예정이기 때문이다.

웹 프론트 대시보드 제작

프론트나 퍼블리싱은 젬병이라 ChaGPT가 해결해 줄 것이다.

 

데이터베이스 설계

 

대충 데이터베이스를 그려보자면 필요한 테이블은 아래와 같다

uri_crawl (데이터베이스) (하위 테이블)

 -> imp_domains : 크롤링 할 도메인 목록

 -> imp_src_patterns : 웹 파일 로드 패턴 등록 (script src, iframe src 등등...)

 -> imp_detect_patterns : 탐지 패턴 등록 (정규식이나 쿼리에서 영향을 받을 수 있기 때문에 Base64로 인코딩하여 등록)

 -> mon_uris : 악성링크 데이터

 -> logic_analyzer : 탐지 패턴에 따른 분석 스크립트 분류

 -> rec_connection : 접속 기록 (domain, uri, ip address, Hash 등등..)

 -> rec_sub_connection : 파싱한 주소 접속 기록

 -> rec_parsing_domains : 파싱한 주소의 도메인 기록

 -> rec_parsing_uris : 파싱한 주소의 전체 주소 기록

 -> rec_detected_uris : 패턴 탐지가 된 uri 기록

접두사는 각 imp(import), mon(monitor), logic(script logic), rec(record)라는 별칭을 지어주었다.

추후에 로직에 따라 테이블이 추가될 수 있다.

 

스크립트 로직

 

사실 뭐... 복잡하지는 않다. 조금 장왕하게 시퀀스를 보여주자면 아래와 같이 할 수 있다.

uris crawl Logic

 로직을 정말 못그렸지만 간단히 설명하면 1번 루프는 현재 도메인에 대한 루프, 2번 루프는 현재 도메인에서 링크를 파싱을 위한 루프, 3번 루프는 현재 도메인에서 파싱한 링크에 대해 분석하는 루프로 나눌 수있다.

 

imp_domain에 미리 입력한 도메인을 불러 첫 번째 도메인으로 1번 루프를 시작한다.

시작된 루프는 하위 도메인을 계속 파싱하기 위해 2번 루프를 실행하여 데이터를 지속적으로 기록하는데, 파싱과 동시에 현재 호스트와는 다른 호스트가 있으면 imp_domain 테이블에 추가해 준다.

한번 파싱을 시작하면 페이지가 끝날때까지 파싱을 계속하기 때문에 택1, 택2로 나뉘어 사용자 설정에 의해 로직이 실행된다.

 

택 1

 - 가능한 많은 곳을 들어가 많은 주소를 파싱한다. 

파라미터가 있는 주소는 파라미터를 제외하고 수집하기 때문에 루프 이후 파마터가 없는 경우 완전한 페이지를 보여주지 못하므로 파싱을 더 이상 진행하진 않을 것 이다 (ex 추출한 링크 dogdrip.net/board.php?no=1 -> 인자 제외 dogdrip.net/board.php)

파라미터를 빼면 완전한 페이지를 보여주는게 아니기 때문에 오류가 생길 수 있지 않는가? 라고 할 수 있는데, 앞서 설명한 사전 정보 수집에서 4. 특수한 경우이나 특정 페이지에 들어가야만 악성링크가 로드되는 것 (sub web page) 은 정말 특수한 경우로 치기 때문에 예외로 두기로 한다

 

택 2

 - 사용자가 지정한 루프만큼만 주소를 파싱한다

이는 택1과 반대로 파라미터가 있는 주소까지 수집하여 모두 파싱하기 때문에 한번 잘못 들어가면 계속 들어갈 우려가 크다.

홈페이지를 분석해야하는데 홈페이지 전체를 파싱하느라 허송세월을 보낼 수 있다는 말이다 (ex 나무위키, 커뮤니티 사이트)

하지만 이미 파싱한 링크는 웹페이지를 이루는 부분 중 설정한 탐지 정책에 부합하면 다시 접속하지 않게 할 수 있다.

*탐지 정책
 중복된 페이지에 파싱을 계속 시도하는 것을 방지하여 아래와 같은 조건의 데이터가 이미 디비에 입력된 상태라면 보지 않는다
예를 들어, dogdrip.net/board.php?no=202&id=48275728를 처음으로 접속하여 파싱한 내용이 아래와 같다고 가정하자

dogdrip.net/home
dogdrip.net/style.css
dogdrip.net/board.php?no=201&id=48275727
dogdrip.net/board.php?no=200&id=48275726
dogdrip.net/board.php?no=199&id=48275725
...
dogdrip.net/board.php?no=185&id=48275710


이렇게 데이터를 저장 해 놓고 다시 같은 uri를 파싱했을 때 같은 이전과 같은 결과가 나오면 예외처리를 하는 것이다.
사실상 보기는 보지만 예외처리가 되었으니 해당 하위 주소에는 다시 들어가지 않고 새로운 uri가 발견되면 해당 uri만 새로 데이터를 넣어주고 새로운 uri만 들어가도록 해준다.

 

모든 하위 주소에 대한 파싱이 끝났으면, 해당 파싱 데이터를 가져와 3번 루프를 시작한다.

아래 시퀀스는 모든 파싱 링크에 적용된다.

1. 파싱 링크 접속
2 .악성코드 의심 uri 확인 -> 2.1 탐지 유/무 상관 없이 디비에 기록
3. 의심 패턴 확인 -> 3.1 탐지시 패턴에 맞는 분석 스크립트 실행 -> 3.2 분석 후 결과 전송
4. 다음 파싱 링크 접속 

위 내용은 파싱한 링크가 모두 끝날 때 까지 3번 루프가 돌게 된다.

3번 루프가 종료되면 다시 1번 루프로 돌아가 imp_domain을 새로 불러와 다음 도메인을 보게된다.

도메인이 더이상 파싱과정에서 추가되지 않으면 1번 루프를 끝내고 스크립트는 종료될 것 인데, 경험상 수억개의 도메인을 사이트를 돌면서 혼자 모으게되는 특성 상 한번 돌리면 최소 몇 달은 논스톱으로 돌아간다. 진짜 404페이지, 403페이지, host가 없어진 페이지를 연속적으로 만나지 않는 이상....

 

그래서 필자는 화이트리스트를 만들고 내가 보지않을 도메인은 별도로 도메인 추가를 못하도록 한 기억이 있다.

화이트리스트로 등록하여 관리하였는데 대부분 정말 답이 안나오는 커뮤니티 사이트 (디씨, 인벤, 위키 등) 이미 보안검증이 되어있는 외부 메이저 라이브러리 도메인 (jquery, threejs, API 등)은 예외로 하였었다.

로직에는 없지만 화이트리스트도 만들어서 추가하도록 하는걸로 하고 최대한 범용적이게 만들어 사용자 설정에 초첨을 둬볼 예정이다.

자 이제 다음편 부터 스크립트를 짜보도록 하자

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유