• 01-05-2026, 03:22:30
    #1
    Merhaba,
    Son dönemde cPanel / WHM tarafında ortaya çıkan kritik login/auth bypass riskleri, özellikle WHM erişimi internete açık olan sunucular için ciddi tehdit oluşturmaktadır.
    Bu tip zafiyetlerde saldırganlar, bazı durumlarda normal kullanıcı adı/şifre doğrulamasını geçmeden WHM API, session, cookie veya login akışındaki zayıflıkları hedefleyerek yetkisiz erişim elde etmeye çalışabilir.
    Aşağıdaki liste; şüpheli UID 0 kullanıcıları, SSH backdoor’ları, cron persistence, reverse shell, web shell, WHM access log izleri ve sistem sertleştirme adımlarını kontrol etmek için hazırlanmıştır.
    Detaylı incelemelerimiz sonucu bu tarz saldırıya mağruz kalan sunucularımızdan elde ettiğimiz bilgileri sizlerlede paylaşmak istiyoruz. Sunucu temizlendikten sonra güvenlik önlemi olarak alabileceğiniz scripti de ek olarak paylaşıyoruz.
    • Açık: cpsrvd üzerinde CRLF injection — saldırgan kimlik doğrulamadan geçmeden root yetkisinde komut çalıştırabiliyor.
    • Patched cPanel sürümleri: 11.110.0.97 / 11.118.0.63 / 11.126.0.54 / 11.132.0.29 / 11.134.0.20 / 11.136.0.5
    • Risk altındakiler: Yukarıdaki sürümlerin altında olan tüm cPanel/WHM kurulumları.
      Not: komut çalıştırmadan önce lütfen yedek alın saldırı izleri her sunucuda farklılık gösterebilir. Bu tek başına yeterli bir koruma önlemi değildir. hack girişiminde bulunulmuş sunucuyu korumaz. ancak eski versionda iseniz yada mevcut sunucunuzda gelecekte bu tarz bir durum ile karşılaşmak istemezseniz. ek güvenlik katmanı eklemek isterseniz bot giriş denemelerini kesecek bir yapıdır.
    Kısaca sistemin çalışma mantığı şu şekildedir, whm cpanel webmail gibi sayfalara girişte öncelikle ngnix karşılar ve domain şifre sorar, örnek: meohost.com:meohost şeklinde giriş denemesi yapıldıgında sorunsuz girecektir. sunucuda aktif domaini kontrol eder, sizin belirlediğiniz varsayılan şifre ile whm panel login sayfası açılır. botlar ise bunu ayırt edemez ve attıkları istek size ulaşmaz.
    örnek giriş adresi: https://95-134-4-44.cprapid.com:2083/- https://95-134-4-44.cprapid.com:2087
    domain: meohost.com
    şifre: meohost
    girişe yazılması gereken: meohost.com:meohost
    İnstall komutu

    #!/bin/bash
    # cPanel/WHM Yetkilendirme Kapısı — otomatik kurucu
    # Hazırlayan: meohost
    #
    # Kullanım:
    #   sudo bash install.sh
    set -euo pipefail
    PKG_DIR="$(cd "$(dirname "$0")" && pwd)"
    NIC="${NIC:-}"
    red()    { printf "\033[1;31m%s\033[0m\n" "$*"; }
    green()  { printf "\033[1;32m%s\033[0m\n" "$*"; }
    blue()   { printf "\033[1;34m%s\033[0m\n" "$*"; }
    yellow() { printf "\033[1;33m%s\033[0m\n" "$*"; }
    [ "$EUID" -eq 0 ] || { red "Bu script root olarak calistirilmalidir."; exit 1; }
    [ -d /usr/local/cpanel ] || { red "cPanel/WHM tespit edilemedi (/usr/local/cpanel yok)."; exit 1; }
    blue "=== 1/8  Genel ag arayuzu tespit ediliyor ==="
    if [ -z "$NIC" ]; then
      NIC=$(ip -o -4 route show to default | awk '{print $5}' | head -1)
    fi
    [ -n "$NIC" ] || { red "Genel ag arayuzu bulunamadi. NIC=ens192 gibi elle ayarlayin."; exit 1; }
    green "  NIC = $NIC"
    blue "=== 2/8  Mevcut iptables yedegi aliniyor ==="
    mkdir -p /root/cpanel-gate-backup
    BACKUP="/root/cpanel-gate-backup/iptables-$(date +%Y%m%d-%H%M%S).rules"
    iptables-save > "$BACKUP"
    green "  Yedek: $BACKUP"
    blue "=== 3/8  Paketler kuruluyor (nginx, iptables-services) ==="
    dnf install -y nginx iptables-services >/dev/null
    green "  nginx + iptables-services yuklendi"
    blue "=== 4/8  Dizinler olusturuluyor, gate.py ve secret yerlestiriliyor ==="
    mkdir -p /opt/cpanel-gate /etc/cpanel-gate/ssl /var/log/cpanel-gate
    install -m 0755 "$PKG_DIR/gate.py" /opt/cpanel-gate/gate.py
    if [ ! -s /etc/cpanel-gate/secret ]; then
      python3 -c "import secrets; print(secrets.token_hex(32))" > /etc/cpanel-gate/secret
      chmod 600 /etc/cpanel-gate/secret
      green "  Yeni HMAC secret uretildi"
    else
      yellow "  Mevcut HMAC secret korundu (/etc/cpanel-gate/secret)"
    fi
    blue "=== 5/8  cpsrvd SSL sertifikasi cert/key olarak ayriliyor ==="
    python3 - <<'PYEOF'
    import re
    src = open('/var/cpanel/ssl/cpanel/cpanel.pem').read()
    key = re.search(r'-----BEGIN (RSA )?PRIVATE KEY-----.*?-----END (RSA )?PRIVATE KEY-----', src, re.DOTALL)
    certs = re.findall(r'-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----', src, re.DOTALL)
    assert key and certs, 'cpanel.pem parse edilemedi'
    open('/etc/cpanel-gate/ssl/key.pem','w').write(key.group(0)+'\n')
    open('/etc/cpanel-gate/ssl/cert.pem','w').write('\n'.join(certs)+'\n')
    PYEOF
    chmod 640 /etc/cpanel-gate/ssl/{key,cert}.pem
    chown root:nginx /etc/cpanel-gate/ssl/{key,cert}.pem
    green "  /etc/cpanel-gate/ssl/{cert,key}.pem hazir"
    blue "=== 6/8  systemd unit + nginx config + sysctl yerlestiriliyor ==="
    install -m 0644 "$PKG_DIR/cpanel-gate.service" /etc/systemd/system/cpanel-gate.service
    install -m 0644 "$PKG_DIR/cpanel-gate-nginx.conf" /etc/nginx/conf.d/cpanel-gate.conf
    install -m 0644 "$PKG_DIR/99-cpanel-gate.conf" /etc/sysctl.d/99-cpanel-gate.conf
    # nginx default 80 sunucusunu pasif yap (LiteSpeed/Apache zaten 80'i tutar)
    if grep -q 'listen       80 default_server' /etc/nginx/nginx.conf; then
      sed -i.bak '/^    server {/,/^    }/{/listen.*80 default_server/,/^    }/d}' /etc/nginx/nginx.conf || true
      yellow "  /etc/nginx/nginx.conf icindeki default :80 server blogu pasif yapildi"
    fi
    # Eger sysctl dosyasinda NIC ens192 ise ve sistemin NIC'i farkli ise duzelt
    if [ "$NIC" != "ens192" ]; then
      sed -i "s/ens192/$NIC/g" /etc/sysctl.d/99-cpanel-gate.conf
      yellow "  sysctl NIC '$NIC' olarak guncellendi"
    fi
    systemctl daemon-reload
    sysctl --system >/dev/null 2>&1 || true
    green "  systemd + nginx + sysctl kuruldu"
    blue "=== 7/8  iptables DNAT/DROP kurallari uygulaniyor ==="
    # Tekrarli calismayi tolere et: ayni kural varsa atla
    add_nat() {
      iptables -t nat -C "$@" 2>/dev/null || iptables -t nat -A "$@"
    }
    add_in() {
      iptables -C "$@" 2>/dev/null || iptables -I "$@"
    }
    add_nat PREROUTING -i "$NIC" -p tcp --dport 2087 -j DNAT --to-destination 127.0.0.1:5087
    add_nat PREROUTING -i "$NIC" -p tcp --dport 2083 -j DNAT --to-destination 127.0.0.1:5083
    add_nat PREROUTING -i "$NIC" -p tcp --dport 2096 -j DNAT --to-destination 127.0.0.1:5096
    add_in INPUT -i "$NIC" -p tcp --dport 2086 -j DROP
    add_in INPUT -i "$NIC" -p tcp --dport 2082 -j DROP
    add_in INPUT -i "$NIC" -p tcp --dport 2095 -j DROP
    iptables-save > /etc/sysconfig/iptables
    green "  Kurallar kaydedildi (/etc/sysconfig/iptables)"
    blue "=== 8/8  Servisler etkinlestiriliyor ==="
    nginx -t
    systemctl enable --now cpanel-gate.service
    systemctl enable --now nginx
    systemctl enable iptables
    systemctl reload nginx 2>/dev/null || systemctl restart nginx
    sleep 1
    green "  Aktif servisler:"
    for s in cpanel-gate nginx iptables; do
      printf "    %-20s %s\n" "$s" "$(systemctl is-active $s)"
    done
    echo ""
    blue "=== Sonuc ==="
    green "Kurulum tamamlandi."
    echo ""
    echo "Test: https://$(hostname):2087/   (gate sayfasi gelmeli)"
    echo "Parola degistirmek icin: /opt/cpanel-gate/gate.py icindeki PASSWORD satiri,"
    echo "ardindan 'systemctl restart cpanel-gate'."
    echo ""
    echo "Loglar:"
    echo "  /var/log/cpanel-gate/gate.log         (basari/hata kayitlari)"
    echo "  /var/log/cpanel-gate/nginx-access.log (HTTP istekleri)"
    echo ""
    echo "Geri almak icin:  bash $PKG_DIR/uninstall.sh"

    Gate.py - Servis kodu
    #!/usr/bin/env python3
    """
    cPanel/WHM Yetkilendirme Kapısı  —  meohost
    nginx önünde 127.0.0.1:8123'te dinler. auth_request + giriş formu sağlar.
      GET  /              -> giriş sayfası (HTML form)
      POST /__gate/verify -> domain:anahtar doğrula, HMAC cookie ver
      GET  /__gate/auth   -> nginx auth_request: cookie geçerliyse 200, değilse 401
    Hazırlayan: meohost
    """
    # ============================================================================
    #  YAPILANDIRMA  —  buradan değiştir
    # ============================================================================
    # Geçiş anahtarı. Kullanıcı 'domain:ANAHTAR' formatında girer.
    #   Örnek:  meohost.com:meohost   ->  PASSWORD = 'meohost'
    PASSWORD = 'meohost'
    # Cookie geçerlilik süresi (saniye). 600 = 10 dakika.
    TTL = 600
    # Servisin dinleyeceği yerel adres ve port. nginx buraya proxy eder.
    LISTEN_HOST = '127.0.0.1'
    LISTEN_PORT = 8123
    # Cookie adı — genelde değiştirmeye gerek yok.
    COOKIE_NAME = 'cp_gate_token'
    # HMAC secret dosyası. İlk kurulumda otomatik üretildi (32 byte hex).
    # Yeniden üretmek için:
    #   python3 -c "import secrets; print(secrets.token_hex(32))" > /etc/cpanel-gate/secret
    SECRET_FILE = '/etc/cpanel-gate/secret'
    # ============================================================================
    #  Aşağısı çekirdek  —  değiştirmeye gerek yok
    # ============================================================================
    import http.server
    import socketserver
    import hmac
    import hashlib
    import time
    import urllib.parse
    import subprocess
    import sys
    import re
    from http.cookies import SimpleCookie
    SECRET = open(SECRET_FILE).read().strip().encode()
    DOMAIN_RE = re.compile(r'^[a-z0-9]([a-z0-9\-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9\-]{0,61}[a-z0-9])?)+$')
    
    def domain_owned(domain):
        if not DOMAIN_RE.match(domain) or len(domain) > 253:
            return False
        try:
            r = subprocess.run(
                ['/scripts/whoowns', domain],
                stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                timeout=3
            )
            owner = r.stdout.decode('utf-8', errors='replace').strip()
            return bool(owner) and owner != 'nobody' and owner != 'root'
        except Exception:
            return False
    
    def make_token(expires):
        msg = str(expires).encode()
        sig = hmac.new(SECRET, msg, hashlib.sha256).hexdigest()
        return f"{expires}.{sig}"
    
    def verify_token(token):
        try:
            expires_str, sig = token.split('.', 1)
            expires = int(expires_str)
            if expires < int(time.time()):
                return False
            expected = hmac.new(SECRET, expires_str.encode(), hashlib.sha256).hexdigest()
            return hmac.compare_digest(expected, sig)
        except Exception:
            return False
    
    GATE_HTML = """<!DOCTYPE html>
    <html lang="tr"><head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
    <meta name="robots" content="noindex,nofollow,nosnippet,noarchive">
    <meta name="referrer" content="no-referrer">
    <title>Yetkilendirme — meohost</title>
    <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10' fill='%23173a5e'/%3E%3Ctext x='12' y='17' font-family='Georgia,serif' font-size='14' font-style='italic' fill='%23faf8f3' text-anchor='middle'%3Em%3C/text%3E%3C/svg%3E">
    <style>
    *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
    :root{
      --bg:#faf8f3;
      --bg-soft:#f0ece2;
      --border:#d4cdb9;
      --border-soft:#e8e3d3;
      --text:#0c1428;
      --text-muted:#5a6275;
      --text-dim:#9a9990;
      --accent:#173a5e;
      --accent-soft:rgba(23,58,94,.07);
      --danger:#a4422f;
    }
    html{height:100%}
    body{
      font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;
      background:var(--bg);
      color:var(--text);
      min-height:100vh;
      display:grid;
      grid-template-rows:auto 1fr auto;
      letter-spacing:-.005em;
      -webkit-font-smoothing:antialiased;
      -moz-osx-font-smoothing:grayscale;
    }
    /* hafif kâğıt dokusu — statik */
    body::before{
      content:'';position:fixed;inset:0;
      background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.18  0 0 0 0 0.18  0 0 0 0 0.22  0 0 0 0.35 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
      opacity:.4;pointer-events:none;mix-blend-mode:multiply;
      z-index:0;
    }
    header,main,footer{position:relative;z-index:1}
    header{
      padding:26px 38px 0 38px;
      display:flex;align-items:center;justify-content:flex-end;
    }
    .brand-link{
      font-family:ui-serif,Charter,Cambria,'Times New Roman',Georgia,serif;
      font-size:14px;font-style:italic;letter-spacing:-.005em;
      color:var(--accent);text-decoration:none;
      border-bottom:1px solid transparent;
      transition:border-color .15s ease;
    }
    .brand-link:hover{border-color:var(--accent)}
    main{
      display:flex;align-items:center;justify-content:center;
      padding:40px 24px;
    }
    .panel{width:100%;max-width:420px}
    .eyebrow{
      font-family:ui-monospace,'SF Mono',Menlo,monospace;
      font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
      color:var(--text-dim);margin-bottom:22px;
      display:flex;align-items:center;gap:12px;
    }
    .eyebrow::before{
      content:'';display:inline-block;width:20px;height:1px;background:var(--accent);
    }
    h1{
      font-family:ui-serif,Charter,'Iowan Old Style',Cambria,'Times New Roman',Georgia,serif;
      font-weight:400;font-size:36px;line-height:1.12;letter-spacing:-.024em;
      margin-bottom:16px;color:var(--text);
    }
    h1 em{font-style:italic;color:var(--accent)}
    .lede{
      font-size:14.5px;line-height:1.6;color:var(--text-muted);
      margin-bottom:38px;max-width:380px;
    }
    form{margin:0}
    .field{
      display:flex;align-items:stretch;
      border-bottom:1px solid var(--border);
      transition:border-color .2s ease;
    }
    .field:focus-within{border-color:var(--accent)}
    input[name=credentials]{
      flex:1;min-width:0;
      background:transparent;border:0;outline:0;
      padding:14px 0;
      font-family:ui-monospace,'SF Mono',Menlo,Consolas,monospace;
      font-size:15px;color:var(--text);letter-spacing:-.005em;
      caret-color:var(--accent);
    }
    input[name=credentials]::placeholder{color:var(--text-dim);font-weight:400}
    button{
      background:transparent;border:0;cursor:pointer;
      color:var(--accent);
      font:inherit;font-size:13px;font-weight:500;letter-spacing:-.005em;
      padding:0 0 0 16px;flex-shrink:0;
      display:inline-flex;align-items:center;gap:6px;
      transition:opacity .15s ease;
    }
    button:hover{opacity:.72}
    button .arrow{display:inline-block;transition:transform .2s ease}
    button:hover .arrow{transform:translateX(3px)}
    .err{
      margin-top:18px;font-size:13px;color:var(--danger);
      font-family:ui-monospace,Menlo,monospace;letter-spacing:-.005em;
      min-height:16px;
      display:flex;align-items:center;gap:10px;
      opacity:0;transform:translateY(-2px);
      transition:opacity .25s ease,transform .25s ease;
    }
    .err.show{opacity:1;transform:translateY(0)}
    .err::before{
      content:'';width:2px;height:13px;background:var(--danger);display:inline-block;flex-shrink:0;
    }
    .hint{
      margin-top:46px;
      padding-top:22px;
      border-top:1px solid var(--border-soft);
      font-size:12.5px;line-height:1.7;color:var(--text-muted);
      max-width:380px;
    }
    .hint code{
      font-family:ui-monospace,'SF Mono',Menlo,monospace;
      font-size:11.5px;
      color:var(--text);
      background:var(--bg-soft);
      padding:1px 6px;border-radius:3px;
      border:1px solid var(--border-soft);
    }
    footer{
      padding:22px 38px 26px 38px;
      font-family:ui-monospace,'SF Mono',Menlo,monospace;
      font-size:10.5px;color:var(--text-dim);letter-spacing:.06em;
      text-align:center;
    }
    footer .credit{
      font-size:11px;letter-spacing:.14em;text-transform:uppercase;
      color:var(--text-muted);
    }
    footer .credit b{color:var(--accent);font-weight:600;font-style:italic;font-family:ui-serif,Charter,Georgia,serif;text-transform:none;letter-spacing:-.005em;font-size:13px}
    footer .ts{
      margin-top:8px;font-size:10px;color:var(--text-dim);letter-spacing:.08em;
    }
    @media (max-width:520px){
      header,footer{padding-left:22px;padding-right:22px}
      main{padding:24px 22px}
      h1{font-size:28px}
      .lede{font-size:14px;margin-bottom:32px}
      .hint{margin-top:36px}
    }
    </style></head>
    <body>
    <header>
      <a href="https://meohost.com" class="brand-link" target="_blank" rel="noopener noreferrer">meohost</a>
    </header>
    <main>
      <div class="panel">
        <div class="eyebrow">Yetkilendirme</div>
        <h1>Yalnızca <em>Müşterilerimize</em>.</h1>
        <p class="lede">Bu sunucuda hesabı olan birinin domain'i ve gizli anahtarı, geçmek için yeterli.</p>
        <form method="post" action="/__gate/verify" autocomplete="off" novalidate>
          <div class="field">
            <input
              name="credentials"
              placeholder="ornek.com:anahtar"
              autofocus required
              spellcheck="false" autocapitalize="off" autocorrect="off"
              inputmode="text">
            <button type="submit">Aç&nbsp;<span class="arrow">→</span></button>
          </div>
          <div class="err" id="err"></div>
        </form>
        <div class="hint">
          Format: <code>domain:anahtar</code>. Domain bu sunucuda barınmıyorsa veya anahtar hatalıysa giriş düşer. Oturum 10 dakika sonra biter ve aynı kapı yeniden açılır.
        </div>
      </div>
    </main>
    <footer>
      <div class="credit"><b>meohost</b> tarafından hazırlanmıştır</div>
      <div class="ts" id="ts">—</div>
    </footer>
    <script>
    (function(){
      try{
        var u=new URL(location);
        var e=u.searchParams.get('err');
        if(e){
          var n=document.getElementById('err');
          n.textContent=e;
          requestAnimationFrame(function(){n.classList.add('show')});
          history.replaceState(null,'',u.pathname);
        }
        var ts=document.getElementById('ts');
        function pad(x){return String(x).padStart(2,'0')}
        function tick(){
          var d=new Date();
          ts.textContent=d.getUTCFullYear()+'-'+pad(d.getUTCMonth()+1)+'-'+pad(d.getUTCDate())+' '+pad(d.getUTCHours())+':'+pad(d.getUTCMinutes())+' UTC';
        }
        tick(); setInterval(tick,30000);
      }catch(_){}
    })();
    </script>
    </body></html>"""
    
    class Handler(http.server.BaseHTTPRequestHandler):
        server_version = 'gate/1.0'
        sys_version = ''
        def log_message(self, fmt, *args):
            sys.stderr.write("[gate] %s - %s\n" % (self.address_string(), fmt % args))
        def _send_simple(self, code, body=b'', ctype='text/plain'):
            self.send_response(code)
            self.send_header('Content-Type', ctype)
            self.send_header('Content-Length', str(len(body)))
            self.send_header('X-Content-Type-Options', 'nosniff')
            self.send_header('X-Frame-Options', 'DENY')
            self.end_headers()
            if body:
                self.wfile.write(body)
        def do_GET(self):
            path = self.path.split('?', 1)[0]
            if path == '/auth':
                cookies = SimpleCookie(self.headers.get('Cookie', ''))
                tok = cookies.get(COOKIE_NAME)
                if tok and verify_token(tok.value):
                    self._send_simple(200)
                else:
                    self._send_simple(401)
                return
            if path == '/health':
                self._send_simple(200, b'ok')
                return
            
            self._send_simple(200, GATE_HTML.encode('utf-8'), 'text/html; charset=utf-8')
        def do_POST(self):
            if self.path != '/verify':
                self._send_simple(404)
                return
            try:
                length = int(self.headers.get('Content-Length', '0'))
            except ValueError:
                self._send_simple(400)
                return
            if length <= 0 or length > 1024:
                self._send_simple(400)
                return
            body = self.rfile.read(length).decode('utf-8', errors='replace')
            params = urllib.parse.parse_qs(body)
            cred = (params.get('credentials') or [''])[0].strip()
            if ':' not in cred:
                return self._fail('Format: domain:sifre')
            domain, password = cred.split(':', 1)
            domain = domain.strip().lower().lstrip('www.')
            if not password or not domain:
                return self._fail('Eksik bilgi')
        
            pw_ok = hmac.compare_digest(password.encode(), PASSWORD.encode())
            dom_ok = domain_owned(domain) if pw_ok else False  
            time.sleep(0.4)  
            if not (pw_ok and dom_ok):
                sys.stderr.write("[gate] FAIL ip=%s domain=%r\n" % (self.address_string(), domain))
                return self._fail('Hatali bilgi')
            sys.stderr.write("[gate] OK   ip=%s domain=%s\n" % (self.address_string(), domain))
            expires = int(time.time()) + TTL
            token = make_token(expires)
            self.send_response(302)
            self.send_header('Location', '/')
            self.send_header(
                'Set-Cookie',
                f'{COOKIE_NAME}={token}; Path=/; Secure; HttpOnly; SameSite=Strict; Max-Age={TTL}'
            )
            self.send_header('Content-Length', '0')
            self.end_headers()
        def _fail(self, msg):
            self.send_response(302)
            self.send_header('Location', f'/?err={urllib.parse.quote(msg)}')
            self.send_header('Content-Length', '0')
            self.end_headers()
    
    class ThreadedServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
        daemon_threads = True
        allow_reuse_address = True
    
    if __name__ == '__main__':
        srv = ThreadedServer((LISTEN_HOST, LISTEN_PORT), Handler)
        sys.stderr.write(f"[gate] listening on {LISTEN_HOST}:{LISTEN_PORT}\n")
        srv.serve_forever()

    Cpanel gate service komutu
    [Unit]
    Description=cPanel/WHM access gate
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=simple
    User=root
    Environment=PYTHONUNBUFFERED=1
    ExecStart=/usr/bin/python3 /opt/cpanel-gate/gate.py
    Restart=on-failure
    RestartSec=2s
    StandardOutput=append:/var/log/cpanel-gate/gate.log
    StandardError=append:/var/log/cpanel-gate/gate.log
    NoNewPrivileges=yes
    ProtectSystem=strict
    ReadWritePaths=/var/log/cpanel-gate
    PrivateTmp=yes
    
    [Install]
    WantedBy=multi-user.target

    cpanel gate nginx conf yapısı
    # cPanel/WHM erişim kapısı
    # Harici 2087/2083 trafiği iptables tarafından bu dahili dinleyicilere DNAT ile yönlendirilir.
    # nginx, gate servisi aracılığıyla geçerli bir HMAC çerezi olup olmadığını kontrol eder; aksi halde
    # gate sayfasını sunar. Geçerli bir çerez varsa, trafik yerel cpsrvd’ye proxy’lenir.
    limit_req_zone $binary_remote_addr zone=cpgate_login:10m rate=10r/m;
    limit_req_zone $binary_remote_addr zone=cpgate_general:10m rate=120r/m;
    
    upstream cpsrvd_whm     { server 127.0.0.1:2087; }
    upstream cpsrvd_cpanel  { server 127.0.0.1:2083; }
    upstream cpsrvd_webmail { server 127.0.0.1:2096; }
    
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5:!3DES;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:5m;
    ssl_session_timeout 1h;
    
    map $server_port $upstream_target {
        5087  https://cpsrvd_whm;
        5083  https://cpsrvd_cpanel;
        5096  https://cpsrvd_webmail;
        default https://cpsrvd_cpanel;
    }
    map $server_port $external_port {
        5087  2087;
        5083  2083;
        5096  2096;
        default 2087;
    }
    server {
        listen 127.0.0.1:5087 ssl http2;
        listen 127.0.0.1:5083 ssl http2;
        listen 127.0.0.1:5096 ssl http2;
        server_name _;
        ssl_certificate     /etc/cpanel-gate/ssl/cert.pem;
        ssl_certificate_key /etc/cpanel-gate/ssl/key.pem;
        error_page 497 =301 https://$host:$external_port$request_uri;
    
        access_log /var/log/cpanel-gate/nginx-access.log;
        error_log  /var/log/cpanel-gate/nginx-error.log warn;
        client_max_body_size 4096m;
        client_body_timeout  3600s;
        proxy_read_timeout   3600s;
        proxy_send_timeout   3600s;
        proxy_connect_timeout 30s;
        send_timeout         3600s;
    
        location = /__gate/verify {
            limit_req zone=cpgate_login burst=3 nodelay;
            proxy_pass http://127.0.0.1:8123/verify;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto https;
        }
        location = /__gate/ {
            proxy_pass http://127.0.0.1:8123/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
        location = /__gate/auth {
            internal;
            proxy_pass http://127.0.0.1:8123/auth;
            proxy_pass_request_body off;
            proxy_set_header Content-Length "";
            proxy_set_header Cookie $http_cookie;
            proxy_set_header X-Real-IP $remote_addr;
        }
        location / {
            limit_req zone=cpgate_general burst=60 nodelay;
            auth_request /__gate/auth;
            error_page 401 = @show_gate;
            proxy_pass $upstream_target;
            proxy_ssl_verify off;
            proxy_ssl_server_name on;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto https;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            # Disable buffering for streaming responses
            proxy_buffering off;
            proxy_request_buffering off;
        }
        location @show_gate {
            proxy_pass http://127.0.0.1:8123;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    Kurulum Dökümanı

    cPanel Gate

    cPanel/WHM giriş portlarının önüne nginx tabanlı bir kapı koyar. Bot taramaları ve CVE-2026-41940 gibi auth-bypass açıkları cpsrvd'ye hiç ulaşmadan kapıda durur.
    Mantık

    cpsrvd 2082/2083/2086/2087/2095/2096 portlarında tüm dünyaya açık dinler — sıfır gün açıkları doğrudan oradan giriyor. Bu paket trafiği iptables ile yerel nginx'e DNAT eder. nginx önce HMAC imzalı bir çerez ister, çerez yoksa cpsrvd'ye tek byte geçmez.
    arola ile bir kerelik doğrulama yapılır. Domain'i cPanel'in kendi /scripts/whoowns'u tanıyor mu, ona bakar — WHM'de yeni hesap açıldığında o domain otomatik geçer.
    Portlar

    2083 / 2087 / 2096 → kapının arkasında2082 / 2086 / 2095 → DROP (HTTPS varken HTTP'ler işsiz)
    Mail portları (25, 465, 587, 110, 143, 993, 995) ellenmez. HTTP olmadıkları için bu kapıyla korunamaz, onları cPHulk + güçlü parola tutar.
    Gereksinimler

    AlmaLinux/RHEL 8 veya 9, cPanel 11.110+, root erişim. nginx yoksa script kurar.
    Kurulum

    cd /root/cpanel-gate-package bash install.sh
    Script şunları yapar:
    • iptables yedeğini /root/cpanel-gate-backup/ altına alır
    • nginx ve iptables-services'i kurar
    • /opt/cpanel-gate/gate.py'ı yerleştirir, HMAC secret üretir
    • cpsrvd sertifikasını key/cert olarak ayrıştırıp nginx'e verir
    • systemd unit, nginx config ve sysctl dosyalarını koyar
    • DNAT/DROP kurallarını uygular ve kalıcı yazar
    • Servisleri başlatır
    Bitince https://sunucun:2087/ kapı sayfasını gösterir.
    Manuel kurulum

    Paketler:
    dnf install -y nginx iptables-services
    Dosyaları yerleştir:
    mkdir -p /opt/cpanel-gate /etc/cpanel-gate/ssl /var/log/cpanel-gate cp gate.py /opt/cpanel-gate/gate.py cp cpanel-gate.service /etc/systemd/system/cpanel-gate.service cp cpanel-gate-nginx.conf /etc/nginx/conf.d/cpanel-gate.conf cp 99-cpanel-gate.conf /etc/sysctl.d/99-cpanel-gate.conf chmod 0755 /opt/cpanel-gate/gate.py
    HMAC secret üret:
    python3 -c "import secrets; print(secrets.token_hex(32))" > /etc/cpanel-gate/secret chmod 600 /etc/cpanel-gate/secret
    cpsrvd sertifikasını key + cert olarak ayır:
    python3 - <<'EOF' import re src = open('/var/cpanel/ssl/cpanel/cpanel.pem').read() key = re.search(r'-----BEGIN (RSA )?PRIVATE KEY-----.*?-----END (RSA )?PRIVATE KEY-----', src, re.DOTALL) certs = re.findall(r'-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----', src, re.DOTALL) open('/etc/cpanel-gate/ssl/key.pem','w').write(key.group(0)+'n') open('/etc/cpanel-gate/ssl/cert.pem','w').write('n'.join(certs)+'n') EOF chmod 640 /etc/cpanel-gate/ssl/{key,cert}.pem chown root:nginx /etc/cpanel-gate/ssl/{key,cert}.pem
    /etc/nginx/nginx.conf içinde listen 80 default_server; olan server bloğunu yorum satırı yap.
    iptables + sysctl (kendi NIC'inle):
    NIC=$(ip -o -4 route show to default | awk '{print $5}') sed -i "s/ens192/$NIC/g" /etc/sysctl.d/99-cpanel-gate.conf sysctl --system
    iptables -t nat -A PREROUTING -i $NIC -p tcp --dport 2087 -j DNAT --to-destination 127.0.0.1:5087 iptables -t nat -A PREROUTING -i $NIC -p tcp --dport 2083 -j DNAT --to-destination 127.0.0.1:5083 iptables -t nat -A PREROUTING -i $NIC -p tcp --dport 2096 -j DNAT --to-destination 127.0.0.1:5096 iptables -I INPUT -i $NIC -p tcp --dport 2086 -j DROP iptables -I INPUT -i $NIC -p tcp --dport 2082 -j DROP iptables -I INPUT -i $NIC -p tcp --dport 2095 -j DROP
    iptables-save > /etc/sysconfig/iptables systemctl enable iptables
    Servisleri ayağa kaldır:
    nginx -t systemctl daemon-reload systemctl enable --now cpanel-gate nginx
    Ayarlar

    /opt/cpanel-gate/gate.py'ın başında duruyor:
    PASSWORD = 'meohost' # giriş parolası TTL = 600 # çerez süresi (sn) LISTEN_HOST = '127.0.0.1' LISTEN_PORT = 8123 COOKIE_NAME = 'cp_gate_token' SECRET_FILE = '/etc/cpanel-gate/secret'
    Default parolayı bırakma. 12+ karakter, harf-rakam-sembol karışık bir şeyle değiştir sonra systemctl restart cpanel-gate.
    Komutlar

    systemctl status cpanel-gate nginx systemctl restart cpanel-gate journalctl -u cpanel-gate -f tail -f /var/log/cpanel-gate/gate.log iptables -t nat -L PREROUTING -n -v
    HMAC secret'i yenilemek istersen (eski çerezlerin hepsi iptal olur):
    python3 -c "import secrets; print(secrets.token_hex(32))" > /etc/cpanel-gate/secret systemctl restart cpanel-gate
    Geri çekme

    DNAT'ı söker, cpsrvd yine doğrudan açılır:
    iptables -t nat -F PREROUTING iptables-save > /etc/sysconfig/iptables
    Yedeği geri al:
    iptables-restore < /root/cpanel-gate-backup/iptables-YYYYMMDD-HHMMSS.rules iptables-save > /etc/sysconfig/iptables
    Tamamen kaldır: bash uninstall.sh
    Sorun giderme

    Bad Gateway — nginx çalışmıyordur. systemctl status nginx, nginx -t.
    Parola doğru ama kabul etmiyor — domain bu sunucuda barınmıyordur. /scripts/whoowns ornek.com boş dönerse cPanel o domaini tanımıyordur.
    Reboot sonrası kapı açılmıyor — systemctl is-enabled iptables enabled olmalı, sysctl net.ipv4.conf.all.route_localnet 1 olmalı.
    Çerez sürekli reddediliyor — HMAC secret değişmiştir, tarayıcıdan çerezleri temizle.
    Terminal/File Manager boş açılıyor — nginx config'inde WebSocket Upgrade header'ı geçmiyor, cpanel-gate.conf'a bak.
    Nasıl çalışıyor

    Çerez HMAC-SHA256 ile imzalı, sunucu state tutmuyor (stateless). DNAT route_localnet=1 sayesinde 127.0.0.1'e yönleniyor, cpsrvd tek satır ayar değişmeden yerinde kalıyor.
    nginx'in auth_request direktifi her istek için gate servisine soruyor — çerez geçerliyse 200, değilse 401 ve giriş sayfası. /__gate/verify dakikada 10 deneme ile rate-limit'li.
    WebSocket (cPanel Terminal, File Manager) için Upgrade header'ları geçer, timeout 1 saat.
  • 01-05-2026, 03:33:07
    #2
    Bu kadar içeriğe gerek yok aslında bilen var bilmeyen var sitene yükleyip wget ile çektirip çalıştırma komutu ekletip açıklamayla zenginleştirebilirdin. Yinede teşekkürler.
  • 01-05-2026, 03:40:49
    #3
    GiRGiN adlı üyeden alıntı: mesajı görüntüle
    Bu kadar içeriğe gerek yok aslında bilen var bilmeyen var sitene yükleyip wget ile çektirip çalıştırma komutu ekletip açıklamayla zenginleştirebilirdin. Yinede teşekkürler.


    teşekkürler geri bildirim için Ama açıkçası güvenlik paylaşımında wget | bash ile çektirip çalıştırın demek bana biraz ters geliyor zaten cPanel-Gate’in çözmeye çalıştığı senaryo tam olarak bu. İçeriği açık bıraktım ki bilen okusun, bilmeyen de en azından sunucusunda neyin çalıştığını görsün.
    Bir dahaki paylaşımda kurulumu daha sade tutmaya çalışırım, tavsiye için teşekkürler
  • 01-05-2026, 03:59:36
    #4
    Paylaşım için teşekkürler. Sunucularımdan birne girildiğini görünce formatlayıp sıfırdan cpanel kurmak zorunda kaldım. Şimdi de nasıl php5.6 kurabilirim onun derdine düştüm Sadece cpsrvd dinlediği portları (2083,2096 vs.) kapatarak güvenlik önlenebilir mi? iptables ile drop, reject yaptım ama emin olamadım.
  • 01-05-2026, 04:08:43
    #5
    ysnozkn adlı üyeden alıntı: mesajı görüntüle
    Paylaşım için teşekkürler. Sunucularımdan birne girildiğini görünce formatlayıp sıfırdan cpanel kurmak zorunda kaldım. Şimdi de nasıl php5.6 kurabilirim onun derdine düştüm Sadece cpsrvd dinlediği portları (2083,2096 vs.) kapatarak güvenlik önlenebilir mi? iptables ile drop, reject yaptım ama emin olamadım.
    iptables -I INPUT -p tcp --dport 2087 -s SENİN_IP -j ACCEPT
    iptables -A INPUT -p tcp --dport 2087 -j DROP
    iptables-save > /etc/sysconfig/iptables
    Şeklinde izin vererek sen erişebilirsin lakin sabit ip değilse bu problem yaratır
    Bu kritik açık 2087 portunu kıllanarak giriyor konuda paylaştığım script yapısını kurarsanız drop etmeye gerek kalmaz gönül rahatlığıyla kullanabilirsiniz hocam
    Ea4 de hala 5.6 php olması lazım ordan kurulabilir ama sürüm olarak çok eski onuda bir an önce yeni sürümlere geçirmekte fayda var script kurmak isterseniz ve yardıma ihtiyacınız olursa yazabilirsiniz kurulumda yardımcı oluruz
  • 01-05-2026, 04:28:04
    #6
    Benim için Capnel arayüzüne girmeye gerek duymadığım için kapatsam sorun olmaz, ssh yeterli olacaktır. Yardım teklifin için teşekkürler. Scriptin yeniden kodlanması gerekiyor da ne zaman nede para yok, o yüzden şimdilik eski düzende devam edeceğim.
  • 01-05-2026, 08:26:13
    #7
    E-ticaret, Seo ve Yazılım
    MeoHost adlı üyeden alıntı: mesajı görüntüle
    Merhaba,
    Son dönemde cPanel / WHM tarafında ortaya çıkan kritik login/auth bypass riskleri, özellikle WHM erişimi internete açık olan sunucular için ciddi tehdit oluşturmaktadır.
    Bu tip zafiyetlerde saldırganlar, bazı durumlarda normal kullanıcı adı/şifre doğrulamasını geçmeden WHM API, session, cookie veya login akışındaki zayıflıkları hedefleyerek yetkisiz erişim elde etmeye çalışabilir.
    Aşağıdaki liste; şüpheli UID 0 kullanıcıları, SSH backdoor’ları, cron persistence, reverse shell, web shell, WHM access log izleri ve sistem sertleştirme adımlarını kontrol etmek için hazırlanmıştır.
    Detaylı incelemelerimiz sonucu bu tarz saldırıya mağruz kalan sunucularımızdan elde ettiğimiz bilgileri sizlerlede paylaşmak istiyoruz. Sunucu temizlendikten sonra güvenlik önlemi olarak alabileceğiniz scripti de ek olarak paylaşıyoruz.
    Faydalı bilgi ve güzel düşünceniz için teşekkür ederim.