diff --git a/rci_auth.ps1 b/rci_auth.ps1 new file mode 100644 index 0000000..ed200d4 --- /dev/null +++ b/rci_auth.ps1 @@ -0,0 +1,82 @@ +$USER = "admin" +$PASS = "12985654" +$IP = "192.168.60.1" + +Write-Host "=== Тест авторизации Keenetic RCI ($IP) ===" + +# 1. Получение challenge и cookies +Write-Host "Step 1: Get challenge and session (cookies)..." +$cookie_file = "cookies.txt" +if (Test-Path $cookie_file) { Remove-Item $cookie_file } + +$resp = curl.exe -s -i -c $cookie_file "http://$IP/auth" +$realm = ($resp | Select-String "X-NDM-Realm: (.*)" | ForEach-Object { $_.Matches.Groups[1].Value.Trim() }) +$challenge = ($resp | Select-String "X-NDM-Challenge: (.*)" | ForEach-Object { $_.Matches.Groups[1].Value.Trim() }) + +if (-not $challenge) { + Write-Host "Error: Challenge not found in headers." -ForegroundColor Red + exit 1 +} + +Write-Host " Realm: $realm" +Write-Host " Challenge: $challenge" + +# 2. Вычисление MD5 +$md5 = [System.Security.Cryptography.MD5]::Create() +$h1_str = "${USER}:${realm}:${PASS}" +$h1_bytes = [System.Text.Encoding]::UTF8.GetBytes($h1_str) +$h1 = [System.BitConverter]::ToString($md5.ComputeHash($h1_bytes)).Replace("-", "").ToLower() + +# 5. Перебор вариантов хеширования +Write-Host "Step 2: Brute-forcing Hash variants..." +$sha256 = [System.Security.Cryptography.SHA256]::Create() +$sha512 = [System.Security.Cryptography.SHA512]::Create() + +$variants = @{ + "SHA256(challenge + MD5)" = [System.BitConverter]::ToString($sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("${challenge}${h1}"))).Replace("-", "").ToLower() + "SHA256(MD5 + challenge)" = [System.BitConverter]::ToString($sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("${h1}${challenge}"))).Replace("-", "").ToLower() + "SHA512(challenge + MD5)" = [System.BitConverter]::ToString($sha512.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("${challenge}${h1}"))).Replace("-", "").ToLower() + "SHA512(MD5 + challenge)" = [System.BitConverter]::ToString($sha512.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("${h1}${challenge}"))).Replace("-", "").ToLower() +} + +$rci_success = $false + +foreach ($v_name in $variants.Keys) { + $v_hash = $variants[$v_name] + Write-Host "`n--- Testing Variant: $v_name ($v_hash) ---" + + $json_payload = @{ + login = $USER + password = $v_hash + } | ConvertTo-Json -Compress + + $payload_file = "payload.json" + $json_payload | Out-File -FilePath $payload_file -Encoding utf8 + + Write-Host " Sending JSON POST to /auth..." + # Используем -b и -c для работы с куками, -d @file для JSON + $auth_resp = curl.exe -s -i -b $cookie_file -c $cookie_file -X POST -H "Content-Type: application/json" -d "@$payload_file" "http://$IP/auth" + $status = ($auth_resp | Select-String "HTTP/") + Write-Host " Status: $status" + + if ($status -match "200 OK") { + Write-Host " SUCCESS! Auth accepted via JSON POST." -ForegroundColor Green + Write-Host " Testing RCI access..." + $rci = curl.exe -s -b $cookie_file "http://$IP/rci/system/hostname" + Write-Host " RCI Response: $rci" + if ($rci -match "hostname") { + $rci_success = $true + break + } + } else { + Write-Host " Failed with status: $status" -ForegroundColor Yellow + $auth_resp | Select-String "{" | ForEach-Object { Write-Host " Server response: $_" } + } +} + +if ($rci_success) { + Write-Host "`nFINAL SUCCESS!" -ForegroundColor Green +} else { + Write-Host "`nFINAL FAILURE." -ForegroundColor Red + exit 1 +} diff --git a/rci_auth_test.sh b/rci_auth_test.sh new file mode 100644 index 0000000..329dd96 --- /dev/null +++ b/rci_auth_test.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# rci_auth_test.sh +# Скрипт для тестирования авторизации Keenetic RCI через X-NDM-Challenge + +USER="${1:-admin}" +PASS="${2:-password}" +IP="${3:-192.168.1.1}" + +echo "=== Тест авторизации Keenetic RCI ($IP) ===" + +# 1. Получаем challenge и realm +echo "Шаг 1: Получение challenge..." +resp_headers=$(curl -s -i "http://$IP/rci/system/hostname" | tr -d '\r') +realm=$(echo "$resp_headers" | grep -i "X-NDM-Realm" | cut -d' ' -f2) +challenge=$(echo "$resp_headers" | grep -i "X-NDM-Challenge" | cut -d' ' -f2) + +if [ -z "$challenge" ]; then + echo "Ошибка: Не удалось получить challenge. Проверьте IP или доступность RCI." + exit 1 +fi + +echo " Realm: $realm" +echo " Challenge: $challenge" + +# 2. Считаем MD5(login:realm:password) +# -n в echo важно, чтобы не было лишнего перевода строки +h1=$(echo -n "$USER:$realm:$PASS" | md5sum | cut -d' ' -f1) +echo "Шаг 2: MD5(login:realm:password) = $h1" + +# 3. Считаем SHA256(challenge + h1) +# challenge — это бинарная строка? Нет, обычно это hex или string. +# В Keenetic challenge (salt) конкатенируется с MD5 хешем (в виде строки). +hash=$(echo -n "$challenge$h1" | sha256sum | cut -d' ' -f1) +echo "Шаг 3: SHA256(challenge + MD5) = $hash" + +# 4. Пробуем авторизоваться +echo "Шаг 4: Попытка запроса с хешем..." +# Мы передаем HASH вместо пароля в Basic Auth +result=$(curl -s -u "$USER:$hash" "http://$IP/rci/system/hostname") + +if echo "$result" | grep -q "hostname"; then + echo "БИНГО! Авторизация через Basic Auth (Password=Hash) успешна." + echo "Ответ: $result" +else + echo "Метод 1 (Basic Auth) не удался. Пробуем Метод 2 (X-NDM-Auth-Hash)..." + result=$(curl -s -H "X-NDM-Auth-Hash: $hash" "http://$IP/rci/system/hostname") + if echo "$result" | grep -q "hostname"; then + echo "БИНГО! Авторизация через заголовок X-NDM-Auth-Hash успешна." + echo "Ответ: $result" + else + echo "Упс... Оба метода не удались." + echo "Полный ответ:" + echo "$result" + fi +fi diff --git a/rproxy b/rproxy index cfb624e..5d79a88 100644 --- a/rproxy +++ b/rproxy @@ -3,7 +3,7 @@ # Публикация локальных сервисов через SSH-туннели + nginx на VPS # http://5.104.75.50:3000/Petro1990/rProxy -VERSION="1.3.9" +VERSION="1.4.0" CONF_DIR="/opt/etc/rproxy" CONF_FILE="$CONF_DIR/rproxy.conf" SERVICES_DIR="$CONF_DIR/services" @@ -193,7 +193,18 @@ get_router_ip() { echo "192.168.1.1" } +# Метод 2: Через ndmq (Локальный доступ в Entware) +# Этот метод не требует пароля и работает мгновенно +rci_call() { + local command="$1" + ndmq -p "$command" 2>/dev/null +} + enable_router_basic_auth() { + log_info "Настройка локального доступа через RCI/ndmq..." + local hostname=$(rci_call "show system" | grep "hostname" | awk '{print $NF}') + log_info "Хостнейм роутера: $hostname" +} # Функция оставлена для обратной совместимости, но более не требуется для v1.3.9 return 0 } diff --git a/verify_hash.py b/verify_hash.py new file mode 100644 index 0000000..67b94db --- /dev/null +++ b/verify_hash.py @@ -0,0 +1,99 @@ +import struct +import hashlib + +def md4(data): + def f(x, y, z): return (x & y) | (~x & z) + def g(x, y, z): return (x & y) | (x & z) | (y & z) + def h(x, y, z): return x ^ y ^ z + def rot(x, n): return ((x << n) | (x >> (32 - n))) & 0xffffffff + + msg = data + b'\x80' + while len(msg) % 64 != 56: msg += b'\x00' + msg += struct.pack('