상세 컨텐츠

본문 제목

오픈소스 취약점을 GitHub에 책임 있게 제보하기 — PVR이 없을 때 겪은 일들

보안

by 코드스키 2026. 7. 27. 15:06

본문

퍼징으로 크래시를 재현하고 근본 원인까지 특정하고 나면, 그다음이 진짜 시작이다. 이걸 어디에 어떻게 알릴 것인가. 나는 네트워크 파서 몇 개에서 메모리 버그를 찾아 GitHub Security Advisory와 공개 이슈로 제보했는데, 그 과정에서 저장소마다 제보 창구가 제각각이라는 걸 몸으로 배웠다. 정리해두면 나중의 나나 비슷한 걸 처음 해보는 사람한테 도움이 될 것 같아서 남긴다.

비공개 제보(PVR)와 공개 이슈는 다른 트랙이다

GitHub에는 Private Vulnerability Reporting(PVR)이라는 기능이 있다. 저장소의 Security 탭에서 "Report a vulnerability" 버튼을 누르면 메인테이너하고만 보이는 비공개 advisory 초안을 만들 수 있다. 이게 활성화돼 있으면 취약점을 공개하기 전에 조용히 수정할 시간을 줄 수 있어서, 책임 있는 제보의 정석 경로다.

문제는 이 기능이 저장소마다 켜져 있기도 하고 아니기도 하다는 점이다. 내가 제보하려던 프로젝트 중 하나는 PVR이 잘 켜져 있어서 advisory를 비공개로 제출했고, 다른 두 개는 /security/advisories/new에 접속하면 404가 떴다. 더 헷갈렸던 건, 그중 하나는 SECURITY.md 파일에 "GitHub의 Security reporting 기능을 사용하라"고 안내까지 해뒀는데 실제로는 그 기능이 꺼져 있었다는 거다. 문서와 실제 설정이 어긋난 상태였다.

오픈소스 취약점 책임 있는 제보 절차 대표 이미지
오픈소스 취약점 책임 있는 제보 절차 대표 이미지
GitHub 공식 문서 — 보안 취약성 비공개 보고(PVR)는 SECURITY.md와 별개 기능
GitHub 공식 문서 — 보안 취약성 비공개 보고(PVR)는 SECURITY.md와 별개 기능

그래서 얻은 첫 교훈은 이거다. SECURITY.md만 믿지 말고, 제보 전에 /security/advisories/new URL로 직접 들어가서 폼이 실제로 뜨는지 확인해라. 폼이 안 뜨면 PVR이 꺼진 거고, 이때는 선택지가 갈린다.

PVR이 없는 저장소는 결국 공개 GitHub Issue로 갈 수밖에 없다. 다만 이건 순간부터 전 세계에 공개되는 거라, 심각도를 먼저 냉정하게 따져야 한다. 내가 찾은 건 전부 읽기 기반(OOB read)의 저심각 DoS라 원격 코드 실행 같은 위험이 없었고, 그래서 공개 이슈로 올려도 악용 위험보다 수정 유도 효과가 크다고 판단했다. 만약 쓰기 primitive나 RCE 가능성이 조금이라도 보였다면 공개 이슈는 절대 안 됐을 거고, 메인테이너 이메일이나 CNA를 통한 비공개 경로를 찾았어야 했다.

PVR이 없는 오픈소스 프로젝트에 GitHub 이슈로 취약점을 제보한 화면
PVR이 없는 오픈소스 프로젝트에 GitHub 이슈로 취약점을 제보한 화면

advisory 폼을 채우는 실무 팁

PVR로 advisory를 쓸 때 몇 가지 편의 기능이 있다. Severity 항목에서 "Assess severity using CVSS"를 고르면 벡터 문자열만 입력해도 Low/Moderate/High 등급과 점수를 자동으로 계산해준다. 내 경우 원격에서 서비스를 죽일 수 있는 건 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H로 7.5가 나왔고, 파일을 열어야 트리거되는 로컬 파서 버그는 그보다 낮은 5.5대가 나왔다. 벡터를 손으로 계산하지 말고 이 기능에 맡기는 게 정확하다.

FIRST.org CVSS v3.1 계산기 — 실제 제보에 쓴 벡터로 7.5(High) 산출
FIRST.org CVSS v3.1 계산기 — 실제 제보에 쓴 벡터로 7.5(High) 산출
CWE-125: Out-of-bounds Read 공식 정의 다이어그램 — MITRE CWE
CWE-125: Out-of-bounds Read 공식 정의 다이어그램 — MITRE CWE

CWE 필드는 번호로 검색하면 자동완성이 뜬다. OOB read면 "CWE-125"를 치면 바로 후보가 나온다. 분류를 정확히 달아두면 메인테이너가 이해하기 훨씬 빠르다. CWE-125 공식 정의를 보면 "의도한 버퍼의 끝(또는 시작) 너머의 데이터를 읽는다"는 게 핵심이고, 그 결과로 비인가 데이터 노출·크래시·예기치 못한 동작으로 이어진다고 명시돼 있다. 내가 겪은 DoS(크래시)가 정확히 이 흐름에 들어맞았다.

PoC 첨부에서 한 번 막혔다. GitHub 코멘트에 .pcap 파일을 그대로 올리려니까 "File type not supported"라고 거부당했다. 바이너리 확장자 일부는 직접 첨부가 안 된다. 이건 .zip으로 압축해서 올리면 통과한다. 압축한 다음에는 원본과 압축본의 SHA-256을 코멘트에 같이 적어뒀다. 나중에 메인테이너가 받은 파일이 내가 보낸 것과 같은지 검증할 수 있게 하려는 목적이었고, 실제로 첨부가 업로드 완료 전에 코멘트가 먼저 게시돼서 링크가 깨진 적이 있어서 재업로드 후 해시를 다시 확인해야 했다.

보고서에 꼭 담아야 하는 것

내가 쓴 제보 초안은 대체로 이런 뼈대였다. 영향받는 버전과 커밋 해시, 취약점 클래스(CWE), 요약, 재현 절차, 취약 코드의 파일·라인과 한 줄짜리 근본 원인, 최소화한 PoC의 크기와 해시, 그리고 영향 범위. 마지막에 "이건 OOB read이고 DoS이며 RCE는 아니다"를 명시적으로 박아뒀다.

이 마지막 한 줄이 생각보다 중요하다. 심각도를 부풀리면 메인테이너 신뢰를 잃는다. 보안 커뮤니티에서 과장은 금방 티가 나고, 한 번 "이 사람 오버한다"는 인상을 주면 그다음 제보가 안 읽힌다. 오히려 "여기까지는 확인했고 여기부터는 확인 못 했다"를 정확히 적는 게 훨씬 신뢰를 준다. 패치 제안이 있으면 검증된 한 줄 diff까지 같이 제시하고, "원하시면 PR 보내겠다"고 열어두면 대화가 매끄럽게 이어진다.

결과와 남은 것

비공개로 제출한 쪽은 메인테이너가 검토 후 advisory를 published로 전환하면서 제보자 크레딧을 accepted 해줬고, 공개 이슈로 올린 쪽도 정상적으로 접수됐다. 한쪽은 이미 수정 커밋까지 반영됐다. CVE 번호는 CNA/메인테이너 판단 영역이라 아직 배정 대기 상태다.

돌이켜보면 제보라는 게 그냥 "버그 찾았어요"를 보내는 일이 아니었다. 어느 창구가 열려 있는지 확인하고, 심각도를 정직하게 매기고, 상대가 검증할 수 있는 형태로 증거를 포장하고, 과장 없이 쓰는 것. 크래시를 재현하는 것보다 이쪽이 훨씬 더 "실무"에 가까웠다.

같이 읽기

실제로 AFL++로 크래시를 찾고 GHSA 제보까지 이어간 과정은 AFL++로 네트워크 포렌식 파서 퍼징해서 GHSA 10건 제보·게재까지에 정리했다.

공식 자료

관련글 더보기