Orbit - Safebrowsing for Tor network

Orbit: Safebrowsing for Tor network

그냥 문뜩 그런 생각이 들었다.

1
왜 꼭 Tor network를 범죄와 연관을 지어서만 생각해야 할까?

물론, 이전의 darklight 에서도 봤다시피 많은 범죄 관련 데이터를 찾을 수 있긴 했다.

그래도. 아주 그래도 극소수의 사용자들은 본래의 목적인 익명 보장과 검열 우회를 위해서 사용하진 않을까? 라는 생각을 했고 이런 곳에서 Tor 버전의 safebrowsing 같은 것이 있으면 좋지 않을까 해서 시작하게 되었다.

뭐.. 원래 만들어 놨던 엔진의 구조 개선도 좀 필요해서 겸사겸사 이 프로젝트도 같이 껴서 하게 되었다.

Enhancement

우선, 제일 필요한 구조 개선이 필요했다.

백수가 되면서 학교로 돌아 가면서 가장 큰 문제는 정기적인 수입이 없다는 점 이였다. 이 떄문에 원래 운영하던 개인 서버에 많은 변화가 생기게 되었고 최소한의 리소스를 효율적으로 사용해야 했다.

이전에 만들어 둔 엔진은 Python 기반으로 작성된 아주 옛날 버전이였고, multiprocessing을 통해서 worker를 관리했다. 많은 사람들이 알다시피 Python 의 GIL 때문에 threading 방식이 우리가 아는 일반적인 thread가 아니기 때문에 여러개의 프로세스를 마치 thread처럼 띄워서 쓰는 방식을 사용했는데 나는 이 방식을 매우 싫었다.

그러던 중에 아는 분으로부터 go 언어에 대해 알게 되었다. 특히, goroutine 이라는 것을 알게 되었는데 이 부분을 듣자마자 도입하지 않을 수 없었다. 마치 내가 꼭 필요했던 부분을 해결 해 주는 친구가 나타난 기분이랄까?

여튼, 그 이후로 go에 대해 공부하게 되었고 darklight 엔진을 개선시켜 orbit 이라는 새로운 엔진의 이름으로 만들었다.

Domains

기존에 여러 프로젝트를 통해 모아뒀던 약 10만개의 Onion 도메인이 있었다.

근데.. next generation onion services (“v3”) 가 나오게 되면서 많은 것들이 바뀌었다.. prop224 참고

사실상 리소스 문제로 새로운 Onion 도메인 수집을 중단 상태로 두었지만, 이를 다시 살리고 업데이트 된 source를 추가해야 했다. 기존의 가지고 있던 도메인은 이미 죽은 경우가 많기 때문에..

또한, 데이터베이스에 v2와 v3를 구분하는 컬럼을 추가해야 했다. 그래도 다행인 점은 그냥 길이를 통해 구분할 수 있어 그리 어려운 작업은 아니였으니 SQL 쿼리문을 짜서 돌리니 생각보다 금방 해결 되었다.

Orbit

Orbit 이라는 이름으로 정하게 된 이유는 아래와 같다.

  • 이름이 마음에 들어서

    • 뭔가 저궤도위성처럼 계속 모니터링 하는 이름과 비슷한 느낌이라서
  • 이달의 소녀 (LOONA) 의 공식 팬덤 이름이라서

    다들 다음 장 보기 전에 영상 하나 보고 가도록 하자.

    LOONA History

여튼 darklight 의 코드 에서 Orbit 으로 이전하기 위해 큰 뼈대만 go로 포팅하고 세부적인 것을 새로 짜는 리모델링 수준의 작업을 진행 하였다.

기존 상황보다 더 열악한 환경에서 운영해야 했고, 시간과 공간 복잡도를 최소화 하기 위해 로직의 많은 부분을 신경썼다.

Tor Proxy

기존과 크게 달라진 부분은 없었다. 원래는 HTTP Tunneling 으로 좀 더 쉽게 proxy에 접근할 수 있도록 하려고 했는데 퍼포먼스 테스팅을 해보니 터널링 해 주는 과정이 별도로 필요해서 그냥 socks 프로토콜을 사용했다.

그 대신 기존의 torproxy 이미지를 사용하진 않았다. 이 이미지는 너무 옛날에 작성한 이미지여서 불편한 점이 너무 많았다. (포트 지정이라던지.. 이미지의 크기라던지) 그땐 그게 큰 문제가 될지 몰랐는데 단 1메가라도 아끼고 싶어서 상대적으로 엄청 가벼운 alpine 이미지를 사용하였다.

결과적으로 기존 75.27MB 의 이미지에서 20.7MB로 1/3 수준으로 줄일 수 있었다.

Interceptor

이번 프로젝트의 주요 목표는 “단 한번의 요청으로 최대한 많은 정보를 가져오자” 였다.

이를 위해 Chrome 브라우저에서 제공하는 DevTool을 많이 이용 하였는데 해당 도구에서 가져올 수 있는 모든 정보에 대해 interface를 만든 뒤 실제로 작업할 때 해당 interface를 이용하는 방식을 택했다.

생각보다 DevTool 에서 제공하는 기능이 많아서 이를 이용해서 기존 darklight 에서는 제공하지 않았던 다양한 정보들을 가져올 수 있었다.

Patterns

탐지에 제일 중요한 부분이였다.

내가 탐지 룰을 만들 수 있는 시간과 능력이 없기 때문에 외부에 공개된 패턴을 많이 가져다가 사용하였다. 거기에 일부 불필요한 패턴이나 중복되는 패턴을 제거하고 탐지명을 일원화 시키는 과정을 진행 하였다.

  • Microsoft
  • Fireeye
  • Crowdstrike
  • US CERT
  • Rapid7
  • Trend Micro
  • Palo Alto

대표적으로 저 위에 있는 목록의 회사, 단체들에서 공개한 패턴을 사용했으며 그 이외에도 최대한 다양한 기업들의 패턴들을 사용하고자 노력했다.

사실 원래는 내가 일일이 만들고자 했는데 시간적인 한계가 분명히 존재했고, 내가 만든 패턴으로 탐지하게 되면 그 신뢰성에 대해서도 문제가 생길 가능성이 있어 최대한 믿을 수 있는 그런 패턴들을 사용하기 위해 저런 방법을 사용했다.

그래도 나중에 시간이 되고 관심이 더 생기면 내가 패턴을 넣을 수 있도록 미리 작업 자체는 해 두었다. (근데 할까?)

Coverage Test

이번 프로젝트부터 코드 커버리지를 검사하기 시작했다. 나중에 코드 베이스가 더 커지게 되거나 새로운 아이디어를 적용할 때도 수치로 딱 보여줄 수 있는 정보가 있어서 객관적인 지표로 사용할 수 있었다.

원래는 개인 프로젝트에선 테스트 코드만 좀 짰었는데 이번에 처음으로 개인 프로젝트에도 커버리지를 도입하게 되었다.

coverage

물론 처음에는 별로 안좋았지만 커버리지가 느는 것을 보면서 개인적으로 만족하기도 했다.

저기서 보이는 커버리지 100%는 사실 테스트 목적으로 코드를 별로 안짜서 그런 것이다. 기능이 점점 늘어나니 반대로 줄어드는 모습을 보였지만 그래도 저 나머지 부분은 짤 수 없는 부분이라 어쩔 수 없는 부분이라..

여튼 나중에 더 개선해서 커버리지를 70% 이상까지 늘려 볼려고 한다.

Performance

위의 작업들을 모두 한 결과 기존 darklight 엔진 대비 새로 개발한 엔진에서 약 40% 의 스캔 속도 개선이 있었다.

여러번 테스트를 더 해보면 다른 결과가 나올 수 있었지만 go에서 제공하는 channel, goroutine 같은 기능을 나름 잘 사용한 것 같아서 기분은 좋았다.

그리고 기존에 도메인을 각 process로 넘기는 과정을 통해 뜻하지 않은 overhead가 있었지만 그 부분도 go언어로 넘어오면서 해결되었다.

Release

2021년 7월 18일 - Alpha version release (v0.5)

2021년 7월 26일 - Initial release (v1.0)

현재까지 1일 평균 3-4건 탐지 (최소 0건, 최대 6건) 건수를 보여주고 있다.

지금 Orbit을 운영하는 환경은 vCPU 2Core에 RAM 4GB를 다른 서비스와 공유하여 사용하고 있다. (돈이 없어요)

사실 높은 건 수를 보여주는건 아니지만 그래도 개인적으로 분석하는데에는 유의미한 건 수를 보여주고 있는 것 같다. (너무 많으면 오히려 오탐인지 확인하기 어렵다 ㅠ)

Open Source

저번 darklight 과 달리 Orbit 은 오픈소스로 하진 않았다.

아직 개선해야 할 점도 많고, 오픈소스 프로젝트로 관리 할 수 있을 자신이 없기 때문이다.

대신, 간단하게 검색을 해보고 싶으신 분들을 위해 GraphQL API를 만들어서 공개하였다.

GraphQL IDE: https://orbit.namjun.kim

GraphQL: https://orbit.namjun.kim/graphql

이렇게 GraphiQL을 따로 오픈한 이유는 쉽게 테스트 해 보면 좋을 것 같아서이다.

graphql

대신, 서버 한계때문에 1시간에 100건 이상 요청할 수 없도록 제한을 걸어 두었다. 그냥 이런 것이 있다 정도만 보는 용도로 사용 하도록 하자.

API Reference

Swagger를 통한 API Reference도 좋지만 이렇게 블로그 글에 담아 두는것도 좋을 것 같아서 한번 써 봤다.

Queries

searchByUrl (Onion URL을 검색하여 탐지 이력이 있는지 확인)

Type: Record!

Arguments

Name Type Description
url String 탐지 이력을 확인 하고자 하는 URL

downloadSample (샘플 다운로드)

Type: Sample!

Arguments

Name Type Description
id Int 탐지 ID

Objects

Record (탐지 이력)

Name Type Description
Id String 탐지 ID
time String 탐지 시간
url String 탐지된 Onion URL
tags String 탐지명 (delimiter: “,”)
sample_file String 샘플 파일의 SHA256 해시 값

Sample (샘플)

Name Type Description
  String base64 인코딩 된 샘플 파일

Conclusion

생각보다 많은 샘플을 얻을 순 없었고, 분석하기엔 본업이 아니라 할 시간이 많지 않다.

그래도 시간이 나거나 심심하다면 한번쯤은 분석해 봐도 좋을 것 같다.

그리고 더 시간이 남는다면 저기 샘플 중 하나를 분석해서 올려 볼 수도 있다. (언젠가?)