Hi, Habr! Today, I’ll tell you what Valve paid the most bounties in the history of their reward program for vulnerabilities. Welcome under the cut!

1. SQL Injection
The service
partner.steampowered.com is intended to provide financial information for Steam partners. On the sales report page, a graph is drawn with buttons that change the display period of the statistics. Here they are in a green rectangle:

')
The request for loading statistics looks like this:
where “UA” is the country code.Well, it's time for quotes!
Let's try "UA '":

The statistics did NOT return, which was to be expected.
Now "UA":

The statistics are back and it looks like an injection!
Why?Suppose that the database instruction looks like this:
SELECT * FROM countries WHERE country_code = `UA`;
If you send UA ', then the instruction to the database will be:
SELECT * FROM countries WHERE country_code = `UA``;
Notice the extra quote? This means that the instruction is invalid.
Corresponding to the SQL syntax, the query below is completely valid (there are no extra quotes):
SELECT * FROM countries WHERE country_code = `UA```;
Note that we are dealing with an array of
countryFilter [] . I assumed that if in the request to duplicate the
countryFilter [] parameter several times, all the values that we send will be combined in the SQL query in the following way:
'value1', 'value2', 'value3'
Check and make sure:

In fact, we requested the statistics of three countries from the database:
`UA`, `,` ,`RU`
The syntax is correct - the statistics returned :)
Web Application Firewall bypassSteam servers are hiding behind Akamai WAF. This disgrace inserts stick in the wheels of good (and not very) hackers. However, I managed to overcome it by combining the values of the array into one query (what I explained above) and commenting. First, make sure the last one:
?countryFilter[]=UA`,`RU
The request is valid, so there are comments in our assortment.
We had several syntax options, local databases for testing payloads, comment symbols and an infinite number of quotes for all encodings, as well as self-written scripts on Python, documentation on all databases, instructions for circumventing firewalls, Wikipedia and anti-mail. Not that it was the necessary reserve for the promotion of the injection, but since it began to break the database, it is difficult to stop ...
WAF blocks the request when it encounters a function. Did you know that
DB_NAME / ** /
() is a valid function call? The firewall also knows and blocks. But, thanks to this feature, we can divide the function call into two parameters!
?countryFilter[]=UA',DB_NAME(),'RU
We sent a request from
DB_NAME / *
anyway * /
() - WAF did not understand anything, but the database successfully processed such an instruction.
Retrieving values from a databaseSo, an example of getting the length of a DB_NAME () value:
https://partner.steampowered.com/report_xml.php?query=QuerySteamHistory&countryFilter[]=',(SELECTCASEWHEN(len(DB_NAME())=1)THEN'UA'ELSE'qwerty'END),'
In SQL:
SELECT CASE WHEN (len(DB_NAME())= 1) THEN 'UA' ELSE 'qwerty' END
Well, humanly:
DB_NAME() "1", “UA”, “qwerty”.
This means that if the comparison is true, then in response we will receive statistics for the country “UA”. It is not difficult to guess that going through the values from 1 to infinity, we will find the right one sooner or later.
In the same way, you can iterate through text values:
DB_NAME() “a”, "UA", "qwerty".
Usually, the “substring” function is used to get the N-th character, but WAF stubbornly blocked it. Here the combination came to the rescue:
right(left(system_user,N),1)
How it works? We get N characters of the system_user value from which we take the last one.
Imagine that system_user = “steam”. This is how the third character will look like:
left(system_user,3) = ste right(“ste”,1) = e
Using a simple script, this process was automated and I got the hostname, system_user, version and the names of all the databases. This information is more than enough (the latter is even superfluous, but it was interesting) to demonstrate criticality.
After 5 hours, the vulnerability was corrected, but the status of triaged (adopted) was put to her after 8 hours and, damn it, for me it was a very difficult 3 hours during which my brain managed to survive the stages from denial to acceptance.
Clarification of paranoiaSince the vulnerability was not designated as accepted, I pushed that the turn to my report had not yet reached. But the bug was fixed, which means it could have been reported before me.
2. Getting all the keys from any game
In the Steam partner interface there is the functionality of generating keys to the games.
Download the generated set of keys, you can use the request:
https://partner.steamgames.com/partnercdkeys/assignkeys/ &sessionid=xxxxxxxxxxxxx&keyid=123456&sourceAccount=xxxxxxxxx&appid=xxxxxx&keycount=1&generateButton=Download
In this query, the
keyid parameter is the id of the key set, and
keycount is the number of keys that must be obtained from the given set.
Of course, my hands instantly reached out to drive in different
keyids , but in response, I
received an error: “
Couldn`t generate CD keys: No assignment for user. ". It turned out not so simple, and Steam checked if the requested set of keys belonged to me. How did I get around this check? Attention…
keycount=0
Generated a file with 36,000 keys from the game Portal 2. Wow.
Only one set turned out that number of keys. And the total sets at the moment more than 430,000. Thus, going through the
keyid values
, I was a potential attacker who could download all the keys ever generated by the Steam game developers.
findings
- Costly WAF systems from top companies are not a guarantee for the security of your web applications.
- If you are a bug hunter, then try to penetrate as deep as possible. The fewer users have access to the interface, the more likely it is to find a vulnerability in this interface.
- Developers and business owners, there are no absolutely safe applications! But you hold on. Have a good mood!
But seriouslyMake pentests, pay for vulnerabilities, think strategically.