PJL 파일시스템 명령이 임의 파일 읽기·쓰기로 이어지는 원리

🛡️ 강사 제공 인가된 실습 환경에서 수행

네트워크 프린터를 조사하다 보면 TCP 9100 포트를 발견할 수 있다. 이 포트는 흔히 JetDirect 또는 RAW printing에 사용된다.

여기서 9100 포트와 PJL은 같은 개념이 아니다.

  • TCP 9100은 인쇄 데이터를 전달하는 통로다.
  • JetDirect는 이 포트를 사용하는 RAW 인쇄 방식이다.
  • PJL은 그 통로를 통해 전달할 수 있는 프린터 제어 명령 언어다.

따라서 9100 포트가 열려 있다는 사실만으로 PJL 서비스라고 단정할 수는 없다. @PJL INFO ID 같은 명령을 보냈을 때 장치 정보가 반환되는지를 통해 PJL 지원 여부를 확인할 수 있다.

PJL은 무엇을 하는가

PJL은 Printer Job Language의 약자로, 프린터 상태와 인쇄 작업을 제어하기 위해 만들어진 언어다.

대표적으로 다음과 같은 기능을 제공한다.

  • 프린터 모델과 상태 확인
  • 인쇄 작업의 시작과 종료
  • 프린터 환경설정 조회 및 변경
  • PCL 또는 PostScript 같은 인쇄 언어 선택
  • 프린터 저장장치의 파일 관리

이 중 보안 관점에서 특히 중요한 것이 파일시스템 명령이다.

명령기능
FSDIRLIST디렉터리 목록 조회
FSQUERY파일 존재 여부와 크기 확인
FSUPLOAD프린터의 파일을 클라이언트로 가져오기
FSDOWNLOAD클라이언트의 데이터를 프린터에 저장하기
FSAPPEND파일에 데이터 추가
FSDELETE파일 삭제
FSMKDIR디렉터리 생성

모든 프린터가 이 명령들을 전부 지원하는 것은 아니다. 제조사와 모델, 펌웨어에 따라 지원 범위가 달라진다.

FSUPLOAD와 FSDOWNLOAD가 헷갈리는 이유

두 명령은 이름만 보면 방향을 반대로 이해하기 쉽다.

FSUPLOAD
프린터 파일시스템 ─────▶ 클라이언트

FSDOWNLOAD
클라이언트 ───────────▶ 프린터 파일시스템

보안 점검자의 입장에서는 다음처럼 기억하면 편하다.

  • 파일을 읽을 때 FSUPLOAD
  • 파일을 쓸 때 FSDOWNLOAD

예를 들어 다음 요청은 프린터 파일시스템에 있는 파일의 일부를 클라이언트로 가져온다.

@PJL FSUPLOAD NAME="0:/data/file.txt" OFFSET=0 SIZE=4096

여기서 OFFSET은 읽기 시작 위치이고, SIZE는 가져올 최대 바이트 수다.

반대로 다음 명령은 클라이언트가 보내는 데이터를 프린터 파일시스템에 저장한다.

@PJL FSDOWNLOAD FORMAT:BINARY SIZE=<크기> NAME="0:/data/file.txt"

같은 이름의 파일이 이미 존재한다면 구현에 따라 기존 내용이 덮어써질 수 있다.

정상적인 파일 관리 기능이 취약점이 되는 과정

정상적인 프린터에서 0:은 프린터 내부 저장장치를 의미한다. 따라서 PJL로 파일을 읽는다고 해서 서버의 /etc/home을 곧바로 읽을 수 있는 것은 아니다.

문제는 소프트웨어로 구현된 프린터 서비스가 PJL 경로를 실제 운영체제 경로에 연결할 때 발생한다.

예를 들어 서비스가 다음 경로를 프린터 전용 디렉터리에 연결한다고 가정해 보자.

PJL 경로:
0:/jobs/document.dat

실제 경로:
/srv/printer/jobs/document.dat

여기까지는 정상이다. 하지만 서비스가 ../를 제대로 검사하지 않는다면 다음과 같이 전용 디렉터리 밖으로 이동할 수 있다.

0:/../../home/user/file

운영체제는 ../를 상위 디렉터리 이동으로 해석한다. 따라서 프린터의 가상 파일시스템을 벗어나 실제 사용자 홈이나 시스템 경로에 접근할 가능성이 생긴다.

이것이 경로 순회(Path Traversal) 취약점이다.

서비스 소스 파일을 읽을 수 있었던 이유

실행 중인 프로세스나 서비스 설정을 조사하면 프린터 서비스를 구현한 소스 파일의 위치를 확인할 수 있다.

저권한 사용자가 해당 소스 파일을 직접 읽지 못하더라도, PJL 서비스에 경로 순회 취약점이 있다면 FSUPLOAD를 통해 내용을 요청할 수 있다.

FSUPLOAD 요청

PJL 서비스가 지정된 경로의 파일을 읽음

파일 내용을 TCP 응답으로 반환

클라이언트가 응답을 파일로 저장

여기서 “파일을 내려받았다”는 것은 대상 서버에서 wget 같은 명령을 실행했다는 뜻이 아니다. PJL 서비스가 파일을 읽고 그 내용을 네트워크 응답으로 돌려준 것이다.

서비스 소스 코드를 확보하면 다음 내용을 확인할 수 있다.

  • 어떤 PJL 명령을 지원하는지
  • PJL 경로를 실제 경로로 어떻게 변환하는지
  • ../를 차단하는 검사가 있는지
  • 파일을 읽고 쓸 때 어떤 함수를 사용하는지
  • 서비스가 어떤 사용자 권한으로 실행되는지

요청을 보낸 사용자와 파일을 여는 사용자는 다르다

이 취약점을 이해할 때 가장 중요한 부분이다.

저권한 사용자가 PJL 요청을 보냈다고 해서 파일도 그 저권한 사용자의 권한으로 열리는 것은 아니다.

저권한 사용자

    │ PJL 요청

프린터 서비스

    │ 서비스 프로세스의 권한으로 파일 접근

운영체제 파일시스템

실제로 파일을 여는 주체는 9100 포트에서 실행 중인 프린터 서비스다.

따라서 프린터 서비스가 다른 사용자 권한으로 실행되고 있다면, 요청을 보낸 사용자가 직접 접근할 수 없는 파일도 서비스가 대신 읽거나 수정할 수 있다.

여기서는 난 lp권한이였지만 9100 포트에서 실행중인 서비스는 archivist 권한으로 실행되고 있었기 때문에 archivist 홈에 있던 파일을 읽을수 있었던 것이다.

파일 쓰기가 SSH 로그인으로 이어지는 이유

SSH 공개키 인증에서는 일반적으로 사용자 홈의 다음 파일을 확인한다.

/home/<사용자>/.ssh/authorized_keys

이 파일에는 해당 사용자로 로그인을 허용할 공개키가 저장된다.

공격자가 자신의 SSH 키 쌍을 만들고 공개키를 authorized_keys에 등록할 수 있다면, 개인키를 이용해 해당 사용자로 인증할 수 있다.

공개키
→ 대상의 authorized_keys에 저장

개인키
→ 클라이언트에 보관
→ SSH 인증 시 서명에 사용

PJL 서비스에 경로 순회와 파일 쓰기 기능이 모두 존재한다면 FSDOWNLOAD의 저장 경로를 사용자 홈의 authorized_keys로 지정할 가능성이 생긴다.

FSDOWNLOAD

경로 순회로 프린터 디렉터리 탈출

사용자의 authorized_keys 수정

공격자 공개키 등록

대응하는 개인키로 SSH 인증

이것은 SSH 자체의 취약점이 아니다. 다른 서비스가 SSH의 신뢰 설정 파일을 수정할 수 있었기 때문에 발생한 문제다.

전체 원리 정리

전체 흐름을 한 번에 정리하면 다음과 같다.

TCP 9100 서비스 발견

PJL 명령 응답 확인

파일시스템 명령 지원 확인

경로 순회 가능성 발견

FSUPLOAD를 이용한 파일 읽기

서비스 구현과 실행 권한 분석

FSDOWNLOAD를 이용한 파일 쓰기

인증에 사용되는 파일 수정

다른 사용자 권한 획득

이 문제가 발생하려면 여러 조건이 함께 충족되어야 한다.

  1. PJL 서비스에 접근할 수 있어야 한다.
  2. 파일시스템 명령을 사용할 수 있어야 한다.
  3. PJL 경로가 실제 운영체제 경로에 연결되어 있어야 한다.
  4. 경로 순회를 막는 검증이 없어야 한다.
  5. 서비스 계정이 대상 파일을 읽거나 수정할 권한을 가지고 있어야 한다.

따라서 FSUPLOADFSDOWNLOAD 자체가 취약한 것은 아니다. 정상적인 프린터 관리 기능이 안전하지 않은 경로 처리 및 과도한 서비스 권한과 결합되면서 임의 파일 읽기·쓰기 취약점으로 변한 것이다.

한 줄 요약

PJL 파일시스템 기능의 핵심은 다음 문장으로 정리할 수 있다.

프린터 전용 파일만 처리해야 하는 서비스가 경로를 제대로 검증하지 않으면, 서비스 프로세스의 권한으로 운영체제의 다른 파일까지 읽거나 덮어쓸 수 있다.

원본 · velog.io