深夜提醒

现在是深夜,建议您注意休息,不要熬夜哦~

🏮 🏮 🏮

新年快乐

祝君万事如意心想事成!

share-image
中
ESC

There Will Only Be More Amateur Shows

English 阅读中文版

This morning I wrote a booking bot for the three cultural-tourism benefit cards our family holds (the backstory is in this post). So I would not have to open the mini program every time, I went through its API — the login state is one long-lived, account-level token. No WeChat, no phone, no browser: one request header and you are in.

The moment it worked, I paused. This account has my name and my national ID number in it.

So I spent the whole morning going through everything about them that is visible from the outside.

The conclusion up front: I found no evidence of a leak — and no reason to believe they can keep this stuff safe.

1. Drawing the boundary first

This piece keeps two kinds of content strictly apart: what I measured myself, and what I inferred. Mixing them would be irresponsible, so let me say what I did and what I did not do.

What I did:

  • used only my own account and my own data;
  • looked at public pages read-only: response headers, TLS handshakes, certificates, static file timestamps, error pages;
  • used curl and openssl, with no automated scanning.

What I did not do:

  • no attempts at unauthorized access, no walking through other people’s orders, no credential stuffing, no port scanning;
  • I never touched a write endpoint.

And the most important line: I cannot see their database.

Whether things are encrypted, how long they are kept, who can query them, whether there are audit logs — none of that can ever be verified from the public internet. Anyone who tells you “I tested it and their database is stored in plaintext” is either bragging or breaking the law.

So this is an exposure-surface assessment, not a penetration test report. What it can answer is “does this company take security seriously”; what it cannot answer is “who has your ID number right now.”

2. There is a door; nobody closed it

First finding: http:// does not redirect to https://.

Not a broken redirect — there was never any intention to redirect. Hit port 80 and it 302s to the same path over http://, then begins the WeChat OAuth flow on that plaintext page.

More specifically. A normal web authorization goes like this: the user clicks login → gets sent to WeChat → WeChat calls back your redirect_uri with a code → you exchange the code for an openid and a session.

The authorization this system triggers at its HTTP entry point has a redirect_uri that looks like this:

redirect_uri=http%3a%2f%2f<domain>%2fCardGroup

Note that http%3a%2f%2f. It is telling WeChat, in as many words: send the authorization code back to a plaintext address.

Anyone on the same Wi-Fi, or anyone able to insert themselves into the path, can see that code — and the ASP.NET_SessionId that follows.

And the session cookie’s attributes are:

Set-Cookie: ASP.NET_SessionId=<value>; path=/; HttpOnly; SameSite=Lax

HttpOnly is there. SameSite is there. Secure is not. Which means the browser has no objection to sending it over plaintext.

I know somebody will say “users click inside WeChat, and WeChat is https.” True — under normal circumstances. But if any link in the chain ever routes a user to http — an old bookmark, a link forwarded in a group chat, an outdated address in an official-account article that nobody updated, a shortened URL — the authorization code and the session run in plaintext from there on. And the system has no mechanism to prevent this from happening.

A single 301 fixes it. Nine years, nobody added it.

3. The padlock in the address bar proves nothing

The certificate is valid: issued by DigiCert, wildcard, expires January 2027. Looks reasonably professional.

But it is a DV (Domain Validation) certificate.

DV verifies exactly one thing: that the applicant controls the domain. It does not verify the company’s name, where it is registered, or whether it exists at all. OV checks the business licence; EV checks deeper — and browsers retired the EV “green company name” years ago, so an ordinary person cannot tell the three apart from the address bar.

In other words: the little padlock only proves “you are connected to this domain”. It does not prove “who is behind this domain.”

For a platform that collects national ID numbers, that distinction is not small.

4. A machine from 2012

I did a double take at the response headers:

HTTP/1.1 302 Found
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-AspNetMvc-Version: 4.0
X-Powered-By: ASP.NET

IIS 7.5 shipped with Windows Server 2008 R2; Microsoft ended extended support for it in January 2020. ASP.NET MVC 4 was released in 2012. Even the date fields are still in the archaic /Date(1777305600000)/ format.

A system that, in 2026, is still selling benefit cards and collecting ID numbers — runs on an operating system fifteen years old (or at least claims to).

Two possibilities: it really is running unsupported Windows bare, or the version strings simply never got changed. Neither is reassuring; they only worry you about different things.

Then I checked the security response headers, one by one:

Header Purpose Result
Strict-Transport-Security Force later requests onto HTTPS absent
Content-Security-Policy Second line of defence against XSS absent
X-Frame-Options Prevent clickjacking absent
X-Content-Type-Options Prevent MIME sniffing absent
Referrer-Policy Control Referer leakage absent

Not one of them.

No HSTS, so downgrade attacks are viable. No X-Frame-Options, so the booking page can be dropped into an invisible iframe and you can be baited into clicking. No CSP, so the moment an XSS exists the browser offers no backstop at all.

Then the error page. I casually requested an endpoint that does not exist; it bounced to a custom error page reading:

An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been aborted.

Translated: the error handler itself crashes, and nobody has ever rehearsed a 500 on production.

One last detail. The response header of a static file on the login page:

Last-Modified: Fri, 04 Aug 2017 08:57:20 GMT

August 4, 2017. Nine years, not one byte of this file has changed.

A thousand patches after end of life

Somebody will say: IIS 7.5 is from 2008 — old, sure, but how many vulnerabilities can it have?

I pulled the numbers from NVD. The scope: the official CVE database matched by CPE against the windows_server_2008 family (including R2), tallied through November 2026:

Measure All history After EOL, 2020-01-14 After paid patches ended, 2023-01-10
Total CVEs 4,078 2,134 1,063
Critical 100 67 37
High 2,818 1,572 749
RCE mentioned in description 937 628 314
Exploited in the wild (CISA KEV) 199 88 40

Translated: after Microsoft stopped supporting it, more than two thousand new vulnerabilities appeared on this system; even after the paid ESU window closed, another thousand arrived that will never receive a patch.

There is a trap here. Search by the product “IIS” and NVD lists 93 entries in total — and zero after 2020.

Not because IIS got safer, but because HTTP.sys runs in the kernel, and its vulnerabilities are filed under Windows. Counting holes by “IIS version number” produces false comfort. The real attack surface is the operating system.

A few names nobody can get around, all inside that set and all on the exploited-in-the-wild list:

  • CVE-2015-1635 (MS15-034), HTTP.sys remote code execution, CVSS 9.8. A single malformed Range header sets it off — the most famous one from the IIS 7.5 era;
  • CVE-2019-0708 (BlueKeep), RDP, 9.8, wormable, the same script as WannaCry;
  • CVE-2020-1350 (SIGRed), the DNS service, 10.0, wormable, disclosed half a year after end of support;
  • CVE-2020-1472 (Zerologon), Netlogon, 10.0 per NVD — one packet takes over a domain controller;
  • CVE-2021-34527 (PrintNightmare), the print spooler, 8.8.

Among the new Criticals published after ESU ended in 2023 there is also a cluster of 9.8 RCEs: PEAP (CVE-2023-21689/21690/21692), iSCSI (CVE-2023-21803), MSMQ (CVE-2023-21554), ICMP (CVE-2023-23415). Those patches will never reach 2008 R2.

So the story runs in three phases:

  1. Before 2020-01-14: Microsoft still shipped patches; problems were containable.
  2. 2020-01-14 to 2023-01-10: only machines that bought ESU got patches; everyone else ran naked from that day.
  3. After 2023-01-10: even ESU ended. Any new vulnerability has no fix path — not “too lazy to patch”, but “nothing exists to patch with.”

That said, a version number is not the same as being break-in-able. What actually decides whether someone can get in is which ports are open, which modules are installed, whether a WAF sits in front, whether ESU was really bought. I saw responses only on 80 and 443 and did no active probing, so I cannot prove the system is compromiseable right now.

What I can prove is something else: it has lost the ability to be fixed.

Security is not a state, it is a process — discover, assess, patch, discover again. This machine left that process in 2020. So the question is not “how many vulnerabilities does it have today” but “from now on, every new vulnerability can only wait.”

Also: the Server: Microsoft-IIS/7.5 header can be forged. It may genuinely run on 2008 R2, or the version string may simply never have been worth changing. The former means “cannot be fixed”, the latter means “wants you to believe it cannot be fixed” — neither is good news, just bad in different places.

One more piece of corroboration, from data others had already scanned and published. Shodan’s record for this machine shows, besides 80 and 443, a 5985 — WinRM, Windows’ remote-management port, and the plaintext one (5986 is the HTTPS variant). The same record tags it eol-os: an operating system at end of life.

I did not connect to that port and did no active probing, so I do not know whether it is open right now. But a Windows box sitting on the public internet with its remote-management port exposed already says a lot — behind that port is password authentication, and passwords can be slowly tried.

By the way, I briefly considered “just scan the ports and be done with it.” Scanning would not have answered it. Ports tell you which doors are open; they cannot tell you whether the doors are locked. Version strings are useful precisely because they speak to the second question.

6. What got saved between renting Youzan and building it yourself

While paging through all this, I remembered something.

A couple of years ago their mini program ran on Youzan underneath.

That is an e-commerce SaaS with more than a decade of history — a listed company with ready-made systems for payments, orders, membership and data compliance. You did not have to trust it unconditionally, but you at least knew: when something goes wrong, someone is on the hook and can actually afford it.

Looking again this year, the mini program had been replaced with a self-built set of pages.

I am not against building in-house. Self-built can fit the business better and cost less. But between “rent a mature system” and “build your own”, what gets saved is not only the subscription fee — it is also a whole body of things somebody else grew by falling into the pits first:

  • enforced HTTPS with HSTS;
  • session lifetimes and invalidation;
  • encryption, masking and access auditing for ID fields;
  • regression testing before launch and alerting after it.

If dropping those bought an obviously better product, fine — worth it.

What I saw instead is the other kind of “cost cutting”: costs down, speed up, and the quality column left blank.

You hear the phrase “cost cutting and efficiency gains” constantly these days. In practice it usually comes in three steps: replace outsourced staff with in-house employees who take fewer holidays, replace in-house engineering with AI-generated code, replace testing and operations with “ship first, figure it out later.”

Writing code with AI is not a problem in itself — I use it every day. The problem is: AI can help you throw a page together in one afternoon, but it will not remind you that “http should redirect to https” unless you already knew that was a thing.

So the delivered result looks like this:

  • a login page untouched since 2017;
  • a server claiming to be IIS 7.5;
  • five security headers, zero of them present;
  • error handling whose error page also crashes;
  • a login flow that sends the authorization code to a plaintext address.

Honestly, this does not look like something an engineering team maintains. It looks like one person threw it together over a weekend — with the “it runs, ship it” kind of throwing-together.

But the point I want to make is not “the person who wrote this is bad at coding.”

It is that nowhere along this chain is anyone responsible for security.

Maybe the role does not exist. Maybe it was optimized away in the “cost cutting” column.

7. A key you cannot change

This system’s login state does not go through cookies; it goes through a custom request header — account-level, long-lived. My script stays logged in entirely on it: the token lives in a 0600 file, and no QR scan is ever needed again.

From an engineering standpoint, very convenient. From a security standpoint, a different story:

  • it has no expiry (at least I never observed one);
  • no two-factor authentication;
  • no device list, so there is no action called “kick the other device offline”;
  • its authority equals the whole account: cards, orders, travellers, ID documents.

Which means one leak anywhere along the chain — a log, a screenshot, a browser extension on a shared computer, a proxy app, somebody borrowing your phone for a glance — equals permanent takeover.

Worse, it is not like a password. When a password leaks you change it; for this token you cannot even find the place to change it.

8. The one part I could not see

What packet capture shows: the traveller’s ID field comes back to the client as-is — no mask, no redaction, not even “last four digits only.”

The interface itself does put on an act — on the ticket page the number renders as asterisks. But that is drawn by the frontend: the same field arrives complete in the API.

Ticket page: document type and document number are fields attached to the ticket

The mask is on the page, not in the data.

This only proves the API layer does no minimization — it is not, by itself, proof of plaintext storage. Plenty of systems really do “encrypt in the database, decrypt and return everything at the API”, which counts as a (bad) design too.

Given all the 2017-era fingerprints, though, I cannot bring myself to assume “they have mature field-level encryption, key rotation and access audits.”

Verification ends here; past this point it is speculation. I mark the speculative parts; judge them yourself.

9. Where is the privacy policy

Under Article 28 of China’s Personal Information Protection Law, a national ID number is explicitly listed sensitive personal information (specific identity). Handling it requires more than “notice” under the law:

  • Article 29: obtain separate consent;
  • Article 30: disclose the necessity and the impact on the individual’s rights;
  • Article 55: conduct a personal-information protection impact assessment beforehand;
  • Article 51: take security measures such as encryption and de-identification;
  • Article 47: when you ask for deletion, it must delete.

And while logged out, I could not find any publicly accessible privacy policy or terms-of-service page — the entire site forces WeChat authorization; without it you see nothing.

That cannot prove the documents do not exist (they may hide behind some second-level page in the mini program, or behind the checkbox you ticked). But it at least shows: they never intended you to read any of it before handing over your data.

If you use a platform like this, thirty seconds is worth spending: go back to the “add traveller” page and look for a separate checkbox, for a sentence saying “here is how we will store your ID information.” If there is none, it fails Article 29.

10. So how dangerous is it

Gathering everything above into one table:

Risk My judgement Basis
Interception in transit (man in the middle) Medium HTTPS available but never enforced; callback addresses can be http
Account takeover Medium-high Long-lived token, no 2FA, no device management
Internal leak or misuse Unverifiable; inferred high from investment level Stack frozen around 2012; nobody even minds the error page
Compliance (separate consent, deletion channel) Not observed No privacy policy findable from outside

Overall: medium-to-high risk.

Not “run” — “do not treat it as a safe.”

One fair word: inside the WeChat mini program the channel is WeChat’s own encrypted link, better than the H5 layer. But the backend is the same IIS and the token is the same token.

11. What I did

A list for myself, posted here too:

  1. Operate only inside the official mini program; click no http:// links; even from a browser, type https:// by hand.
  2. Give the number, not the photo, whenever possible. Many venues verify with name + ID number only; uploading both sides of the card raises the risk by an order of magnitude.
  3. If the photo is unavoidable, watermark it: “For verification at XX venue on Month X Day X only; void for any other use”, and cover the address and issuing authority.
  4. Never store anything long-term in “frequent travellers”. Delete after use; leave no dossier in the account.
  5. Ask customer service three questions: how long is it kept, is it stored encrypted, who can access it. Request deletion after the event ends and keep the ticket number.
  6. If deletion is refused, complain: 12315, 12377, or the local cyberspace administration. Article 47 gives you the right to deletion; using it is a separate decision.
  7. Do not screenshot order pages or verification codes to outsiders. They carry your name, document and order number; one image circulates in group chats for a long time.

12. A hot take

Enough about me. Here is a bigger claim.

None of the above is one company’s accident. One firm collects ID numbers on a five-year-old architecture; another does the same — nobody just goes and digs. And “nobody digs” is itself the industry standard.

My judgement: within the next two or three years there will be a large-scale personal-data breach, and its source will be exactly this kind of AI-accelerated build.

The reason is a missing generation.

Companies everywhere are cutting staff and “cutting costs while raising efficiency”: one person doing several people’s jobs, dressed up as DRI, or in the grander phrasing, the “super individual”. Experienced seniors get optimized out; young people look at the market and conclude that computer-science master’s graduates cannot even convert an internship, that studying all this buys you no job, that the input-output ratio is simply broken — so they do not study it.

What fills the market is a new kind of operator: mediocre skills, asking AI for a service, the web page comes up, so it ships. It is not that he has no HTTPS — he installed a DV certificate, a free one, and the padlock in the browser glows just the same; but when his system talks to WeChat for authorization, the callback runs over plain http. His token stays valid forever and never expires. That this architecture was audacious enough to become a product put up for sale — sometimes I genuinely do not know what to say.

AI is powerful, but only in hands more powerful than it. What AI has actually done is hide the distance between “it runs” and “it works”. Someone who does not know HTTP, who has never heard of Nginx, can stand up a service facing the public internet — because until something errors, everything looks fine. The bold eat well and the timid starve. If it runs, it is good enough; nobody ever said it could not be used.

Real samples from my own work lately: people who cannot even format JSON — fine, you do not use AI, but if the JSON is malformed, can you at least spot that? Programmers sitting in IDEA writing Java who cannot handle the most basic error, yet go stubborn to the end swearing their code is the problem-free side. I have met both kinds.

What these people ship comes with no documentation, no regression testing, and no accountability. Handing in something to the boss is the whole assignment; whether it works, whether the data is safe — not my problem, since next year this position may not exist anyway.

And the defending side? People with real skill can use AI to surface latent risks and close them before anything breaks. But many companies do not have such a person — that person was either optimized away or never planned for.

The PHP world pops a new vulnerability every few weeks, with countless ancient systems lying bare underneath — that is a rehearsal of this exact structure: the attacker’s tools get stronger while the defender does not even have an asset inventory.

So yes, it is a hot take, but not mysticism: the more amateur shows get built, the higher the odds one collapses; and the crew trained to catch the collapse is being laid off right now. Those two curves cross at some point. On the day they do, the headline will read “Suspected intrusion at some platform: tens of millions of verified identities for sale.”

When we look back then, every line in this article will not have been a surprise.

In the end

A leaked password can be changed. A leaked phone number can be swapped. A leaked ID number — it follows you for life.

So the bar for holding it should always have been much higher than for an ordinary account.

A company can be too poor for certification audits, too poor to keep a security team — I understand that. But “http redirects to https” is one line of configuration, “add an HSTS header” is one line, “turn off X-Powered-By“ is one line too. Those things missing simultaneously does not indicate no money. It indicates nobody is watching.

And I handed my ID number to a system nobody is watching.

No need to worry on their behalf either: business grows, more companies sign up, teams get replaced — those few lines of configuration still get written by nobody. There will only be more amateur shows, until the day one of them collapses on someone else’s head.

So next year, when the cards expire, I plan to delete the account and stop using it.

Not because this exercise dug up hard evidence of a leak — it did not.

Because I cannot see in it any ability to block the next one.


For compliance reasons, the platform’s domain and operating entity are withheld from this piece. All technical conclusions come from passive probing in November 2026 and reflect the state at that time; the platform may patch at any moment, and this article is not an ongoing warranty of its security posture. No other person’s data was involved and no unauthorized-access testing was performed.

文章作者:阿文
文章链接: https://www.awen.me/post/39e8fec7.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿文的博客!

评论

0 条评论
😀😃😄 😁😅😂 🤣😊😇 🙂🙃😉 😌😍🥰 😘😗😙 😚😋😛 😝😜🤪 🤨🧐🤓 😎🥸🤩 🥳😏😒 😞😔😟 😕🙁☹️ 😣😖😫 😩🥺😢 😭😤😠 😡🤬🤯 😳🥵🥶 😱😨😰 😥😓🤗 🤔🤭🤫 🤥😶😐 😑😬🙄 😯😦😧 😮😲🥱 😴🤤😪 😵🤐🥴 🤢🤮🤧 😷🤒🤕 🤑🤠😈 👿👹👺 🤡💩👻 💀☠️👽 👾🤖🎃 😺😸😹 😻😼😽 🙀😿😾 👍👎👏 🙌👐🤲 🤝🤜🤛 ✌️🤞🤟 🤘👌🤏 👈👉👆 👇☝️✋ 🤚🖐️🖖 👋🤙💪 🦾🖕✍️ 🙏💅🤳 💯💢💥 💫💦💨 🕳️💣💬 👁️‍🗨️🗨️🗯️ 💭💤❤️ 🧡💛💚 💙💜🖤 🤍🤎💔 ❣️💕💞 💓💗💖 💘💝💟 ☮️✝️☪️ 🕉️☸️✡️ 🔯🕎☯️ ☦️🛐⛎ ♈♉♊ ♋♌♍ ♎♏♐ ♑♒♓ 🆔⚛️🉑 ☢️☣️📴 📳🈶🈚 🈸🈺🈷️ ✴️🆚💮 🉐㊙️㊗️ 🈴🈵🈹 🈲🅰️🅱️ 🆎🆑🅾️ 🆘❌⭕ 🛑⛔📛 🚫💯💢 ♨️🚷🚯 🚳🚱🔞 📵🚭❗ ❕❓❔ ‼️⁉️🔅 🔆〽️⚠️ 🚸🔱⚜️ 🔰♻️✅ 🈯💹❇️ ✳️❎🌐 💠Ⓜ️🌀 💤🏧🚾 ♿🅿️🈳 🈂🛂🛃 🛄🛅🛗 🚀🛸🚁 🚉🚆🚅 ✈️🛫🛬 🛩️💺🛰️
加载中...

留言反馈

😀😃😄 😁😅😂 🤣😊😇 🙂🙃😉 😌😍🥰 😘😗😙 😚😋😛 😝😜🤪 🤨🧐🤓 😎🥸🤩 🥳😏😒 😞😔😟 😕🙁☹️ 😣😖😫 😩🥺😢 😭😤😠 😡🤬🤯 😳🥵🥶 😱😨😰 😥😓🤗 🤔🤭🤫 🤥😶😐 😑😬🙄 😯😦😧 😮😲🥱 😴🤤😪 😵🤐🥴 🤢🤮🤧 😷🤒🤕 🤑🤠😈 👿👹👺 🤡💩👻 💀☠️👽 👾🤖🎃 😺😸😹 😻😼😽 🙀😿😾 👍👎👏 🙌👐🤲 🤝🤜🤛 ✌️🤞🤟 🤘👌🤏 👈👉👆 👇☝️✋ 🤚🖐️🖖 👋🤙💪 🦾🖕✍️ 🙏💅🤳 💯💢💥 💫💦💨 🕳️💣💬 👁️‍🗨️🗨️🗯️ 💭💤❤️ 🧡💛💚 💙💜🖤 🤍🤎💔 ❣️💕💞 💓💗💖 💘💝💟 ☮️✝️☪️ 🕉️☸️✡️ 🔯🕎☯️ ☦️🛐⛎ ♈♉♊ ♋♌♍ ♎♏♐ ♑♒♓ 🆔⚛️🉑 ☢️☣️📴 📳🈶🈚 🈸🈺🈷️ ✴️🆚💮 🉐㊙️㊗️ 🈴🈵🈹 🈲🅰️🅱️ 🆎🆑🅾️ 🆘❌⭕ 🛑⛔📛 🚫💯💢 ♨️🚷🚯 🚳🚱🔞 📵🚭❗ ❕❓❔ ‼️⁉️🔅 🔆〽️⚠️ 🚸🔱⚜️ 🔰♻️✅ 🈯💹❇️ ✳️❎🌐 💠Ⓜ️🌀 💤🏧🚾 ♿🅿️🈳 🈂🛂🛃 🛄🛅🛗 🚀🛸🚁 🚉🚆🚅 ✈️🛫🛬 🛩️💺🛰️