已迁移平台:segmentfault,搜索 erma0
换平台了,发什么都锁定,广告一堆,趁早倒闭吧。
JavaScript逆向练习4
0x01 目标网址
http://j.esf.leju.com/ucenter/login
0x02 定位JS
1. 随便输入账号密码(经纪人账号登录),点击登录,查看提交的参数
可以看到,在登录包http://j.esf.leju.com/ucenter/login
里有加密过 的password
参数。这个就是下面要找的加密。
这个包还有一个参数ckey
,是在网页源码里生成的,每次请求都会更新这个值,做登录的话,每次都去取一下就行了。
2. Ctrl+Shift+F调出搜索面板,搜索password
,结果中稍作分析,发现有前面几个结果都不是目标域名下的js文件,剩下两个打开看看。
在所有可疑的地方都下断点,当然,自信一点可以直接定位到图中这个地方,毕竟一眼可以看到$("#password")
,找到这是用来取明文密码的值,而且还有RSA这样的关键字。
3. 直接在可疑的地方都下断点,重新输入账号密码验证码,点击登录,JS被断了下来(就是上图中的位置)。
var t = $("#password")
, i = $.trim(t.val());
if (i.length < 100) {
(0,
l.setMaxDigits)(129);
var n = new c.RSAKeyPair("10001", "", $("#pubkey").val())
, s = (0,
c.encryptedString)(n, i);
$("input[name='password']").val(s)
加密函数就是encryptedString
,两个参数,第一个是RSAKeyPair
返回的对象,第二个是明文密码。
而前一行的RSAKeyPair
参数$("#pubkey").val()
就是公钥了,直接console里输出一下,复制出来。
F11跟进,再或者鼠标悬停在断点处函数名RSAKeyPair
上,点击弹出的内容,也能跳转进去。
加密函数的核心就是在这里,把这段整个290函数全部拿走。
4. 上一步进去之后,发现同时调用了291
、217
两个函数,直接在这里搜数字+冒号,就能定位到这两个函数定义处,全部拿走就完事儿了。
0x03 改写JS
1. 仿照图2的位置,写一个调用函数。
function test() {
setMaxDigits(129);
var n = RSAKeyPair("10001", "", "BC087C7C00848CE8A349C9072C3229E0D595F817EDDE9ABF6FC72B41942A759E97956CE9CB7D1F2E99399EADBACC0531F16EAE8EFCB68553DE0E125B2231ED955ADBF5208E65DC804237C93EB23C83E7ECDA0B586ECF31839038EE6B640E0EEC5FF17D219FDEA33E730F287F0D384C74A53DFE1F91ACC63C7C92039A43AC6E97")
//第三个参数就是上文提的公钥,console里输出复制来的,直接替换在这了。
var enPass = encryptedString(n, pass);
return enPass
}
2. 把扣出来的290、291、217
三个函数放到调试工具里,再开始改写。
先把三个函数改成标准的函数定义的格式。
function i_217(e, t, i) {
***
代码
***
}
function i_291(e, t, i) {
***
代码
***
}
function i_290(e, t, i) {
***
代码
***
}
同时把每个函数内部的
"use strict";
Object.defineProperty(t, "__esModule", {
value: !0
}),
这段代码删掉。
其中290
里有217 291
的调用、291
里有217
的调用,改成正常格式。
//290里的
var m = i_217(e, t, i)
, s = i_291(e, t, i)
//291里的
var c = i_217(e, t, i);
3. 此时加载运算,会出现各种未定义和对象不存在,因为现在的代码里并没有调用加密代码,而且抠下来的3个函数都没有返回值。
所以先给每个函数加一个返回值:在函数尾部加上return t
(因为函数都是在操作t
)
再在一开始写的调用函数里,调用290
,因为没有参数可以传,所以顺手把290
的参数给删掉。
function i_290() {
t = this
e = ""
i = ""
//其他代码
}
function test(pass) {
t=i_290()//调用加密核心:290
//往下的所有函数、对象的调用,都是对象`t`里面的了
t.setMaxDigits(129);
var n = t.RSAKeyPair("10001", "", "BC087C7C00848CE8A349C9072C3229E0D595F817EDDE9ABF6FC72B41942A759E97956CE9CB7D1F2E99399EADBACC0531F16EAE8EFCB68553DE0E125B2231ED955ADBF5208E65DC804237C93EB23C83E7ECDA0B586ECF31839038EE6B640E0EEC5FF17D219FDEA33E730F287F0D384C74A53DFE1F91ACC63C7C92039A43AC6E97")
var enPass = t.encryptedString(t, pass);//这里如果传入n的话,会提示'chunkSize' 为 null 或不是对象
//传入t的话就OK了,因为所有函数、属性都在`t`里面,同时上面的`var n =`也可以删掉了,因为没有调用,当然不删也没有影响,慢慢熟悉JS语法。
return enPass
}
此时就能正常运行了。
0x04 Python代码
PS. 其实可以尝试直接调用Python的RSA加密库,如果这个JS没改过,结果应该是能用的(也就是另一个思路:JS功能可以通过所使用的语言直接实现)。
# -*- encoding: utf-8 -*-
'''
@File : 0x04-j.esf.leju.com.py
@Time : 2019/12/10 20:19:22
@Author : 独孤孤独嘟咕噜犊子
@Version : 1.0
@Link : https://www.jianshu.com/u/6a4c6ef97be7
@Desc : 新浪二手房登录rsa
'''
# start
import execjs
import requests
# 初始化参数
HEADERS = {
'User-Agent':
'Mozilla/5.0 (Linux; Android 8.0; DUK-AL20 Build/HUAWEIDUK-AL20; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/044353 Mobile Safari/537.36 MicroMessenger/6.7.3.1360(0x26070333) NetType/WIFI Language/zh_CN Process/tools'
}
s = requests.Session()
s.headers.update(HEADERS)
loginURL = 'http://j.esf.leju.com/ucenter/login'
# 登录参数
username = '15555555555'
password = '[email protected]'
# 加载js
with open('js/0x04-j.esf.leju.com.js') as f: # 坑0x01 相对路径前面不带/,带/不报错但读不出数据
jscode = f.read()
ctx = execjs.compile(jscode) # execjs载入js代码
def get_ckey():
url = 'http://j.esf.leju.com/ucenter/login'
res = s.get(url).text
ckey = res.split('name="ckey" value="')[1].split('" />')[0]
return ckey
def login(user, password):
enPass = ctx.call('test', password) # 通过call调用js代码里的函数
ckey = get_ckey()
data = {
'username': username,
'password': enPass,
'imgcode': '',
'ckey': ckey
}
result = s.post(loginURL, data=data).json()
print(result)
return result
if __name__ == "__main__":
login(username, password)