tag:cvk.posthaven.com,2013:/posts Christian von Kleist 2022-02-15T15:05:52Z Christian von Kleist tag:cvk.posthaven.com,2013:Post/301883 2012-02-11T05:39:15Z 2013-10-08T16:26:11Z Effie's high score on Galaga

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301884 2012-02-07T06:14:59Z 2013-10-08T16:26:11Z The Midnight

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301885 2010-12-10T03:37:00Z 2017-02-15T03:53:51Z Just me and The Woz

I met Steve Wozniak at a talk he gave. I asked him what he thought about the Singularity (which is when the machines take over the world, very roughly speaking), and he talked with me about it for a minute. He was really cool! He even gave me his business card, which is made of titanium and can cut steak (seriously!).

I asked him to sign my copy of his book iWoz, and he inscribed it for me:

Christian, When the computers take off over, you will be missed.
-- Woz

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301887 2010-09-10T02:16:00Z 2019-09-29T20:58:22Z SQL injection with raw MD5 hashes (Leet More CTF 2010 injection 300)

Team Kernel Sanders t-shirts

The University of Florida Student Infosec Team competed in the Leet More CTF 2010 yesterday. It was a 24-hour challenge-based event sort of like DEFCON quals. Ian and I made the team some ridiculous Team Kernel Sanders shirts at our hackerspace just before the competition started. The good colonel vs. Lenin: FIGHT!

Here’s a walkthrough/writeup of one of the challenges.

Injection 300: SQL injection with raw MD5 hashes

One challenge at yesterday’s CTF was a seemingly-impossible SQL injection worth 300 points. The point of the challenge was to submit a password to a PHP script that would be hashed with MD5 before being used in a query. At first glance, the challenge looked impossible. Here’s the code that was running on the game server:

The only injection point was the first mysql_query(). Without the complication of MD5, the vulnerable line of code would have looked like this:

$r = mysql_query("SELECT login FROM admins WHERE password = '" . $_GET['password'] . "'");

If the password foobar were submitted to the script, this SQL statement would be executed on the server:

SELECT login FROM admins WHERE password = 'foobar'

That would have been trivial to exploit. I could have submitted the password ' OR 1 = 1; -- instead:

SELECT login FROM admins WHERE password = '' OR 1 = 1; -- '

…which would have returned all the rows from the admins table and tricked the script into granting me access to the page.

However, this challenge was much more difficult than that. Since PHP’s md5() function was encrypting the password first, this was what was being sent to the server :

SELECT login FROM admins WHERE password = '[output of md5 function]'

So how could I possibly inject SQL when MD5 would destroy whatever I supplied?

1337 hax0rs

The trick: Raw MD5 hashes are dangerous in SQL

The trick in this challenge was that PHP’s md5() function can return its output in either hex or raw form. Here’s md5()’s method signature:

string md5( string $str [, bool $raw_output = false] )

If the second argument to MD5 is true, it will return ugly raw bits instead of a nice hex string. Raw MD5 hashes are dangerous in SQL statements because they can contain characters with special meaning to MySQL. The raw data could, for example, contain quotes (' or ") that would allow SQL injection.

I used this fact to create a raw MD5 hash that contained SQL injection code.

But it might take years to calculate

In order to spend the least possible time brute forcing MD5 hashes, I tried to think of the shortest possible SQL injection. I came up with one only 6 characters long:

'||1;#

I quickly wrote a C program to see how fast I could brute force MD5. My netbook could compute about 500,000 MD5 hashes per second using libssl’s MD5 functions. My quick (and possibly wrong) math told me every hash had a 1 in 28 trillion chance of containing my desired 6-character injection string.

So that would only take 2 years at 500,000 hashes per second.

Optimizing: Shortening the injection string

If I could shorten my injection string by even one character, I would reduce the number of hash calculations by a factor of 256. After thinking about the problem for a while and playing around a lot with MySQL, I was able to shorten my injection to only 5 characters:

'||'1

This would produce an SQL statement like this (assuming my injection happened to fall in about the middle of the MD5 hash and pretending xxxx is random data):

SELECT login FROM admins WHERE password = 'xxx'||'1xxxxxxxx'

|| is equivalent to OR, and a string starting with a 1 is cast as an integer when used as a boolean. Therefore, my injection would be equivalent to this:

SELECT login FROM admins WHERE password = 'xxx' OR 1

By Just removing a single character, that got me down to 2.3 days' worth of calculation. Still not fast enough, but getting closer.

Lopping off another character, and more improvements

Since any number from 1 to 9 would work in my injection, I could shorten my injection string to just '||' and then check to see if the injection string were followed by a digit from 1 to 9 (a very cheap check). This would simultaneously reduce my MD5 calculations by a factor of 256 and make it 9 times as likely that I’d find a usable injection string.

And since || is the same as OR, I could check for it too (2x speedup) and all its case variations (16x speedup). Running my program on a remote dual-core desktop instead of my netbook got me another 10x speedup.

The final hash

After computing only 19 million MD5 hashes, my program found an answer:

content: 129581926211651571912466741651878684928
count:   18933549
hex:     06da5430449f8f6f23dfc1276f722738
raw:     ?T0D??o#??'or'8.N=?

So I submitted the password 129581926211651571912466741651878684928 to the PHP script, and it worked! I was able to see this table:

admins-table

Last step

The last step of the challenge was to turn the MD5 hash into a password. I could have used a brute forcer like John, but instead I just searched Google. The password had been cracked by opencrack.hashkiller.com and was 13376843.

The code

Final scoreboard

hax hax hax

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301888 2010-09-02T07:00:00Z 2022-02-15T15:05:52Z How to crack VxWorks password hashes

Recently H.D. Moore discovered a serious vulnerability in the widespread VxWorks 5.x operating system (or at least rediscovered it, expanded upon it, and publicized it). I was lucky enough to be one of the sixty people who saw his DEFCON 18 skytalk on the subject. He released information about the vulnerability to the public on August 2nd, 2010.

In addition to the main vulnerability (the fact that many vendor implementations of VxWorks 5.x had a publicly-accessible debug service running on port 17185), H.D. Moore discovered that the default VxWorks password hashing algorithm was insecure. He said he would release much more information about it one month from the initial release, which would mean September 2nd, 2010.

That was too long a wait for me, so I started working on the problem soon after I got home from DEFCON, and I was successful in finding the weakness H.D. talked about. I’ve also written a tool which can turn any password hash into a workalike password (i.e., a password different from the original password that produces the same hash), and another tool which scans VxWorks memory dumps for password hashes and reveals the workalike password for each one.

Although I’ve completed this work in early August, I have decided to follow the same release schedule that H.D. Moore has committed to, so this post is set to publish at midnight on September 2nd. I’m sure that I’ve only discovered part of what he will reveal on that day.

Background on VxWorks' weak password hash

By default, passwords in VxWorks 5.x are hashed using a really stupid one-way hashing mechanism. I don’t understand why the VxWorks developers didn’t use a stronger hash. MD5 was popular as a one-way hash when VxWorks 5.x was being written, but they didn’t use it. Instead, they created their own hash, what H.D. Moore called a “Bob the Wizard” hash at the skytalk.

The meat of VxWorks password hashing algorithm is a checksumming step where each byte of the plaintext (the password to be hashed) is multiplied by and then XOR’d with its position in the string, and added to an accumulator. It looks like this in C (modified slightly from the original source for clarity):

checksum = 0;
for (ix = 0; ix < strlen(plaintext); ix++)    /* sum the string */
  checksum += (plaintext[ix]) * (ix+1) ^ (ix+1);

Next, this checksum (an integer) is multiplied by a magic number and turned into a string in decimal:

sprintf (hash, "%u", (long) (checksum * 31695317)); /* convert interger
                                                    to string */

(The lulzy typo in interger is original.)

The final step of computing a hash is doing a character substitution for each byte in the string:

for (ix = 0; ix < strlen (hash); ix++)
{
  if (hash[ix] < '3')
    hash[ix] = hash[ix] + '!';    /* arbitrary */

  if (hash[ix] < '7')
    hash[ix] = hash[ix] + '/';    /* arbitrary */

  if (hash[ix] < '9')
    hash[ix] = hash[ix] + 'B';    /* arbitrary */
}

This character-substitution step really has no point other than making the hash look “hashlike.” It simply converts decimal digits to letters (except 9, which isn’t changed). For example, it always converts 0 to Q, 1 to R, 2 to S, 3 to b, etc. The input string 0123456789 would come out as QRSbcdeyz9 every time. It might look more like a bonafide hash of some sort, but it’s not.

In the end, every VxWorks 5.x password hash is composed of the alphabet QRSbcdeyz9 and is up to 10 characters long.

Reversing the VxWorks password hash

For any password hashing algorithm, if you can generate a list of all possible outputs, and you know the inputs that generated those outputs, then you can turn any given password hash into a workalike password. (This is also called “reversing” the hash.) For a hashing algorithm like MD5 or SHA1, doing this is infeasible; however, the VxWorks checksum space is so small that it’s possible to generate such a list pretty easily.

The weakness in the VxWorks hashing algorithm is the summing step (the first step). For any output value of the summing step (checksum), it’s easy to modify the input (plaintext) to produce an output incremented by one (checksum + 1). This means you can start with an input that generates the lowest possible checksum and then, by continuously generating subsequent checksums, you can create a table of all possible output values and all the input values needed to create them.

I started working on this approach, but very quickly realized that there is a simpler way to generate such a table that requires very little thinking!

The VxWorks hashing algorithm just happens to be so bad (because it crunches so large an input space into so small an output space), that randomly generating a few million input values generates nearly every possible output value. In my tests, this approach found a workalike password for 100% of dictionary words and more than 99.99% of random input strings composed of all legal input characters.

Download my code

I’m releasing some Ruby scripts and a C program here:

vxworks_hash on GitHub

This is what’s included:

  1. vxworks_collide.rb: VxWorks password hash reverser library (plus vxworks_find_collision.rb, a handy command-line tool for using the library)
  2. vxworks_mem_search.rb: Scanner that finds and reverses hashes in VxWorks memory dumps
  3. vxworks_make_hash_table.rb and vxworks_collide.c: C program for generating VxWorks password checksum tables and a Ruby script to turn checksum tables into hash tables (for use with #1 and #2)

I’m also releasing a precomputed hash table (lookup.txt) for use with #1 and #2.

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301889 2010-07-31T19:08:40Z 2013-10-08T16:26:11Z DEFCON trip day 3, part 2: Ninja badges, vertically ambitious mohawks, Dual Core, and a PDP-11 This post is catch-up from yesterday.

We finally got an up-close look at some Ninja badges today. They are
crazy awesome, and the Ninja crew are geniuses at promotion. All their
members (and a bunch of their friends) have computerized badges
hanging around their necks which serve as party passes to tonight's
Ninja party, AND EACH BADGE IS A COMPLETE VIDEOGAME CONSOLE! It's sort
of reminiscent of Digimon, but with long-range wireless, and instead
of battling monsters, the players battle each other. There's also a
row of LEDs down one side of the badge that indicates which of 10
special achievements each holder has unlocked.

Each Ninja badge holder also has an unlock code that will turn a
friend's regular DEFCON badge into a ninja party pass. People are
walking around begging Ninjas for unlock codes! It's pure promotional
genius.

There's a picture of the badge below, and here is a Wired article:

http://www.wired.com/threatlevel/2010/07/defcon-ninja-badge/

Other highlights from yesterday: Some amateur hair stylists were
giving mohawks for a donation of $15 to the EFF; DDTek's Wall of Sheep
featured real usernames, passwords, bank accounts, and proctology exam
results sniffed from wireless traffic; we met Int80 from the nerdcore
band Dual Core (he is extremely nice, and while he wouldn't sign my
boobs, he did sign my CDs); and we got to play with a real PDP-11
computer made before any of us was born.

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301890 2010-07-31T05:31:25Z 2013-10-08T16:26:11Z DEFCON trip day 3: unmanned aerial vehicles, Fyodor Last night we went to the EFF party in the Riv's penthouse. It was fun
-- and the $40 at-the-door "donation" extended my EFF membership for
another year -- but the venue was a little too big for the crowd.
Int80 from Dual Core did hilarious freestyle hacking rap. That got
things going. There was also a fundraising auction for the EFF
featuring Joe Grand, designer of the uber-sweet DEFCON 18 badges
(which are microcontrollers with e-ink screens).

This morning actual DEFCON began. It is awesome! My favorite
presentation of the day was "How to build your own unmanned aerial
vehicle" (approximate title), which featured video of autonomous
electric planes (hobby-type foam planes) carrying computers, cameras,
WIFI sniffers, and even model rockets as mini Hellfire missles. I'd
like to get into UAV, so maybe I'll talk some people at Gainesville
Hackerspace into it.

There was a good presentation on hacking Canon Powershot consumer
cameras. The guys reverse engineered the BASIC-flavored scripting
language that every Powershot camera has (and that's a lot of camera
models). They were able to have it record audio surreptitiously (at
least they hinted at it), write images to a card without the user ever
shooting them (great for incriminating people), and other awesome
things. The presentation style was really rough, but they did a good
job on the hacks.

The coolest presenter of the day was Fyodor, creator of Nmap. He and a
collaborator gave a tutorial on scripting Nmap with Lua. He also
showed the results of scanning a range of Microsoft-owned IP addresses
with new scripts for the Nmap scripting engine. It was amazing that
the company that extols Windows' security had so many unsecured
machines. Fyodor has excellent presenting presence and is totally
hilarious. (Of course, his security clout acts as a 10X humor
multiplier.)

There was much more today, but we have to go to the Hacker Pimps party now...

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301891 2010-07-30T04:21:36Z 2013-10-08T16:26:11Z Decoding DEFCON 18 badge's QR code Today we went to BlackHat to get our DEFCON 18 badges. (Thanks to John
Sawyer for making that possible!) The line was way shorter than at the
big DEFCON registration area in the Riviera.

Immediately after we got our badges, we headed back out to B-Sides to
check them out. We made sort of a finite state machine diagram of the
menu system in the badge, and the first thing we attacked was the QR
code. (Start the badge on the DEFCON screen, then press the top
button, and then hold both buttons down.) Pictures are below.

When we first tried to decode it with http://zxing.org/w/decode.jspx ,
we got nothing, but it turns out it just couldn't handle an image as
big as our picture was. When we rescaled it with Gimp, it immediately
decoded to the following:

VANDALS WANG

Vandal's wang? That's pretty funny, but we're not exactly sure if it
means anything. Next we're going to check out the badge source.

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301892 2010-07-29T16:58:37Z 2013-10-08T16:26:11Z DEFCON: B-Sides day one, parties, etc. B-Sides is a mini security convention happening before DEFCON this
year. That's where Manny, Marly, and I were most of the day yesterday,
and it's where we'll be again today.

Highlight of last night was the Qualys party. We met John Sawyer there
and watched an 80s cover band called Tainted Love. Open bars are great
when you're in a club with $15 beers! (Before that we got free food
and drinks at the Tenable party.) John and his wife Sarah are SUPER
nice. They introduced us to Daniel Suarez, writer of Daemon
(http://www.amazon.com/Daemon-Daniel-Suarez/dp/0525951113 ), a book
that Marly and I have both read and talked a lot about.

I really wished Effie were there for the clubbing! I like clubbing,
but she loves it WAY more.

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301893 2010-07-28T05:12:13Z 2013-10-08T16:26:11Z DEFCON day 1: dinner We went to Bellagio for the $30 all-you-can-eat buffet this evening.
It was awesome! I ate two giant plates of food. Bellagio has a HUGE
casino, but we liked Caesars Palace more.

The only thing that isn't awesome so far is the Las Vegas buses. We
paid $7 each for a 24-hour pass, but we had to wait for the bus for
almost an hour each time we wanted one, and they were PACKED. Tomorrow
we're probably going to use cabs instead.

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301894 2010-07-27T21:59:19Z 2013-10-08T16:26:11Z DEFCON day 1 We left Tampa at 7am, and got to Las Vegas 7 hours later. It's 3pm
local time and we just checked into Circus Circus. It is so classy!
There is clown art everywhere, and parts of the room are coin
operated. The entire downstairs floor is a freaky
clowncasinomallfoodcart.

We're going to go eat!

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301895 2010-05-24T15:32:00Z 2013-10-08T16:26:11Z Defcon 18 quals pt400 walkthrough (t400 writeup)

Update 1: See the last section below (The Code) for my Ruby code.

Update 2: Yes, I've written a server that acts just like the real one and will post the code soon.

Update 3: Decompiled RankMe.class is here: RankMe.java

Update 4: The official t400 server source has been released.

Overview

The pt400 challenge was a game in which you had to correctly rank a set of 71 famous people (including hackers in both senses of the word [e.g., Kevin Mitnick, Richard Stallman], famous coders [e.g., Knuth], cryptographers [e.g., Bruce Schneier], etc.). I'll call them all hackers for simplicity.

The client

The game client was a simple Java Swing app called RankMe. You run it from the command line and it connects to the game server. Then, it presents two hackers and you have to pick the better one. If you pick the wrong hacker, the message "Better luck next time!" pops up and the game exits. The ranking of hackers was chosen by DDTek, I think somewhat arbitrarily, and is consistent from game to game.

Analysis

The first thing I tried was to analyze the game traffic with Wireshark. This was pretty fail. There was no JPEG/PNG/GIF/BMP data in the stream, and no understanding of the protocol immediately came to me. I could have spent more time analyzing it, but instead I decided to decompile the client with JD-GUI (http://java.decompiler.free.fr/). This worked perfectly and gave me RankMe.java, which was easy to read and understand. I discovered how the game protocol worked by reading this code.

The game protocol

The protocol is simple. Basically, the server sends two images to the client at a time, and after receiving each pair of images and displaying them to the user, the client sends back the user's guess. If the user is right, the client can download two new images. If the user is wrong, the server sends an error code and disconnects. If the user has guessed enough matches correctly, the server asks for the combination of guesses that won (a string of LRLR..). If the user correctly inputs that combination, the server sends a secret message which is the key for pt400.

Protocol in outline form:

  1. on connect, client sends string "illogical\n"
  2. client reads two JPEG images
    • to read each image from the server:
      • first, read four bytes and convert them to an integer datalen which is the number of bytes in the incoming picture (datalen is xor-encrypted with 0xc932f768)
      • read datalen bytes of picture data (xor-encrypted by lower 8 bits of datalen)
  3. after user clicks a picture, client sends the user's guess:
    • left picture: send 0x0 byte to server
    • right picture: send 0x1 byte to server
  4. goto step 2, but sometimes step 2 will fail with an error code which indicates information about the user's most recent guess:
    • if guess was wrong, the integer -1 or -2 will be read from server when attempting to get image length
    • if game has been won (64 correct guesses in a row), -3 will be read, and client should send sequence of LRLR used to win game
    • if sequence guess is correct, server responds with a secret phrase which is the key to pt400

How to win

My strategy for winning was simple. First, I needed to get the outcome of a bunch of matches so that I'd be able to predict future matches. To do that, I implemented the protocol in Ruby in a class called Game which could connect to the game server and play games forever, collecting match results in a file called gamelog. (The Game class played stupidly, always picking the left picture.) The server was able to run about one round a second, and I had about 3,000 match results by the time I was ready for the next step. The match results (gamelog) looked like this:

9032d9fc7f0eec084636a35a0a623e84b2ee91a0 beat 895de4c13c326b60730786288465f91b5e5ba982
61ed2a78cf49d8c7eaa598fcbd9805825fae291c beat b94386c69bb0f04efe15863c480a2ba73bb7895d
2aea1a34c5cea68d5dd0678f04749e956935a11f beat 7b2fc387378a45a70c9db5cd384d3729ece8b3e7
50232549de83970517f50a6956e905004534919b beat f2f84e9514243c187cda82828569994a49ca481b
6e29bc1551abebad76297c5e3028a14188298d77 beat 815fc7e20b430cb34e98b632e081c8cfc0cbf9fa 

(I uniquely identified each hacker by the SHA1 hash of his picture. For example, ac1dburn was ab74176394caeb1158df9c4d9fac004fff27377a, and Tsutomu Shimomura was 39ddc6e687b0cd8255ea44a0a9a7b2cb88601454.)

The next step was to write an extension of the Game class called SmartGame which knew the results of all the matches and, given two hackers, could predict which one would win the match. Here's the prediction logic:

  • given hacker1 and hacker2 and previous match data, predict a winner:
    • when will hacker1 defeat hacker2?
      • when hacker1 has beaten hacker2, or
      • when any hacker that hacker1 has beaten, has beaten hacker2, or
      • when any hacker that hacker1 has beaten, has beaten any hacker that has beaten hacker2
      • etc. (recursion)
  • if the match data doesn't provide a conclusive answer, then predict based on statistics (wins/loss ratio for hacker1 and hacker2)

Pwnsauce

My original plan for SmartGame was to have it learn from the game as it played; however, it turned out that having a gamelog of 3,000 matches made this unnecessary. SmartGame was able to pick the correct hacker 100% of the time. It could connect to the server and win in less than 30 seconds (or maybe a little longer, but it was fast).

Update: I updated my code to learn from the game as it's played, just because it was an easy change.

Output of a typical run

The key

The secret key was None hold a candle to Bessie the sh33p!

The code: Try it out

My pt400 solver is written in Ruby. It's messy. Sorry for the delay in putting it up. I meant to do it right after quals.

http://github.com/cvonkleist/defcon18quals/tree/master/pt400/

Check it out of Github (or just download protocol.rb and smart.rb). To run it, do this:

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301896 2009-12-03T17:09:00Z 2013-10-08T16:26:11Z Running BackTrack 4 on a MacBook Pro Here's what you do:

1) Google for stuff
2) Fail
3) Step 1 again]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301897 2009-11-19T16:56:00Z 2013-10-08T16:26:11Z Both scp and rsync have bandwidth limit control

I was talking to someone today who knew about rsync's bandwidth control, but not about scp's. Yep, they both have it.

rsync

For rsync, you specify the bandwidth limit it KB/s.

rsync --bwlimit 200 foo server:bar

My common usage is:

rsync -avz -e ssh --bwlimit 200 --progress foo server:bar

scp

For scp, you specify the limit in Kb/s (kilobits vs. kiloBytes).

scp -l 1600 foo server:bar
]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301898 2009-11-10T18:37:00Z 2013-10-08T16:26:11Z Speeding up SSH with ControlMaster connections

You can make SSH much faster with the ControlMaster configuration directive. When enabled, only the first SSH connection to a server will incur slow connection overhead. Each additional connection will simply reuse the first connection via a tunnel.

This is especially useful when using command-line completion over SSH (using zsh, for example). Using tab completion on a predicate like scp gibson:www/ht will result in a multi-second delay each time, but with SSH ControlMaster connections, the delay is sub-second.

Just add this to your ~/.ssh/config:

Host *
ControlPath /tmp/%r@%h:%p 
ControlMaster auto

That's it. Now your first SSH connection will automatically act as a ControlMaster. Each subsequent connection will be almost instantaneous. In my extremely scientific sample size of one host-server pair, this made subsequent connections about 16 times as quick (0.1 seconds to connect instead of 1.6).

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301899 2009-11-04T21:41:00Z 2015-08-21T11:26:45Z Watching my ill-gotten Twitter followers dwindle

About a year ago I wrote a script that crawled my Twitter network and made me follow lots of new people. Then it waited 24 hours to see if they followed me. If they did, I kept them in my list; otherwise, I chopped them. After a couple of days I had almost 600 followers. I just did this for fun, and I knocked it off after a couple of days for fear of being banned.

I wish I'd recorded stats from the beginning of the experiment, but I only started recording them in August (2009). This is what it looks like so far.

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301900 2009-11-04T20:40:00Z 2013-10-08T16:26:11Z Caching a slow block of Ruby code with almost no effort

This is a method I use when working with slow ruby scripts that operate on huge datasets.

It caches the return value of a block of extremely slow code in a file so that subsequent runs are fast.

It's indispensable to me when doing edit-debug-edit-debug-edit cycles on giant datasets.

]]>
Christian von Kleist
tag:cvk.posthaven.com,2013:Post/301901 2009-11-04T20:19:35Z 2013-10-08T16:26:11Z This really is dead simple. ]]> Christian von Kleist