I have a ISP provided router which does not allow me ssh or telnet access but I have access to login page using user credentials. I want to setup a python script that can login to the router and restart it at fixed intervals. I have tried to replicate the process in code but as I am not knowledgeable with Javascript I am not sure where the issue is. Here is the router page
The username and password is encoded in submit function with token and nonce variables.
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);
The encrytion takes place in crypto_page.js as follows:
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)
};
};
Stanford Javascript Crypto Library(SJCL) is used as well for RNG and base64 encoding.
I monitored the request in my browser and got the following curl for login POST request:
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."
With this said, here is how I am replicating this in my python code:
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 = \'[\S\s]+\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)
At this stage I expect status code 299 to suggest I have successful login and then I can use the Sid from cookie for further requests, but I get the login page again with 200 code.
Here is what data I am generating with my code:
{'encrypted': '1', 'ct': '6gnxUtvKvU8URRNtvbR0wzQXSflWZeJMKKykcpYhHjD7Bq5SZfHF0qONXj1iLpbR1WhbCNDcCxnI9ETs8bnzzCS4dxFVxL3qk6MplnngNHcmQXRE93vF49VlhjaBNG3SbLUZaLIeTNFf2pAypWZ2ZC6CEXy_j46MOGq0uAeQcmx2_gqywEcXd2Qsr54Q9Vs0mCLeukVo-CvgFkGYfX4VvCUdru2FBh1pjipwwWHsE-UMg8SZm50lr7EscEIblzte', 'ck': 'cJUmZmywxagF2diMCHGiYepOaNkqIZOrQr_jTwGxsEJ-vWF5ewCHyqV5_FPn3YcNIuMv97WlWbSsz6fftIXVzOKxpT2f-S0yu4DzkseJheA44lTaNbXB_9k4V-rF9q1Gnrjx8ZFeefRUghIW6eVY64uuMG4M-aXcButYnowDG3o.'}