
Forgejo 16.0.5 - 높음 - 원격 - SSRF - hostmatcher가 0.0.0.1을 공인 IP로 허용
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · forgejo-hostmatcher-0001-ssrf
분류: SSRF 도달 범위: 원격
Forgejo 16.0.5 (8d24b7d) - Forgejo
기본 [webhook] ALLOWED_HOST_LIST가 비어 있으므로, services/webhook/deliver.go는 내장 external로 클라이언트를 구성합니다. 이 술어는 Go의 IsGlobalUnicast() && !isPrivate()입니다. isPrivate는 RFC1918/4193에 v16.0.2 CGNAT 추가를 더한 것입니다. RFC 6890 0.0.0.0/8은 그 어디에도 해당하지 않습니다. 127.0.0.1은 루프백이므로 거부됩니다. 0.0.0.1은 허용됩니다.
로그인한 사용자는 http://0.0.0.1로 웹훅을 보내고 훅 기록에서 HTTP 본문을 읽을 수 있습니다. 같은 포트의 127.0.0.1은 거부됩니다.
| ID | 아직 CVE 없음 |
| 분류 | SSRF |
| 도달 범위 | 원격 (인증됨; 저장소 웹훅을 생성할 수 있는 모든 사용자) |
| CWE | CWE-918 |
| CVSS | 높음: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
| 제품 | Forgejo |
| 영향받는 버전 | v16.0.5 (8d24b7d)까지; v16.0.2 CGNAT 추가의 잔재 |
| 인증 | 저장소 웹훅을 생성할 수 있는 로그인 사용자 |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1 전용 |
기본 설치에 등록하고, 저장소를 생성하고, forgejo 웹훅을 http://0.0.0.1:<port>/...로 설정하고, 테스트한 뒤 응답 본문을 읽습니다.
| 권한 | 메서드 / 싱크 | 정찰 / 무결성 / 가용성 | 랩 입증 또는 추론 |
|---|---|---|---|
기본 웹훅 허용 목록을 우회하여 이 네트워크 0.0.0.1로 다이얼 | POST /api/v1/repos/{owner}/{repo}/hooks 후 URL http://0.0.0.1:8080/ssrf로 POST .../hooks/{id}/tests | 정찰 (Forgejo 호스트에서의 아웃바운드 HTTP) | 랩 입증. 기록에 HTTP 200 본문 FORGEJO-HOSTMATCHER-0001-WITNESS 저장됨 |
| 응답한 대상의 전체 HTTP 응답 읽기 | GET /{owner}/{repo}/settings/hooks/{id}가 ResponseInfo.Body를 출력 | 정찰 (전체 응답 SSRF) | 랩 입증. 기록 페이지에 동일한 증거 |
IPv4 매핑 [::ffff:0.0.0.1]을 통한 동일 클래스 | 동일한 매처 To4() 후 checkIP | 정찰 | hostmatcher에서 추론. 랩은 0.0.0.1 사용 |
| Forgejo netns에서 루프백보다 넓게 바인딩된 리스너에 도달 | 0.0.0.1이 lo에 있으면 Linux connect()에서 0.0.0.0/8은 이 호스트 | 정찰 | 사이드카 오라클에 대해 랩 입증. 특정 클라우드 IMDS에 대한 주장은 아님 |
동일한 기본 매처에서 차단됨: 127.0.0.1 (기록에서 랩 입증 거부), 127.0.0.0/8, RFC1918, CGNAT 100.64.0.0/10, IPv4 IMDS를 포함한 링크 로컬 169.254.0.0/16, 미지정 0.0.0.0. 아웃바운드 웹훅은 X-Forgejo-Internal-Auth / INTERNAL_TOKEN을 얻지 못합니다. 이것만으로는 RCE가 아닙니다.
이것은 v16.0.2 CGNAT 수정이 아닙니다. Forgejo는 Gitea 스타일의 예약 네트워크 목록을 갖추지 않았습니다. gitea-hostmatcher-0000-ssrf와 동일한 클래스입니다.
v16.0.2는 CGNAT를 private에 넣었습니다. 거부 목록을 추가하는 패치는 선물입니다: 목록을 읽고, IANA에 아직 남아 있는데 목록이 놓친 것이 무엇인지 묻습니다. 형제 사냥은 Go 1.26 IsGlobalUnicast가 0.0.0.1을 차단했다고 주장했습니다. 술어와 트리 내 테스트를 확인했습니다. 0.0.0.1을 다루지 않습니다.
이미 기록된 잘못된 방향: 웹훅 생성 HTTP 201을 오라클로 취급 (기록이 오라클); 127.0.0.1 전달 성공 (그것은 다른 버그일 것 - 랩은 거부를 요구); X-Forgejo-Internal-Auth로 /api/internal 접근; 리버스 셸. 연극.
python3 forgejo-hostmatcher-0001-ssrf-Abraxas-Labs.py
또는:
cd lab
./run.sh
오직 http://127.0.0.1:19320만 대상으로 합니다. 이미지 codeberg.org/forgejo/forgejo:16.0.5. 오라클 사이드카는 포트 8080에서 Forgejo netns를 공유합니다.
create-hook name=neg-loopback ...
negative-control 127.0.0.1 blocked
create-hook name=hit-0001 ...
poll id=2 witness=True
SUCCESS forgejo-hostmatcher-0001-ssrf how=live pin=v16.0.5 0.0.0.1:8080 HTTP 200 FORGEJO-HOSTMATCHER-0001-WITNESS
RFC 6890 0.0.0.0/8을 비외부로 취급하십시오 (IsGlobalUnicast가 아닌 예약 네트워크 목록). 0.0.0.1은 127.0.0.1과 같은 방식으로 거부되어야 합니다.
hostmatcher.goGNU Affero GPL v3.0. LICENSE 참조. 루프백 랩 전용. 보증 없음.