요약

  • FTP의 COMPRESSED 모드는 일반 데이터, 임의의 반복 바이트, 필러를 서로 다른 세 가지 형태로 인코딩했습니다. 필러 형태만이 반복되는 값을 생략했습니다.
  • 수신자는TYPE에서 그 값을 재구성했습니다: ASCII 또는 EBCDIC에서는 공백, Image 또는 Local 바이트 표현에서는 제로 바이트였습니다.
  • 매개변수 타임라인 없이 캡처된 MODE C 스트림은 전송된 모든 옥텟을 보존하더라도 파일을 재현하기에는 불충분할 수 있습니다.

뒤에 값이 없는 유효한 카운트

FTP의 구형 압축 문법에서 가장 짧은 런은 잘못 읽기 쉽습니다. 선행 두 비트는11이고, 나머지 여섯 비트가 카운트를 담습니다. 이 단일 바이트는 필러들의 연속을 나타냅니다. 그 뒤에 샘플 바이트는 없습니다.

이것이 반복 데이터의 보편적인 형태는 아니었습니다.RFC 765와RFC 959는 별도로 복제 바이트(replicated-byte) 형태를 정의했습니다. 이 형태의 선행 클래스는10이고, 하위 여섯 비트가 카운트를 담으며, 다음 바이트가 반복할 값을 제공했습니다. 따라서 앰버 바이트를 스무 번 반복하려면 디스크립터와 앰버 샘플이 모두 필요했습니다. 필러는 더 우대받는 처리를 받았습니다: 카운트만으로 충분했습니다.

이 차이는 파서에 유용한 불변식을 제공합니다. 복제 디스크립터 뒤에 샘플이 없으면 잘림(truncation)입니다. 필러 디스크립터 뒤에 샘플을 기대하는 것 자체가 오류입니다. 올바름은 무엇이 뒤따라야 하는지 결정하기 전에 클래스를 인식하는 데서 나옵니다.

MODE가 놓친 것을 TYPE가 채웠다

누락된 값은 표현 상태에 있었습니다. ASCII에서는 필러가 코드 32의 ASCII 공백을 의미했습니다. EBCDIC에서는 그 표현의 공백, 즉 코드 64를 의미했습니다. Image 또는 Local 바이트 표현에서는 제로 바이트를 의미했습니다.

따라서 동일한 필러 디스크립터는 전송되는 비트를 하나도 바꾸지 않고 다른 출력을 낼 수 있었습니다.TYPE A아래에서 이를 디코딩하는 수신자는 공백을 만들 수 있고, 동일한 바이트를TYPE I아래에서는 제로를 만들 수 있습니다. 모드 자체에는 결과를 확정하기에 충분한 정보가 없었습니다.

RFC 959는 이 의존성을 유난히 명확하게 서술합니다. 전송 모드와 표현은 기본적으로 독립적이지만, Compressed 모드는 필러 바이트의 성격이 표현 타입에 의존하기 때문에 예외라고 말합니다. 이 문장은 MODE C를 자립적인 스트림 코덱으로 취급하는 편리하지만 거짓인 추상화를 막아 줍니다.

세 가지 형태가 세 가지 질문에 답했다

일반 데이터는 최상위 비트가 0이고 나머지 일곱 비트가 최대 127까지의 양수 카운트를 담는 디스크립터를 사용했습니다. 다음n바이트는 그대로 복사되었습니다. 이 형태는 이렇게 말합니다: 여기 여러 값이 있고, 어떤 것도 추론되지 않았다.

복제 데이터는10클래스, 여섯 비트 카운트, 하나의 명시적 바이트를 사용했습니다. 이 형태는 이렇게 말합니다: 이 제공된 값을 반복하라. 한 바이트짜리 필러 형태는11클래스와 여섯 비트 카운트만 사용했습니다. 이 형태는 이렇게 말합니다: 이 표현이 이미 빈 재료로 지정해 둔 값을 반복하라.

압축은 그러므로 단순한 통계적 반복이 아니라 의미론적 구분 위에 놓여 있었습니다. ASCII 아래에서 임의의 제로들의 런이 곧 ASCII 필러 런이 되지는 않았습니다; ASCII에서 우대받는 필러는 공백이었습니다. 반대로 Image 모드는 명시적 제로 샘플을 받지 않고도 제로를 재구성할 수 있었습니다. 어떤 형태가 유효한지는 런 바깥에서 확립된 의미에 달려 있었습니다.

제어 정보는 네 번째 경로를 차지했습니다. 제로 이스케이프 바이트 뒤에 디스크립터가 따르면 레코드 또는 파일 경계 같은 블록 모드 코드가 운반되어 이후 문자열에 적용되었습니다. 문법은 바이트를 저장소에 넘기기 전에 리터럴 구간, 명시적 복제, 암묵적 필러, 제어를 구분해야 했습니다.

프린터 파일이 이 거래를 매력적으로 만들었다

RFC 959는 COMPRESSED 모드를 아주 큰 전송에서 약간의 CPU 작업을 대역폭과 맞바꾸는 것으로 묘사했습니다. 원격 잡 엔트리 호스트가 만들어내는 출력을 포함한 프린터 파일을 특히 효과적인 사례로 꼽았습니다. 긴 공백 필드는 그 환경에서 추상적인 가능성이 아니었습니다; 고정 레이아웃과 포맷된 출력의 일상적 비용이었습니다.

공백에 한 바이트짜리 런 형태를 주는 것은 역사적으로 타당했습니다. Image와 Local 바이트 데이터에서 제로에 그 역할을 주는 것도 마찬가지였습니다. 이 최적화는 표현이 “필러”가 될 수 있는 것을 이미 좁혀 두었기 때문에 컴팩트했습니다.

그러나 그 경제성은 의존성도 만들어 냈습니다. 데이터 연결만 보존한 캡처 어플라이언스는 디코더의 일부를 버린 셈입니다. 로그에서TYPE만 정규화하고 압축 바이트는 그대로 둔 게이트웨이는 나중의 재구성이 원래 수신자와 어긋나게 만들 수 있습니다. 카운트-무값을 전부 “잘림”으로 분류하는 포렌식 도구는 합법적 트래픽을 거부할 것입니다.

협상은 스트림을 에포크로 나누었다

TYPE,STRU,MODE는 제어 연결을 통해 전달되는 전송 매개변수였습니다.MODE C는 압축 문법을 선택했고,TYPE은 표현을 선택했습니다. 두 효과는 필러 형태 안에서 만났습니다.

신뢰할 수 있는 트레이스는 각 데이터 구간을 전송이 시작될 때 활성 상태였던 매개변수에 묶어 둡니다. 클라이언트가 시도한 명령만이 아니라 수락된 명령과 응답을 기록합니다. 프로토콜 구현과 동일한 경계에서 디코더 상태를 재설정하거나 닫습니다. 다른 전송 전에 매개변수가 바뀌면, 동일한 디스크립터에 새로운 해석 맥락이 필요합니다.

이것은 FTP 고고학보다 넓은 이야기입니다. 협상된 상태에서 값을 끌어오는 모든 인코딩은 그 상태를 증거의 일부로 만듭니다. 페이로드 해시는 어떤 인코딩된 바이트가 보존되었는지는 증명하지만, 그 바이트가 어떤 디코딩된 객체를 지칭했는지는 증명하지 않습니다.

기본 명령이 모든 인수를 증명하지는 않았다

RFC 959는S,B,C를 각각 Stream, Block, Compressed 모드에 배정하고 Stream을 기본으로 정했습니다.RFC 1123은 이후 최소 구현을 Stream 모드 중심으로 정의했습니다. 또한 FTP에는 실재하지만 널리 구현되지 않은 기능들이 있다고 언급했습니다. 역사적 사양을 “MODE C가 보편적이었다”거나 “현재 소프트웨어가 이를 받아들인다”는 주장으로 바꾸어서는 안 됩니다.

RFC 5797과IANA FTP Commands and Extensions 레지스트리는MODE를 RFC 959에 근거한 기본 매개변수 설정 명령으로 보존하고 있습니다. 이것은 어휘 증거입니다.C인수, 런 파서, 왕복 보존에 대한 살아있는 적합성 테스트가 아닙니다.

재구성의 양쪽을 모두 보존하라

유용한 증거는 원시 데이터 스트림과 그 해시·길이; 수락된 TYPE/STRU/MODE 타임라인; 디코딩된 모든 구간과 제어 이벤트; 최종 파서 상태; 정규화된 재구성 출력의 해시를 유지합니다. 우연히 동일한 바이트를 만들어 내더라도 필러 런과 명시적 복제를 구분합니다.

카나리아 테스트는 하나의 필러 디스크립터를 ASCII, EBCDIC, Image, Local 바이트 표현 아래에서 재사용해야 합니다. 임의의 반복 값을 그 옆에 놓고, 디스크립터와 샘플을 수신 호출들 사이에 분할하고, 최대 일반 구간을 사용하고, 샘플 없는 복제 디스크립터 뒤에서 닫아야 합니다. 기대 결과는 패킷 경계가 아니라 문법에 따라 달라집니다.

발신자가 필러 바이트를 보내지 않은 이유는 세션이 이미 그 값을 지명해 두었기 때문입니다. FTP는 협상된 맥락에서 의미를 빌려 대역폭을 아꼈습니다. 그 절약은 실재했고, 그 바이트가 증거로 남아야 한다는 기대가 있을 때 그 맥락을 보존해야 하는 의무도 실재했습니다.

출처