Featured image of post Duckduckgo新版加密参数逆向

Duckduckgo新版加密参数逆向

省流

目前请求时需要添加四个关键头部参数用于验证,其中必需的是 x-vqd-4x-vqd-hash-1(不过都拿出来了):

参数

x-fe-signals

在浏览器控制台中执行:

1
window.__DDG_BE_VERSION__ + window.__DDG_FE_CHAT_HASH__

大概是固定的

x-fe-version

这是一个点击控制信息,解码格式如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "start": 1748338159233,
  "events": [
    {"name": "onboarding_impression_1", "delta": 2},
    {"name": "onboarding_impression_2", "delta": 345451},
    {"name": "onboarding_finish", "delta": 346530},
    {"name": "startNewChat", "delta": 346549}
  ],
  "end": 352948
}

直接伪造并进行 Base64 编码即可

x-vqd-4x-vqd-hash-1

这两个参数需要从 DuckDuckGo 的状态 API 获取:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
curl -X GET 'https://duckduckgo.com/duckchat/v1/status' \
  -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'cache-control: no-store' \
  -H 'priority: u=1, i' \
  -H 'referer: https://duckduckgo.com/' \
  -H 'sec-ch-ua: "Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Windows"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: same-origin' \
  -H 'x-vqd-accept: 1' \
  -H 'Cookie: dcm=3'

(x-vqd-accept 设为 1 时有效)

x-vqd-4

直接使用 API 返回的 x-vqd-4

x-vqd-hash-1

这部分可以玩玩

  1. 请求头的API解码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "server_hashes": [
    "sez/jUZEafdmWjGTy4HVDNZozO44dIEpSp2HvA4jigE=",
    "DZG60hz+ay4TVu9z1fk2z5JIyCfrLEhq1CSQxmjsaGU="
  ],
  "client_hashes": [
    "W3E/w78cQGJhqE9wUYViQEE9dVr74wWbHL7AP1zYWoA=",
    "Ey9ukEo8q2xSmzDx11eOZ+mm84S4jrSj3gzq6K3qU3A="
  ],
  "signals": {},
  "meta": {
    "v": "3",
    "challenge_id": "8f2d8c3d15195ef21b8426efccd558dc9fea43cdd986355f9ff7018053af76b3h8jbt",
    "timestamp": "1749730629886",
    "origin": "https://duckduckgo.com",
    "stack": "Error\nat Cn (https://duckduckgo.com/dist/wpm.chat.652b7f6eac131ba0090b.js:1:62737)\nat async dispatchServiceInitialVQD (https://duckduckgo.com/dist/wpm.chat.652b7f6eac131ba0090b.js:1:87463)" // 非必需,不用纠结这个
  }
}
  1. 对 上文API响应头 返回的 x-vqd-hash-1 进行 Base64 解码,得到一个 IIFE(立即执行函数表达式),反混淆后的代码结构如下:
 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
(function () {
  return {
    server_hashes: [
      "sez/jUZEafdmWjGTy4HVDNZozO44dIEpSp2HvA4jigE=",
      "DZG60hz+ay4TVu9z1fk2z5JIyCfrLEhq1CSQxmjsaGU="
    ],
    client_hashes: [
      navigator.userAgent +
        (navigator.userAgentData
          ? navigator.userAgentData.brands
              .map((brand) => `"${brand.brand}";"v="${brand.version}"`)
              .join(", ")
          : ""),
      (function () {
        const div = document.createElement("div");
        div.innerHTML = "<div><div></div><div></div";
        return String(
          0xb85 + div.innerHTML.length * div.querySelectorAll("*").length
        );
      })(),
    ],
    signals: {},
    meta: {
      v: "1",
      challenge_id:
        "8f2d8c3d15195ef21b8426efccd558dc9fea43cdd986355f9ff7018053af76b3h8jbt",
    },
  };
})();

3.对应的俩加密函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
client_hashes: await Promise.all(
    n.client_hashes.map((e) =>
    (async function (e) {
        const t = new TextEncoder().encode(e),
        n = await crypto.subtle.digest("SHA-256", t),
        a = new Uint8Array(n);
        return btoa(
        a.reduce((e, t) => e + String.fromCharCode(t), "")
        );
    })(e)
    )
)

meta: Et(
    Et({}, n.meta || {}),
    {},
    {
    origin: Rt(),
    stack: St(new Error()),
    }
)

然后把返回的值替换掉再进行base64编码

其实,直接复用client_hashes就好了,

毕竟你看源码SHA-256 digest…它没办法复原的,好迷

(但是太离谱了仍然会伪掉,建议自己拿固定值digest一下,前者看着来,后者3000-5000即可)

  1. 将返回值转换为 Base64 编码

注意:每次调用 API 返回的 js 都会变化

成功截图

Licensed under CC BY-NC-SA 4.0
这里也许没有你想要的东西...?