Skip to content

Latest commit

 

History

History
406 lines (268 loc) · 30.7 KB

02_ipinside_lws_agent.md

File metadata and controls

406 lines (268 loc) · 30.7 KB

IPinside: 대한민국의 필수 설치 스파이웨어

  • 🇰🇷 번역상태: 1차 검토/수정 중
이 글은 저자의 허락을 받아 영문으로 된 원문을 한국어로 번역한 글입니다.
번역에 참여하실 분은 기존에 생성된 issue를 참조하시고 필요시 새 issue 등록 후 작업 바랍니다.

번역 관련 정보


대한민국의 소위 보안 애플리케이션이라 불리는 것들을 살펴보는 여정 (번역판)을 시작하며 이미 TouchEn nxKey에 대해서 살펴보았는데, 이 애플리케이션은 키로거를 막기 위해 ... 노트를 뒤적이며 ... 키로깅을 더 쉽게 만들어 버렸다. 오늘은 대한민국에 거주하는 많은 사람이 컴퓨터에 설치해야만 하는 또 다른 애플리케이션을 조명해 보겠다. 그것은 인터리젠 (Interezen) 에서 만든 IPinside LWS Agent 이다.

이 애플리케이션의 명시된 목적은 사용자의 "실제 (real)" IP 주소를 확인하여 온라인 사기를 막는 것이다. 하지만 원래 용도보다 훨씬 더 많은 양의 데이터를 수집하는 것으로 확인하였다. 어떤 웹사이트든 격식을 갖춰 요청하면 많은 데이터를 제공하지만, 실제 사기를 막는데는 그다지 유용해 보이지 않는다.

목차

어떻게 동작할까?

TouchEn nxKey와 유사하게 IPinside LWS Agent 애플리케이션도 로컬 웹서버를 통해서 웹사이트와 통신을 한다. 한국의 은행 웹사이트가 접속자에 대한 정보를 더 얻기 위해 localhost:21300JSONP 요청을 보낸다. 이 요청이 실패하면, 은행 웹사이트는 접속을 거부하고 IPinside LWS Agent를 먼저 설치할 것을 요구한다. 그러므로 한국에서 이 애플리케이션의 실행 여부는 선택의 여지가 없다.

한편, 이 애플리케이션이 실행되고 있다면 웹사이트는 wdatandata와 udata 필드를 통해서 다양한 데이터를 받아 볼 수 있다. 사실 꽤 많은 데이터가 전송된다:

127.0.0.1:21300/?t=A&value= 주소로 접속한 브라우저 창의 화면 캡쳐. 응답은 jQuery 콜백으로 wdata, ndata 와 udata 필드를 포함한 여러 데이터와 base64 인코딩된 값들이다.

이 데이터는 당신의 IP를 포함하고 있을 것이다. 하지만 크기로 봤을 때, 그것만이 포함된 것이 아님은 분명하다. 실제로 훨씬 더 많은 데이터가 전송된다.

어떤 데이터를 포함하는가?

wdata

일단 가장 흥미로운 자료 구조를 가지고 있는 wdata 부터 살펴보자. 복호화하면 꽤 많은 양의 바이너리 데이터를 얻을 수 있다:

약간의 바이너리 데이터를 포함한 16진수 데이터 덤프. 다음 문자열이 눈에 띈다: QEMU Harddisk,  Gigabit Network Connection

결과물에서 보듯이, 가상 머신 내에서 IPinside를 실행중이다. 심지어 이 컴퓨터가 더 이상 VirtualBox 상에서 실행되고 있지 않음에도, 결과물 끝에 VirtualBox가 나타난다.

또 다른 명백한 데이터는 내 가상 머신에 장착된 두 개의 하드 드라이브로, 하나는 시리얼 번호가 QM00001이고 다른 하나는 abcdef이다. F0129A45는 주 하드 드라이브 볼륨의 시리얼 번호이다. 또한 Intel(R) 82574L Gigabit Network Connection으로 표시된 네트워크 카드 두 개의 정보도 확인할 수 있다. 키보드 모델(Standard PS/2 Keyboard)과 키보드 레이아웃(de-de) 정보도 있다.

그리고 자세히 살펴보면 다음과 같은 연속된 바이트가 보인다; c0 a8 7a 01 (내 게이트웨이 IP 주소인 192.168.122.1을 나타냄), c0 a8 7a 8c (192.168.122.140, 첫 네트워크 카드의 IP 주소) 그리고 c0 a8 7a 0a (192.168.122.10, 두번째 네트워크 카드의 IP 주소).

하지만 이보다 더 많은 정보들이 있다. 예를 들면 하드 드라이브 정보 전에 나오는 65(문자 e)는 GetProductInfo() 함수를 호출한 결과값이다. 이것은 내가 윈도우 10 홈 에디션을 사용하고 있는 것을 나타낸다. 그리고 그 전에 74(문자 t)는 내가 사용하는 정확한 윈도우 버전 정보를 저장하고 있다.

실행중인 프로세스에 대한 정보

한 가지 데이터가 특별히 더 흥미롭다. 어디서 firefox.exe 라는 데이터가 왔는지 궁금하지 않은가? 이것은 모질라 파이어폭스 프로세스가 백그라운드로 실행 중이라는 것을 나타낸다. 실제 사용하는 애플리케이션이 구글 크롬인데도 불구하고 이 정보가 전송된다.

웹사이트는 결과물의 내용을 정하는 여러 매개변수를 IPinside agent에게 전달한다. 그 매개변수 중의 하나는 winRemote이다. 간단한 난독화가 적용(obfuscated)되어 있지만 난독화를 풀고 나면 다음과 같은 데이터를 얻을 수 있다:

TeamViewer_Desktop.exe|rcsemgru.exe|rcengmgru.exe|teamviewer_Desktop.exe

이것을 보면 은행 웹사이트에서는 당신이 원격 접속 도구를 사용하는지 알고 싶어 한다. 만약 위 문자열에 일치하는 프로세스가 감지되면 wdata 응답에 추가된다.

물론 이 기능은 원격 접속 도구만 찾는 용도로만 제한되지 않는다. winRemote 매개변수를 AGULAAAAAAtmaXJlZm94LmV4ZQA=으로 대체하면 파이어 폭스의 현재 실행여부에 대한 정보를 받아올 수 있다. 그러므로 이 기능은 관심있는 어떤 애플리케이션이든 그것의 실행여부를 찾는데 악용될 수 있다.

이것이 전부가 아니다. IPinside agent는 부분 문자열로도 매칭을 한다. 그래서 fire라는 이름이 들어간 프로세스가 실행중인지 알려 줄 수 있다.

웹사이트에서는 이 기능만으로 실행 가능한 프로세스를 모르는 상태에서도 충분히 실행 중인 프로세서의 목록을 얻어올 수 있다. 나는 .exe 접미사로 시작해서 깊이 우선탐색 (depth-first search)을 하는 웹페이지를 만들었다. 여기서 문제점은 IPinside의 각 요청에 대한 응답속도가 0.5초 정도로 느리다는 것이었다. 성능을 약간 향상시키기 위해 여러 개의 추측을 하나의 요청으로 묶었고 이것을 바탕으로 40-50초마다 프로세스 이름을 찾아내는 개념 증명 (proof of concept) 페이지를 만들었다:

웹 페이지 스크린샷: "잠시만 기다려주세요, 프로세스 목록을 가져오고 있습니다… 접미사 테스트 중 oerver-svg.exe cortana.exe.” 이미 찾은 다음 프로세스들도 보여준다: i3gproc.exe asdsvc.exe wpmsvc.exe i3gmainsvc.exe

충분한 시간만 주어진다면 이 웹페이지는 시스템에서 실행되는 모든 프로세스를 알아낼 수 있다.

ndata

응답에서 ndata 부분은 훨씬 더 단순하다:

��HDATAIP=▚▚▚.▚▚▚.▚▚▚.▚▚▚��VD1NATIP=▚▚▚.▚▚▚.▚▚▚.▚▚▚��VD1CLTIP=192.168.122.140��VD2NATIP=��VD2CLTIP=192.168.122.10��VPN=2��ETHTYPE=ETH1

내가 데이터 복호화 과정에 실수를 한 것이 아니다. 가 실제 응답에 포함되어 있다. 원래 의도는  (reverse tilde 부호)를 구분자로 사용하려는 것인데, 내 운영체제에 한국어 설정이 되어 있지 않기 때문에 (IPinside LWS Agent와 같이) 유니코드를 사용하지 않는 애플리케이션의 경우 문자 인코딩이 EUC-KR로 설정되지 않는다. 애플리케이션이 이런 상황을 지원하도록 만들어지지 않아서 UTF-8로의 변환을 제대로 처리하지 못한다.

한편 ▚▚▚.▚▚▚.▚▚▚.▚▚▚는 내 IP주소를 숨기기 위해 직접 대체한 문자이다. 애플리케이션은 두 가지 방식을 사용해서 IP 주소를 얻는다. VD1NATIP는 내 집에 있는 라우터로부터 얻어온 듯 보인다.

하지만 HDATAIP는 웹서버로부터 온다. 어떤 웹서버일까? 그것은 웹사이트가 애플리케이션에 전달하는 host_info 매개변수로 결정된다. 이 값 또한 난독화 처리된 값이며, 실제 값은 다음과 같다:

www.securetrueip.co.kr:80:/vbank_01.jsc:_INSIDE_AX_H=

처음 두 부분만 사용되는 것처럼 보이며, 애플리케이션은 http://www.securetrueip.co.kr:80/androidagent.jsc로 요청을 보낸다. 응답 헤더의 내용 중 하나는 RESPONSE_IP이다. 여러분이 맞췄듯이 이것은 웹서버가 봤을 때의 접속자 IP주소이다.

여기서 애플리케이션은 로우 레벨의 WS2_32.DLL API를 사용하는데, 아마도 웹으로 전송되는 데이터가 프록시 서버나 VPN을 통하는 것을 방지하려는 듯하다. 결국 목표는 사용자를 비익명화(deanomymizing)하는데 있으니 말이다.

udata

마지막으로 udata가 있는데 “u” 는 “unique”의 약자다. 여기에는 몇 가지 다른 결과물 타입이 있으며, 아래는 타입 13이다:

[52-54-00-A7-44-B5:1:0:Intel(R) 82574L Gigabit Network Connection];[52-54-00-4A-FD-6E:0:0:Intel(R) 82574L Gigabit Network Connection #2];$[QM00001:QEMU HARDDISK:];[abcdef:QEMU HARDDISK:];[::];[::];[::];

역시 네트워크 카드들과 하드 드라이브의 목록이 보이는데, 이번에는 네트워크 카드의 MAC 주소도 목록에 포함되어 있다. 다른 결과물 타입은 대체로 유사한 데이터를 별도의 포맷으로 저장한 것이지만, 타입 30은 예외이다. 이것은 16바이트의 데이터로 16진수 CPU 식별자를 포함하여 15개의 서로 다른 CPUID 명령호출의 결과를 합쳐서 만든 것이다.

이 데이터는 어떻게 보호하는가?

따라서 사용자를 비익명화(deanonymizing)하고 해당 사용자가 쓰는 하드웨어와 소프트웨어를 알아내어, 시스템에 어떤 취약점이 있는지 노출시켜 잠재적인 추가 공격을 도울 수 있는 수많은 데이터가 있다. 이런 종류의 데이터는 잘 보호되고 있을까? 물론 한국의 모든 온라인 뱅킹 웹사이트는 이 정보에 접근할 수 있다. 정부 기관 웹사이트도 마찬가지고. 아마 많은 인터리젠 고객도 가능할 것이다. 하지만 그 외의 사람은?

localhost:21300에 있는 서버는 누구에게 응답하는지는 전혀 신경쓰지 않는다. 어떤 웹사이트든 데이터를 달라고 요청할 수 있다. 하지만 그 데이터를 해독(decode)하는 방법은 알고 있어야 한다.

wdata로 말할 것 같으면, 여기에는 난독화(obfuscation), 압축, 암호화라는 3가지 보호 계층이 있다. 그렇다. 고작 무작위 1바이트로 데이터를 XOR 처리하여 알아보기 어렵게 만드는 정도로는 데이터를 제대로 보호한다고 보기 어렵다. 그리고 압축도 실제 보호 효과가 없는데, 왜냐하면 인터리젠에서 사용 조건을 준수하지 않은 채 쓰고 있는 GPL 라이선스로 된 유명한 소스 코드를 다른 사람들이 쉽게 찾을 수 있기 때문이다. 그러나 마지막으로 암호화가 있는데, 심지어 여기에는 공개키 암호화 기술이 사용된다!

해당 애플리케이션 안에는 RSA 공개키만 포함되어 있기 때문에, 이것만으로는 데이터를 해독할 수 없다. 비밀키는 오직 인터리젠과 그들의 여러 고객사만 가지고 있다. 부디 이 모든 고객이 비밀키를 잘 지키고 있어서 해커에게 유출되지 않기만을 바랄 뿐이다.

비밀키가 유출되지 않는다면, RSA 암호화는 적당한 길이의 키를 써도 충분히 안전하다. 다만… 여기서 사용된 키는 적당한 길이의 키라고 할 수 없다. 심지어 약한 키가 사용된 것도 아니다. 여기서 쓰인 키는 고작 320비트 길이에 불과하다. 이건 첫 RSA 인수분해 대회(RSA Factoring Challenge)에서 사용되었던 키의 길이보다도 더 짧은 것이다. 그리고 그 대회는 30년도 넘게 지난 1991년 4월에 열렸었다. 정상적인 RSA 라이브러리라면 이렇게 짧은 키로는 아예 작동조차 하지 않는다.

역주: RSA 인수분해 대회는 1991년부터 2007년까지 열렸던 대회입니다. 첫 대회에서 가장 먼저 풀린 숫자는 RSA-100이라고 하는데, 십진수로는 100자리이고 이진수로는 330비트 길이의 숫자입니다. 이 숫자는 1991년 4월 1일에 인수분해되었는데, 관련 논문에 따르면 당시 사용 가능했던 미니슈퍼컴퓨터로도 며칠만에 풀 수 있었다고 합니다. 따라서 일반적인 개인용 컴퓨터나 심지어 스마트폰조차 당시의 슈퍼컴퓨터를 능가하는 연산성능을 보유하고 있는 2023년 현재 이 정도 길이의 키는 고작 수십 분 만에 풀리게 됩니다. 참고로 현 시점에서 RSA 키의 적당한 길이는 적어도 2048비트 이상이라고 합니다.

내 노트북에서 msieve를 다운로드하여 하나의 CPU 코어만 사용하게끔 실행했다.

$ ./msieve 108709796755756429540066787499269637…

sieving in progress (press Ctrl-C to pause)
86308 relations (21012 full + 65296 combined from 1300817 partial), need 85977
sieving complete, commencing postprocessing
linear algebra completed 80307 of 82231 dimensions (97.7%, ETA 0h 0m)
elapsed time 02:36:55

그렇다. 이런 기본적인 성능의 하드웨어에서 겨우 2시간 36분만에 비밀키를 계산해냈다. 여기서 사용된 RSA 암호화가 제공하는 보호란 것이 딱 그 정도 수준이다.

ndata와 udata를 살펴보면 더 끔찍하다. 여기에 적용된 유일한 보호 수단은 암호화이다. 아니, 공개키 암호화가 아니라 AES-256를 사용하는 대칭 암호화이다. 그리고 물론 암호화 키는 애플리케이션 내에 하드코딩되어 있고, 그 외의 다른 보호수단은 없다.

설상가상으로 이 애플리케이션은 실행할 때마다 동일한 암호문(ciphertext)을 생성한다. 나는 처음에 이것이 더 이상 사용되지 않는 ECB 블록 연쇄 모드를 사용했기 때문이라고 생각했다. 하지만 아니었다. 이 애플리케이션은 CBC 블록 연쇄 모드(CBC block chaining mode)를 사용한다. 하지만 암호화 라이브러리에 초기화 벡터(initialization vector)를 제대로 전달하지 못해서, 해당 라이브러리는 초기화 벡터를 항상 0으로 채운다.

앞에서 했던 긴 이야기를 간단히 말하자면, 이것은 애플리케이션에서 암호화 키를 추출할 수 있는지와 상관없이 그 암호화를 깨트릴 수 있다는 말이다.

요약하자면, 이 데이터는 실제로는 제대로 보호받지 못하고 있다. 어떤 임의의 웹사이트에서도 IPinside LWS Agent를 설치한 사용자의 정보를 수집할 수 있다. 여기에 적용된 암호화는 무용지물이다.

그러면 애플리케이션의 전반적인 보안은 어떨까?

21300 포트에서 실행되는 웹서버 애플리케이션은 어떤 것인가? 이것은 로우레벨 네트워크 소켓 기능을 사용해 자체 개발한 코드이다. 이 사실 만으로는 아무 문제가 없지 않은가? 누가 부분 문자열 매칭을 사용해 요청을 파싱하는 기초적인 웹서버를 만들어서 수백만 명에게 배포해본 적이 없겠는가?

이 웹서버도 여전히 SSL 지원이 필요하므로 OpenSSL 라이브러리를 사용하고 있다. 어떤 라이브러리 버전일까? 물론 OpenSSL 1.0.1j 버전이다. 그렇다, 이것은 8년 전에 릴리즈 된 것이다. 그렇다, 6년전에 OpenSSL 1.0.1에 대한 지원이 중단되었다. 그렇다, 1.0.1j 이후로 1.0.1 브랜치에 11번의 추가 릴리즈가 있었고 수많은 취약점이 수정되었지만 IPinside LWS Agent에는 하나도 적용되지 않았다.

역시 그 웹서버는 단일 쓰레드로 동작한다. 왜 그럴까? 사람들이 은행 웹사이트 2곳을 동시에 띄우지는 않을 테니까. 그렇다, 악성 웹사이트가 장시간 실행되는 요청(서비스 거부 공격)으로 해당 웹서버를 잠그는 것(lock up)은 어렵지 않다. 하지만 이는 사용자들이 온라인 뱅킹 및 정부 웹사이트에 접속하지 못하게 할 뿐 큰 문제가 되지는 않는다.

서버의 구현을 살펴보니 다음과 유사한 동작을 하는 코드를 볼 수 있다:

BYTE inputBuffer[8192];
char request[8192];
char debugString[8192];

memset(inputBuffer, 0, sizeof(inputBuffer));
memset(request, 0, sizeof(request));

int count = ssl_read(ssl, inputBuffer, sizeof(inputBuffer));
if (count <= 0)
{
  …
}

memcpy(request, inputBuffer, count);

memset(debugString, 0, sizeof(debugString));
sprintf(debugString, "Received data from SSL socket: %s", request);
log(debugString);

handle_request(request);

이 코드의 문제점을 찾을 수 있는가?
...

기다리고 있다. 빨리 찾아보아라.
...

물론 난 약간의 도움을 받았다. 난 여러분과 다르게 실제 코드를 디버깅했고 얼마나 안 좋은 결과들이 있는지 실시간으로 확인하였다.
...

먼저 ssl_read가 정확히 8192 바이트를 생성해 버퍼를 꽉 채우는 경우가 생길 수 있다. 이 경우, inputBuffer는 널 문자로 종료되지 않는다. 그리고 이것을 복사한 request도 마찬가지로 널 문자로 종료되지 않는다. 그래서 sprintf() 또는 handle_request()에서 request를 널 문자로 종료되는 문자열로 취급할 경우, 버퍼의 끝 부분을 초과해서 읽게 된다. 사실 메모리 구성을 보면 동일한 데이터를 가진 inputBuffer 메모리 영역을 읽고 무엇이든지 그 다음에 오는 데이터까지 읽어버린다.

그렇게 되면 sprintf() 는 16384 바이트 이상의 데이터를 읽을 텐데, 타깃 버퍼에 다 담기는 너무 크다. 만약 널 종료 문자가 있다고 해도, 8192 바이트의 문자열에 더 많은 문자를 추가한 후 그것을 8192 바이트의 버퍼에 넣을 수는 없다.

이것은 코드에서 발견한 단순 한 건의 오류가 아니다. 이 애플리케이션의 기능을 연구하면서 이외에도 여러 건의 스택 오버플로우나 버퍼의 범위를 벗어난 읽기를 발견했다. 내 (매우) 제한적인 바이너리 취약점 공격에 대한 이해를 바탕으로는 이 취약점을 사용해 원격코드 실행을 할 수는 없을 것이다. 고맙게도 StackGuard와 SafeSEH 기능이 효과적으로 동작하고 있어서 그렇다. 그러나 좀 더 경험이 많은 사람이 이것을 우회하는 방법을 찾는다면 문제는 매우 심각해질 것이다. 애플리케이션은 ASLR이나 DEP 보호가 사용되지 않고 있다.

하지만 이 취약점 중 일부는 확실히 애플리케이션을 죽일 수도 있다. 난 2개의 개념증명(proof of concept) 웹페이지를 만들어서 여러 차례 이 사실을 확인하였다. 이것은 또 다른 서비스 거부 공격으로 사용될 수 있고 사용자들이 대한민국에서 온라인 뱅킹을 사용하는 것을 막는 데에도 쓰일 수 있다.

언제 수정될까?

2022년 10월 21일에 취약점 3건을 KrCERT에 신고했다. 11월 14일, KrCERT에서는 이 신고 내용을 인터리젠에 전달했다고 회신하였다. 그리고 그 뒤로 아무런 소식도 듣지 못했다.

이번 취약점 공개 이전에 어떤 한국 기자가 인터리젠에 입장을 물어보았다. 그들은 내 취약점 신고를 받기는 하였으나, 2023년 1월 6일에 단 1건을 받았을 뿐이라고 주장했다. 아마도 그 때문에 오는 2월에 수정 버전을 출시할 예정이고, 그 시점에서 새 버전을 사용자에게 배포하는 것은 고객사(즉, 은행 등)에 달려 있다고 하였다.

다른 비슷한 애플리케이션과 마찬가지로, 이 소프트웨어에도 자동 업데이트 기능이 없다. 따라서 사용자는 수동으로 업데이트를 다운로드받아 설치하거나, 아니면 Wizvera Veraport와 같은 관리 애플리케이션을 통해 업데이트를 해야 한다. 하지만 은행에서 예전 버전의 IPinside 사용을 거부하고 사용자에게 업데이트를 강요하지 않는 한, 이 두 가지 모두 실현 가능성은 낮다.

과연 IPinside는 온라인 뱅킹을 더 안전하게 만들까?

인터리젠은 단순히 IPinside agent 애플리케이션만 제공하는 것이 아니다. 그들의 자기소개에 따르면, 인터리젠은 빅 데이터 전문 기업이다. 수많은 은행, 보험사, 정부 기관에 데이터 수집 및 분석 서비스를 제공하고 있다.

다음 제목의 웹사이트 섹션 스크린샷: “Client Companies. With the number one products in this industry, INTEREZEN is providing the best services for more than 200 client companies.” 그 아래는 우리은행, 산업은행, KEB 하나은행, 국세청, MG손해보험, 현대카드 로고 외에 "더보기" 버튼

역주: 위에서 링크된 인터리젠 홈페이지의 한국어 버전 페이지에서는 고객사 수를 “120여개”로 소개하고 있습니다.

2009년에 작성된, 인터리젠의 백엔드 솔루션 스크린샷을 보여주는 매뉴얼을 온라인에서 찾았다. 이걸 보면 웹사이트에 접속하는 모든 방문자와 그 데이터가 추적되는 것을 볼 수 있다. 2009년에는 IP 주소 이외의 정보는 별로 수집하지 않았지만, 현재 버전에서는 에이전트 애플리케이션이 수집하는 모든 정보를 다 보여줄 것이라고 추측할 수 있다.

역주: 2023년 2월 초 현재 위에 있는 매뉴얼 링크에 접속을 시도하면 HTTP 404 Not Found가 뜹니다. 아마도 매뉴얼 파일이 삭제된 것으로 보입니다.

특정 날짜 범위의 목록을 요청하는 웹 인터페이스 스크린샷. 테이블의 행 중 일부는 date, webip, proxyip, natip, attackip 등이다.
IPinside 3.0 제품 설명서 화면 캡쳐

이 애플리케이션은 2009년 시점에서 각 사용자의 상세 정보뿐만 아니라 IP 주소, 위치, 브라우저 및 운영체제 등의 정보를 바탕으로 한 통계 요약을 보여주는 것이 이미 가능했다.

다음 운영체제에 대한 사용자 정보 통계를 보여주는 웹 인터페이스의 스크린샷: Windows 98, Windows 2000, Windows 2003, Windows XP
IPinside 3.0 제품 설명서 화면 캡쳐

여기서 목표는 사용자를 보호하는 것이 아니라, 은행을 비롯한 인터리젠의 고객사를 보호하는 것이다. 은행이 더 많은 정보를 확보할 수 있으면 금융사기나 공격을 더 쉽게 탐지하고 차단할 수 있다는 생각인 것이다. 아무튼 사기꾼이 단순히 프록시나 VPN을 쓰는 것만으로는 신분을 위장할 수 없게 만들면, 은행에서 그들을 차단할 수 있을 것이다.

실제로 인터리젠은 이 아이디어로 한국 내에서 특허를 여러 건 출원했다. 첫 번째 특허인 특허 등록번호 10-1005093은 “클라이언트 식별 방법 및 장치”라고 되어 있다. 특허 명세서에 나온 “발명”의 배경은 다음과 같다.

불특정 다수를 대상으로 하는 인터넷 환경에서 클라이언트를 식별하는 방법의 중요성과 가치는 점점 증대되고 있다. 그러나 다양한 위장 및 은폐수단의 발전과 현존하는 식별 기술의 한계로 인해 제대로 된 식별과 분석이 현실적으로 매우 어렵다.

역주: 원문에서는 한국어를 기계번역하여 사용했으나, 위 특허의 제목 및 인용문은 해당 특허 문서에서 직접 가져왔습니다.

그 뒤로는 쿠키는 클라이언트 식별에 충분하지 않다며 사용자의 실제 IP 주소를 찾아야 하는 이유를 설명하는 내용이 이어진다.

“전자상거래 불법 침입 감시 및 차단 방법과 시스템”이라는 제목의 특허 등록번호 10-1088084에서는 앞에서 나왔던 논리를 다음과 같이 더욱 확장하고 있다.

 본 발명은 인터넷을 통한 모든 전자상거래서비스와 관련된 불법거래의 탐지/차단에 있어서 기존 보안시스템으로는 불가능했던 실시간 처리를 가능케 하는 기술과 기존 보안 기술로는 정상적인 거래로 판단될 수밖에 없는 전자상거래 불법거래까지도 탐지/차단하는 기술 및 서비스 업체의 정책에 따라 상기 탐지/차단을 운영할 수 있게 하는 기술을 제공하는 것이다.

역주: 위 인용문 역시 해당 특허 문서에서 직접 가져왔습니다.

또한 이 특허에서는 사용자가 웹사이트를 이용하려면 에이전트(agent)를 강제로 설치해야 한다는 개념도 도입하고 있다.

하지만 이러한 접근 방식이 효과가 있기는 할까? 사기꾼이 자체 웹 서버를 localhost:21300에 띄워서 은행 웹사이트에 가짜 데이터를 전송하는 것을 막을 수 있을까?

그런 짓을 하려면 누군가 IPinside LWS 에이전트 애플리케이션을 역설계하여 기능을 재현해야 한다. 그러니까 그렇게 간단한 일은 아닐 것이다. 나는 이걸 하는데… 노트를 찾아보니… 개념 증명(proof of concept) 만드는 것까지 다 합쳐서 꼬박 일주일 걸렸다. 아무튼 사기꾼이 이런 여러 층의 난독화를 모두 뚫기 위해 그 정도나 되는 시간을 투자할 리는 없지 않은가.

하지만 잠깐, 굳이 그렇게까지 할 필요가 있을까? 미리 저장해둔 유효한 응답을 웹사이트로 보내기만 하면 되는 재전송 공격(replay attack)을 하는 편이 훨씬 간단하다. 여기에는 챌린지 핸드셰이크 방식(challenge-handshake scheme)도 없고, 타임스탬프(timestamp)도 없고, 이런 공격을 막을 수 있는 어떠한 수단도 없다. 어쩌면 웹사이트에서 이런 응답이 이전과 동일하다는 것을 눈치챌 수도 있지 않을까. 그러나 그래 봤자 아무 소용이 없다. ndataudata의 난독화에는 어떠한 무작위성(randomness)도 없기 때문에, 데이터는 언제나 동일할 것이다. 그리고 wdata는 난독화를 할 때 고작 무작위 1바이트만 사용하기 때문에, 이게 정상적인 응답인지 아니면 그걸 기록한 응답의 재생인지를 확실하게 구분하기에는 충분하지 않다.

역주: 챌린지 핸드셰이크 인증 프로토콜(Challenge-Handshake Authentication Protocol; CHAP)은 서버와 클라이언트가 각각 평문으로 된 패스워드를 지니고 있을 때, 이를 네트워크에 직접 노출하지 않은 채 인증 처리를 하기 위한 방법입니다. 먼저 서버에서 매번 바뀌는 난수를 하나 골라서 클라이언트로 보내는데, 여기서는 이를 챌린지(Challenge)라고 합니다. 그러면 클라이언트에서는 챌린지와 패스워드의 내용을 합쳐서 해시 함수로 처리한 뒤, 그 결과를 다시 서버에 응답합니다. 클라이언트에서 보낸 응답이 서버에서 동일한 방식으로 계산한 값과 일치하면 인증 통과입니다. 서버에서 보내는 챌린지는 매번 바뀌기 때문에, 결과적으로 재전송 공격을 막을 수 있습니다. 이 프로토콜 자체는 패스워드를 서버에 평문으로 저장해서는 안 된다는 지금의 보안 기준을 충족시키지 못합니다만, 개념적인 원리는 HMAC(Hash-based Message Authentication Code)에 재전송 공격을 막기 위한 일회용 비표(nonce)를 추가해서 사용하는 것과 사실상 동일합니다.

따라서 IPinside는 막대한 개인 정보 침해를 하고 있으며 그러한 데이터를 누구에게나 너무나도 많이 노출하고 있지만, 그들이 주장하는 것처럼 불법 거래를 실제로 막을 수는 없는 것으로 보인다. 부디 내가 틀렸음을 증명해주기를 바란다.