RSS
 

Archive for the ‘Security’ Category

A closer look at Dine & Discover NSW Vouchers

25 Mar 2021

Overview

The NSW Government has launched Dine & Discover NSW to encourage the community to get out and about and support dining, arts and tourism businesses.

NSW residents aged 18 and over can apply for 4 x $25 vouchers, worth $100 in total.

  • 2 x $25 Dine NSW Vouchers to be used for dining in at restaurants, cafés, bars, wineries, pubs or clubs.
  • 2 x $25 Discover NSW Vouchers to be used for entertainment and recreation, including cultural institutions, live music, and arts venues.

The Vouchers

There are two ways to access these vouchers:

  1. Email – Vouchers are initially emailed to eligible participants of the program after they sign up.
  2. ServiceNSW App – Vouchers can be accessed from the ServiceNSW

The initial email contains a PDF attachment that contains vouchers that look like this:

Discover NSW Voucher

Dine NSW Voucher

Each voucher has a few pieces of metadata attached:

  • The voucher type (Dine or Discovery)
  • A 9 character voucher code such as ED04K31MB (omitted from screenshots above)
  • A QR code with embedded data
  • An expiry date (in this case 30-Jun-2021)
  • Value (in this case $25)

Using the vouchers

There are two ways you can redeem your vouchers online or in-person:

  1. You can supply a participating business with your 9-char voucher code such as ED04K31MB
  2. You can show a participating business the QR code which exists to promote COVID-safe contactless interactions.

Email Vouchers vs ServiceNSW App Vouchers Discrepancy

There appears to be a discrepancy between the voucher codes and QR codes sent in the original email and the ones that appear in the ServiceNSW mobile app. The codes sent in the email seem to be the original codes and are “long life”. The codes generated in the ServiceNSW app seem to be generated each time you click on a voucher. They are likely temporary codes that, in the end, are all linked back to the unique identifier for your voucher. So once you use the code in the email or any code generated by the app, your uniquely identified voucher will be marked as used.

ServiceNSW App Dine Voucher

I didn’t test using old codes though but I’ll give ServiceNSW the benefit of the doubt here. 😉

Voucher format

Dine and Discover NSW Voucher Format

Each voucher is 9 characters in length.
Discovery vouchers always start with the letter G. Dine vouchers always start with the letter E.
The remaining 8 characters seem to be random alphanumeric characters (case-insensitive).

That QR code though…

Scanning any of the voucher QR codes gives us some plain text.

Dine/Discover Voucher (ServiceNSW App)

For example here is the output from scanning a Discover voucher from the ServiceNSW app:

This is a Base64 encoded string that represents the following JSON object:

We have a few fields present:

  • v: voucher
    • d: data – the first 8 characters here appended to G or (based on type) to create the voucher code; it is unclear what the last 8 digits represent (checksum or additional code?)
    • t: type of voucher, either CSG (for discovery vouchers) or CSE (for dine vouchers)
  • ts: timestamp – ISO8601 time string representing the time the voucher code was generated
  • m: unknown – as a 64 character hexadecimal hash it may be a SHA256 hash but I am not sure what it could represent (perhaps member?)

Dine/Discover Voucher (Email)

Interestingly, the QR code output from the vouchers in the email is different.

For example here is the output from scanning a Dine voucher from the email:

This is a Base64 encoded string that represents the following JSON object:

We have a few fields present:

  • v: voucher
    • d: data – this appears to be the unique identifier of our voucher in UUID format (which is what the temporary voucher/QR codes from the ServiceNSW app are likely linked to)
    • t: type of voucher, either CSG (for discovery vouchers) or CSE (for dine vouchers)

Notice how the ts (timestamp) and m (unknown) fields are missing here.

Third-Party Validation API

The obvious attack vector here is the participating businesses that accept these codes. There is likely a private API exposed by ServiceNSW and provided to participating businesses to validate and use these vouchers. I discovered that Hoyts (a participating business) was using this API behind the scenes through their own API proxy here:
https://store.hoyts.com.au/payments/snsw-voucher-checkout?voucher=EXAMPLE

This API takes a voucher code and returns a few responses based on the result of the validation.

Valid (unused) voucher:

Valid (unused) voucher but the user has already redeemed voucher at this business during this business day:

Valid (unused) voucher but wrong type:

Invalid (already used) voucher:

Invalid (not found) voucher:

Invalid (wrong format) voucher:

Bruteforce attack

Each voucher code is 9 characters long. As the first character is hardcoded, this leaves only 8 characters. It appears that the next 8 characters are random alphanumeric characters. Furthermore, by testing an API we discover we can confirm that upper and lowercase characters are equivalent. In other words, the voucher codes are case insensitive.

This leaves us with a character set of 36 characters (26 letters of English alphabet + 10 digits):

There are roughly 8 million residents in NSW. Modestly assuming 60% of those are adults over 18 and eligible for the NSW Dine & Discover voucher program, there are potentially up to 4.8 million vouchers originally generated and sent to a users email address. However, the total number of unique 8 character combinations with the above character set is 36 pick 8, or 2 821 109 907 456 (~2 trillion). Double that for each voucher type. That means that at best only 0.0001701% of the maximum combinations will be valid voucher codes for each voucher type. We are ignoring the temporary generated voucher codes here as they may expire and are likely not frequently generated until just before they are used.

If this were a password hash, with only 8 characters of entropy and a 36 character set, it would definitely be feasible to brute force the entire combination set. 8 character passwords with a character set this small are not secure and can be cracked offline from a hash within 10 – 30 days with moderately cheap hardware.

Unfortunately, though, we are forced to validate our codes online through the Hoyts API. This is much, much slower than GPU hash cracking due to the cost of networking and has other unwanted side effects:

  • Slow network-based validation technique
  • Raising suspicion by server maintainers through metrics they collect around API usage
  • Bringing additional load onto Hoyts servers and by extension ServiceNSW servers
  • Running into rate limiting issues (severely reducing the effectiveness of our brute force attack)

Bruteforce attempt and results

Nevertheless, in the spirit of giving it a go and in good faith I attempted a hopeless brute force attempt. I started a cluster of AWS lambdas in the ap-southeast-2 region running a simple Python scraping script. For simplicity, I logged any unexpected API responses to Cloudwatch. This code will not be released publically for obvious reasons. Furthermore, I time-boxed this experiment to only 12 hours as I would prefer to not needlessly burn money through AWS fees.

Unsurprisingly, I found no other valid voucher codes and that is good news for everyone.

Now get out there and kick start NSW again!

✌️

 
3 Comments

Posted in Penetration Testing

 

sanitize-html-react Vulnerability

21 Nov 2020

Overview

At work, we tend to test our near-complete features internally as a team to capture any bugs before they are shipped to customers. We call this process a ‘blitz’ and it has successfully caught many bugs for us that can be addressed before shipping a feature.

In this case, I was testing a sanitization library we had used to sanitize some use provided input (as HTML) called sanitize-html-react. After testing some common payloads from the OWASP XSS Cheat Sheet I discovered the library was flawed.

This library was selected because it was already in use in other parts of the codebase. For this reason, we believed it to be safe as we assumed it has passed the required security checks and standards we have set in place. However, in this case, it wasn’t.

sanitize-html-react library

The sanitize-html-react library is a fork at some point in time off the sanitize-html library.
However, it has no dependencies on sanitize-html and therefore it will not benefit from security updates.
The sanitize-html library is actively maintained where as sanitize-html-react library has not been updated for more than four years!

sanitize-html-react Github Repo

https://github.com/zacharystenger/sanitize-html-react

Proof of Concept

The discovered bug was found using the recursive XSS payload above.
The above config states we should discard all tags (as allowedTags list is empty).
In the above example the inner <u> in <<u>u>underlined is stripped away which forms a new <u> element resulting in the final string <u>underlined.

The same concept can be used to render <script> tags (or any other tag).

External Usage

Many open-source public repositories are or have used this library, potentially opening themselves up to security vulnerabilities.

Disclosure

An issue has been created on the Github repo here:
https://github.com/zacharystenger/sanitize-html-react/issues/3

Proposed Solutions

✅ sanitize-html

Replace package with sanitize-html (https://www.npmjs.com/package/sanitize-html)
This is a maintained, up to date, drop-in replacement.

✅ DOMPurify

Replace package with DOMPurify (https://www.npmjs.com/package/dompurify)
Popular and maintained XSS sanitizer.

✅ lodash.escape

Consider using Lodash escape (https://lodash.com/docs/4.17.15#escape) if you were only using sanitize-html-react in escape mode.

 
No Comments

Posted in XSS

 

BlankMediaGames/Town Of Salem Data Breach (2020 Update)

06 Jan 2020

Overview

Town of Salem, a video game produced by BlankMediaGames was breached around 1 year ago on the 3rd of January 2019. It is reported that the total row count of that database that was breached is 8,388,894 which included some 7,633,234 unique email addresses.

Shortly after this breach in early 2019, hackers attacked and successfully cracked ~2.4 million passwords from the breach due to the weak MD5 encryption used. These cracked passwords were initially sold on the black market but were later released to the public on public forums.

Shockingly however, these passwords were never reset as part of the breach. Exactly one year later, a huge number of accounts are still working with the same credentials that were exposed during the breach. This has led to malicious bot writers creating Town of Salem bots with the only purpose of joining ranked games to:

  • intentionally game throw,
  • post hate speech/racial slurs,
  • spam chat to advertise,
  • impersonate other players in hopes of getting them falsely reported/banned,
  • afk / idle

Validating the breach (1 Year Later)

The cracked dump included 2,418,341 cracked entries in email:user:password format.
I decided to write a script to categorise these breached accounts. This script would iterate all user:password combinations and attempt to authenticate with the main Town of Salem game server at live4.tos.blankmediagames.com:3600. There is no login rate limiting in place so we could bruteforce the credentials at full throttle.

For each authentication attempt, one of the following responses was sent back by the server:

  • LOGIN_SUCCESS (successfully authentication)
  • LOGIN_FAILED (invalid credentials, password has been changed since breach)
  • LOGIN_BANNED (account is banned)
  • LOGIN_PASSWORD_CHANGE_REQUIRED (account has been marked as requiring a password change)

We are only interested in accounts that return LOGIN_SUCCESS as these are the only accounts that can be used by bot makers to ruin games.
Running a quick scan shows that many of the very first entries are still authenticating successfully.

Out of the 2,418,341 accounts scanned, it was discovered that as of 29/12/2019:

  • 961,874 accounts returned LOGIN_SUCCESS
  • 1,048,665 returned LOGIN_FAILED
  • 29,159 returned LOGIN_BANNED
  • 378,643 returned LOGIN_PASSWORD_CHANGE_REQUIRED

Responsible Disclosure

These findings were reported to BlankMediaGames via email.
Shapesifter13 (community manager) also reached out via Discord to help fast-track the process (thanks!):

Resolution

On 03/01/2020, the Town of Salem game servers were restarted for an update to address this issue:

It is important to note that it appears that BlankMediaGames were already addressing this issue coincidentally at the same time I was researching it and they moved a lot of accounts from the LOGIN_SUCCESS state to the LOGIN_PASSWORD_CHANGE_REQUIRED state. However, they did not initially address every exposed password I had access to in the first batch of database patches.

After the patches, previously cracked passwords now require the user to go through the password change recovery process.

Hopefully this resolves the problematic botting situation in Town of Salem for now.

Timeline

29/01/2019 – Obtain breach dump to perform analysis
01/01/2020 – Finish running script and gather results. Report to BMG via email.
03/01/2020 – BlankMediaGames perform database patch addressing majority of leaked passwords.
10/01/2020 – BlankMediaGames perform database patch addressing remaining leaked passwords.

 
3 Comments

Posted in Exploitation

 

BlankMediaGames/Town Of Salem XSS

03 Jan 2020

While looking on the BlankMediaGames.com website (creators of Town Of Salem) I came across an api.php file which one inside one of the folders listed in the sites robots.txt  file.

The file in question is:

Upon visiting the page we get the following output with a 200 response code:

At first I thought this was a hidden API but it turns out to be nothing more than a simple echo script. I suspected the script takes in some input as GET or POST parameters so we use a script called parameth to bruteforce the GET and POST parameters using a dictionary attack.

We run the script with the following parameters:

The argument --sizeignore  allows us to ignore responses that have a particular size (in our case 19 bytes). Typically we’d use the response code to filter out default responses but in this case the response code was always 200.

We find the following inputs:

Therefore, the two input GET parameters are hello and request. These parameters are echo’d to the page without any input sanitation meaning the original PHP script would look something like this:

As a result this script is vulnerability to XSS:

Output:

Timeline

01/01/2019 – Reported to BMG
03/01/2019 – Patched by BMG.

 
No Comments

Posted in XSS

 

Creating an Extremely Strong and Unique Password

10 Dec 2015

In this post I’ll give you a method to create a really strong and flexible password.

Now i’ve personally only gone through 5-6 passwords in the last 8 years. The first 2 passwords were insanely weak (6 digit numbers!). I then started picking stronger passwords but unfortunately I’ve had to change my password multiple times due to database leaks on websites i’ve joined.
However, my current password fixes all of these problems!

Desirable requirements for passwords

  • No dictionary words or simple phrases (so no QWERTY or password!)
  • No obvious patterns (ie 12345679, abcdef)
  • No obvious substitutions (ie hello -> he11o)
  • Length greater than 8
  • Length lower than 16 (unfortunately password length limits exist on many services so this is needed)
  • Starts with capital letter
  • Contains 1+ capital letter
  • Contains 1+ number
  • Contains 1+ special characters
  • Easy to remember
  • Unique for every website
  • Can be used with systems that make your password expire (without need for a new password)

The password we will make will meet all of the criteria above!

Step by Step Guide

  1. First, pick an easy easy to remember dictionary word or a phrase that means something to you. Make sure it is around 5-6 characters long. As an example, I will secure.
  2. Chose a position to split the phrase on. Don’t just split the phrase at its syllables! I’ll decide to split at secu so I have secu and re.
  3. Now capitalize the first letter in the first string. So se becomes Se. (This is so we meet the start with capital requirement on many websites)
  4. Now pick two 2-3 digit numbers. I’ll pick 101 and 42. Add these in between your strings! Thus far we have Secu101re42 (length 11)
  5. Now pick a few other lowercase letters and capitalise them. I go with: SecU101Re42
  6. At this stage, all we need is 1-2 special characters. I decide to go with # and add it in somewhere: SecU101#Re42.
    The password is already a very strong password at this stage but still fails to meet the unique requirement.
  7. For the unique requirement, you have to come up with a system for creating unique passwords for each service you use. I came up with the following, look at the domain of the website you are using and take the first 2 letters from the domain. So on facebook.com you would take fa. Capitalize the first or last letter of this. Then add this to the start or end of your password or put 1 character at the start and 1 at the end. I decide to capitalize the last letter and put the 2 characters at the end of my password. So our password for Facebook is: SecU101#Re42fA ( length 14). The benefit of this is that even if someone manages to get your plaintext password on a service, they are unlikely to notice your system and will be unable to access your other services.
    Note: You can also use a 2 letter prefix that works with many websites so Facebook becomes fb. Furthermore, you can vary the length of the tag for each service as this will also make your password vary in length from service to service which is another bonus!
  8. If your password expires on a service and you are not allowed to reuse the password again, simply add the year, semester, term, quarter or something similar to the start or end of the password. I usually add the year to the end: ie 15 for 2015

Remembering your password…is easy!

I came up with this system in such a way that remembering the password is no issue at all. Most people fail to remember passwords because they have so many different ones across various services. In our case, the domain gives you a tiny bit of the password. All that left to remember is your phrase (in our case secure), the selected split position, the few letters that are uppercase and two 2-3 digit numbers. It turns out that this system is very easy to remember. Simply typing your password over and over again for 2 minutes will get it stuck in your head and then you can begin using it on all of your services.

Password Strength

I put the above password through various password strength websites. The passwords do very well in these tests.

Here are some results from online tests:

The Password Meter

How Secure is my password Dot Net

My1login Password Checker

Final thoughts

Again these passwords are great because they are unique for every service you use (a few may clash but that is generally not an issue, however ensure you keep the number of clashes to a minimum and ensure important websites DO NOT clash). If your password gets compromised on a service you can rest easy knowing all your other passwords are secure.

Just for fun, there are the SHA1 sums for the password we created together below. If one hash gets compromised, the risk is almost nothing. If one plaintext password gets compromised, the risk is low if automated tools are used (often the case with large database leaks for major services). If someone is targeting you specifically, then it will take them a while to figure out your system but chances are they won’t.

Oh and my password isn’t the password above so don’t try to hack me 😀

 SHA1 sums for our password on various services

 
1 Comment

Posted in Security

 

Simple Buffer Overflow Example

23 Nov 2015

I’m posting this example because the current buffer overflow examples on the web are not that good. This is a step by step guide to how to exploit a program that is vulnerable to a buffer overflow attack.

The Code

This is the vulnerable code that we will be attacking. In this case, the call to strcpy is unsafe as the number of bytes that we copy into buffer (buf) is controlled by the caller which allows us to write more bytes than the size of the buffer (ie more than 1024 bytes).

Test Machine

Code was compiled on and exploited on a Ubuntu 14.04 64-bit machine.

Preparation

Compile the C program using gcc with the following flags:

This turns off stack execution and the stack protector which are anti-exploit features built into GCC. This particular example will require executing code from the stack.

We also compiled for 32 bit architecture for the purposes of this example.

Furthermore, we must turn off Address Space Layout Randomization (ASLR), a memory protection feature implemented in most modern UNIX operating systems. You can review all the different Ubuntu security features here.

To disable ASLR on Ubuntu:

You can also try this to spawn a shell with ASLR disabled:

Step by step guide

At this stage you should have compiled your program and have disabled ASLR. I decided to call my program exploit.

Start up gdb with your program like this:

Then run:

0

In this case we know strcpy returns a pointer to its destination (which is our buffer) and this pointer will be stored in EAX register after strcpy returns. So we want to put a breakpoint after strcpy returns so we can look at the values of the registers. So we note the next
address which in this case is: 0x080484c8

Now make a breakpoint by typing:

Okay, now let’s feed our program enough bytes to cause it to crash! Our buffer size is 1024 so let’s pick a number larger than that like 1050 to try and get a segmentation fault.
Type:

Note: Here we are using perl to easily print 1050 bytes. The character we are printing (raw) is the ‘\x90’ character which corresponds to the NOP assembly instruction. The NOP instruction is the no operation instruction which does nothing but take up a clock cycle. You will see why this is important later.
Now, gdb should stop at the breakpoint that we set earlier.

Now type:

1

This shows you the registers at this point in the code (right after strcpy has returned). So we know EAX will hold the result of strcpy which will be the pointer to our buffer! We always want to determine the location of our buffer in this type of buffer overflow attack. This is easily done with ASLR off but becomes more challenging when ASLR is left on (see ret2libc exploits).

Note down EAX address. EBP (Extended Base Pointer) is important too but you don’t need to remember it at this stage, just know that we will be overwriting the data at that address later on (EBP overwrite).
Press c to continue and to go past the breakpoint, the program should then end in a segmentation fault. The reason the program is seg faulting is because we have written enough bytes to overwrite the EBP (Extended Base Pointer). So when our C program returns, the IP (instruction pointer) points to the EBP which will contain \x90\x90\x90\x90.  The computer will try to execute assembly inst ructions at this address but will run into invalid instructions resulting in a crash.

Now we need to discover what the smallest number of NOP’s that can be fed to the program that still causes a segmentation fault is.
This is done by trial and error (answer depends on buffer size), simply run the following from gdb until you close in on an answer (obviously substituting in a number for ‘some num’):

Note: Actually its usually buffer size plus 12 bytes (ie 1024+12 bytes) on a typical x86 machine but this is not the case on all architectures. Note that this relies on the buffer variables position in the code. If there was an integer declared before the buffer with a size of 4 bytes then you would have to write 4 bytes more to reach the EBP and cause a segmentation fault.

In this case, we seg fault with exactly 1036 NOPs. So if we feed in 1035 NOPs, no segmentation fault occurs and all is fine.

Now its time for some calculations.
You want to write 1036 NOPs and then (after 1036 NOPs) you want to write 4 bytes (for the return address of EBP we wish to overwrite). Essentially, we want the IP to point to somewhere on the stack that has data that is controlled by us. So lets make it point to the buffer and add valid assembly instructions there which are then executed! This assembly code is our payload or shellcode (because it will spawn a shell).

Our 1036 bytes should also include our shellcode. The shell code we will use is 35 bytes long.

Shell code (standard x86 shellcode):

So, we have:

1036 – 35 = 1001 bytes

So we will write 1001 NOP’s! The reason we do this is simple. We are going to be jumping to a location on the stack (somewhere in our buffer). There is no need to be precise and jump to the exact starting byte of our shell code. We can utilise many NOP operations that are valid assembly instructions that do nothing and put our shellcode at the end of the buffer where it will eventually be executed. This leaves us some margin for error. The sequence of many NOPs is refereed to as a NOP sled.

So our payload now looks like this:

1001 NOPs + 35 bytes (shell code) + 4 bytes (return address which will overwrite EBP)
So now we will construct our almost complete payload, we have:

Now we used AAAA as a dummy address because it’s easy to see in gdb as it shows up as 0x41414141 and is 4 bytes large.
So go back to gdb and run your program using the above arguments like we have done before:

If prompted to start the program from the beginning, press yes .
gdb should have stopped at the breakpoint again. Press c to continue.
Now the program should have seg faulted. Inspect the address in which the program segfaulted. It should be 0x41414141 (which is the “AAAA”, our dummy address). If you see this then you have completed all steps correctly thus far, otherwise you did not.

The final step is to look into memory to find a good address (somewhere in the middle of the NOP sled is best) to use as our actual overwrite address.

Type:

This will show you the memory contents at the ESP (extended stack pointer). Now because we went past our breakpoint and let the program end, our base pointer would have been changed to some garbage. The stack pointer (ESP) still points to the
top of the stack though so that is why we are looking around there.

Press ENTER a few times to inspect memory at higher addresses.
Eventually you will see our 1001 NOPs, once you see our NOPs (series of 0x90 byte values), stop and pick one address that is within the NOP sled. Again, try to pick an address corresponding to somewhere near the middle of the NOP sled.

2

In this case I pick: 0xffffd0a0 which is nice because it is roughly in the middle of the NOP sled.

Finally, exit gdb by typing q and confirming.
We can now create our full payload:

Remember to reverse the address (due to little endianness). So FF FF D0 A0 becomes A0 D0 FF FF.

Make sure the address you picked doesn’t contain a null (0x00) or a space (0x20) as that could break your exploit. This is because a null would stop the assembly instructions from being executed and a space (0x20) would cause your argument to the program to be treated as two arguments.
Now in terminal, simply run your program (exploit) with that argument. And you should see a shell spawn!

Now run whoami, if you see root or another user then you successfully entered their account without a password which demonstrates the severity of the exploit.

3

Some things to note

If you manage to spawn a shell within gdb that could mess up the static addresses of various things so you might want to restart gdb in order to continue to inspect memory etc.

The addresses from gdb and terminal will be somewhat different. So if you pick an address right in the middle of the NOP sled your address could work right off the bat. If you pick an edge address (not a middle one) then it might not work but you can manually try and change the address. So if I pick an edge address in gdb of: 0xffffcf60 and then I go into terminal and it doesn’t work then I could try 0xffffcf80 , then maybe 0xffffcfff , then maybe 0xffffd100 . So I am just increasing the address until it works (ie going up in memory until I land within the NOP sled).

If there is something that is unclear in this guide or incorrect, please let me know!

 
No Comments

Posted in Exploitation

 

Finland MSN XSS Vulnerability

08 Sep 2014

The search bar on this page fails to encode the quotes () and as a result a onMouseOver event tag can be attached to the search bar which allows an XSS attack to occur.

Finland MSN XSS Vulnerability

Code:

 
No Comments

Posted in XSS

 

CSE.UNSW.EDU.AU XSS Vulnerability

08 Sep 2014

The staff search page does not sanitise the input of the ID field allowing an XSS attack to be executed.

UNSW (CSE) XSS Vulnerability

Code:

 
No Comments

Posted in XSS

 

Mashable XSS Vulnerability

08 Sep 2014

The Mashable subscription script fails to sanitise the email field and prints the invalid email on an error page.

Mashable XSS Vulnerability

Code:

 
No Comments

Posted in XSS

 

ABC Mail XSS Vulnerability

08 Sep 2014

The ABC Mail subscription script fails to sanitise the email field or check that a valid email was provided. An attack can easily be executed as the unsanitized “invalid email” is printed on the produced error page.

ABC Mail XSS Vulnerability
Code:

 
No Comments

Posted in XSS