TECH 으로 돌아가기
TECH HACKER NEWS 오늘 9분 읽기 21 READS

안드로이드 VPN 잠금을 우회하는 NAT-T 킵얼라이브 오프로드

안드로이드 VPN 잠금을 우회하는 NAT-T 킵얼라이브 오프로드
SOURCE IMAGE · HACKER NEWS

안드로이드의 '상시 VPN(Always-on VPN)'과 'VPN 없이 연결 차단' 설정은 사용자와 관리자에게 하나의 약속처럼 받아들여진다. 해당 앱의 트래픽은 VPN 터널이 살아 있지 않으면 물리 네트워크로 새어 나가지 않고 '닫힌 상태로 실패(fail closed)'한다는 기대다. 그런데 최근 공개된 기술 보고서는 루트 권한이나 ADB, 숨겨진 API 없이도 평범한 앱 하나가 이 경계를 넘어 물리 라우터로 UDP/4500 패킷을 내보낼 수 있음을 실측으로 보여준다. 문제의 통로는 안드로이드가 공식적으로 제공하는 NAT-T 소켓 킵얼라이브 API다.

무엇이 새는가

핵심은 IPsec의 NAT 통과에 쓰이는 UDP 4500번 포트 매핑을 유지하는 기능이다. 앱은 공개 API인 IpSecManager.UdpEncapsulationSocket을 만들고 ConnectivityManager.createSocketKeepalive를 호출해 NAT 매핑 유지를 요청한다. 이 요청은 내부적으로 startNattKeepaliveWithFd로 흘러가 Wi-Fi 킵얼라이브 오프로드 경로에 완성된 패킷을 넘긴다. 오프로드 경로는 앱을 깨우거나 매번 소켓 쓰기를 수행하지 않고 프레임을 내보내기 때문에, 최종 송신 지점이 VPN 잠금이 통제하는 일반 소켓 계층보다 아래에 놓인다. 결과적으로 잠금 방화벽 규칙과 UID별 라우팅이 잡아내지 못하는 지점에서 패킷이 빠져나간다.

다만 이 우회가 유출하는 정보의 범위는 분명히 제한적이다. 패킷의 형태(payload)는 플랫폼이 고정하므로 공격자가 임의의 데이터를 실어 보내지는 못한다. 대신 공격자는 API와 라우팅 제약 안에서 목적지 주소를 정할 수 있고, 물리 네트워크가 드러내는 실제 소스 IP 주소와 킵얼라이브의 반복 주기(타이밍)를 관찰할 수 있다. 즉 사용자가 VPN 없이 차단을 켜 놓았음에도, 실제 네트워크 신원과 접속 시점이 터널 밖 목적지에 노출된다. 이는 잠금이 보장해야 할 '신원 은닉' 속성을 정면으로 깨뜨린다.

세 단계의 실측 증거

보고서는 서로 다른 제조사 기기에서 세 수준의 증거를 제시한다. 안드로이드 16(빌드 CP1A.260505.005)이 돌아가는 픽셀 8 프로에서는 상시 VPN과 잠금이 켜진 상태로, 연구자가 통제한 액세스 포인트 캡처가 공개 최소 간격인 10초 주기로 나가는 UDP/4500 패킷을 실제로 기록했다. 삼성 SM-F966B에서는 물리 IPv4 기본 게이트웨이를 향한 활성 Wi-Fi 슬롯 임대가 24시간 32분 동안 연속으로 관측됐다. 세 번째로 낫싱 A059(Asteroids)에서도 동일 구현이 물리 게이트웨이를 선택하고 활성 슬롯을 확보하는 것이 확인됐는데, 다만 이 기기에서는 독립적인 패킷 캡처나 지속 시간 측정은 이뤄지지 않았다. 픽셀에서는 백그라운드 전환, 잠금, 도즈, 배터리 세이버, 대기 버킷 제한, Binder 프리저, 강제 종료·삭제·네트워크 상실·재부팅 경계까지 생명주기 전반이 검토됐다.

노출 규모에 대해 보고서는 안드로이드 12 이상이 공유하는 프레임워크 경로와 추정 출하량의 91.24%를 차지하는 7개 WLAN 계열의 펌웨어 커버리지를 근거로, 대부분의 안드로이드 12+ 기기가 이 기기군 노출에 해당한다고 본다. 나머지 8.76%는 미해결로 남겨 둔다. 참고로 F-Droid/IzzyOnDroid의 4,679개 저장소 조사에서는 프레임워크 IPsec·IKE·NAT-T API 사용이 탐지되지 않았고, 수작업 감사로 73개의 VpnService 앱이 확인됐다. 즉 정상 생태계에서는 이 API가 거의 쓰이지 않는 반면, 악용 경로는 열려 있는 셈이다.

신뢰 모델의 붕괴

근본 원인은 startNattKeepaliveWithFd에서 두 개의 신뢰 모델이 뒤섞인 데 있다. 원래 raw-fd 기반 NAT-T 킵얼라이브 API는 권한이 필요한 특권 경로였다. 이후 공개 IPsec/IKE 앱을 지원하려고 공개 UdpEncapsulationSocket 킵얼라이브를 같은 메서드로 통과시키면서 무조건적 권한 검사를 제거했고, 대신 공개 호출자는 살아 있는 IpSec 자원의 소유권을 증명하도록 요구하는 것이 안전한 설계였다. 그러나 실제로는 두 검사가 모두 빠져 있다. 서버는 복제된 fd가 호출자 소유의 실제 IpSec 캡슐화 소켓 자원에 대응하는지 증명하지 않고, 오프로드를 시작하기 전에 해당 UID의 VPN·잠금 정책이 물리 언더레이 송신을 막는지도 확인하지 않는다. 진단에 따르면 isNattKeepaliveSocketValid는 null이 아닌 모든 fd를 통과시키며 resourceId를 실질적으로 참조하지 않아, 0이나 -2, 정수 최댓값·최솟값 같은 엉터리 값도 소유권 검사를 통과한다.

한때 이 구멍을 막는 코드가 존재하기도 했다. 소스 이력을 보면 호출자 UID 소유권을 검증하고 킵얼라이브 수명 동안 캡슐화 소켓을 고정하며 중복 사용을 거부하는 자원 검증·수명 잠금이 잠시 추가됐다. 그러나 서비스 의존성과 데드락 우려로 되돌려졌고, 그 자리를 UID·네트워크별 쿼터가 대신했다. 쿼터는 자원 고갈을 제한할 뿐, fd와 자원 쌍을 인증하거나 IpSec 수명 임대를 유지하거나 VPN 정책을 강제하지 못한다. 앞서 안드로이드 오토모티브 접근 제어 연구가 fd 기반 경로에 PACKET_KEEPALIVE_OFFLOAD 권한 검사가 빠진 불일치를 지적한 적은 있으나, 공개 UdpEncapsulationSocket의 신뢰 분리나 되돌려진 자원 검증, 잠금 상태에서의 물리 Wi-Fi 송신까지는 추적하지 못했다.

실무자가 봐야 할 지점

국내 보안·모바일 실무자에게 이 보고는 몇 가지 현실적 함의를 던진다. 첫째, VPN 잠금은 암호화만이 아니라 '라우팅과 신원 은닉'까지 보장한다는 사용자 기대를 걸고 있으므로, 특정 앱을 잠금 대상으로 지정했다고 해서 실제 IP와 접속 패턴이 완전히 감춰진다고 가정해서는 안 된다. 둘째, 악용에 특별한 위험 권한이 필요 없고 INTERNET·ACCESS_NETWORK_STATE 같은 평범한 능력만으로 성립한다는 점에서, 앱 심사나 MDM 정책에서 킵얼라이브·IpSec API 호출을 단순히 '정상 네트워킹'으로 넘기기 어렵다. 다만 한계도 분명하다. 이 원시 기능은 임의 콘텐츠를 나르는 데이터 채널이 아니라 소스 주소와 주기를 흘리는 메타데이터 유출이며, 공격자가 인터넷상의 UDP/4500 엔드포인트를 통제하거나 관측할 수 있어야 한다. 결국 완전한 차단은 프레임워크가 오프로드 이전에 호출자 UID의 실효 VPN 정책을 강제하고 fd·자원 소유권을 인증하도록 고치는, 플랫폼 차원의 수정에 달려 있다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://supuk.ch/papers/android-natt-keepalive-vpn-bypass
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...