Air-Gapped Sites — Defence and Government
Build a Transfer Bundle and Find Where Signature Verification Breaks
한국어 원문으로 표시합니다.
목표
폐쇄망에 넣을 반입 번들을 처음부터 만들어 서명하고, 받는 쪽이 되어 검증하고, 검증이 어디서 깨지는지를 두 가지 변조로 직접 확인합니다.
왜 중요한가
이 파드에는 gpg 가 없고 설치할 수도 없습니다. 실제 폐쇄망도 대개 그렇습니다 — 인가 목록에 없는 소프트웨어는 그것부터 반입 심사이기 때문입니다. 그래서 어디에나 있는 openssl 로 서명과 검증을 합니다. 그리고 무결성은 한 층이 아니라 두 층입니다. 파일 해시는 '목록이 말한 그 파일인가' 를, 서명은 '그 목록을 아는 사람이 만들었는가' 를 지킵니다. 한 층만 보면 반드시 하나를 통과시키고, 그 사실은 두 경우를 직접 만들어 보기 전에는 실감이 안 납니다.
단계
python3로/root/bundle/payload/아래 여섯 개 파일을 만듭니다. 둘은 바이너리입니다./root/bundle/stage/MANIFEST.sha256에 파일별 SHA-256 을 상대 경로로 뜹니다./root/bundle/keys/에 RSA 2048 키쌍을 만들고 개인키 권한을 600 으로, 공개키 지문을release.pub.sha256에 남깁니다.openssl dgst -sha256 -sign으로/root/bundle/stage/MANIFEST.sig를 만듭니다./root/bundle/out/intake-2609-01.tar.gz로 묶고 같은 이름의.sha256을 뜹니다. 키 파일은 넣지 않습니다./root/bundle/recv/에 풀어 번들 해시 → 서명 → 파일 해시 순으로 검증하고 결과를receipt.txt에 적습니다./root/bundle/tamper-a/와/root/bundle/tamper-b/에 두 가지 변조를 넣고 결과를/root/bundle/tamper.txt에 적습니다. A 는payload/deps/libpq.so.5.bin한 바이트만, B 는 거기에 더해 매니페스트의 그 줄까지 고칩니다./root/bundle/intake.md에 일곱 절짜리 반입 승인 요청서를 씁니다.
참고
- 바이너리는
head나grep으로 열지 마세요. NUL 이 섞여 있어 결과를 믿을 수 없습니다.file,sha256sum,openssl만 씁니다. sha256sum -c는 매니페스트에 적힌 경로를 현재 디렉터리 기준으로 찾습니다. 페이로드 디렉터리 안에서 실행하세요.- 한 바이트만 바꾸는 데는
printf '\xff' | dd of=파일 bs=1 seek=1024 count=1 conv=notrunc가 편합니다. - 흔한 실수 1:
openssl dgst -sign에-out을 빼고>로 리다이렉트하는 것. 바이너리가 깨집니다. - 흔한 실수 2: 번들을 다시 묶고
.sha256을 다시 뜨지 않는 것. 묶기와 해시 뜨기를 한 스크립트에 붙여 두세요.
반입할 트리 만들기
python3 로 /root/bundle/payload/ 아래 여섯 개 파일을 만듭니다. 둘은 바이너리입니다.
python3 로 여섯 개 파일을 만듭니다. 그중 둘은 바이너리라 눈으로 확인할 수 없고 지문으로만 확인합니다. 생성 스크립트는 고치지 말고 그대로 쓰세요.
파일별 해시 매니페스트 뜨기
/root/bundle/stage/MANIFEST.sha256 에 파일별 SHA-256 을 상대 경로로 뜹니다.
payload 디렉터리 안에서 상대 경로로 뜹니다. find 의 -printf '%P\n' 가 앞의 ./ 를 빼 줍니다. 받는 쪽은 다른 디렉터리에 풀기 때문에 절대 경로는 그 자리에서 쓸모가 없습니다.
서명 키쌍 만들고 지문 남기기
/root/bundle/keys/ 에 RSA 2048 키쌍을 만들고 개인키 권한을 600 으로, 공개키 지문을 release.pub.sha256 에 남깁니다.
openssl genrsa 로 2048비트 개인키를 만들고 권한을 600 으로 둡니다. 공개키는 -pubout 으로 뽑고, 지문은 DER 로 변환한 뒤 sha256sum 을 뜹니다.
매니페스트에 서명하기
openssl dgst -sha256 -sign 으로 /root/bundle/stage/MANIFEST.sig 를 만듭니다.
서명 대상은 페이로드 전체가 아니라 매니페스트 한 장입니다. openssl dgst -sha256 -sign 에 -out 을 반드시 붙이세요 — 화면 출력을 리다이렉트하면 바이너리가 깨집니다.
번들로 묶기 (키는 넣지 않는다)
/root/bundle/out/intake-2609-01.tar.gz 로 묶고 같은 이름의 .sha256 을 뜹니다. 키 파일은 넣지 않습니다.
페이로드와 매니페스트와 서명을 하나로 묶습니다. 공개키는 넣지 않습니다 — 번들과 같은 길로 오면 그 길을 쥔 사람이 넷 다 바꿔 넣을 수 있습니다. 묶은 뒤 tar -tzf 로 무엇이 들어갔는지 눈으로 확인하세요.
받는 쪽 절차를 순서대로 밟기
/root/bundle/recv/ 에 풀어 번들 해시 → 서명 → 파일 해시 순으로 검증하고 결과를 receipt.txt 에 적습니다.
순서가 핵심입니다. 번들 전체 해시 → 서명 → 파일별 해시. 파일 해시를 먼저 보면 아직 진위가 확인되지 않은 목록을 기준으로 대조하게 됩니다.
검증이 어디서 깨지는지 두 경우로 확인하기
/root/bundle/tamper-a/ 와 /root/bundle/tamper-b/ 에 두 가지 변조를 넣고 결과를 /root/bundle/tamper.txt 에 적습니다. A 는 payload/deps/libpq.so.5.bin 한 바이트만, B 는 거기에 더해 매니페스트의 그 줄까지 고칩니다.
사본 두 벌을 만들어 A 에는 바이너리 한 바이트만 바꾸고, B 에는 같은 변조에 더해 매니페스트의 그 줄까지 새 해시로 고칩니다. 두 벌 모두에 서명 검증과 파일 해시 대조를 걸어 보세요.
반입 승인 요청서 쓰기
/root/bundle/intake.md 에 일곱 절짜리 반입 승인 요청서를 씁니다.
일곱 절이 필요합니다. 심사자가 우리 화면 없이 같은 검증을 다시 할 수 있어야 하므로 '통과했습니다' 가 아니라 값과 명령을 적습니다.