QRZ.com security vulnerability report

Exploit 1, staff impersonation

In this exploit, I abuse the CSS styles to change my user-page to look identical to that of a QRZ.com staff member.

Using the CSS visibility: hidden; property and :after selector, I can change my visible user type to QRZ Engineering Manager.

Then, using the :before and :after selector, I load and place the staff and verified badges next to my profile image.


Exploit 2, redirect to phishing, via user interaction

In this exploit, I abuse the visibility: hidden; property once again, this time hiding all user related info, and navigation items, and only displaying the contents of the bio. Then, I can place a phishing redirect link in my bio, for the user to follow.


Exploit 3, redirect to phishing, automatic

In this exploit, I simply add an HTML meta refresh to the contents of my bio, and automatically redirect the user to a phishing page.


Exploit 4, arbitrary JS execution, via user interaction

In this exploit, we go one step further. We can abuse the formaction property of a button to execute arbitrary JavaScript. From this, we redirect the user to a malicious site, along with their current cookies. We can then steal their session cookie, and gain access to the users account.


Exploit 5, arbitrary JS execution, automatic

This is the most pressing of all vulnerabilities.

We can abuse the srcdoc property of an iframe to automatically execute malicious JavaScript, accomplishing the same Exploit 4, without user interaction.

Rather than redirecting the user to a page, we can simply make a request to a malicious server with the contents of document.cookie, which will then log the session key. Once set up, you immediately gain full access to the account of any user who accesses your callsign page.

This access allows us to construct a JavaScript virus. We use the compromised session key to edit that users bio, adding the same malicious code. As more users visit callsign pages, this will propagate throughout the entire userbase until every active account on the website is compromised.

In addition, the xf_session cookie also allows you to log-in to accounts that have explicitly logged-out, because logging-out only removes the client-side cookie. Sessions are not destroyed server-side, so they may be resumed.


NEW: Exploit 6, arbitrary JS execution, automatic (logbook)

The following pages in the QRZ logbook allow basic <script> injection

  • List page XSS:
    • Reciever Info > Op
  • Detail XSS:
    • QSO Info > Info: Recieved
    • QSO Info > Info: Sent
    • Reciever Info > QTH
    • QSL Info > QSL to VE9DLL Via


  • David Gilbertson at HackerNoon has great articles (1, 2) on similar attacks.


  • All vulnerabilities were disclosed using the responsible disclosure model.
  • To ensure I didn’t capture any session keys other than my own during testing, all connections that gave me access to user data were made over localhost. Additionally, connections to https://drakeluce.com/vulnerable simply displayed data back to the end user, and were not logged.

UPDATE (June 7th, 2018)

As of today, it appears all of the disclosed JavaScript vulnerabilities have been patched by the team at QRZ. This vulnerability report is now public.

DrakeCoin blog, pt. 1

I first learned about cryptocurrencies back when it was still useful to mine Bitcoin on a GPU. While I didn’t capitalize on that opportunity (hindsight is 20/20), a few years later I picked the interest up again. Ethereum made me a bit of profit, but I was never drawn heavily by the economics and trading of cryptocurrencies. The technology is the cool part.

I decided to make my own cryptocurrency, simply to learn how they work on a more intimate level. Among the hundreds of ICOs, it would be wasted effort to try and promote it as a legitimate coin, but the knowledge you can get from building a project from the ground up is quite valuable. And again, the tech is really neat!

Note: The commits linked below are not representative of the final code! Take a look at master branch instead.

Implementing the basics


Commit 93ba4b7

I started off with one of the key aspects to most cryptocurrencies: proof-of-work!

It’s a pretty simple algorithm. You have your base data, and you have a number (the nonce). Concatenate them, and hit the result with SHA-256. If the hash output isn’t under the target hash, increase the nonce and try again! In the below example, we’re looking for a hash that starts with a minimum of four zeroes.

This is mining. Instead of the string , the computer performs the algorithm on a block (or more accurately, a block header) of cryptocurrency transactions. The quicker a computer can calculate SHA-256 hashes, the more likely it is to find a hash under the target, and consequentially, produce a valid or solved block. Once found, the winning nonce can be passed with the block to other nodes. They can then very easily validate the incoming block, as a node only needs to hash a block header with its nonce once to verify that the corresponding block has been solved.

To make this computation worthwhile for the computer, miners include a reward transaction to themselves in each block that they mine. Essentially, this special transaction makes the coins that compose a cryptocurrency. This means that the number of coins in a cryptocurrency circulation depends completely on the number of blocks mined. At least, for a while.

In most (or probably all) cryptocurrencies, to enforce a cap on the number of coins that can exist, the amount of the reward transaction in blocks halves every time a certain number of blocks have been mined. In Bitcoin, for example, this is every 210,000 blocks. Eventually, it can be halved no more, and miners will no longer get rewarded just for discovering a block, and rather, will only get fees from included transactions. Anyway, we’re getting off track!

Proof-of-work is important. To show why, say, hypothetically, blocks don’t exist. The state of the network (who has what, who did what) is then based solely on the consensus of the majority of nodes.

In this case, an attacker can take over the network by holding that majority. They then have the final say on what is and isn’t valid. Even if we increase the percentage needed for consensus, this is a major security flaw. It’s cheap to run nodes that only require memory and connectivity, with say, a VPS provider like AWS.

To combat this, the state of the network is instead based on the product of hashing: the mined block. To attack this model, one must instead take over the majority of the hashing power. This is significantly more difficult and expensive, as scaling hashing power means scaling processing power, not just memory.

Merkle trees

Commit 905ce15

Next, I implemented Merkle trees! These are really cool. Simply put, a Merkle tree is a tree of SHA-256 hashes, where the top row L0 contains the hashes of all the transactions in the block. The hashes are paired up in twos, concatenated, and then hashed to make a child hash in the next row. Each row needs to have an even number of elements to allow this pairing, so if it’s odd, we can duplicate the last hash in a row.

For ease of viewing, I am only showing the first two bytes of each hash.

This process continues until there is a single hash in the final row. We can call this hash the Merkle root. Because of the cascading nature of the tree, the Merkle root is guaranteed to change if any transaction in the block changes, and thus can be used as a single representative hash of all the transactions. So, we can include this, rather than all the transaction data, in the block header when mining, reducing the amount of data we need to hash. This speeds up the process of calculating hashes. Pretty cool huh?


Commit 996c70b

A namesake of cryptocurrencies, the mighty blockchain. This brings together the last two topics into a nicely formed package.

A blockchain is exactly what it sounds like, a chain of mined blocks. More importantly, the blocks in the chain are linked in such a way that it’s impossible to change any data in the chain without everything further along completely changing too.

On the left, we have a valid blockchain. Each block header has been mined to find a hash under the target. That hash is then included in the header of the next block, linking them together.

On the right, we make a small change to the same blockchain: tweaking the Merkle root of 2397. Uh-oh! First, the block data and the nonce no longer produce a hash under the target. This new invalid hash is then fed into the next blocks header, which causes the same effect to it’s hash as well. This effect cascades down the chain, causing all blocks below the modified one to become invalid.

NOTE: Blocks vs block headers

Previously in this blog entry, I’ve explicitly defined blocks and block headers as two different things.

During mining, the unmined block is first converted into a hashable block header. This is done to remove any data that is not required to be hardened by mining, such as the height of the block, and the raw transaction data. Then, the block header is mined with its nonce to produce the hash for the block.

That’s about it, really!

Final remarks

While the first prototype of DrakeCoin implemented the cool data structures and algorithms that are fundamental to a cryptocurrency, it still needed a lot of pizazz to be fully-functional. In particular:

  • Asymmetric key generation and addresses.
  • Communication, synchronization, and consensus.
  • Transactions: signing, verification, and UTXOs.
  • Finally, practical data structures for all of the above.

In the next part, I’ll describe what it took to put all these concepts together into a functional coin.

Happy New Year folks!

Ordering my coloured pens

I got a set of Staedtler pens recently, and they have easily been the best purchase I’ve made in a long time. It’s easy to keep notes organized when I can so easily colour-code things. My professors have had a few laughs at how vibrant my tests and exams have been lately. That being said, there’s a problem.

I’m having trouble deciding what order I should keep them in, while they’re in the case. Currently, I have them organized (left to right) RGB, alternative RGB shades, very-dark separators, then two outliers. It seems strange, though, to have orange in the outliers section, because orange is sort of a red-ish shade. I’ve decided to try a different approach.


There’s an image of the product on the front of the product (which is nicely recursive), that has the order in which the colours are placed into in the factory.

It seems to be sort-of following the visible light spectrum, starting in the middle. I think it seems sort of strange to include dark brown and black at the end, separating the 600nm to 750nm range and the 400nm to ~550nm range (if we’re looping back around the spectrum).

Say we order the pens explicitly based on the spectrum. It’s a neat concept, but some colours look a bit out of place because they’re darker shades (like brown, which is just “dark orange”). Still, it satisfies the criteria of being a logically-sound ordering of the pens.

RGB magnitude

Another idea I had was to determine the magnitude of each color from black. This would roughly order the colours from darkest to lightest, and be a bit prettier than the last. Unlike the visible spectrum option, this one relies on the RGB color space, which has room for brown. I took an image of the ink of each pen with flash. I then used the dropper tool to grab the hexadecimal color code from each color.

I plotted the colours on a 3D graph, and drew lines from the origin (black) to the individual colours. The numbers displayed next to the points represent the rounded magnitude of the vector.

From this, we can see that the colours can be arranged as the following:

Color Magnitude
BLUE 157
RED 226
PINK 258
CYAN 286

Interestingly, though it looks lighter, LIGHT GREEN has a lower magnitude than RED. This is likely due to the fact that green light is more readily accepted by the cones in the eye, and therefore appears brighter. In the below graph, you can see that the 520ish nanometer range is covered better by all cones than the 650nm+ range.

Figure 2. Reprinted from “Visual pigments of rods and cones in a human retina.” by Bowmaker, J. K., & Dartnall, H. J. (1980). The Journal of Physiology, 298, p. 505.


Anyway, I think my final ordering is going to follow the RGB origin-color magnitudes, in ascending order. It seems more sane than the direct wavelength placement method, and much less arbitrary than my original method.