요약

  • RFC 1948은 공격자가 실제 TCP 연결의 첫 번호를 얻고, 서버가 신뢰하는 컴퓨터의 IP 주소를 사칭한 뒤, 볼 수 없는 서버 응답의 번호를 맞혀 세 단계 연결 시작을 끝내는 방식을 설명했다.
  • 제안은 같은 연결 조합이 다시 쓰일 때 앞으로 가야 하는 시간 값 M을 남기고, 주소 둘과 포트 둘의 조합마다 비밀 오프셋 F를 더했다. 한 창구의 번호표로 옆 창구의 첫 번호를 읽을 수 없게 했다.

도둑의 첫 행동은 정상 고객과 같았다. 직접 TCP 연결을 열고 서버가 내놓은 초기 순서 번호를 적었다. 1996년 5월의 RFC 1948은 그 한 장의 표본으로 다음 연결 번호를 예측하는 공격을 다뤘다. 공격자는 서버가 신뢰하는 다른 컴퓨터의 IP 주소를 가장했다. 서버의 답장은 진짜 컴퓨터로 가므로 공격자는 볼 수 없었다. 그래도 번호를 맞히고 진짜 컴퓨터를 잠시 침묵시키면 마지막 확인 응답을 보낼 수 있었다. 주소만 믿는 원격 명령 서비스는 낯선 사람을 아는 손님으로 받아들이게 된다.

빈틈은 두 대화 사이에 있었다. 정상 연결은 서버의 초기 순서 번호, ISN을 보여 줬다. RFC가 설명한 Berkeley 계열 구현에서는 번호가 시간과 새 연결 수에 따라 예측 가능한 폭으로 증가했다. 사칭한 대화에는 다음 ISN이 필요했다. 앞선 표본에서 계산할 수 있다면 공격자는 가짜 주소로 간 답장을 보지 않고도 연결 시작을 마쳤다. 번호표 기계가 다음 표가 어떻게 나올지 함께 알려 주는 셈이었다.

새 연결마다 번호를 무작위로 뽑으면 될 듯했다. TCP의 원래 규격인 RFC 793은 초기 번호를 계속 전진시키는 시계를 썼다. 같은 끝점의 연결이 다시 열릴 때 이전 연결의 늦은 패킷이 새 패킷처럼 끼어들지 않도록 거리를 두려는 것이었다. RFC 1948은 BSD 계열의 TIME-WAIT 처리도 고려했다. 서로 다른 대화는 관계를 알아볼 수 없는 출발점이 필요했고, 같은 조합이 다시 등장하면 앞으로 가는 순서가 필요했다. 번호를 무턱대고 뽑는 방식은 두 요구를 함께 만족시키기 어려웠다.

RFC 1948은 일을 나눴다. TCP 연결의 신원은 로컬 IP 주소와 포트, 원격 IP 주소와 포트의 네 항목으로 정해진다. 초기 번호를 M + F(네 항목)으로 계산했다. M은 움직이는 시간 값, F는 네 항목과 비밀을 섞어 얻는 오프셋이다. 각 조합은 자기 번호 트랙을 갖고, 같은 조합이 다시 쓰이면 시계가 그 트랙의 출발을 앞으로 밀었다. 한 트랙에서 표를 받아도 다른 트랙의 출발선은 알 수 없었다.

오프셋은 밖에서 계산할 수 없어야 했다. RFC 1948은 네 항목과 비밀 자료를 넣은 암호학적 해시를 제안했고, 당시 널리 구할 수 있었던 MD5를 예로 들었다. 난수, 기계의 비밀, 부팅 시각과 기계 신원도 논의했다. F가 읽히면 도둑에게 번호표 기계의 배치도가 다시 돌아간다. 이 계산법은 끝난 모든 연결의 상태를 오래 보관하는 부담도 줄였다.

비밀을 바꾸는 데도 비용이 있었다. 작동 중인 기계의 키가 바뀌면 같은 네 항목으로 다시 열린 연결의 번호 트랙이 이동한다. 이전 연결의 패킷이 아직 네트워크에 남아 있을 수 있다. 그래서 RFC 1948은 종료된 연결의 상태를 보관하거나 최대 세그먼트 생존 시간 두 번에 해당하는 정적 기간을 요구했다. 비밀이 트랙 사이를 가르고, 시간은 한 트랙 안의 앞뒤를 지켰다. 자물쇠를 교체하기 전에 문틈에 지난 손님의 우편물이 끼어 있는지 살펴야 했다.

2012년의 RFC 6528은 이 설계를 표준 트랙으로 올리고 RFC 1948을 대체했으며 RFC 793을 갱신했다. 네 항목과 비밀 키를 쓰는 의사난수 함수를 더 분명히 적고 M + F를 유지하면서 키 길이와 교체도 다뤘다. 1996년의 분업은 살아남았다. 시간은 같은 연결 신원의 반복을 정렬하고, 비밀은 서로 다른 연결의 번호 관계를 숨긴다.

도둑은 자기 번호표 한 장을 계속 받을 수 있었다. RFC 1948이 빼앗은 것은 그 표로 옆 창구의 출발선을 재는 자였다. 차이는 창구 뒤에 남았다.