Two of the 23 PHP2Shell Attacker IPs in Zscaler's Report Are Shared CDN Edges. Paste That Table Into a Firewall and You Block Part of the Internet.

Zscaler published a clean piece of PHP2Shell research this week: 1,156 exploitation attempts from 156 unique source IPs against 70 organizations in the first 48 hours after disclosure, and a table of 23 addresses that actually delivered a payload, each with its JA4 fingerprint and what it tried.
The telemetry is not in dispute. We checked something else: what each of those 23 addresses is, before any of them went near a blocklist.
Two of them are shared CDN edges.
The two that bite
[104.28.220.174](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=104.28.220.174) resolves to CLOUDFLARENET. [18.68.23.52](https://analytics.dugganusa.com/stix/register?ref=ioc-click&q=18.68.23.52) resolves to AMAZON-CF, Amazon CloudFront.
Both almost certainly did what Zscaler says they did. An attacker can route an exploit attempt through a CDN as easily as anyone else routes a web request. But a CDN edge address is not the attacker's address. It is shared infrastructure sitting in front of an unknown number of unrelated sites, and if you take that table, paste it into a firewall or a feed, and block it, you have cut your users off from a slice of the public internet. Then you spend a week not knowing why your staff cannot reach some SaaS tool, because nothing in your change log says "we blocked Cloudflare."
This is not a criticism of the research. The vendor's job is to report what it observed, and it did that accurately and fast. Turning an observation into an enforcement rule is a second job, and it needs a question the table cannot answer for you: whose address is this when the attacker is finished with it?

We resolved all 23, then shipped them at four different confidences
Nine are attacker-rented dedicated hosting. Four Contabo ranges, plus M247, HostPapa, Global Connectivity Solutions, a Berlin hoster and a small US network. These are the real ones. The operator rents the box, keeps it for a while, and uses it. One of the nine planted a persistent webshell rather than probing. Another ran genuine phpggc deserialization gadget chains, not a scanner's canned check. We publish these at confidence 85, which puts them in the default feed and above our own edge shield's blocking floor.
Four are rented cloud VMs — two Microsoft Azure, one Amazon EC2, one Google Cloud. Real attack sources today, somebody else's development box in three weeks, because that is what a cloud lease does. We publish them at 60: present in the default feed, deliberately under our shield's blocking floor, so the block expires from our own enforcement before the lease recycles.
Eight are telco or consumer broadband — Bharti Airtel, Türk Telekom, Viettel, Batelco, AT&T, TE Data and two more in Egypt. Behind carrier NAT, one address can represent thousands of subscribers, and the machine that did the scanning will have a different address tomorrow. We hold these at 45, which is below the default feed floor. They are answerable if you look one up, and they do not ship in anybody's blocklist by default.
Two are the CDN pair, at confidence 20. Withheld by type, not by doubt. We wrote the reason into the record itself, so anyone who queries the address gets told why it is scored the way it is rather than concluding we had never heard of it.
Four tiers, one source table. The difference between them is not how much we believe Zscaler. It is who else is standing behind the address.
A cluster we did not publish
Three of the 23 shared an RDAP contact name across three separate German networks. That looked like one registrant running infrastructure under three netblocks, which would have been a nice finding.
It was wrong. Pulling the full RDAP records instead of the summary, the shared name is Contabo GmbH's own RIPE administrative and technical role contact, present on every Contabo range in the registry. Not an operator. A hosting company employee whose name is in a public database because that is what the database is for.
We mention it because anyone else running the same RDAP sweep will hit the same false pattern, and because naming a private individual as a threat actor on the strength of a summary field is a mistake you cannot take back. A person is never an indicator. What survived the check is duller and true: four of the 23 payload-delivering addresses, about 17 percent, are Contabo ranges.
What our own edge saw: nothing, and that is a measurement
None of the 23 has touched us. We measured that against 2,374 distinct blocked addresses, 1,384 addresses that hit our canaries and 95 origin captures since October 1, and we checked same-subnet neighbors too, in case the operator moved next door. Nothing.
We are not reading that as safety, for a structural reason worth more than the number.
PHP2Shell is not a path. The exploit is an ordinary POST to an ordinary endpoint — the site root, an index file, an upload handler — carrying a crafted multipart boundary. Zscaler lists four tool signatures in that boundary, including one beginning with the string shellflow and two from a scanner that stamps its own build name. Our canaries trap paths. A path trap cannot see an attack whose distinguishing feature is a header.
It gets more pointed. We derive detection fingerprints automatically from public proof-of-concept repositories, and for the second of the two CVEs that machinery produced paths like an app1 index file, a seed directory and a lab shell script. Those are artifacts of somebody's test harness, not attack traffic. Matching on them would never have fired no matter who was scanning us.
So the honest sentence is not "we were not attacked." It is "we have no visibility into this technique at the edge, and we know exactly why." A zero without a denominator and a reason is a comfort. A zero with both is a work item.
What to do with a vendor IOC table
Screen it before you block it. The question is not whether the indicator is real, it is what sits behind the address when the attacker moves on. Three rules cover most of it: never block shared CDN or cloud load-balancer space on the strength of a single observation; treat cloud VM addresses as short-lived and expire them; and treat consumer broadband as a lookup signal rather than an enforcement one.
If you do not have a tool for that, our feed carries this set, screened and tiered, written in today under the source name manual-batch-php2shell-zscaler so you can pull exactly these 23 records and see the reasoning on each one. The confidence floor is yours to set rather than ours. A defender who cannot afford a commercial feed should not have to choose between no indicators and a table that breaks their own network.
And patch PHP. The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35, CISA added both CVEs to the Known Exploited Vulnerabilities list on October 26 under Binding Operational Directive 26-04, and Censys counts tens of millions of PHP services still exposed. The indicator work above is what you do for the hosts you cannot patch today. It is not a substitute for the patch.
Indicators in this post are Zscaler's telemetry, cited, not a DugganUSA observation. Ownership resolution, tiering and the edge measurement are ours, performed October 28, 2026. We cap ourselves at 95 percent confidence.
Every indicator in this post is in the feed. Free.
1.58M+ IOCs, STIX 2.1 / TAXII, 88% novel vs ThreatFox, exploited-CVE leads ahead of CISA. No credit card — a free API key in 30 seconds, and you can audit every claim above against the live endpoints.