0%

Instashop Web解密分析

本篇文章主要记录分析Instashop Webresponse解密。

地址:aHR0cHM6Ly9pbnN0YXNob3AuY29tL3Nob3AvYnJvd3NlL2NhdGVnb3J5L19hbGw=

分析过程

  1. 无法右键选择检查

    • 直接快捷键F12
    • 先打开一个空白浏览器,右键->检查,再打开目标网址。
  2. 定位解密位置

    由于没有关键字可以用来全局search,但发现每个response都是以U2FsdGVkX1开头。随便复制一个reponse百度一下看看是什么加密。有回答说是AES加密。

    那就以decrypt为关键全局搜索一下试试。毕竟,试试又不会怀孕。第一个结果main-es2015.7a101775553ac2492439.js中存在decryptencrypt方法。打个断点试试。果然,就是这个位置。其中n为响应密文,e500 Server error,o为undefined

  3. 逆向分析

    这个过程主要就是慢慢调试,需要时间和耐心,对了,还有就是看对算法的熟悉程度,不熟的话就多猜测,多尝试。

    下面记录一下关键方法。

    1. line 14521行_parse方法,是base64解码。其中前两个字节是Salted,后面两个字节是对应的salt值。

    2. line 14525行execute方法,是计算动态获取AESkeyiv。主要关注里面的compute方法。compute方法,入参a500 Server errortObject对象,主要用到t.words属性,也就是上一步获取的salt值。经过o.update(a).finalize(t)计算,得到一个长度为4的数组。这一点比较符合md5摘要算法的特征,那就盲猜一下为md5摘要算法。经过验证,确认这一步为md5。然后通过断点调试,发现逻辑是经过三次md5,得到一个长度为12个数组,其中前8个为key,后4个为iv

    3. 最后就是确认AES算法的模式和填充方式。其实也就那么几种,尝试一下就好了。

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# -*- coding:utf-8 -*-
import base64
import ctypes
import hashlib

from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers import algorithms
from cryptography.hazmat.primitives.ciphers import modes
from cryptography.hazmat.primitives import padding


def parse_cipher_text(content):
content = base64.b64decode(content)
result = []
for i in range(0, len(content), 4):
result.append(int.from_bytes(content[i: i + 4], byteorder='big', signed=True))
return result[2:4], result[4:]


def md5_encrypt(content):
"""
MD5内容摘要
:param content:
:return:
"""
if isinstance(content, str):
content = content.encode('utf8')
m = hashlib.md5()
m.update(content)
return m.hexdigest()


def aes_decrypt(content, key, iv):
"""
AES CBC PKCS7 padding
:param iv:
:param key:
:param content:
:return:
"""
if not isinstance(content, bytes):
content = content.encode()

cipher = Cipher(algorithm=algorithms.AES(key), mode=modes.CBC(iv))
decryptor = cipher.decryptor()
content = decryptor.update(content)

unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
return unpadder.update(content) + unpadder.finalize()


def array_to_bytes(array):
result = b''
for arr in array:
result += arr.to_bytes(length=4, byteorder='big', signed=True)
return result


def generate_aes_key_iv(salt):
result = []
# 第一次
md5_result = md5_encrypt(b'500 Server error' + array_to_bytes(salt))
for i in range(0, len(md5_result), 8):
result.append(ctypes.c_int32(int(md5_result[i: i+8], 16)).value)
# 第二次
md5_result = md5_encrypt(array_to_bytes(result) + b'500 Server error' + array_to_bytes(salt))
for i in range(0, len(md5_result), 8):
result.append(ctypes.c_int32(int(md5_result[i: i+8], 16)).value)
# 第三次
md5_result = md5_encrypt(array_to_bytes(result[4:]) + b'500 Server error' + array_to_bytes(salt))
for i in range(0, len(md5_result), 8):
result.append(ctypes.c_int32(int(md5_result[i: i+8], 16)).value)
return array_to_bytes(result[:8]), array_to_bytes(result[8:])


def run():
cipher_text = "U2FsdGVkX1/GN1cLWc8heoJDQNxNpcASTl/CnjDuARekPQqjXk/8jFgs1eDbcBbpCGXY6GIdZsntUXKaA64gmg=="
salt, cipher_text = parse_cipher_text(content=cipher_text)
key, iv = generate_aes_key_iv(salt=salt)
content = aes_decrypt(content=array_to_bytes(cipher_text), key=key, iv=iv).decode()
print(content)
# r:f99daa6a7513d03e51617e8323bd983a


if __name__ == '__main__':
run()

总结

response密文是一个AES CBC PKCS7加密,其中keyiv是通过三次md5动态获取的。

您的支持将鼓励我继续创作!