Part 2: From 900 to 80,000 – How a University's Entire Network Was Left Wide Open

Part 2: From 900 to 80,000 – How a University's Entire Network Was Left Wide Open


Update (June 23, 2026): Core vulnerability remediated

Company Y has deployed server-side authentication on the XHR API gateway. Account IDOR and password exposure on this platform are no longer exploitable. See revalidation details below.

LOW MEDIUM HIGH CRITICAL

CVSS 3.1: 9.8 (CRITICAL)

Mass credential exposure + account takeover via unauthenticated API.

Summary

Part 1 ended with 896 exam candidates’ personal data and national ID card photos exposed through University X’s Testing Center – all built on Company Y’s “Connections” platform. I closed that report with a question I could not shake: if the exam system had zero access controls, what about the university’s main platform running on the exact same infrastructure?

I went looking. The answer was far worse than I imagined.

University X operates connections.universityx.vn – its central in-house network, also built on Company Y’s Connections SaaS. This is not a side portal. It is the platform that holds every student, every alumnus, every staff member, every faculty – the university’s entire digital population in one place. Using the same techniques from Part 1 – unauthenticated API calls and sequential ID enumeration – I extracted 80,053 user accounts. Each one contained up to 40 fields of personal data.

But the number was not what kept me up that night. It was the password field. The API returned staff credentials – some in plaintext, others masked with a trivial algorithm that could be reversed using data from the same API response. Of 554 accounts with exposed passwords, I obtained 208 usable credentials – 50 read directly in plaintext, 158 recovered offline – without a single login attempt.

To prove the severity, I logged into a Vice Rector’s account. No bruteforce. No exploit. I read a field and typed it into the login form. That was it.

1. Introduction

1.1. Recap: Part 1

If you haven’t read Part 1, here’s the short version. I investigated tec.universityx.vn, the Testing Center’s exam registration system, and found:

  • 896 candidates’ PII extracted via unauthenticated API
  • CCCD numbers and ID card photos accessible to any visitor
  • Root cause: Company Y’s Connections platform enforces zero server-side authentication

The platform’s architecture was the problem – not a single misconfigured endpoint. Every table, every field, every uploaded file was accessible to anyone who knew (or guessed) the right object ID.

1.2. The Natural Next Question

The Testing Center was a relatively small deployment: a few hundred candidates per exam cycle. But University X’s presence on the Connections platform extended far beyond exams.

The university also operates connections.universityx.vn – its central in-house network, holding accounts for every student, staff member, and faculty across the entire institution. Same vendor. Same JavaScript framework. Same cdn.companyy.com scripts. Same architecture.

If the exam system leaked data for 896 people, what would the main university network expose?

I had to find out.

1.3. Scope and Ethics

The same ethical principles from Part 1 apply:

  • All data access used publicly available, unauthenticated endpoints.
  • No authentication was bypassed – because none existed.
  • No data was modified, deleted, or shared with third parties.
  • No brute-force attacks were performed. Password recovery was done offline using data already exposed by the same API.
  • Account takeover was performed once, solely to confirm the vulnerability’s severity, and the session was immediately terminated.

2. A Bigger Target: The University Network

2.1. Platform Discovery

I opened connections.universityx.vn in my browser and looked at the source. There it was – the identical JavaScript framework loading from the same CDN:

<script src="https://cdn.companyy.com/js/include.core.isj"></script>

The same internal API functions were available: CAN.db() for database queries, config() for reading cached responses, and xửLý() for search operations. The only difference was the data – instead of exam candidates, this platform manages the university’s entire user base.

I already knew this architecture had no locks on the door. Now I just needed to see how much was behind it.

2.2. Mapping the Account Space

I used the same enumeration technique from Part 1 – iterating through sequential account IDs via CAN.db(). An initial probe estimated the active range at 7,787–97,744 (89,958 potential IDs). But as the crawl ran, accounts kept appearing well beyond that estimate:

Parameter Value
Lowest active ID 7,787
Highest active ID 215,212
Total ID span 207,426
Accounts with data 80,053

Over 200,000 potential IDs probed. 80,053 returned complete user records. Every single one – accessible without authentication.

That number hit differently than 896. This was not a corner of the university. This was the entire university.

3. The Crawl: 80,053 Accounts

3.1. Extraction Method

The technique was identical to Part 1. For each account ID in the range, a single API call retrieved the full record:

CAN.db("taiKhoan.{id}", function() {
    var d = config("taiKhoan.{id}");
    // d now contains all fields for this account
});

No tokens. No cookies. No authentication headers. The platform auto-creates an anonymous session (I received session ID kID=186xxxxx) and serves the data. Just like Part 1 – the database simply answers anyone who asks.

3.2. Two Directions of Exposure

The 80,053 accounts were not a single uniform dataset. They split into two very different exposure stories – each with its own severity:

Account Category Count Account Type (loai_tk)
Current students 21,393 Type 0
Alumni / former students 57,588 Primarily type 0
Staff & faculty 562 Primarily type 2, with 6 type 1 (admin)
Companies / partners 166 Type 3
Guests 344 Type 3
Total 80,053  

The first story is about 78,981 students and alumni – the sheer mass of personal data exposed. The second is about 562 staff and faculty – a smaller group, but with a far more dangerous payload: their passwords were in the API response.

Let me walk through each.

4. Direction 1: Student and Alumni Exposure – 78,981 Accounts

4.1. The Scale

The vast majority of exposed accounts – 21,393 current students and 57,588 alumni – belonged to people who had studied at University X going back nearly two decades. These were standard accounts (type 0), each containing up to 40 fields of personal data.

Sample student account data from API Figure 1: Sample student account record as returned by the unauthenticated API – showing personal data fields including name, date of birth, student ID, and contact information.

4.2. What Was Exposed for Each Student

Every student record was a personal dossier. Here is how completely the data was filled in:

Field Filled Percentage
Full name (ho_ten) 80,037 100.0%
Student ID (ma_sinh_vien) 79,448 99.2%
Date of birth (ngay_sinh) 78,637 98.2%
Gender (gioi_tinh) 70,028 87.5%
Hometown (que_quan) 50,042 62.5%
Faculty (khoa) 41,340 51.6%
Major (nganh) 41,226 51.5%
Enrollment year (khoa_hoc) 39,768 49.7%
Position/title (chuc_vu) 36,390 45.5%
Email (email) 29,640 37.0%
Phone (sdt) 23,970 29.9%
Ethnicity (dan_toc) 7,845 9.8%
Profile photo (avatar_id) 6,586 8.2%
CCCD number (cmnd_cccd) 2,750 3.4%
Mother’s name 344 0.4%
Father’s name 333 0.4%

80,037 full names. 78,637 dates of birth. 23,970 phone numbers. 2,750 national ID numbers. And for 333 accounts – even their parents’ names. This was not a list of usernames. It was a complete personal dossier for nearly the entire university population.

4.3. A Portrait of the Student Body

The data painted a detailed picture of University X. The 4:1 female-to-male ratio (54,815 female, 13,522 male) reflected the university’s specialization in foreign languages. Enrollment records spanned from K2008 to K2025 – nearly two decades of students, all in one exposed database.

Of the 29,640 email addresses, 20,793 were personal Gmail accounts, while 5,329 used the university’s Microsoft 365 domain. The 68 accounts with gmai.com and 48 with gmail.con confirmed this was real, human-entered data – typos and all.

50,042 accounts listed a hometown, heavily concentrated in northern Vietnam – 35.5% from Ha Noi alone. The top faculties were Business Administration & Tourism (5,579), English (5,510), and Chinese (4,422), across 42 unique faculty names and 56 programs.

The full statistical breakdowns – faculties, majors, geographic distribution, demographics – are available in the Appendix.

4.4. Why This Matters

Student exposure was about volume and depth. Any attacker with a browser could build a dossier on nearly 80,000 people: their name, birthday, phone number, home province, what they study, and when they enrolled. For 2,750 people, their national ID number was exposed too – a piece of data that cannot be changed, ever.

But the students did not have login credentials exposed. That distinction belongs to the second group.

5. Direction 2: Staff and Faculty Exposure – 562 Accounts (with Passwords)

5.1. A Smaller Group, a Bigger Problem

The staff and faculty accounts were a fraction of the total – just 562 out of 80,053. But what made them uniquely dangerous was four extra fields that student accounts did not have: username, password, password_unmasked, and password_type.

The API was returning credentials. Not just personal data – actual login credentials for university staff.

5.2. The Password Field

While mapping the fields returned for staff accounts, one field stopped me cold: .

For student accounts, this field was empty. For staff and administrative accounts, it contained what appeared to be credentials. I stared at the screen for a moment, not quite believing what I was seeing. The platform was returning passwords in its API response.

Credential data was exposed for 561 accounts across three account types:

Account Type Accounts with Credentials With Password With Empty Password
Staff (type 2) 547 546 1
Admin (type 1) 6 4 2
External (type 3) 8 4 4
Total 561 554 7

The 554 accounts with non-empty passwords fell into two categories:

Category Count Format Example
Plaintext 50 (9.0%) Raw password string mypassword123
Masked 504 (91.0%) first2**last2[length] xx**xx[13]

Fifty passwords in cleartext. Just sitting there in the API response. And the other 504? They were “masked” – but as I would soon discover, the masking was barely an obstacle at all.

Sample staff account with credential fields Figure 2: A staff account record from the API response – note the credential fields including username and password data, served to an unauthenticated anonymous session.

5.3. The Masking Algorithm

The masked passwords followed a consistent pattern:

first 2 characters + "**" + last 2 characters + "[" + total length + "]"

For example, a masked password like xx**xx[13] reveals:

  • Starts with: first 2 characters
  • Ends with: last 2 characters
  • Total length: 13 characters
  • Unknown: only 9 middle characters

This is not encryption. It is not hashing. It is a display mask that preserves information about the original password – information that dramatically reduces the search space for recovery.

And that is when I realized something worse.

5.4. Offline Password Recovery

The data needed to guess these passwords was in the same API response.

Think about it. Vietnamese users commonly construct passwords from personal information: date of birth, name components, phone numbers. And the API response for each staff account included all of these fields – name, date of birth, phone number, email – right alongside the masked password.

The platform was not just handing out the locked door. It was handing out the key and pointing to which lock it fit.

I wrote an offline dictionary generator that combined:

  • Date of birth (various formats: ddmmyyyy, dmyyyy, dd/mm/yyyy)
  • First name, last name (with Vietnamese diacritics removed)
  • Phone number (full and last 4–6 digits)
  • Common patterns: name + dob, dob + name, phone + name

The masked format acted as a validator: a candidate password could be instantly verified if its first 2 characters, last 2 characters, and length matched the mask – no login attempt required. No contact with the server. Entirely offline.

Against 554 exposed passwords, the results:

Metric Count Rate
Accounts with exposed passwords 554
Plaintext (no recovery needed) 50 9.0%
Masked – recovered offline 158 28.5%
Masked – not recovered 346 62.5%
Total usable credentials 208 37.5%

Recovery method breakdown for the 208 usable credentials:

Method Count Description
Plaintext 50 Password stored in cleartext, no recovery needed
Known plaintext 63 Password matched a known common pattern exactly
Dictionary (unique match) 78 Single dictionary candidate matched the mask
Dictionary (ranked match) 17 Multiple candidates matched; correct one identified by rank

208 credentials obtained without ever touching the login page. The passwords were not “cracked” in the traditional sense – they were either read in plaintext or reconstructed from biographical data served by the same unauthenticated endpoint.

5.5. Who Were These Staff Members?

These were not test accounts or abandoned profiles. The 562 staff accounts in the staff roster included individuals across 35 distinct positions – from academic advisors to department heads:

Position Count
Academic advisors 147
Administrative staff 110
Specialists 71
Lecturers 56
Committee members 22
Administrative assistants 17
Academic affairs assistants 17
Faculty leadership 14
Department heads 12
Vice department heads 20
Other positions (25 types) 76

The PII completeness for staff accounts was striking – 99.6% had usernames, 98.6% had passwords in some form, and 92.9% had email addresses:

Field Filled Of 562
Username 560 99.6%
Password (any form) 554 98.6%
Email 522 92.9%
Phone 389 69.2%
Date of birth 333 59.3%
CCCD number 30 5.3%
Recovered/plaintext password 208 37.0%

5.6. The Irony

Let me spell out the absurdity of this situation:

  1. The API serves staff passwords (masked or plaintext) to anonymous users.
  2. The same API serves the personal data needed to recover masked passwords.
  3. No authentication is required for any of it.

The platform hands an attacker both the locked door and the key, then points to the correct door.

At this point, I had 208 working credentials for university staff. I had not logged into anything. I had not interacted with any login form. But I needed to prove that these credentials actually worked – that the theoretical risk was real.

6. Account Takeover – Proof of Concept

6.1. Choosing a Target

To demonstrate the real-world impact, I selected the highest-privilege account I could identify: a Vice Rector of University X. This was account #xxxx, retrieved via the same unauthenticated API call as every other account:

CAN.db("taiKhoan.xxxx", function() {
    var d = config("taiKhoan.xxxx");
    console.log(d["a"]);   // username
    console.log(d[""]);   // password
});

The API returned the username and a password. For this particular account, the password was recoverable.

6.2. The Login

The process was absurdly straightforward:

  1. Read the credentials from the API response.
  2. Navigate to the login page.
  3. Enter the username and password.
  4. Press “Login.”

That was it. No bruteforce. No password cracking tool. No session hijacking. No exploit. I read a field from an unauthenticated API and typed it into a form.

The login succeeded.

I was now authenticated as a Vice Rector of University X with full administrative access to the platform.

Admin platform UI after account takeover Figure 3: The in-house platform’s administrative interface – accessed using a Vice Rector’s credentials read directly from the unauthenticated API response.

6.3. What Was Accessible

With administrative credentials, the scope of access expanded dramatically:

  • Full user management capabilities
  • Access to internal announcements and communications
  • Ability to modify user records
  • Access to administrative functions and settings

I took a screenshot to document the access, then immediately terminated the session. No actions were taken, no data was modified, and no further exploration was performed under the compromised account. One screenshot was enough. The point was proven.

7. The “Fix” That Wasn’t

7.1. Vendor Response: tec.universityx.vn

After responsible disclosure in February 2026, Company Y implemented changes to tec.universityx.vn (the exam system from Part 1). I was cautiously optimistic. The “fix” had two components:

Data-level cleanup: Some PII fields were emptied for candidate records. Full names, phone numbers, emails, and CCCD numbers were removed from API responses.

Obfuscation layer: Remaining data was wrapped in a custom Base64 encoding scheme called “b6x.”

The first part was a step in the right direction. The second part was not.

7.2. The b6x Encoding: A 9th-Century Cipher

Before the patch, the API returned plaintext fields:

{
    "16xxxxx": "Tran Thi Hxxx",       // First name
    "16xxxxx": "23xxxxxxxx",           // CCCD number
    "16xxxxx": "09xxxxxxxx",           // Phone
    "16xxxxx": "hoaixxxx@gmail.com"    // Email
}

After the patch, fields were encoded into a single blob:

{
    "i": "16xxxxx",
    "_5a9fxxxxxxxxxxxxxxxxxxxxxxxxxxxx": "wqD2xxxxxxxxxxxxxxxxxxxx..."
}

This looks encrypted at first glance. It is not. The encoding uses a monoalphabetic substitution cipher – a technique broken since the 9th century (Al-Kindi, Manuscript on Deciphering Cryptographic Messages, circa 850 CE).

The “encryption” is a simple character substitution between two alphabets:

Custom (b6x):  OsCmIBxZDQxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxtz0dV:e5vFb
Standard B64:  ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=

And the decryption function? It ships in the same JavaScript bundle that every browser downloads:

d64 = function(v, k) {
    switch(k) {
        case "x":
            if (!v) return "";
            v = strtr(v,
                "OsCmIBxZDQxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxtz0dV:e5vFb",
                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
            );
            return decodeURIComponent(
                atob(v).split("").map(function(c) {
                    return "%" + c.charCodeAt(0).toString(16);
                }).join("")
            );
    }
};

A five-line function to undo the entire “security” measure. The key, the algorithm, and the implementation are all delivered to the attacker’s browser as part of normal page load. Security through obscurity – and not even good obscurity.

7.3. connections.universityx.vn: Completely Untouched

While Company Y applied their cosmetic fix to the exam system, the in-house platform at connections.universityx.vn received no changes whatsoever. All 80,053 accounts remained fully accessible. Staff passwords were still in the API response.

They patched the window and left the front door wide open.

7.4. Revalidation Results

On March 10, 2026, I went back and systematically re-tested every attack vector across both platforms:

Test Target Result
T1: JS framework accessible Both VULNERABLE
T2: Auto-session without auth Both VULNERABLE
T3: Account IDOR connections.universityx.vn VULNERABLE
T4: Candidate table access tec.universityx.vn VULNERABLE (b6x only)
T5: Registration table search tec.universityx.vn FIXED
T6: Bulk data loading Both VULNERABLE
T7: XHR authentication tec.universityx.vn FIXED
T8: CDN image access tec.universityx.vn FIXED
T9: Password field exposure connections.universityx.vn VULNERABLE
T10: In-house platform access connections.universityx.vn ACCESSIBLE

Score: 7 out of 10 tests still vulnerable. The registration search and image CDN were restricted, and one XHR endpoint added a token check. But the core vulnerability – unauthenticated database access via IDOR – remained fully exploitable on both platforms.

The vendor treated symptoms while leaving the disease untouched.

8. Impact Assessment

8.1. Scale of Exposure

Category Count
Total accounts exposed 80,053
Full names 80,037
Dates of birth 78,637
Phone numbers 23,970
Email addresses 29,640
CCCD/CMND numbers 2,750
Hometowns 50,042
Profile photos 6,586
Family members’ names 677 (333 fathers + 344 mothers)
Accounts with credential data 561
Accounts with passwords 554 (50 plaintext + 504 masked)
Usable credentials obtained 208 (50 plaintext + 158 recovered)
Account takeover demonstrated Yes (Vice Rector)

8.2. Attack Complexity

This is not a sophisticated attack. The entire exploitation chain requires:

  1. Open a browser.
  2. Open the developer console.
  3. Type a single API call.
  4. Read the response.

CVSS 3.1 Vector: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N9.8 Critical.

No special tools. No technical expertise beyond basic JavaScript. No interaction from the victim. Fully automatable. Executable at scale. A motivated attacker could extract all 80,053 accounts in a matter of hours.

8.3. Real-World Risk Scenarios

Identity theft and fraud: 2,750 national ID numbers combined with full names, dates of birth, and addresses – everything needed for identity fraud, unauthorized financial account creation, or SIM-swapping attacks.

Targeted phishing: 29,640 email addresses and 23,970 phone numbers, combined with detailed personal context (faculty, major, enrollment year) – the ingredients for highly targeted social engineering campaigns that would be almost impossible to distinguish from legitimate university communications.

Account takeover cascade: 208 usable staff credentials that may be reused across other systems – university email, internal portals, government services. A Vice Rector’s compromised account could be leveraged for further lateral movement into more sensitive systems.

Institutional damage: Unauthorized administrative access could allow modification of student records, academic results, or official communications. Imagine a compromised account changing grades or sending fake announcements to the entire university.

Family members at risk: 677 accounts exposed parents’ names and birth years – data that can be used in social engineering or identity verification bypass. The exposure reaches beyond the individuals who signed up.

Vietnam protects personal data through two layers of legislation: Decree 13/2023/NĐ-CP (effective July 1, 2023) and the newer Law on Personal Data Protection – Law 91/2025/QH15 (passed June 26, 2025, effective January 1, 2026). Together, they form a legal framework that requires organizations to protect personal data, grants citizens control over their own information, and imposes serious penalties for violations.

Both University X and Company Y are in direct, documented violation of this framework. This is not a matter of interpretation. The violations are architectural, systematic, and ongoing.

What the law defines as protected data

Decree 13, Article 2, Clause 3 classifies the following as basic personal data: full name, date of birth, gender, place of birth, place of residence, nationality, personal image, phone number, CMND/CCCD number, and family relationship information. Every single one of these categories was exposed in this investigation – for over 80,000 individuals – with zero access controls.

Decree 13, Article 2, Clause 4 defines sensitive personal data as data that, when violated, directly affects the rights and legitimate interests of the individual – including political and religious views, ethnicity data, and data about criminal records. This investigation exposed ethnicity for 7,845 accounts and religion for 5,476 accounts.

Law 91/2025, Article 2 reinforces these definitions and adds that sensitive personal data is determined by a Government-issued list, and that even de-identified data – once re-identified – is still personal data (Article 2, Clause 1).

Who is responsible?

Both laws draw the same clear line between two roles:

  • Data Controller (Bên Kiểm soát dữ liệu cá nhân): the organization that determines the purpose and means of processing personal data (Decree 13, Art. 2 Cl. 9; Law 91, Art. 2 Cl. 7). University X is the Data Controller. The university decided to collect student and staff data, determined what data to store, and chose to deploy the Connections platform. The data belongs to University X’s students and staff. The responsibility starts here.

  • Data Processor (Bên Xử lý dữ liệu cá nhân): the organization that processes data on behalf of the Data Controller, through a contract or agreement (Decree 13, Art. 2 Cl. 10; Law 91, Art. 2 Cl. 8). Company Y is the Data Processor. They built, hosted, and operated the Connections platform that stored and served University X’s data. They are the ones who decided the platform needed no authentication.

What they were required to do – and did not

Decree 13, Article 26 requires that data protection measures be applied from the very beginning and throughout the entire processing lifecycle. These measures must include both organizational and technical safeguards (Clause 2). Company Y’s platform had no technical safeguards. No authentication. No access control. No encryption. The API served raw personal data to anonymous visitors.

Decree 13, Article 27 requires controllers and processors to check network security of systems and devices used for processing personal data, and to erase or destroy data on unrecoverable devices (Clause 4). The platform had no network security checks – any browser could query the database directly.

Law 91/2025, Article 3 establishes the core principles: personal data may only be collected and processed within a defined scope, for a clear and lawful purpose (Clause 2); effective institutional, technical, and human measures must be implemented to protect personal data (Clause 4); and organizations must proactively prevent, detect, block, and handle all violations in a timely manner (Clause 5). Company Y violated every single one of these principles. University X failed to verify that any of them were being followed.

Law 91/2025, Article 12 explicitly requires the encryption of personal data – converting it into a form that cannot be recognized without decryption (Clause 1). Company Y did the opposite: they stored passwords in plaintext and personal data in raw, unencrypted API responses accessible to anyone.

Decree 13, Article 38 defines the Data Controller’s obligations: implement organizational and technical measures to demonstrate compliance (Clause 1), maintain processing logs (Clause 2), report violations per Article 23 (Clause 3), choose only Data Processors with adequate protection measures (Clause 4), and bear responsibility to data subjects for damages caused by processing (Clause 6). University X chose Company Y as its Data Processor without verifying – or despite – the complete absence of security measures.

Decree 13, Article 39 defines the Data Processor’s obligations: only accept data after a contract or agreement (Clause 1), process data according to that agreement (Clause 2), fully implement all protection measures required by the decree (Clause 3), and bear responsibility to data subjects for damages caused during processing (Clause 4). Company Y failed on every count.

What the law demands after a violation is discovered

Decree 13, Article 23 mandates that upon discovering a data protection violation, the Data Controller must notify the Ministry of Public Security (Department of Cybersecurity and High-Tech Crime Prevention) within 72 hours using the prescribed Form No. 03 (Clause 1). The Data Processor must notify the Data Controller as soon as possible (Clause 2). The notification must include: the nature of the violation, the time and location, the types of personal data affected, the number of data subjects involved, and the measures taken to address the harm (Clause 3).

Law 91/2025, Article 23 strengthens this requirement: when a violation that could harm national security, social order, life, health, reputation, dignity, or property of data subjects is discovered, notification to the data protection authority must happen within 72 hours (Clause 1). The Data Controller must prepare an official incident report and cooperate with authorities to handle the violation (Clause 2).

Law 91/2025, Article 21 requires Data Controllers and Processors to prepare and maintain a Data Processing Impact Assessment – filed with the data protection authority within 60 days of starting data processing (Clause 1). This assessment must be updated annually (Clause 2) and must always be available for inspection by the Ministry of Public Security (Decree 13, Article 24, Clause 4). There is no evidence that University X or Company Y ever filed such an assessment.

What are the consequences?

The penalties are no longer vague. Law 91/2025, Article 8 spells them out:

  • Violations may result in disciplinary action, administrative penalties, or criminal prosecution depending on the nature and severity (Clause 1).
  • For individuals who buy, sell, or illegally trade personal data: fines of up to 10 times the revenue derived from the violation (Clause 3).
  • For organizations violating cross-border data transfer rules: fines of up to 5% of the organization’s annual revenue in Vietnam (Clause 4).
  • For all other data protection violations: maximum administrative fines of 3 billion VND (~$120,000 USD) for organizations (Clause 5).
  • Individuals committing the same violations face fines of up to half the organizational maximum (Clause 6).
  • If violations cause damages, compensation is mandatory under the law (Clause 1).

Decree 13, Article 4 adds that violations may be handled through disciplinary measures, administrative sanctions, or criminal prosecution depending on severity.

What 80,053 data subjects can demand

The rights of data subjects are not theoretical. They are enforceable:

  • Right to know about data processing activities (Law 91, Art. 4 Cl. 1a; Decree 13, Art. 9 Cl. 1)
  • Right to withdraw consent (Law 91, Art. 4 Cl. 1b; Decree 13, Art. 9 Cl. 2)
  • Right to access and correct their data (Law 91, Art. 4 Cl. 1c; Decree 13, Art. 9 Cl. 3)
  • Right to request deletion and restriction of processing (Law 91, Art. 4 Cl. 1d; Decree 13, Art. 9 Cl. 5–6)
  • Right to file complaints, lawsuits, and demand compensation for damages (Law 91, Art. 4 Cl. 1đ; Decree 13, Art. 9 Cl. 9–10)
  • Right to request that competent authorities and related organizations implement data protection measures (Law 91, Art. 4 Cl. 1e)

Every one of the 80,053 exposed individuals has these rights. Every one of them can demand answers from University X and Company Y. Every one of them can demand that their data be secured, that they be notified of the breach, and that they be compensated for the violation.

The bottom line

University X cannot hide behind Company Y. The law makes the Data Controller explicitly responsible for choosing a Data Processor with adequate protection measures (Decree 13, Art. 38 Cl. 4) and liable for damages (Decree 13, Art. 38 Cl. 6). By outsourcing their platform to a vendor with zero security architecture, University X did not outsource their responsibility – they amplified their liability.

Company Y cannot claim ignorance. They built the platform. They decided that client-side JavaScript was sufficient security. They chose to return passwords in API responses. They failed to encrypt personal data as required by Law 91 Article 12. Every architectural decision that led to this exposure was theirs.

The law requires them to act. Not eventually. Not when convenient. Now.

  • Notify the Ministry of Public Security within 72 hours of discovering the violation.
  • Prepare and file Data Processing Impact Assessments.
  • Implement real authentication, access control, and encryption.
  • Notify all 80,053 affected individuals.
  • Remove exposed credentials from the API immediately.
  • Conduct a full security audit of the Connections platform – not just the University X deployment, but every client running on the same architecture.

80,053 people trusted University X with their personal data. That trust was delegated to Company Y. Both failed. The law says that failure has consequences. It is time those consequences are enforced.

9. Conclusion

In Part 1, I found 896 people’s identity cards exposed through a university exam portal. The natural follow-up question – “what about the bigger platform?” – led to an answer that was orders of magnitude worse: 80,053 accounts, staff passwords in the API, and administrative account takeover.

The root cause has not changed since Part 1. It is the same architectural failure: Company Y’s Connections platform enforces no server-side authentication or authorization. The JavaScript framework running in the user’s browser is the only thing between a visitor and the database. That is not a security boundary. It is the absence of one.

What makes this finding particularly troubling is the password exposure. Storing credentials in a client-accessible API response – even masked – is not a design oversight. It is a fundamental misunderstanding of how authentication systems should work. Passwords should never leave the server, in any form, under any circumstances. The fact that the masking algorithm could be reversed using data from the same response elevates this from a data leak to a complete authentication compromise.

The vendor’s initial response – removing some field values and adding a client-side substitution cipher – demonstrated a pattern of treating visible symptoms rather than addressing root causes. As of the March 10 revalidation, 7 of 10 attack vectors remained exploitable, and the in-house platform had received no security changes at all. (See Section 10 for the June 23 update, where the vendor deployed server-side authentication and remediated the core vulnerability.)

These are not abstract risks. Behind the 80,053 account IDs are real people: students who trusted their university with personal information, alumni who expected their data to remain private, and staff whose credentials were exposed to anyone who cared to look. A Vice Rector’s account was compromised not through a clever exploit, but by reading a field in an API response and typing it into a login form.

The fix this platform needs is not another layer of client-side obfuscation. It is the implementation of what should have existed from day one: server-side authentication, role-based access control, and the basic principle that a database should not answer questions from strangers.

10. Revalidation Update – June 23, 2026

On June 23, 2026, ten days after this report was published, I retested all attack vectors on both platforms. Company Y had updated the codebase that same day (version 14484523062026), and the results were substantially different from the March 10 revalidation.

What changed

The most significant fix is architectural: the XHR API gateway now enforces server-side authentication. Both connections.universityx.vn/xhr/ and the secondary endpoint at xhr.companyy.com/xhr/ return HTTP 403 with {"error":403,"code":"access_denied"} for unauthenticated POST requests. This is the first time server-side access control has been observed on this platform.

With the API gateway locked, the downstream effects are immediate:

  • Account IDOR is dead. All tested account IDs (including the original range of 7,787–215,212) return null. The database no longer answers questions from strangers.
  • Password exposure is eliminated. With account records blocked, the password field () is no longer accessible. The 208 usable staff credentials documented in this report are no longer retrievable.
  • Bulk enumeration is blocked on connections.universityx.vn. The mass-load endpoint returns an empty array.
  • The b6x cipher has been removed from tec.universityx.vn. The monoalphabetic substitution layer documented in Section 7.2 is gone entirely.

What remains vulnerable

The JS framework and auto-session creation are still exposed on both platforms. Anonymous visitors still receive session IDs and access to the full client-side API surface. These are low-severity on their own, as they only become dangerous when combined with data access, which is now blocked.

On tec.universityx.vn (the Part 1 exam system), the fix is less complete:

Issue Status
Candidate #1662402 still exposes DOB, gender, and CCCD image reference IDs VULNERABLE
Bulk load returns 50,403 candidate IDs (records mostly empty) VULNERABLE
CDN image nodes (i0/i3) responding again after being fixed in March REGRESSED

Scorecard

Platform Tests Fixed Vulnerable Score
connections.universityx.vn (Part 2) 13 11 2 85% fixed
tec.universityx.vn (Part 1) 13 6 5 46% fixed
Previous (March 10, both) 10 3 7 30% fixed

Is this safe enough?

For connections.universityx.vn, the platform documented in this report: yes, the critical vulnerability is remediated. The 80,053 accounts and staff passwords are no longer accessible to unauthenticated users. The vendor has moved from “no security” to “server-side enforcement at the API gateway,” which is the correct architectural fix rather than another layer of client-side obfuscation.

That said, the fix is an authentication gate bolted onto an existing architecture, not a redesign. The client-side framework still loads, sessions are still auto-created, and the underlying data model presumably still returns all fields to authenticated users without role-based filtering. A compromised or low-privilege authenticated session might still access more data than it should. A full security audit would need to verify that the post-authentication access controls are equally robust.

For tec.universityx.vn, the picture is mixed. The exam system still leaks partial candidate data and has regressed on CDN image access. The Part 1 findings are not fully resolved.

The vendor has made real progress. Whether it is sufficient depends on whether they continue: the pattern so far has been incremental patches in response to published reports rather than a comprehensive security review of the platform. The next step should be a professional penetration test of the authenticated attack surface, something that is outside the scope of this research.


Appendix

A. Responsible Disclosure Timeline

Date Event
2026-02-25 Vulnerability discovered on tec.universityx.vn (Part 1)
2026-02-26 connections.universityx.vn investigation; 80,053 accounts extracted
2026-02-26 Technical report completed
2026-02-27 CVE request submitted to MITRE
2026-02-28 Company Y acknowledged report, confirmed remediation started
2026-03-10 Revalidation: 7/10 attack vectors still vulnerable
2026-03-24 Part 1 published (anonymized)
2026-06-13 Part 2 published (this report)
2026-06-23 Revalidation: 85% fixed on connections.universityx.vn, 46% on tec.universityx.vn

B. Technical Attack Classification

Technique Framework Application
IDOR OWASP Top 10 Sequential account ID enumeration (7,787–215,212)
Broken Access Control OWASP Top 10 No authentication on any API endpoint
Credential Exposure OWASP Top 10 Passwords returned in API responses
Broken Authentication OWASP Top 10 No server-side auth; client-side only
Security Misconfiguration OWASP Top 10 Default-open database access
Insufficient Cryptography CWE-327 Monoalphabetic substitution as “encryption”

C. Data Source Summary

All statistics in this report are derived from the following extracted datasets:

File Records Description
universityx_conn_accounts.csv 80,053 Complete account dataset (40 fields each)
universityx_conn_current_students.csv 21,393 Current students subset
universityx_conn_old_students.csv 57,588 Alumni/former students subset
universityx_conn_staff.csv 562 Staff & faculty subset (44 fields, includes credentials)
universityx_conn_companies.csv 166 Corporate/partner accounts subset
universityx_conn_guests.csv 344 Guest accounts subset
credentials_exposed.csv 561 All accounts with credential fields
unmasked_staff.csv 208 Successfully recovered/plaintext credentials

Sub-CSV totals: 21,393 + 57,588 + 562 + 166 + 344 = 80,053 (matches main dataset exactly).

D. Glossary

Term Definition
CCCD Citizen Identity Card
CMND People’s Identity Card – old format
IDOR Insecure Direct Object Reference
University X Pseudonym for the affected university
Company Y Pseudonym for the platform vendor
Platform Z The “Connections” SaaS platform operated by Company Y
PII Personally Identifiable Information
b6x Custom Base64 alphabet used by Platform Z’s obfuscation layer

E. Sample Account Record (Redacted)

{
  "account_id": "[REDACTED]",
  "ho_ten": "[REDACTED]",
  "ngay_sinh": "xx/xx/1999",
  "gioi_tinh": "Nữ",
  "sdt": "098XXXXXXX",
  "email": "[redacted]@gmail.com",
  "cmnd_cccd": "001XXXXXXXXX",
  "que_quan": "[Province]",
  "ma_sinh_vien": "19XXXXXX",
  "khoa": "Faculty of [REDACTED]",
  "nganh": "[REDACTED]",
  "khoa_hoc": "20xx-20xx",
  "loai_tk": "0"
}

F. Detailed Statistical Tables

This appendix contains the full statistical breakdowns referenced in Section 3.5.

Email Domain Distribution (29,640 email addresses):

Domain Count Significance
gmail.com 20,793 Personal accounts
ms.universityx.edu.vn 5,329 Official university Microsoft 365 accounts
s.universityx.edu.vn 1,309 Student email system
universityx.edu.vn 1,124 Faculty/staff email
Others 1,085 Including typos: gmai.com (68), gmail.con (48), yahoo.com (63), icloud.com (55), qq.com (39)

Gender Distribution:

Gender Count Percentage
Female 54,815 68.5%
Male 13,522 16.9%
Not set / Unknown 11,716 14.6%

Geographic Distribution – top 15 hometowns (of 50,042 with data):

Province Count Percentage
Ha Noi 17,775 35.5%
Nam Dinh 2,839 5.7%
Thai Binh 2,587 5.2%
Ha Tay 2,567 5.1%
Hai Duong 2,158 4.3%
Hai Phong 2,082 4.2%
Bac Ninh 1,588 3.2%
Bac Giang 1,501 3.0%
Vinh Phuc 1,466 2.9%
Ha Nam 1,289 2.6%
Hung Yen 1,216 2.4%
Phu Tho 1,185 2.4%
Thanh Hoa 1,178 2.4%
Nghe An 972 1.9%
Ninh Binh 941 1.9%

Top 10 Faculties:

Faculty Count
Business Administration & Tourism 5,579
English 5,510
Chinese 4,422
Distance Learning Center 3,887
Information Technology 3,315
Japanese 2,829
Korean 2,705
French 2,102
German 1,750
International Studies 1,694

41,340 accounts had faculty data, across 42 unique faculty names.

Top 10 Majors:

Major Count
English Language 9,298
Chinese Language 3,792
Japanese Language 2,814
Korean Language 2,099
German Language 1,741
International Studies 1,642
Russian Language 1,542
French Language 1,497
Information Technology 1,490
Business Administration 1,397

41,226 accounts had major data, across 56 unique programs.

Program Types:

Program Count
Full-time 32,076
Distance learning 4,035
Part-time 1,576
Second degree 1,017
International joint program 402
Double major 366

Enrollment Year – Top 10:

Year Count
K2019 4,720
K2020 4,526
K2021 4,281
K2018 3,941
K2022 3,615
K2024 3,023
K2023 3,017
K2025 2,918
K2017 2,805
K2015 2,031

39,768 accounts had enrollment year data, spanning from K2008 to K2025.

Student Status:

Status Count
Currently enrolled 19,325
Graduated 10,226
Dropped out 1,460
On leave 284
Other 5

Ethnicity – 7,845 accounts:

Ethnicity Count
Kinh (majority) 7,363
Tay 196
Muong 100
Nung 77
San Diu 23
Thai 19
Dao 14
Others (11 groups) 53

Nationality – 5,655 accounts: 5,621 Vietnamese, plus 34 foreign nationals from China, Japan, Indonesia, South Korea, Thailand, Philippines, Taiwan, New Zealand, and others.

Religion – 5,476 accounts: 5,134 none, 215 Buddhist, 104 Catholic, 14 Christian, 8 other, 1 Protestant.

Note: Detailed reproduction code, staff credentials, and unredacted data have been withheld from this publication. Full technical details were shared with the affected parties during responsible disclosure.