无法使用python登录路由器页面



我有一个ISP提供的路由器,它不允许我ssh或telnet访问,但我可以使用用户凭证访问登录页面。我想设置一个python脚本,可以登录到路由器,并在固定的时间间隔重新启动它。我试图在代码中复制过程,但由于我不熟悉Javascript,我不确定问题在哪里。这是路由器页面

用户名和密码在submit函数中使用token和nonce变量编码。

function submit() { 
var username = $(":input[id=username]").val();
var password = $(":input[id=password]").val();
var nonce = "xdtQP+ohCWNJ+cFPgHA+6METS83JPNO8qwrmFRV0Fos=";   
var token ="jFwIaetZSYKVzzDg";
var base64 = sjcl.codec.base64;
var dec_key = base64.fromBits(sjcl.random.randomWords(4, 0));
var dec_iv = base64.fromBits(sjcl.random.randomWords(4, 0));
var postdata  = '&username=' + username + '&password=' + encodeURIComponent(password) + '&csrf_token=' + token + '&nonce=' + nonce+'&enckey='+crypto_page.base64url_escape(dec_key)+'&enciv='+crypto_page.base64url_escape(dec_iv); 

var encryptdata = crypto_page.encrypt_post_data(pubkey, postdata);

加密在crypto_page.js中进行,如下所示:

var encrypt = function(pubkey, plaintext) {
var aeskey = sjcl.random.randomWords(4, 0);
var iv = sjcl.random.randomWords(4, 0);
var pt = sjcl.codec.utf8String.toBits(plaintext);
var aes = new sjcl.cipher.aes(aeskey);
var ct = sjcl.mode.cbc.encrypt(aes, pt, iv);

var rsa = new JSEncrypt(); 
if(rsa.setPublicKey(pubkey) == false)
return fasle;
var base64url = sjcl.codec.base64url;
var base64 = sjcl.codec.base64;
var aesinfo = base64.fromBits(aeskey) + ' ' + base64.fromBits(iv);
var ck = rsa.encrypt(aesinfo);
if(ck == false)
return false;
return {
ct:base64url.fromBits(ct),
ck:base64url_escape(ck)
};
};

斯坦福Javascript加密库(SJCL)也用于RNG和base64编码。

我在我的浏览器中监视请求,并得到以下curl登录POST请求:

curl "https://192.168.1.254/login.cgi" -X POST -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0" -H "Accept: */*" -H "Accept-Language: en-GB,en;q=0.5" -H "Accept-Encoding: gzip, deflate, br" -H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" -H "X-Requested-With: XMLHttpRequest" -H "Origin: https://192.168.1.254" -H "Connection: keep-alive" -H "Cookie: admin=deleted; lang=eng" -H "Sec-Fetch-Dest: empty" -H "Sec-Fetch-Mode: cors" -H "Sec-Fetch-Site: same-origin" --data-raw "encrypted=1&ct=YjxWNkPl51dGvfiKc2Rh9lmqNhrsAc0rRbIg0V1kuA5355vMdOOB85gv6skt4O--KlWE-h-mToXTnosw2YmcM7g2pn8YgmGyWGRHMiusRhUy9Qo3moXaysyGtIWGvqALeJIKXlI6NrqvtZO2UyIAguNizN__3E2b1JsRHZPwsuSC9joz0GVj7HgM3YyZm2L-IZk5E-Ge5lTwipvtvvKwFxlij_2raWRJuXzPssF62BLCJ33KLSs69Qdwxm8opTDg&ck=F7GyQZi4xH924EvF2RO9ZFRNDzZ2MTyLD_U5lrw2pofQ73xt0FNxuKLEiOvHYIlb_2mqaazr80sZlonLYRqYrFEoBkulpVa1PAzt6fzoH1n8wPN0mb4moKWDt5b0pT5SJHPIRub_sd6La96_mQvQFaJGm6_MeItaTMw8DLIvLag."

说了这些,下面是我如何在我的python代码中复制它:

import requests,re,json,base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
from urllib import parse
from config import username, password
from pprint import pprint
import warnings
import time
warnings.filterwarnings('ignore')
def randomWords(n):
return get_random_bytes(n)

def base64url_escape(b64):
out=""
b64 = b64.decode('utf-8')
for i in range(len(b64)):
c = b64[i]
if c == '+':
out += '-'
elif c == '/':
out += '_'
elif c == '=':
out += '.'
else:
out += c
return out.encode('utf-8')
def encrypt_post_data(pubkey, plaintext):
aeskey = randomWords(16)
iv = randomWords(16)
pt = pad(plaintext.encode('utf-8'), AES.block_size)
aes = AES.new(aeskey, AES.MODE_CBC, iv=iv)
ct = aes.encrypt(pt)

recipient_key = RSA.import_key(pubkey)
rsa = PKCS1_OAEP.new(recipient_key)
aesinfo = base64.b64encode(aeskey) + ' '.encode('utf-8') + base64.b64encode(iv)
# aesinfo = aeskey + ' '.encode('utf-8') + iv
ck = rsa.encrypt(aesinfo)
return {
'encrypted': '1',
'ct': base64.urlsafe_b64encode(ct).decode('utf-8'),
'ck': base64url_escape(base64.b64encode(ck)).decode('utf-8'),#base64.urlsafe_b64encode(ck).decode('utf-8'),
}

def main():
url = 'https://192.168.1.254/'
with requests.Session() as session:
cookies = {
'admin': 'deleted',
'lang': 'eng',
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'en-GB,en;q=0.5',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
}
response = session.get(url, cookies=cookies, headers=headers, verify=False)
print(response.status_code)
print(response.headers)
pubkey=re.findall(r"var pubkey = '[Ss]+n'",response.text)[0].split("'")[-2]
pubkey = re.sub(r"\","",pubkey)
nonce=re.findall(r"var nonce = "[S]+"",response.text)[0].split('"')[-2]
token=re.findall(r"var token ="[S]+"",response.text)[0].split('"')[-2]
dec_key = base64url_escape(base64.b64encode(randomWords(16)))
dec_iv = base64url_escape(base64.b64encode(randomWords(16)))
postdata  = '&username=' + username + '&password=' + parse.quote(password) + '&csrf_token=' + token + '&nonce=' + nonce + '&enckey=' + dec_key.decode('utf-8') +'&enciv=' + dec_iv.decode('utf-8')

data = encrypt_post_data(pubkey, postdata)
print(data)
cookies = {
'admin': 'deleted',
'lang': 'eng',
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0',
'Accept': '*/*',
'Accept-Language': 'en-GB,en;q=0.5',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
'Origin': 'https://192.168.1.254',
'Connection': 'keep-alive',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
}
time.sleep(5)
response = session.post(url+'login.cgi', cookies=cookies, headers=headers, data=data, verify=False)
print(response.status_code)

在这个阶段,我期望状态码299表示我已经成功登录,然后我可以使用cookie中的Sid进行进一步的请求,但是我再次获得200码的登录页面。

下面是我用代码生成的数据:

{'encrypted': '1', 'ct': '6gnxUtvKvU8URRNtvbR0wzQXSflWZeJMKKykcpYhHjD7Bq5SZfHF0qONXj1iLpbR1WhbCNDcCxnI9ETs8bnzzCS4dxFVxL3qk6MplnngNHcmQXRE93vF49VlhjaBNG3SbLUZaLIeTNFf2pAypWZ2ZC6CEXy_j46MOGq0uAeQcmx2_gqywEcXd2Qsr54Q9Vs0mCLeukVo-CvgFkGYfX4VvCUdru2FBh1pjipwwWHsE-UMg8SZm50lr7EscEIblzte', 'ck': 'cJUmZmywxagF2diMCHGiYepOaNkqIZOrQr_jTwGxsEJ-vWF5ewCHyqV5_FPn3YcNIuMv97WlWbSsz6fftIXVzOKxpT2f-S0yu4DzkseJheA44lTaNbXB_9k4V-rF9q1Gnrjx8ZFeefRUghIW6eVY64uuMG4M-aXcButYnowDG3o.'}

如果用户名和密码没有使用某种变量(例如时间)加密,则加密后的输出在任何给定时间都是相同的你可以在请求

中硬编码如果不是一个更简单的选择,那么使用selenium驱动程序登录,然后在成功登录后将cookie从selenium传递给请求,并简单地使用requests进行其余的调用

所以我研究了在@ahmed-mani建议的无头树莓派上使用硒,经过多次试验和错误,我得到了它的工作。首先,当前大多数支持selenium的浏览器都是无用的,而且对于pi - 0 w来说,它们是内存的大赘。Firefox和Chromium是200-400MB,然后像XFCE这样的桌面环境是另外150MB,再加上VNC服务器又是另外50MB。而且我实际上不能使用浏览器(Firefix拒绝打开,Chromium在加载网页时卡住)。

终于找到了关于RPi的phantomJS(早就弃用了)。删除了以前的浏览器,桌面和vnc服务器。安装selenium 3.141(支持PhantomJS的版本)。PhantonJs唯一的缺点是它不处理window.alert;window.confirm,所以我需要通过在该步骤插入javascript来解决问题。下面是我在一小部分内存中的工作脚本:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import warnings
from config import username, password
warnings.filterwarnings('ignore')
def main():
driver = webdriver.PhantomJS(executable_path="/usr/local/share/phantomjs-c2.1.1/bin/phantomjs")
try:
driver.get('http://192.168.1.254/')
print('loading page')
uname_input = '//*[@id="username"]'
pass_input = '//*[@id="password"]'
submit = '//*[@id="loginBT"]'
WebDriverWait(driver, 60).until(EC.presence_of_element_located((By.XPATH, uname_input)))
print('login form loaded ',driver.title)
driver.find_element_by_xpath(uname_input).send_keys(username)
driver.find_element_by_xpath(pass_input).send_keys(password)
driver.find_element_by_xpath(submit).click()
maintenance = '/html/body/div/section/div[1]/div/div[1]/ul[5]'
reboot = '//*[@id="do_reboot"]'
WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.XPATH, maintenance)))
print('login success')
driver.get('http://192.168.1.254/reboot.cgi')
WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.XPATH, reboot)))
print('found reboot button')
js_confirm = 'window.confirm = function(){return true;}'
driver.execute_script(js_confirm)
driver.find_element_by_xpath(reboot).click()
driver.execute_script('return window.confirm')
print('reboot done')
except Exception as e:
print(e)
finally:
driver.quit()
if __name__ == "__main__":
main()

最新更新