qianshi 发表于 2024-11-12 12:32

某校WebVPN登录接口分析

本文仅用于学习研究,请勿用于非法用途和商业用途!如因此产生任何法律纠纷,均与作者无关!如有问题,麻烦版主立即删除

闲来无事,分析了某校WebVPN登录接口,首先抓个包:



我们可以从登录接口得到这几个参数:
username: 用户名。
password: 被加密的密码。
execution: 验证令牌,用于防范CSRF攻击。
_eventId: 表单提交的事件标识(这里为submit)。
geolocation: 地理位置数据(这里为空)。
captcha: 验证码(这里为空)。
rememberMe: 是否记住用户(true或者false。true代表记住,false表示不记住)。
domain 和 tenantId: 指定登录域和租户ID(这里为空)。

省略为空的字段,这里主要分析password、execution是如何获取的

获取password:

回到WebVPN登陆页面,F12切换到源代码窗口,全局搜索password,定位到这一段代码:


(n) {
    const l = t,
          {username: s, password: e, captcha: i, rememberMe: o, rememberUsername: u, rememberPassword: r, tenantId: c} = n;
    u ? this(s) : this(),
    r ? this(e) : this();

    const h = window;
    let a;
    a = e(l(12916)) && e(l(9346)) || e(l(3346)) ? e : qk(e, Da, Da);

    const f = {
      username: s,
      password: a,
      execution: bridgeData,
      _eventId: l(3859),
      geolocation: "",
      captcha: i || "",
      rememberMe: o || !1,
      domain: h,
      tenantId: c || ""
    };
    this(l(4312), f)
}

分析这段代码可知:代码里函数 qk 和参数(Da 和 Da)对密码e了进行处理,得到新的密码 a。

由于代码进行了混淆,这里不妨尝试反混淆,再进行进一步分析,这里用到了开源项目https://github.com/kuizuo/js-deobfuscator
将整个代码复制进去,进行反混淆操作(会卡一会儿):


反混淆成功后,定位到之前的代码,此时已经知道Da, Da分别是AES KEY(加密密钥)和AES IV(初始化向量(IV)),往前翻一翻,还可以看到函数qk的源码、加密密钥的值、初始化向量的值:




    function qk(t, n, l) {
      const i = Gk.enc.Utf8.parse(t);
      const o = Gk.enc.Utf8.parse(n);
      const u = Gk.enc.Utf8.parse(l);
      return Gk.AES.encrypt(i, o, {
      iv: u,
      mode: Gk.mode.CBC,
      padding: Gk.pad.Pkcs7
      }).toString();
    }


可以看到,这里指定了加密模式为 CBC,填充方式为 PKCS7,其实也已经拿到password的值了,可以通过AES在线加解密网站或者本地执行python代码验证结果:
提供一个示例代码:

def encrypt_password(self, password, key, iv):
    """使用 AES 加密算法加密给定的密码。"""
    key_bytes = key.encode('utf-8')
    iv_bytes = iv.encode('utf-8')
    password_bytes = password.encode('utf-8')

    # 创建 AES 加密器
    cipher = Cipher(algorithms.AES(key_bytes), modes.CBC(iv_bytes), backend=default_backend())
    encryptor = cipher.encryptor()

    # 对密码进行填充,使其长度为 16 的倍数
    pad_length = 16 - len(password_bytes) % 16
    padded_password = password_bytes + bytes( * pad_length)

    # 加密
    encrypted = encryptor.update(padded_password) + encryptor.finalize()
    return b64encode(encrypted).decode('utf-8')


获取execution
回到之前调用qk函数的地方,观察代码:


可以看到,execution的值是通过bridgeData.flowExecutionKey获取的。通过命名和经验可以判断,这个值通常是从服务器端的响应或者在页面加载时获取,索性全局搜索flowExecutionKey:


在login页面的HTML源码处找到了flowExecutionKey,因此验证了我们的猜想:flowExecutionKey在页面加载时获取
提供一个示例代码:
def get_execution(session, login_url):
    """从登录页面获取 flowExecutionKey。"""
    response = session.get(login_url)

    if response.status_code == 200:
      soup = BeautifulSoup(response.text, 'html.parser')
      script_tags = soup.find_all('script')

      for script in script_tags:
            if 'flowExecutionKey' in script.text:
                match = re.search(r'flowExecutionKey:\s*"([^"]+)"', script.text)
                if match:
                  return match.group(1)

    print("未找到 flowExecutionKey")
    return None


到这里,登录接口的分析已经结束了,总体来说算是比较简单。我在本地测试登录接口确实也能正确登录,文章完毕。

Pinhaa 发表于 2024-11-12 21:54

厉害,我试过用selenium之类的浏览器自动化去自动认证,就是因为网页中的加密参数不太了解,学习到思路了

cch1234 发表于 2024-11-12 15:38

大佬牛逼,谢谢大佬

rayman8560 发表于 2024-11-12 15:41

学习了,思路很好

J3ggedPeak 发表于 2024-11-12 16:25

大佬牛逼,谢谢大佬

zxilong38 发表于 2024-11-12 16:31

这个很有用,研究研究试试看

wyd926 发表于 2024-11-12 16:38

学习 思路很棒!

desire1911 发表于 2024-11-12 16:43

膜拜大佬

PBCgogogo 发表于 2024-11-12 17:15

学习了,膜拜大佬

alidadai 发表于 2024-11-12 17:47

莫名没看懂,就感觉很厉害

cyxnzb 发表于 2024-11-12 17:47

这种学校webvpn好像大多数都是今日校园的东西
页: [1] 2 3 4 5 6
查看完整版本: 某校WebVPN登录接口分析