本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
此文章重在学习调试技巧。模拟参数生成。
今日遇到一个新的安全产品网站,名字都没听说过,后来去了解,发现这产品叫360磐云盾,整体难度不算难。花了差不多两小时就搞定了。(同样是安全产品,为什么阿里滑块那么难!!!)
目标网站:aHR0cHM6Ly93d3cubHVvamlhbmcuZ292LmNuL2luZm8vaUxpc3QuanNwP2NhdF9pZD0yNDE3NyZjdXJfcGFnZT0x
安全产品介绍:aHR0cHM6Ly9iLjM2MC5uZXQvcHJvZHVjdC1jZW50ZXIvMzYwLW5ldC1zYWZlL3Bhbi15dW4=
关键词:
PYCCS ,answer,PYCCV,重定向,异步
逆向思路
网页加载频繁会有这样特征

同一个数据接口会加载三次

第一次请求得出JS文件

并且第一段会响应PYCCV
第二次请求特征,重定向

关键参数answer,由第一次请求里的JS文件获得而来,也是唯一难点

第三次请求携带answer和PYCCVcookies成功获取最终cookies

实操
首先第一次请求获取JS代码。
获取规整的JS代码并报$未定义
接下来把规整的代码扣下来去问ai,chatgpt、claude、deepseek
以下是deepseek的回答:
我们有一段被混淆的JavaScript代码,它使用了十六进制转义序列和变量名混淆。我们将逐步解码并理解这段代码。
首先,我们可以将代码进行格式化,并尝试将十六进制转义序列转换为可读的字符串。
注意:代码开头有一个变量 _$,它是一个包含三个字符串的数组,这些字符串是用十六进制表示的。我们可以将它们转换为ASCII字符串。
转换方法:将每个十六进制序列去掉\x,然后作为ASCII码解析。
例如:
'\x23\x63\x68\x61\x6c\x6c\x65\x6e\x67\x65'
23 -> '#',63->'c', 68->'h', 61->'a', 6c->'l', 6c->'l', 65->'e', 6e->'n', 67->'g', 65->'e'
所以第一个字符串是 "#challenge"
同样,第二个字符串:'\x23\x77\x61\x66\x5f\x61\x6e\x73\x77\x65\x72'
23->'#', 77->'w', 61->'a', 66->'f', 5f->'_', 61->'a', 6e->'n', 73->'s', 77->'w', 65->'e', 72->'r'
所以是 "#waf_answer"
第三个字符串:'\x23\x43\x68\x61\x6c\x6c\x65\x6e\x67\x65\x46\x6f\x72\x6d'
23->'#', 43->'C', 68->'h', 61->'a', 6c->'l', 6c->'l', 65->'e', 6e->'n', 67->'g', 65->'e', 46->'F', 6f->'o', 72->'r', 6d->'m'
所以是 "#ChallengeForm"
因此,数组_$实际上是:
_$ = ['#challenge', '#waf_answer', '#ChallengeForm']
然后,代码:
(_[0])"show"; -> $('#challenge').show();
接下来,是一个jQuery的文档就绪函数:
$(function(){ ... });
在这个函数中,设置了一个超时,延迟1000毫秒(0x3e8=1000)执行。
超时函数内部:
定义了一个空对象x06dd1a,变量x0fcad9,以及x08c924初始化为0x61(即97)。
然后获取navigator.userAgent并转换为小写。
然后进行一系列正则表达式匹配,以检测浏览器类型和版本,并将版本号存入x06dd1a对象。
之后,对x08c924进行一系列算术运算,并根据条件调整值。
最后,如果检测到任意一种浏览器,则继续计算x08c924,然后将其值设置到id为#waf_answer的输入框中,并提交表单#ChallengeForm。
具体步骤:
x08c924初始值:97
x08c924 = x08c924 * 0x31 (49) -> 97*49=4753
然后根据浏览器类型,x06dd1a会设置相应的属性,但不管设置什么,都会进入后面的条件判断。
如果检测到浏览器(即x06dd1a有任意一个属性被设置),则:
x08c924 = (x08c924 * 3 + 7) -> (4753*3+7)=14266
如果x08c924 < 123 (0x7b),则加上0x929(2345),但14266不小于123,所以不加。
然后,获取#waf_answer元素(即x0b515d)
如果x08c924 > 0x929(2345),则x08c924 = Math.floor(x08c924 / 0x7b) -> Math.floor(14266/123) = 115(因为123*115=14145,14266-14145=121,所以为115)
text
然后将x08c924的值设置到#waf_answer输入框,并提交表单。
所以,这段代码的目的是:
显示一个id为#challenge的元素,然后在1秒后,根据用户代理字符串计算一个值(115),并将这个值填入id为#waf_answer的输入框,然后自动提交id为#ChallengeForm的表单。
注意:这个计算过程与浏览器类型无关(因为不管什么浏览器,只要被识别,都会进行计算,而且计算过程中浏览器版本并没有被使用,只是检测了浏览器类型),最终结果都是115。
因此,这段代码可能是一个简单的反爬虫机制,通过计算一个固定的值来验证请求,但这里计算的值是固定的,所以可能只是一个简单的挑战。
由此可见x08c924这个就是answer,也是我们要求,并且这是个定时器,要异步去获取参数。
缺$直接控制台输出

也就是在https://www.360panyun.net/__panyun/jquery-1.4.2.min.js文件里

我采取全扣代码,然后导出函数为例.这个环境非常好补。(可以利用ai进行纯算,或者扣代码也行)
补好了直接导出即可

现在就是如何调用这个JS文件了
因为是异步的,所以直接输出,用subprocess调用

直接获取eval包括,输出不了这个值(也行有其他方法)。
所以只能利用文件存储和正则了
先获取eval里面的JS,不包括eval()

让它赋值给自己定义的参数aaaa
这样子

输出的就是规则的代码了

然后调用aaaba获取规整的JS代码
因为规则JS代码没有输出x08c924,
所以找特征进行替换,

这里是一直不变的,用replace替换,

接着把环境和网页JS文件和已还原的规则代码直接塞入一个新的JS文件里,文件写入类型为覆盖
然后用异步调用。
注意点:

记住第二个接口是重定向302,要设置allow_redirects=False,让它不跳转其它接口,跳转其它接口拿的是其它接口的cookie,跟正确的cookies无关。
输出结果

附nodeJS环境
javascript
html = {}
fieldset = {}
implementation = {}
form = {}
body = {}
body.childNodes = function (args) {
return [form.form]
}
body.body = body
implementation.createHTMLDocument = function (args) {
return body
}
select = {}
option = {}
a = {}
a.href = 'https:'
option.selected = true
select.appendChild = function (args) {
}
div = {}
div.appendChild = function (args) {
}
input = {}
input.setAttribute = function (args, b) {
}
fieldset.getAttribute = function (args) {
if (args == 'disabled') {
return null
}
}
window = globalThis
window.jQuery = function (args) {
console.log(args)
}
Node = function Node() {
}
Document = function Document() {
}
HTMLDocument = function HTMLDocument() {
}
Object.setPrototypeOf(Document.prototype, Node.prototype)
Object.setPrototypeOf(HTMLDocument.prototype, Document.prototype)
document = new HTMLDocument()
Node.prototype.nodeType = 9
Document.prototype.documentElement = html
Document.prototype.implementation = implementation
Document.prototype.createElement = function createElement(args) {
if (args == 'fieldset')
return fieldset
if (args == 'input')
return input
if (args == "div")
return div
if (args == "select")
return select
if (args == "option")
return option
if (args == "a")
return a
}
Document.prototype.getElementById = function getElementById(args) {
if (args == 'waf_answer')
debugger
if (args == 'fieldset')
return fieldset
if (args == 'input')
return input
if (args == "div")
return div
if (args == "select")
return select
if (args == "option")
return option
if (args == "a")
return a
}
Document.prototype.createDocumentFragment = function createElement(args) {
return {
appendChild: function (args) {
return {
appendChild: function (args) {
return {
setAttribute: function setAttribute(args) {
}
}
}
}
}
}
}
Navigator = function Navigator() {
}
Navigator.prototype.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
navigator = new Navigator()
window.navigator = navigator
class Location {
constructor() {
this.Symbol = "Location"
}
}
location = new Location()
location.href = "https:"
location.ancestorOrigins = {}
location.origin = "https:"
location.protocol = "https:"
location.host = "",
location.hostname = ""
window.location = location
这安全产品太简单了,同样是安全产品,为什么这这么简单。哎,最近想写一篇阿里滑块V2文章加深印象,但是不知道从何写起,虽然已经搞定了,但还是会想起被支配的恐惧,不想跟栈第二次了。有时间有灵感就写吧。