It’s worth noting that nothing here is particularly advanced, and neither is my security knowledge – which is sort of what makes this scary.

If you don’t want to read the details, here is a summary:

Ster-Kinekor had a vulnerability in their site/api that allowed anyone to get the profile details of every single user in their system. Those details included names, addresses, phone numbers, and plain text passwords (amongst a lot of other fields that you can see below).

To be clear, this wasn’t a hard thing to find at all, and from Ster-Kinekors side, it was just pure negligence. Not only did the API hand off details to anyone, they were also storing password in their database in plaintext (and returning those to the client!).

Some important points

As of a couple months ago, this has been fixed and is no longer an issue, and that is why I can now talk about it.

I do not know if anyone else pulled this data. And I doubt SK knows either. But I would say it is safe to assume that someone dodgy has your data. If you’ve used your SK password anywhere else, stop reading this and go change it to something unique right now.

The ID’s went up to about 6.7 million. But there were some deleted accounts.

A bunch of accounts had password that followed a certain format. I’m going to assume that those were people that signed up in-person instead of on the site.

The new site

The site recently got redone, with a fancy new app and web front-end that doesn’t use Flash (yay). From the comments I see, it seems like it doesn’t always actually work, but at least it [probably] isn’t leaking your data. From the image below, which I took from the Android app, it looks like their new system runs on something called Vista. It also looks like they either didn’t license the app, or forgot to change the about page.

The old site

The old site was a bunch of Flash, connected to a PHP backend.
When logging into the site with your SK account, the call looked something along the lines of

1

2

GET http://www.sterkinekor.com/profile/script_newsite.php?params=l989335e75fa2dd55dba8411bc1a315a0j76537fs89851dd7f5338f554da9092 HTTP/1.1

Host:www.sterkinekor.com

That’s the entire request. So basically we can assume that the credentials you’re logging in with are in params.So let’s try see how that params value is actually generated.
It’s safe to assume that this is being done locally, and since this is a Flash app, it’ll be done inside there somewhere.

So, I downloaded the site, which was a bunch of HTML, and some Flash bits. The flash bits are what we’re interested in.

I should note at this point that I had no idea how flash worked – I still don’t – or if it is possible to pull it apart.

So I Googled “Flash decompiler”. The first result was JPEXS Free Flash Decompiler, so I installed that.
It actually worked! Although it needed me to install Java :-/
Out of the three .swf files, side_panel was probably the place to start, as the login is in a side panel.
Searching for “login” gets a few results:

There were a few button results, but the interesting one looked like the third one (SidePanel).That file had a bunch of code relating to registration and logging in.

Midway through the file there was the login function, which presumably does the login.

Looking at that code there is firstly some bulletproof email validity checking (they consider an email address valid if it contains an @). And below it is the call that works out what to put in the query string. Specifically, it looks like we need to find what ENCRYPT_DATA is.

Globals is just a string definition, and SidePanel is where we just came from. But maybe it is in one of the other swf’s.

Doing the same search for “ENCRYPT_DATA” in sk_main_040313.swf gives some better results

You can tell which is the interesting file there…

Opening up the Encryption file, we find a bunch of keys

ActionScript

1

2

3

4

5

6

7

8

9

10

11

com.prezence.utils.Encryption.arr_encryptKeys=newArray();

com.prezence.utils.Encryption.arr_encryptKeys[0]="27f38a5016b37e9a";

com.prezence.utils.Encryption.arr_encryptKeys[1]="24634a362c7b212f";

com.prezence.utils.Encryption.arr_encryptKeys[2]="437c5e514b5f5e42";

com.prezence.utils.Encryption.arr_encryptKeys[3]="265f3b6471267352";

com.prezence.utils.Encryption.arr_encryptKeys[4]="5734273e2963702a";

com.prezence.utils.Encryption.arr_encryptKeys[5]="3a6c57476e786b2f";

com.prezence.utils.Encryption.arr_encryptKeys[6]="2d70345d617942d2";

com.prezence.utils.Encryption.arr_encryptKeys[7]="7e506ce578347549";

com.prezence.utils.Encryption.arr_encryptKeys[8]="457b7939206e687b";

com.prezence.utils.Encryption.arr_encryptKeys[9]="5d713d56612f5920";

Plus a bunch of methods for doing encryption and decryption.

Since I don’t know ActionScript, my first though was to try convert it to C#, but decided it would be better to just get it working in a standalone Actionscript app.

After a bit of Googling I found a free Actionscript editor called FlashDevelop.

I downloaded that, pasted the whole encryption file, and got it working in a little app to encrypt and decrypt these strings.

So back to the first login call we looked at, if we decrypt that, it shows that params is:

1

method=login_user_pass&email=[email]&password=[password]

Calling the API with that returns a user ID.

Next, it does another call to the API, which looks the same as the first login call (obviously with a different value for params. This call returns the users profile.

Decrypting this one we can see the parameter is

1

method=login_profile&id=[id]

3 points if you can see what is wrong here…

Yup, there is no session key, or auth, or anything checking that the person getting the profile is that same person.

Not all profiles had every field (like addresses for instance). Here is a list of all the fields:

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

id

first_name

last_name

birthdate

gender

email

cellphone

telephone_home

telephone_work

residental_address_1

residental_address_2

residental_address_3

residental_address_4

residental_code

postal_address_1

postal_address_2

postal_address_3

postal_address_4

postal_code

receive_info

last_down_sync_status

mc_number

mc_type

mc_status

mc_moola

mc_movies_current

mc_movies_required

mc_activation

mc_id

demographic

language

title

password

blacklisted

mc_end_date

mc_start_date

As a side note, it’s interesting that there is a PHP session key in the headers and response body. They just never use it.

Next I wanted to see if I could pull all of the accounts. I already had the encryption code working in my FlashDevelop project, so I wrote some crappy ActionScript that went through all the ID’s, encrypted a call for that ID to get the profile, and then saved that out to a bunch of text files (I say a bunch because when you’re dealing with this many items, things get big).

These files looked like this:

1

2

3

4

5

6

7

8

9

10

11

50447fcb3ac9f9687a33cffbb309fa2f63159760137cac3cbe348

50447fcb3ac9f9687a33cffbb309fa2f63159760137cac3cbe358

d749335e75fa2dd55dba8411bc1a315a0b145b1275351dd7f2349

bb8a357c8ae3114e22e283cb9a98ae999467f3c17217d3174bfd6

d6eeb10b075c67bf50d8d8be92a0bd0c6672fed9e229ab325f297

21d7880e7b000848e6453022e3e5188dd8c5be39661561a6aa263

b32ab066867bbe16acf146d905f1df279e37fa2f123992bac9670

b32ab066867bbe16acf146d905f1df279e37fa2f123992bac9660

bb8a357c8ae3114e22e283cb9a98ae999467f3c17217d3174bf66

55a99a379e0a500efbb9d635c0b35489f34f602871c47eb7161f1

b32ab066867bbe16acf146d905f1df279e37fa2f123992baca610

Next I needed to do 6.7 million calls to their API. I tried a whole bunch of scraping and data dumping software, and they were all just too slow.

So eventually I gave up and just wrote a little C# app that spun up about 200 concurrent requests at a time and pulled the data nice and quickly.

I did hit something interesting at this point though. From my single home internet connection, after doing a few hundred calls the API would just start breaking and return an error that it couldn’t contact the database. Obviously I didn’t want to break their whole system, so I artificially slowed down the requests to the point where it didn’t cause any slow down for them.

A few quick points of stuff they did wrong

No HTTPS, so everything is sent insecurely over the wire

Using their own magical encryption

No authentication on profile requests

They were returning what looks to be the entire user record out of the database – including plaintext password

Storing passwords in the database in plaintext

The API couldn’t handle the load from a single home-internet connection. This is kind of bizarre for a company of this size.

As far as I can tell, they had no monitoring of their service. After doing all the above, I got through to the person who I was told was in charge of the platform, and they sounded surprised that I had done this.
A) If their service was getting hit with downtime, they should probably have some automated warnings
B) An API as big as this should probably notify the admins when it gets hit with 6.7million queries in a day – when it likely only got tens of thousands in previous days.