已迁移平台:segmentfault,搜索 erma0
换平台了,简书发什么都锁定,广告一堆,趁早倒闭吧。
JavaScript逆向练习1
0x01 目标网址
0x02 定位JS
1. 随便输入账号密码验证码,点击登录,查看提交的参数
可以看到,发出了两个请求,第一个是获取key,第二个是登录,在登录包里有加密过 的password参数。
2. Ctrl+Shift+F调出搜索面板,搜索jxy_parameter
,有三条结果,依此打开查看,发现第三个才是需要的。直接在这里下一个断点。
3. 重新输入账号密码验证码,点击登录,JS被断了下来。F11跟进,或者鼠标悬停在断点处函数名上,点击弹出的内容,也能跳转进去。
这里可以看到e
就是之前的第一个请求包的返回内容,而n
就是待加密的明文密码。加密函数的调用核心就是在这里。
4. 同上,F11跟进,进入一个JSEncrypt.min.js
,传入参数为第一步返回的hash + 明文密码
这里一看到JSEncrypt,再看一下上下文,可以确认是AES加密(可以百度一下JSEncrypt),那就直接把整个JSEncrypt.min.js
拿走,再改写一个调用的函数就行了。
0x03 改写JS
1. 仿照图3的位置,写一个调用函数。
function getrsa(pKey, password) { // 也可以把key写死,这个公钥是不会变的
var encrypt = new JSEncrypt();
encrypt.setPublicKey(pKey);
var encrypted = encrypt.encrypt(password);
return encrypted
}
此时在多数情况下就能正常运行了。
2. 练习在Python中调用时发现,这样会报错JSEncrypt is not defined
,一下整懵了,尝试直接赋值一个空对象,结果不行,仔细看了一下代码,发现第一部分是在判断客户端。
! function(t, e) {
"function" == typeof define && define.amd ? define(["exports"], e) : e("object" == typeof exports && "string" != typeof exports.nodeName ? module.exports : t)
}(this, function(t) {**函数主体**});
浏览器中调试发现"function" == typeof define && define.amd
为true
,那就直接把函数给改一下,把函数主体暴露出来。
var JSEncryptExports = {};
(function(t) {
**函数主体**
})(JSEncryptExports);
var JSEncrypt = JSEncryptExports.JSEncrypt;
此时JSEncrypt
就是一个构造函数了,接着把上面5中的代码贴在下面,就可以在Python中运行了。
0x04 Python代码
一开始尝试了js2py,报错,可能是js代码比较多,所以又直接改用execjs了。
PS. 其实可以尝试直接调用Python的AES加密库,如果这个JS没改过,结果应该是能用的(也就是另一个思路:JS功能可以通过所使用的语言直接实现)。
# -*- encoding: utf-8 -*-
'''
@File : 0x01-juxiangyou.com.py
@Time : 2019/11/06 10:42:43
@Author : 独孤孤独嘟咕噜犊子
@Version : 1.0
@Link : https://www.jianshu.com/u/6a4c6ef97be7
@Desc : 聚享游www.juxiangyou.com 登录例子
'''
# start
import execjs
import requests
import time
import json
HEADERS = {
'User-Agent':
'Mozilla/5.0 (Linux; U; Android 8.0.0; zh-cn; Mi Note 2 Build/OPR1.170623.032) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.128 Mobile Safari/537.36 XiaoMi/MiuiBrowser/10.1.1'
}
s = requests.Session()
s.headers.update(HEADERS)
imageURL = 'http://www.juxiangyou.com/verify?v={}000'.format(int(time.time()))
keyURL = 'http://www.juxiangyou.com/login/getkey'
loginURL = 'http://www.juxiangyou.com/login/auth'
# 下载验证码
image = s.get(imageURL).content
with open('image.png', 'wb') as f:
f.write(image)
# 获取公钥
publicKey = s.post(keyURL).json()
jskey = publicKey['key']
jshash = publicKey['hash']
# 登录
username = 'ermao6@qq.com'
password = '******'
varifyCode = input('请输入验证码(当前目录下image.png):')
# varifyCode = '1111'
timestamp = int(time.time() * 1000)
with open('js/0x01-juxiangyou.com.js') as f: # 坑0x01 相对路径前面不带/,带/不报错但读不出数据
jscode = f.read()
ctx = execjs.compile(jscode) # execjs载入js代码
xsign = ctx.call('get', timestamp) # 坑0x02 这里传入的时间戳为整数型(老版本验证的参数,现在不计算也行)
# xsign = ctx.eval('get({})'.format(timestamp)) # 通过eval调用js代码里的函数
enPass = ctx.call('getrsa', jskey, jshash + password) # 通过call调用js代码里的函数
data = {
'jxy_parameter': # 坑0x03 提交json格式参数需要转成json字符串,否则请求失败
json.dumps({
"c": "index",
"fun": "login",
"account": username,
"password": enPass,
"verificat_code": varifyCode,
"is_auto": True
}),
'timestamp':
timestamp
}
result = s.post(loginURL, data=data).json()
print(result) # {'code': 10000} 登录成功