欢迎光临
我们一直在努力

JS逆向入门实战:通达OA 2019登录密码SHA-1加密算法分析与Python复现

1. 项目概述与背景

最近在整理企业级应用安全审计的案例时,我又翻出了通达OA Office Anywhere 2019这个经典的靶场。很多刚接触JS逆向的朋友,一上来就想搞那些大型电商或社交平台,结果往往被复杂的混淆和反调试机制劝退。其实,从一些成熟但架构相对传统的企业应用入手,是理解前端加密逻辑、培养逆向手感绝佳的路径。通达OA作为国内广泛使用的办公自动化系统,其2019版本的前端登录密码加密,就是一个结构清晰、非常适合新手入门的逆向分析样本。它没有使用极度复杂的混淆,但包含了完整的加密函数定位、算法还原、参数模拟等核心环节,堪称“JS逆向三百例”中的标准示范课。

这个项目要解决的问题很明确:当我们面对一个系统的登录接口,发现提交的密码是一串毫无规律的密文时,我们如何从浏览器出发,一步步追踪到生成这串密文的JavaScript代码,并最终理解其加密原理,甚至能够用Python或其他语言独立复现这个加密过程?这不仅是为了“破解”,在安全测试、数据对接、自动化脚本开发等合规场景下,这种分析能力都至关重要。接下来,我就以通达OA 2019为例,带你完整走一遍这个流程,我会把每个步骤的意图、可能遇到的坑以及我调试时用的“骚操作”都交代清楚。

2. 逆向分析的核心思路与准备工作

2.1 分析目标与工具选型

我们的终极目标是独立实现通达OA 2019登录时密码字段的加密算法。具体表现为,输入明文密码(如“123456”),能输出与浏览器发送请求时完全一致的密文。这需要我们先通过浏览器开发者工具,捕捉到登录请求,观察其数据格式,尤其是密码参数(通常叫

PASSWORD

或

password

)的样子。然后,以此为线索,逆向寻找生成它的JavaScript代码。

工欲善其事,必先利其器。对于这类传统Web应用的分析,我习惯用Chrome DevTools,它足够强大。有几个关键功能会高频使用:

  • Network(网络)面板

    :用于捕获登录请求,查看请求载荷(Payload)。重点关注

    Form Data

    或

    Payload

    标签页下的内容。

  • Sources(源代码)面板

    :用于搜索、查看和调试JavaScript文件。全局搜索(

    Ctrl+Shift+F

    )是定位关键词的神器。

  • Console(控制台)面板

    :用于执行JavaScript代码片段,测试我们还原的加密函数。

为什么不推荐用Fiddler或Burp Suite?它们当然也能抓包,但对于前端JS代码的动态调试和追踪,浏览器自带的DevTools有着无可替代的便捷性。Burp更适合用于拦截、重放和修改HTTP请求,属于另一层面的工具。

注意

:在开始分析前,请确保你有一个合法的、用于学习和测试的通达OA 2019测试环境。所有分析操作应仅限于你自己拥有权限的测试系统,严格遵守法律法规,切勿对任何未授权的系统进行测试。

2.2 初步侦察:网络请求抓取与特征观察

首先,我们打开通达OA 2019的登录页面(通常是类似

/login.html

或

/

的地址)。打开DevTools的Network面板,并勾选“Preserve log”(保留日志),防止页面跳转后请求记录被清空。

在登录框输入一个简单的测试密码,比如

test123

,点击登录。此时Network面板会立刻出现一个新的请求,通常是向

/logincheck.php

或类似接口发起的POST请求。点击这个请求,查看其

Headers

和

Payload

。

以我手头的环境为例,捕获到的请求关键信息如下:

  • 请求URL

    :

    http://your-oa-server/general/logincheck.php

  • 请求方法

    : POST

  • 表单数据 (Form Data)

    :
    UNAME: admin
    PASSWORD: 7c4a8d09ca3762af61e59520943dc26494f8941b

这里,

UNAME

是明文用户名,而

PASSWORD

是一串40位的十六进制字符串。40位长度,这强烈暗示了可能是SHA-1哈希算法的结果(SHA-1输出正好是160位,即40个十六进制字符)。当然,这只是一个假设,需要代码验证。我们的任务就是找到把

test123

变成

7c4a8d09ca3762af61e59520943dc26494f8941b

的那段JavaScript代码。

3. 加密函数定位与关键代码追踪

3.1 基于关键词的全局搜索定位

知道密码参数名是

PASSWORD

,密文是40位十六进制串,这给了我们明确的搜索线索。在DevTools的Sources面板,点击左侧文件列表上方的“{}”图标(格式化代码),然后使用

Ctrl+Shift+F

进行全局搜索。

搜索关键词的选择有技巧:

  • 直接搜索参数名

    :搜索

    PASSWORD

    。但这个词可能在HTML、JS里出现很多次,需要过滤。

  • 搜索可能的加密函数名

    :既然怀疑是SHA-1,可以搜索

    SHA1

    、

    sha1

    、

    encrypt

    、

    hash

    等。

  • 搜索特征常量

    :有时算法会用到一些常量,比如SHA-1的初始哈希值

    0x67452301

    等,但这对新手要求略高。

  • 在通达OA 2019中,最有效的方法是搜索

    PASSWORD

    。在搜索结果中,我们不是漫无目的地看,而是要找那些

    在JS文件里,并且看起来在给

    PASSWORD

    赋值

    的代码行。很快,我们可能会定位到一个类似下面的代码片段(代码已做简化示意):

    // 可能在某个 .js 文件中,例如 login.js 或 common.js
    function do_login() {
    var username = document.getElementById('UNAME').value;
    var password = document.getElementById('PASSWORD').value;

    // 关键行!这里对password进行了处理
    document.getElementById('PASSWORD').value = hex_sha1(password);

    // 然后提交表单
    document.forms[0].submit();
    }

    或者,在表单的

    onsubmit

    事件处理函数中:

    <form action="/general/logincheck.php" method="post" onsubmit="return checkForm(this);">

    然后去找到

    checkForm

    函数,里面也会有类似的加密逻辑。

    实操心得

    :全局搜索时,不要只看一眼就过。点击搜索结果跳转到对应文件后,要

    查看上下文

    。看看这个

    PASSWORD

    是在哪里被定义的,是谁修改了它的值。通常,加密操作就发生在这个修改的地方。

    3.2 深入加密函数:hex_sha1 的逆向

    找到了

    hex_sha1(password)

    这行代码,就等于找到了加密的核心函数。接下来,我们的目标就是找到

    hex_sha1

    这个函数的定义。继续使用全局搜索,搜索

    function hex_sha1

    或

    hex_sha1 = function

    。

    在通达OA 2019中,这个SHA-1的实现通常是直接嵌入在一个公共JS文件里的,比如

    /static/js/sha1.js

    或者直接写在某个大文件的头部。找到后,你会看到一整套SHA-1算法的JavaScript实现。代码可能长这样:

    function hex_sha1(s) {
    return binb2hex(core_sha1(str2binb(s), s.length * chrsz));
    }
    function core_sha1(x, len) { … }
    function str2binb(str) { … }
    function binb2hex(binarray) { … }
    // … 以及其他辅助函数

    到了这一步,很多新手会觉得大功告成,直接把这一坨JS代码复制出来就完事了。其实这才是最容易出错的地方。

    你不能假设这个

    hex_sha1

    函数就是标准的、独立的SHA-1。你必须验证两件事:

  • 它是否依赖外部变量或函数?

    查看

    hex_sha1

    函数体内,是否使用了在它外部定义的全局变量(比如

    chrsz

    、

    hexcase

    等)。这些变量的值必须一并获取。

  • 它的输入输出是否经过额外处理?

    在通达OA的这个案例里,

    hex_sha1

    看起来就是标准的SHA-1。但在其他系统里,可能会在SHA-1前后进行Base64编码、拼接时间戳、加盐(salt)等操作。一定要在调用

    hex_sha1

    的上下文中确认,传入的参数是不是纯粹的明文密码,有没有被拼接其他字符串。

  • 验证方法很简单:在Console面板里,直接调用我们找到的

    hex_sha1

    函数。输入我们测试的密码

    test123

    ,看输出是否与抓包得到的密文

    7c4a8d09ca3762af61e59520943dc26494f8941b

    一致。

    // 在Console中测试
    hex_sha1('test123');
    // 观察输出是否等于 "7c4a8d09ca3762af61e59520943dc26494f8941b"

    如果一致,恭喜,算法还原成功。如果不一致,就要回头仔细检查上下文,看密码在传入

    hex_sha1

    前是否被修改。

    4. 算法还原与Python代码实现

    4.1 分析JavaScript加密逻辑

    经过上一步的验证,我们确认通达OA 2019的密码加密就是标准的SHA-1哈希,没有额外的盐或混淆。其逻辑链条非常清晰:

  • 获取用户输入的明文密码字符串。
  • 将该字符串直接传入自定义的

    hex_sha1

    函数。

  • hex_sha1

    函数内部按标准SHA-1算法计算哈希值,并返回40位的十六进制字符串。

  • 将这个十六进制字符串赋值给表单的

    PASSWORD

    字段,随表单提交。

  • 标准SHA-1算法虽然自己实现起来有点复杂(涉及位运算、循环位移等),但幸运的是,我们几乎不需要自己重写。我们的目标是用Python模拟这个加密过程,而Python的标准库

    hashlib

    已经提供了成熟且高效的SHA-1实现。

    4.2 Python复现加密过程

    既然算法是标准SHA-1,Python复现就非常简单了。但这里我想强调一个

    非常重要的细节

    ,也是很多人在跨语言实现加密时容易踩的坑:

    字符串的编码(Encoding)

    。

    JavaScript的字符串通常是UTF-16吗?不完全是。在涉及加密哈希时,尤其是这种比较老派的、自己实现算法的JS代码,它处理字符串的方式往往是基于字符串的字符码(charCode)。而Python的

    hashlib

    的

    update

    方法需要接收字节(bytes),而不是字符串(str)。

    那么,JS的

    hex_sha1('test123')

    在计算时,到底相当于对什么样的字节序列进行哈希呢?在绝大多数没有指定特殊编码的Web场景下,可以等价于对

    UTF-8编码的字节序列

    进行哈希。这是一个非常常见且安全的假设。

    因此,我们的Python实现代码如下:

    import hashlib

    def encrypt_password_oa(password):
    """
    模拟通达OA 2019前端密码加密
    :param password: 明文密码字符串
    :return: 40位小写十六进制SHA-1哈希值
    """
    # 1. 将字符串转换为utf-8编码的字节
    password_bytes = password.encode('utf-8')

    # 2. 创建sha1哈希对象并更新数据
    sha1_obj = hashlib.sha1()
    sha1_obj.update(password_bytes)

    # 3. 获取十六进制哈希值
    encrypted_password = sha1_obj.hexdigest()

    return encrypted_password

    # 测试
    if __name__ == '__main__':
    plain_password = 'test123'
    encrypted = encrypt_password_oa(plain_password)
    print(f'明文密码: {plain_password}')
    print(f'加密后: {encrypted}')
    # 输出应该与浏览器捕获的一致: 7c4a8d09ca3762af61e59520943dc26494f8941b
    assert encrypted == '7c4a8d09ca3762af61e59520943dc26494f8941b', '加密结果不匹配!'

    为什么一定要用

    utf-8

    编码?

    这是为了确保与前端JavaScript行为一致。虽然JS内部是UTF-16,但其

    charCodeAt

    等函数在处理ASCII范围内的字符(如字母数字)时,与UTF-8编码的ASCII部分是完全兼容的。而对于SHA-1这种将输入视为字节流的算法,使用UTF-8编码是Web领域的通用实践。如果你不放心,可以在JS控制台和Python中同时对一个包含中文的密码进行加密测试,来验证编码是否一致。

    踩坑记录

    :我曾经遇到一个系统,它的前端JS在计算哈希前,居然用

    escape()

    函数对密码进行了编码,这会导致空格变成

    %20

    ,加号变成

    %2B

    。如果你用Python的

    hashlib.sha1(password.encode('utf-8'))

    计算,结果肯定对不上。这时就需要在Python里用

    urllib.parse.quote

    来模拟

    escape()

    的行为。好在通达OA 2019没有这个“骚操作”。

    5. 逆向过程中的常见问题与深度排查技巧

    即使是一个简单的SHA-1逆向,在实际操作中也可能遇到各种问题。下面我总结了一个排查清单,覆盖了从入门到进阶可能遇到的坑。

    5.1 问题一:搜索不到明显的加密函数调用

    现象

    :全局搜索

    PASSWORD

    、

    encrypt

    、

    SHA1

    都找不到明显的加密代码行。

    排查思路

    :

  • 检查表单提交方式

    :可能不是通过

    onsubmit

    ,而是通过按钮的

    onclick

    事件触发了某个JavaScript函数,在这个函数里完成了加密和Ajax提交。搜索

    onclick

    或按钮的ID/Name。

  • 检查是否被混淆

    :虽然通达OA 2019没有,但很多现代应用会对JS进行混淆。代码可能变成

    a=b[c](d)

    这种形式。这时,搜索

    PASSWORD

    可能只能找到字符串本身,而找不到调用逻辑。你需要:

    • 在Network面板,找到登录请求的

      Initiator

      (发起者)列,点击它可能直接跳转到发起这个请求的JS代码行。

    • 在

      do_login

      或类似函数入口处打上断点(在Sources面板对应行号点击),然后重新登录,让代码执行暂停,再单步调试(F11),一步步跟踪

      password

      变量的变化。

  • 检查是否在框架内

    :如果系统使用了Vue、React等框架,密码绑定和提交逻辑可能藏在组件和方法里。你需要熟悉框架的调试方式,例如在Vue Devtools中查看组件数据。

  • 5.2 问题二:加密结果与Python实现不一致

    现象

    :成功找到了JS加密函数,在Console里测试结果也与抓包一致,但用Python实现的代码算出来的结果就是不对。

    排查步骤(逐层验证)

    :

  • 验证基础用例

    :先用一个简单密码,如

    123456

    ,分别在JS Console和Python中计算。如果简单密码都对,复杂密码不对,问题可能出在编码;如果简单密码就不对,问题更根本。

  • 验证编码环节

    :这是最常见的坑。在JS加密函数的第一行打上断点,或者手动在函数开始时添加

    console.log('输入:', password, '类型:', typeof password);

    。看看传入的到底是什么。然后在Python里,在调用

    encode

    之前,也打印一下字符串。确保两者完全一致(包括不可见字符?)。

  • 验证算法细节

    :如果JS用的是自定义的、非标准的哈希算法(比如魔改的SHA-1),那么

    hashlib

    就不管用了。你需要仔细阅读JS算法代码,理解其每一步操作(填充、循环、位运算等),然后用Python原样实现一遍。这个过程非常繁琐,但也是JS逆向的精髓所在。对于通达OA,我们确认它是标准SHA-1,所以这步跳过。

  • 验证最终输出格式

    :JS函数返回的是40位小写十六进制吗?有没有可能是Base64?在Console里捕获加密后的值,观察其长度和字符集。Python的

    hexdigest()

    输出小写,如果JS输出大写,你需要用

    .lower()

    转换,或者JS里可能有一个

    hexcase

    变量控制大小写。

  • 5.3 问题三:加密依赖了动态参数(加盐)

    现象

    :每次登录,即使密码相同,生成的密文也不同。或者密文长度远超40位。

    排查思路

    :

  • 观察请求参数

    :除了

    UNAME

    和

    PASSWORD

    ,查看登录请求是否还提交了其他参数,比如

    token

    、

    nonce

    、

    timestamp

    、

    salt

    等。这些很可能被用于和密码拼接后再加密。

  • 分析加密函数输入

    :在JS加密函数处打上断点,不仅查看

    password

    ,还要查看函数内部是否拼接了其他变量。例如,可能是

    hex_sha1(password + salt)

    或

    hex_md5(timestamp + password)

    。

  • 追踪动态参数来源

    :如果发现了加盐,需要找到这个

    salt

    或

    token

    是怎么生成的。通常它会在页面加载时由后端渲染到HTML中(如一个隐藏的

    input

    标签),或者通过一个前置的Ajax请求获取。你需要用同样的思路,去逆向生成这个动态参数的JS代码。

  • 通达OA 2019的总结

    :它属于最简单的情况——无盐、静态、标准哈希。这让我们可以把重点放在理解逆向分析的完整流程上,而不是陷入复杂的算法对抗。

    6. 逆向分析的进阶思考与防御视角

    通过这个简单的案例,我们走完了一个完整的JS逆向流程。但作为安全研究者或开发者,我们不能只停留在“能逆向”的层面,更要思考背后的安全问题。

    6.1 从攻击者视角看,这种加密的弱点

    通达OA 2019仅使用前端SHA-1加密密码,存在几个明显弱点:

  • 哈希而非加密

    :SHA-1是哈希算法,不可逆,但它是公开的、确定性的。相同的密码永远产生相同的哈希值。

  • 无盐(Salt)

    :这是最大的问题。攻击者可以预先计算一个巨大的“彩虹表”,里面包含海量常用密码及其SHA-1哈希值。一旦从数据库或网络流量中拿到这个哈希,直接查表就能瞬间得到明文密码。

    7c4a8d09ca3762af61e59520943dc26494f8941b

    在彩虹表里一查,立刻就知道是

    test123

    。

  • 前端加密意义有限

    :前端加密无法替代HTTPS。它在传输过程中可以防止密码明文被直接窥视,但如果网络层本身不安全(比如HTTP协议),攻击者依然可以轻松截获请求并重放(Replay Attack),因为密文本身就可以作为登录凭证。前端加密的主要作用是避免密码在传输中因明文而意外泄露(例如被浏览器历史记录、日志无意记录),但无法抵御主动攻击。

  • 6.2 从开发者视角看,如何改进?

    如果我们是OA系统的开发者,应该如何设计更安全的密码处理方案?

  • 必须使用HTTPS

    :这是底线。在HTTPS加密通道下,即使前端不加密,密码明文传输也是安全的。前端加密变成了锦上添花,而非安全依赖。

  • 后端进行强哈希加盐

    :前端可以做一次哈希(像通达OA这样),但

    后端在存储前必须再做一次加盐哈希

    。盐(Salt)应该是每个用户独立、足够长的随机字符串。推荐使用像

    bcrypt、scrypt或Argon2

    这样的密码哈希函数,它们设计得计算缓慢,能有效抵抗彩虹表和暴力破解。即使前端哈希值泄露,攻击者也无法直接用其登录(因为后端还会用不同的盐再哈希一次),也极难反向破解出原密码。

  • 引入动态挑战值

    :可以考虑引入类似CSRF Token的机制。登录时,后端先返回一个随机数(Nonce),前端将密码哈希后,再与这个Nonce进行某种计算(如HMAC),将结果提交。后端用同样的Nonce进行验证。这样,每次登录的密文都不同,有效防止重放攻击。

  • 避免在前端实现核心加密逻辑

    :核心的、带盐的哈希运算应在后端完成。前端可以承担一些轻量的、不涉及密钥的预处理工作。

  • 6.3 逆向分析技能的合规应用

    最后,强调一下这项技能的合规用途,这远比“破解”更重要:

    • 安全审计与渗透测试(授权范围内)

      :帮助企业发现自身系统的安全漏洞,如弱加密、逻辑缺陷等。

    • 自动化测试与数据对接

      :在需要模拟用户登录进行自动化操作(如数据采集、巡检)时,理解加密流程是编写脚本的前提。

    • 第三方系统集成

      :当需要与一个已有系统进行数据打通,而对方文档不全时,逆向分析可以帮你弄清楚接口的调用方式。

    • 学习与研究

      :就像我们这次做的一样,通过分析经典案例,深入理解Web安全机制、加密算法应用和前后端交互逻辑。

    通过“通达OA 2019密码加密”这个麻雀虽小五脏俱全的案例,我希望你掌握的不仅仅是一个SHA-1的还原,而是

    一套通用的JS逆向分析方法论

    :从抓包观察、关键词搜索、断点调试、逻辑分析到代码复现和验证。这套方法,稍加变通,就能应用到更多、更复杂的场景中去。记住,耐心和细致是逆向分析中最宝贵的品质,每一个细节都可能成为突破的关键。

    赞(0)
    未经允许不得转载:171主机测评 » JS逆向入门实战:通达OA 2019登录密码SHA-1加密算法分析与Python复现
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址