Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix no rsa javascript file found #31

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
, platforms = 'Linux'
, url = "https://github.com/schlatterbeck/snxvpn"
, scripts = ['snxconnect']
, install_requires = [ 'bs4', 'pycrypto', 'lxml' ]
, install_requires = [ 'bs4', 'pycrypto', 'lxml', 'rsa' ]
, classifiers = \
[ 'Development Status :: 3 - Alpha'
, 'License :: OSI Approved :: ' + license
Expand Down
146 changes: 71 additions & 75 deletions snxconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
import os.path
import sys
import socket
import rsa
import ssl
import time
try :
from urllib2 import build_opener, HTTPCookieProcessor, Request
from urllib2 import build_opener, HTTPCookieProcessor, Request, HTTPSHandler
from urllib import urlencode
from httplib import IncompleteRead
rsatype = long
except ImportError :
from urllib.request import build_opener, HTTPCookieProcessor, Request
from urllib.request import build_opener, HTTPCookieProcessor, Request, HTTPSHandler
from urllib.parse import urlencode
from http.client import IncompleteRead
rsatype = int
try :
from cookielib import LWPCookieJar
except ImportError :
Expand All @@ -23,7 +24,6 @@
from getpass import getpass
from argparse import ArgumentParser
from netrc import netrc, NetrcParseError
from Crypto.PublicKey import RSA
from struct import pack, unpack
from subprocess import Popen, PIPE
from snxvpnversion import VERSION
Expand Down Expand Up @@ -76,13 +76,19 @@ def __init__ (self, args) :
self.args = args
self.jar = j = LWPCookieJar ()
self.has_cookies = False
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
if self.args.cookiefile :
self.has_cookies = True
try :
j.load (self.args.cookiefile, ignore_discard = True)
except IOError :
self.has_cookies = False
self.opener = build_opener (HTTPCookieProcessor (j))
handlers = [HTTPCookieProcessor (j)]
if self.args.ssl_noverify:
handlers.append(HTTPSHandler(context=context))
self.opener = build_opener (*handlers)
self.nextfile = args.file
# end def __init__

Expand Down Expand Up @@ -114,7 +120,19 @@ def call_snx (self) :
f.write (answer)
f.close ()
print ("SNX connected, to leave VPN open, leave this running!")
answer = sock.recv (4096) # should block until snx dies
try:
while True:
time.sleep(4000000)
# answer = sock.recv (4096) # should block until snx dies
except KeyboardInterrupt:
sys.stdout.write ('\b\b\r')
sys.stdout.flush()
sys.stdout.write ("Shutting down ...\n")
sys.stdout.flush()
try:
sys.exit(0)
except SystemExit:
os._exit(0)
# end def call_snx

def debug (self, s) :
Expand Down Expand Up @@ -159,7 +177,7 @@ def login (self) :
self.open ()
self.debug (self.purl)
if self.purl.endswith ('Portal/Main') :
self.open ('sslvpn/SNX/extender')
self.open (self.args.extender)
self.parse_extender ()
self.generate_snx_info ()
return True
Expand All @@ -168,6 +186,7 @@ def login (self) :
self.jar.clear ()
self.next_file (self.purl)
self.debug (self.nextfile)
print ('Visiting login page ...')
self.open ()
self.debug (self.purl)
# Get the RSA parameters from the javascript in the received html
Expand All @@ -179,6 +198,7 @@ def login (self) :
else :
print ('No RSA javascript file found, cannot login')
return
print ('Fetching RSA javascript file ...')
self.open (do_soup = False)
self.parse_rsa_params ()
if not self.modulus :
Expand All @@ -191,22 +211,34 @@ def login (self) :
break
self.debug (self.nextfile)

enc = PW_Encode (modulus = self.modulus, exponent = self.exponent)
self.debug(self.purl)
password = rsa.pkcs1.encrypt(self.args.password.encode('UTF-8'), rsa.PublicKey(self.modulus, self.exponent))
password = ''.join ('%02x' % b_ord (c) for c in reversed (password))
d = dict \
( password = enc.encrypt (self.args.password)
( password = password
, userName = self.args.username
, selectedRealm = self.args.realm
, loginType = self.args.login_type
, vpid_prefix = self.args.vpid_prefix
, HeightData = self.args.height_data
)
self.debug (urlencode(d))
print ("Sending login information ...")
self.open (data = urlencode (d))
self.debug (self.purl)
self.debug (self.info)

errorMessage = self.soup.select_one(".errorMessage")
if errorMessage :
print ("Error: %s" % errorMessage.string)
return

while 'MultiChallenge' in self.purl :
d = self.parse_pw_response ()
otp = getpass ('One-time Password: ')
d ['password'] = enc.encrypt (otp)
otp = rsa.pkcs1.encrypt(self.args.password, rsa.PublicKey(self.modulus, self.exponent))
otp = ''.join ('%02x' % b_ord (c) for c in reversed (otp))
d ['password'] = otp
self.debug ("nextfile: %s" % self.nextfile)
self.debug ("purl: %s" % self.purl)
self.open (data = urlencode (d))
Expand All @@ -215,7 +247,8 @@ def login (self) :
if self.args.save_cookies :
self.jar.save (self.args.cookiefile, ignore_discard = True)
self.debug ("purl: %s" % self.purl)
self.open ('sslvpn/SNX/extender')
print ("Fetching extender information ...")
self.open (self.args.extender)
self.debug (self.purl)
self.debug (self.info)
self.parse_extender ()
Expand All @@ -224,6 +257,9 @@ def login (self) :
else :
print ("Unexpected response, looking for MultiChallenge or Portal")
self.debug ("purl: %s" % self.purl)
self.debug (getattr(self.soup.find('span', attrs={'class': 'errorMessage'}), 'string', ''))
if not self.soup.find('span', attrs={'class': 'errorMessage'}):
self.debug(self.soup)
return
# end def login

Expand All @@ -245,7 +281,7 @@ def open (self, filepart = None, data = None, do_soup = True) :
url = '/'.join (('%s:/' % self.args.protocol, self.args.host, filepart))
if data :
data = data.encode ('ascii')
rq = Request (url, data)
rq = Request (url, data, headers={'User-Agent': self.args.useragent})
self.f = f = self.opener.open (rq, timeout = 10)
if do_soup :
# Sometimes we get incomplete read. So we read everything
Expand All @@ -267,12 +303,13 @@ def parse_extender (self) :
program via a socket.
"""
for script in self.soup.find_all ('script') :
if '/* Extender.user_name' in script.text :
text = script.text or script.string or ""
if '/* Extender.user_name' in text :
break
else :
print ("Error retrieving extender variables")
return
for line in script.text.split ('\n') :
for line in text.split ('\n') :
if '/* Extender.user_name' in line :
break
stmts = line.split (';')
Expand Down Expand Up @@ -331,70 +368,13 @@ def parse_rsa_params (self) :
print ('No RSA parameters found, cannot login')
return
self.debug (repr (vars))
self.modulus = rsatype (vars ['modulus'], 16)
self.exponent = rsatype (vars ['exponent'], 16)
self.modulus = int (vars ['modulus'], 16)
self.exponent = int (vars ['exponent'], 16)

# end def parse_rsa_params

# end class HTML_Requester

class PW_Encode (object) :
""" RSA encryption module with special padding and reversing to be
compatible with checkpoints implementation.
Test with non-random padding to get known value:
>>> p = PW_Encode (testing = True)
>>> print (p.encrypt ('xyzzy'))
451c2d5b491ee22d6f7cdc5a20f320914668f8e01337625dfb7e0917b16750cfbafe38bfcb68824b30d5cc558fa1c6d542ff12ac8e1085b7a9040f624ab39f625cabd77d1d024c111e42fede782e089400d2c9b1d6987c0005698178222e8500243f12762bebba841eae331d17b290f80bca6c3f8a49522fb926646c24db3627
>>> print (p.encrypt ('XYZZYxyzzyXYZZYxyzzy'))
a529e86cf80dd131e3bdae1f6dbab76f67f674e42041dde801ebdb790ab0637d56cc82f52587f2d4d34d26c490eee3a1ebfd80df18ec41c4440370b1ecb2dec3f811e09d2248635dd8aab60a97293ec0315a70bf024b33e8a8a02582fbabc98dd72d913530151e78b47119924f45b711b9a1189d5eec5a20e6f9bc1d44bfd554
"""

def __init__ (self, modulus = None, exponent = None, testing = False) :
m = rsatype \
( b'c87e9e96ffde3ec47c3f116ea5ac0e15'
b'34490b3da6dbbedae1af50dc32bf1012'
b'bdb7e1ff67237e0302b48c8731f343ff'
b'644662de2bb21d2b033127660e525d58'
b'889f8f6f05744906dddc8f4b85e0916b'
b'5d9cf5b87093ed260238674f143801b7'
b'e58a18795adc9acefaf0f378326fea19'
b'9ac6e5a88be83a52d4a77b3bba5f1aed'
, 16
)
e = rsatype (b'010001', 16)
m = modulus or m
e = exponent or e
self.pubkey = RSA.construct ((m, e))
self.testing = testing
# end def __init__

def pad (self, txt) :
l = (self.pubkey.size () + 7) >> 3
r = []
r.append (b'\0')
# Note that first reversing and then encoding to utf-8 would
# *not* be correct!
for x in iterbytes (reversed (txt.encode ('utf-8'))) :
r.append (x)
r.append (b'\0')
n = l - len (r) - 2
if self.testing :
r.append (b'\1' * n)
else :
r.append (os.urandom (n))
r.append (b'\x02')
r.append (b'\x00')
return b''.join (reversed (r))
# end def pad

def encrypt (self, password) :
x = self.pad (password)
e = self.pubkey.encrypt (x, '')[0]
e = ''.join ('%02x' % b_ord (c) for c in reversed (e))
return e
# end def encrypt

# end class PW_Encode

def main () :
# First try to parse config-file ~/.snxvpnrc:
home = os.environ.get ('HOME')
Expand Down Expand Up @@ -439,12 +419,23 @@ def main () :
, help = 'File part of URL default="%(default)s"'
, default = cfg.get ('file', 'sslvpn/Login/Login')
)
cmd.add_argument \
( '-E', '--extender'
, help = 'File part of URL default="%(default)s"'
, default = cfg.get ('extender', 'sslvpn/SNX/extender')
)
cmd.add_argument \
( '-H', '--host'
, help = 'Host part of URL default="%(default)s"'
, default = host
, required = not host
)
cmd.add_argument \
( '--ssl-noverify'
, help = 'Skip SSL verification default="%(default)s"'
, default = False
, required = False
)
cmd.add_argument \
( '--height-data'
, help = 'Height data in form, default "%(default)s"'
Expand Down Expand Up @@ -483,6 +474,11 @@ def main () :
' want a full path here'
, default = cfg.get ('snxpath', 'snx')
)
cmd.add_argument \
( '-u', '--useragent'
, help = 'User-Agent to be passed to Checkpoint Portal, default="%(default)s"'
, default = cfg.get ('useragent', 'Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0')
)
cmd.add_argument \
( '-U', '--username'
, help = 'Login username, default="%(default)s"'
Expand Down
1 change: 1 addition & 0 deletions snxvpnversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VERSION="1.3-0-be631df"