vrijdag 9 oktober 2020

Adding USB ports to an old router (WRT54GL)

I needed a wireless Linux device with an antenna, preferably free. Luckily I had an old router laying around, as you do, and it already has Linux on it, aren't I a lucky old fella. I hooked it up, brute-forced the password manually by memory and gave the device a long needed firmware upgrade... a 9 year old image.

The problem

Device up and running, next step is popping a shell and start getting software on the thing. Sadly the Linksys WRT54GL only has 4MB internal flash storage, and the OS takes a (relatively) big chunk. If only this old friend had a USB port or something...

The Mod

I quickly stumbled upon possible hardware hacks which listed adding an SD card or USB port(s). For multiple reasons I chose USB, you can disagree with me in the comments below.

Easily I found multiple guides, for multiple versions. The general idea was the same, solder some wires to the PCB, some resistors a USB port and some jenky way of getting 5V, simple enough.

I followed this guide on the OpenWrt wiki and soldered wires to the appropriate resistors (21, 23, 25, 26). Then I proceeded to complete the circuit on a breadboard. I installed the needed USB kernel modules and checked dmesg.

usb.c: new USB bus registered, assigned bus number 1
hub.c: USB hub found
hub.c: 2 ports detected

Halfway there, right? Let's connect a drive.

usb.c: USB device not accepting new address=2 (error=-145)

That doesn't seem like it's working properly...

The this-always-happens-to-me

Okay, so here's the thing. Whenever I attempt to do anything like this it never works for me, either I have some slightly different version or some other glitch-in-the-matrix type thing causes me to encounter corner cases that don't seem to be documented anywhere else. For those who are on the same quest as I have been, I hope this post is helpful to you.

So maybe the guide is wrong? I came across other guides like the DD-WRT one which point to other resistors (19 and 20). So what the hell, let's try that, what have I got to loose?

Long story short this didn't work at all. The USB hub was still detected but alas no devices at all. So I re-soldered the tiny SMDs back to the board. This was a step in the wrong direction, back to the first mod.

Soldered the wires back to 21, 23, 25 and 26 and broke out my Google-Fu.

The clue

Since I am messing with hardware I'm assuming the fault is there so searching for the error seemed to return a lot of useless results as most of them were not soldering shit on their machines. There was however one slight clue on this site where someone builds their own USB device? (didn't read it fully, sorry bro.)

Apparently the error 'device not accepting address' means that the pull-up resistor is detected, but not necessarily anything else. Clock speed, zener diodes and I/O setting of pins can be wrong.

 The shot in the dark

So, there's something going on with the pull-up resistor apparently. Maybe the signal is too weak? I don't know, I'm not qualified to work on this. So I removed the 22ohm resistors between the data wire and the USB port and tried again, what's the worst that could happen?

hub.c: new USB device 00:03.0-1, assigned address 2

Fuck yeah! Success at last.

The end-result

I created a prototype PCB and screwed that to the case, I'm tired of using hot glue, after a while it loses it's grip. I added a connector to the board so that I could just plug in a regular PC USB bracket I had.

Cut 2 holes with a knockoff Dremel for the USB ports and drilled two holes to mount it.

The end result looks nice, except for the leftover duct-tape from previous abuse.

The solution

So this is what I ended up doing. I soldered a wire to each of the resistors 21, 23, 25 and 26 on the side closest to the Broadcom chip. These are the USB data lines.

21 = data+

23 = data-

25 = data+

26 = data-

Each wire is connected directly to the appropriate USB pin and to a 15K resistor which goes to ground.

USB also requires a 5V power source which I provided with a 7805. I stole 12V from the WRT54GL's power socket as input for the 7805 and tapped the ground from the power socket as well. 12v and ground go to the 7805 and 5V comes out, simple! All of the power circuits I encounter add capacitors so I did this as well, however as I don't have a lot of capacitors laying around so I used different ones (50V, 0.22uF or something, does this really matter that much? What's the science behind this?) The electrolytic capacitors are connected between 12V-GND and 5V-GND, mind the capacitor direction!

The firmware I used is this image: http://downloads.openwrt.org/backfire/10.03.1/brcm-2.4/openwrt-brcm-2.4-squashfs.trx

To get the USB mod working we need drivers:

opkg update
opkg install kmod-usb-ohci kmod-usb-storage kmod-usb-core

Well, that's it, now my old router has two USB 1.1 ports.


zaterdag 27 juli 2013

Propagate nested Python contexts

Recently I've been playing around with Python's context managers. Context managers are used in combination with the 'with' statement. As an example a file can be opened and closed in a context.
The file will be open in the 'with' block and closed  after the 'with' block.

with file('filename.txt') as f:
    for line in f:
        print(f)

To implement a context manager you'll have to create a class that inherits from object with a '__enter__' and '__exit__' method:

class Context(object):
    def __enter__(self):
        """Enter context"""

    def __exit__(self):
        """Exit context"""

Note that the syntax 'with <context_manager> [as <target>]:' assigns a target variable. This is the returned value of the '__enter__' method.

In my case I wanted to be able to nest contexts and ensure that nested contexts were propagated to the root context. My first idea was to have a class attribute and assign that in the '__enter__' method and return that value (even in nested contexts). This was the result:

class Context(object):
    current = None

    def __enter__(self):
        """Enter context"""
        if not Context.current:
            Context.current = self
        return Context.current

    def __exit__(self):
        """Exit context"""
        Context.current = None

After a quick test I noticed a nested context set the current context as None upon exit (the third 'print' statement will print None):

with Context() as root:
    print(Context.current)
    with Context() as nested:
        print(Context.current)
    print(Context.current)

To prevent this I modified the '__exit__' method to check if the '__exit__' is called for the root context. The final result looks like this:

class Context(object):
    current = None

    def __enter__(self):
        """Enter context"""
        if not Context.current:
            Context.current = self
        return Context.current

    def __exit__(self):
        """Exit context"""
        if self is Context.current:
            Context.current = None

By using the 'is' comparison the context only gets closed when the '__exit__' method is called for the root context manager.

vrijdag 23 november 2012

Cracking Redmine passwords

As I am running Redmine on my server and I wanted to test security I wanted to see if I could crack some passwords of my users.

Redmine uses sha1 to store passwords, salted with a random uhm.. salt.

In pseudo code the algorithm to create a password hash would look like this:
sha1(salt + sha1(password))

I made a dump of the users table in the format "login:password_hash:salt" and wrote a small Python based cracker that could parse this and iterate through a dictionary file.

Out of the 16 users in the table 2 of them used their login as their password.. 1 used his first name and 1 used a password that was cracked within seconds.
Needless to say, I locked their accounts [insert evil laugh] and I'm going to do this more often. (Maybe automated, with email notifications or something.)

O, and for those who want to do this sort of unethical thing too, here's the script:
#!/usr/bin/env python

from optparse import OptionParser
import hashlib

parser = OptionParser(usage="usage: %prog [options] userfile wordlist")
(options, args) = parser.parse_args()

if len(args) != 2:
    parser.error("Incorrect number of arguments")

def import_users():
    user_file = open(args[0], 'r')
    users = [] # format: (login, password_hash, salt)
    for line in user_file.readlines():
        if not line.startswith('#'):
            users.append(line.strip().split(':'))
    user_file.close()
    return users

def calculate_hash(password, salt):
    hashed_password = hashlib.sha1(password).hexdigest()
    hash = hashlib.sha1(salt+hashed_password).hexdigest()
    return hash


users = import_users()

# Try username as password
cracked = []
for user in users:
    word = user[0].strip()
    if user[1] == calculate_hash(word, user[2]):
        print "Password of user '%s' is '%s'" % (user[0], word)
        cracked.append(user)
for user in cracked:
    users.remove(user)

wordlist = open(args[1], 'r')
for word in wordlist:
    cracked = []
    for user in users:
        if user[1] == calculate_hash(word, user[2]):
            print "Password of user '%s' is '%s'" % (user[0], word)
            cracked.append(user)
    for user in cracked:
        users.remove(user)
wordlist.close()

Linux shorthand for rename/move/copy

As a Linux user you frequently use the terminal as it is powerful as [insert sentence enhancer].
A frequent thing you do in a terminal is copy or move/rename a file. When you do frequent things you'll get frequent annoyances (should sound familiar).

Example:
When you want to copy a file from "some/subdir/of/the/current/working/dir/file.ext" to "some/subdir/of/the/current/working/dir/file.ext.bak" you have to type the entire path twice. Even with tab completion it gets annoying.

Solution:
Use them fancy curly bracket things!
Instead of this:
 cp some/subdir/of/the/current/working/dir/file.ext some/subdir/of/the/current/working/dir/file.ext.bak  
Do this:
 cp some/subdir/of/the/current/working/dir/file.ext{,.bak}  

The curly brackets are interpreted to multiply the string it is in and make all combinations. So the latter is transformed to the first.
You can also embed them:
 echo a{,b{,c}}  
That's translated to:
 echo a ab abc  

donderdag 22 november 2012

PXE boot SystemRescueCd using a Linux server

I needed to get some things done on a PC that didn't have a optical drive so I needed to get creative. I know there is this thing called PXE (Preboot eXecution Environment) but I never tried it. When I started doing some research it was exactly what I needed.


I always hated it when I saw PXE initializing because it meant the PC couldn't boot from disk. Little did I know PXE is an awesome invention.

Get on with the how-to already!! Ok, Ok..

The ingredients I used are:
  • A Linux server (I prefer Gentoo, but any distribution will do).
  • Root access to the server.
  • A client PC which supports PXE. psst.. VirtualBox supports PXE ;-)
  • A network to connect the client and server. You will be running a DHCP server on this network. I used a cross cable. You can bridge VirtualBox to the interface you use.
  • An Internet connection on the server (you need to download a few things).
  • A couple hours of time.
The server will need a static IP. I use 192.168.1.1 and a subnet of 255.255.255.0. 192.168.1.1/24 for short. Wherever you see this, replace it with your own.
Since I want this set-up to work with a specific interface I've used 'eth0'. So If you want to have this too, substitute 'eth0' with your own.

For those who compile their own kernel, these options need to be enabled:
 CONFIG_EXPERIMENTAL=y  
 CONFIG_PACKET=y  
 CONFIG_UNIX=y  
 CONFIG_INET=y  
 CONFIG_IP_MULTICAST=y  
 CONFIG_NFSD=y  
 CONFIG_NFSD_V3=y  

The interface you will be running the DHCP server from should be configured for multicast. The way to do this is:
 ifconfig eth0 multicast  

Running ifconfig should then mention the interface is multicast, something like
 UP BROADCAST MULTICAST  

or
 UP BROADCAST RUNNING MULTICAST  

As long as it says "MULTICAST" you're doing good so far! Hooray!

The interface will need a static IP, you can do it using ifconfig or use your network manager for this.
 ifconfig eth0 192.168.1.1/24  

Note: I had some trouble since I'm using NetworkManager, the init scripts kept starting NetworkManager somehow so I needed to configure my eth0 with NetworkManager.

Now it's time to install the DHCP server, as a good Linux user you use your distro's package manager. As a Gentoo user you would normally use Portage for this, but I'm using Paludis. Mind that you want to run a DHCP server, so the 'server' use flag should be turned on for this package.

Using Portage you should do:
 emerge dhcp  

But since I'm using Paludis I do:
 cave resolve dhcp -x  

Now that we have dhcpd installed we can configure it.
Fire up your favorite text editor hint: vim and edit "/etc/dhcp/dhcpd.conf"
This is a minimal config I did, so consult the documentation if you want more.
 subnet 192.168.1.0 netmask 255.255.255.0  
 {  
   range 192.168.1.2 192.168.1.10;  
   filename "pxelinux.0";  
   next-server 192.168.1.1;  
 }  

Now if you want your DHCP server to run on a specific interface, on Gentoo you can edit "/etc/conf.f/dhcpd":
 IFACE="eth0"  

If you (and I) did everything right you should be able to start the DHCP daemon:
 /etc/init.d/dhcpd start  

IIRC if you do this on a Debian flavored machine it will slap you on the wrist and tell you to use something like "services [service] start" instead. The choice is yours.

Now the second part of the PXE server, the TFTP service.
Go ahead and install the TFTP server using your favorite package manager (package is called "tftp-hpa" in the Gentoo repo):
 cave resolve tftp-hpa -x  

Once the TFTP service is installed it's time to configure it. In my Gentoo machine I only had to edit the file "/etc/conf.d/in.tftpd":
 INTFTPD_PATH="/var/tftpd"  
 INTFTPD_OPTS="--listen --secure ${INTFTPD_PATH} --address 192.168.1.1"  

I like to use the long version of switches in config files because they are more readable then -a, -t, -3, etc.
The "--listen" switch tells TFTP to listen, the "--secure" switch locks TFTP in the directory specified (you may only specify one directory) and the "--address [IP]" switch tells TFTP to only accept connection to a specified IP address.
The directory specified by INTFTPD_PATH must exist, if it doesn't, create it.

You can now start the TFTP server:
 /etc/init.d/in.tftpd start  

You may have noticed the directory TFTP is serving is empty. We're now going to fix that. For that, we need syslinux.
Go ahead and install syslinux with your favorite package manager:
 cave resolve syslinux -x  

Once syslinux is installed you can copy over the PXELinux files to the TFTP directory:
 cp /usr/share/syslinux/pxelinux.0 /var/tftpd  
 cp /usr/share/syslinux/menu.c32 /var/tftpd  
 mkdir /var/tftpd/pxelinux.cfg  
 touch /var/tftpd/pxelinux.cfg/default  

Why did we touch "/var/tftpd/pxelinux.cfg/default"? Because we're going to edit it now. So fire up your text editor and write something like:
 prompt 0  
 timeout 200  
 default menu.c32  
   
 menu title siebz0r's awesome PXE boot  
   
 LABEL SystemRescueCd  
   kernel sysresccd/rescue32  
   append initrd=sysresccd/initram.igz dodhcp setkmap=us rootpass=somepassword nfsboot=192.168.1.1:/var/nfs/sysresccd  

This is a sample to present a menu with the option to boot SystemRescueCd. SystemRescueCd accepts some kernel parameters, the interesting one for this set-up is nfsboot. "nfsboot=192.168.1.1:/var/nfs/sysresccd" instructs SystemRescueCd to mount "/var/nfs/sysresccd" from server "192.168.1.1" through NFS.

NFS? Yeah, we're getting to it..

Install NFS using your package manager:
 cave resolve nfs-utils -x  

When this is done edit "/etc/exports" using your favorite text editor:
 /var/nfs/sysresccd  192.168.1.0/24(ro,no_subtree_check,all_squash,insecure,anonuid=1000,anongid=1000)  

This configures NFS to export "/var/nfs/sysresccd" to clients in the network "192.168.1.0/24". Options are copied from SystemRescueCd's wiki.

Now download SystemRescueCd to "/some/dir/sysresccd.iso" and mount it:
 mount -o loop /some/dir/sysresccd.iso /mnt/cdrom  

Now copy over the needed files:
 mkdir /var/tftpd/sysresccd  
 cp /mnt/cdrom/isolinux/rescue32 /var/tftpd/sysresccd  
 cp /mnt/cdrom/isolinux/initram.igz /var/tftpd/sysresccd  
   
 mkdir /var/nfs/sysresccd  
 cp /mnt/cdrom/sysrcd.dat /var/nfs/sysresccd  
 cp /mnt/cdrom/sysrcd.md5 /var/nfs/sysresccd  

And at last start NFS:
 /etc/init.d/nfs start  

Well.. that's all there is to it.
Now fire up a client with PXE support on the same network and see your awesome PXE boot menu:

maandag 19 november 2012

Wham! Bam! Thank you, mdadm!

For all who don't know what mdadm is, mdadm is a software RAID tool for Linux. Now we've got that out of the way...

As any other Über-geek I have a Linux server running some software and storing all of my precious data and of course my software repositories. As I have had the pleasure of data loss a couple years back I decided to use RAID 5 for my server. Of course I'm a student (at the time writing), and I don't have a lot of money because I'm not a greedy bastard. :-) I of course went for a software RAID using mdadm as my weapon of choice.

My server is an old desktop computer (I won't name the brand, but can ensure you it is notorious for.. well.. (since there is no other way to put this) sucking). After infecting the PC with penguins I found the server pretty damn good. Lucky me :-D
The *cough* server *cough* has a AMD Athlon 64 X2 3800+, 2GHz isn't too bad for downloading crap, serving Redmine and some software repositories like SVN and Mercurial. (No GIT, I should be ashamed.) It came with IIRC 1GB of RAM which I naturally upgraded to 2GB as I had 1GB laying around. After stripping some useless hardware like a graphics card I popped in a cheap-ass soft-RAID card and 4x500GB, you can guess the rest.

After some time I wanted to upgrade the thing as I had another system that I didn't use. The upgrade would consist of dual core to quad core, 2GB to 5GB (2x2GB 2x512MB) and 4x500GB to 4x1TB.

The memory upgrade is successful and the CPU upgrade didn't work as the motherboard didn't support the quad core.

The hard drives would be tricky as it would consist of yanking out one drive at a time and replacing them with a bigger drive. As mdadm f*ckt me royally in the past I decided to backup my RAID first. I had an external drive of 1TB laying around and used rsync to create a full backup (yeah I have a sh*tload of hardware I don't actually use but comes in handy sometimes).

Today I spent a couple hours of upgrading and soon it was time for the big drive swap. Mdadm reported earlier today that one of the drives failed, so that one was first to be replaced. Using hdparm to check the serial of the drive I recognized the drive and replaced it with a 1TB disk. After running "mdadm /dev/md0 --add /dev/new-drive" it started recovery. Of course this is where mdadm screwed me over again and showed me something like "[U_U_]" or "[_UU_]". (U means that a drive is Up and _ means a drive is down, or something.)

As I have a ramdisk (initramfs, initrd, etc.) with mdadm build in and busybox as a rescue shell I tried to force mdadm to assemble the array. Assembly didn't work so I tried re-creating them (keeping the data) by issuing "mdadm --create /dev/md0 --raid-devices=4 --level=5 --assume-clean /dev/sd[abc] missing" this worked.. until I invited /dev/sdd to the party (what a party pooper).

I am now creating a RAID 5 consisting of 4x1TB using mdadm (I guess I never learn). I guess I'll be restoring backups somewhere tomorrow.

Lesson learned: never trust mdadm! (or make backups before screwing around with mdadm)

dinsdag 9 oktober 2012

Starting development UML tool

Today I decided to start development on an idea I had a couple months earlier. I'll start with a quick intro.

The idea is to develop the ultimate UML tool. A free to use (at least for non-profit and education) UML tool capable of creating a wide range of tightly entwined diagrams. Of course I did some research about tools that are already available. A commonly used tool is Visual Paradigm. Visual paradigm supports a wide range of diagrams and is quite easy to use. However it is a tool that quickly loses great functionality when using the free version. I'm also (ironically as a Java developer) not too fond of Java based desktop applications. I find such applications to feel 'heavy'.

There are more UML applications available (Umbrella, BoUML to name a few), but they support only a couple of diagrams and are not (in my opinion) ready for professional use.

The application I'm developing is currently named OUDS (Open UML Documentation standard, see my earlier post about it), but it needs another name for obvious reasons.
I chose Python as my weapon of choice to develop the application. Python has high performance, is cross platform and seems simple enough. For the GUI I picked wxPython. wxPython seems to provide a native look and feel, so my users will feel home right away. :-)

As each software project starts with requirements gathering I thought I'd start off with creating a solution for this. Obviously the requirements need to be stored in some way. Luckily the guys at omg.org had a (sort of) solution available. Requirements Interchange Format (ReqIF) provides a standardized way to store requirements, just what I needed. I also found a page on the Eclipse site stating plans to use this format for UML tools.

As I am quite new to Python this should be a nice experiment. :-)