Readelf – a sexy little elf

Problem

From crackmes.de

  • Get a working key/keygen.
  • Allowed are only GPLed-tools.
  • Patching/Hijacking prohibited ;)

You can get the executable at http://crackmes.de/users/lagalopex/cm1/ or I’ve mirrored it here.

Now, I know what you’re thinking, but no cheating and looking at the solutions without giving it a whirl or I WILL murder ball you. You DONT want to find out what that means.

objdump

objdump is pretty limited, but it does what you need. Fine for dissasembly.

$objdump -D ./cm1 > textfileiusetomakenotes

If you’re hard core you’ll only use objdump.

gdb

gdb is a debugger. You also could just use this as a disassembler since it disassembles, but that’s just not the way I roll.

Some useful commands off the top of my head:

-break <memlocation> – set breakpoints throughout your program
-run/continue – ironically these actually mean walk/stop
-disassemble <memlocation> – disasemble the code at a memory location or label
-info registers – display the values in most the registers
-x *0×1234567 – display the contents of what’s at 0×1234567. Also related is x $eax which will display eax. Useful because you can do things like x $ebp+8 to view offsets of different registers. Another thing you can do is specify a data type, like for ints it would be x/i.

Assembly Notes

Now I have written a bit of code in assembly before, but reversing is a whole new ballgame.

Some things that took me awhile to wrap my mind around:

-Interleaving instructions are everywhere, although it makes sense when considering an ia36 processor
-Weird (to me) optimizations. Some ones that are extremely common ones I noticed are:
-xor %eax, %eax (set eax to 0) this is apparently faster than mov 0×0,%eax
-The only difference between cmp and sub is that sub stores a value. They both can set the ZF
-test %eax,%eax is just short for see if eax is 0
-It’s easier to try to guess at conditionals than it is to follow the gotos

Solution

Where to begin?

cm1 is looking at a file called /home/username/.key_username for a valid key. A valid key is ascii text that has the following properties:

esi must equal the ascii value of the current letter in the username, username[i]
esi is modified by the following symbols

* means esi+=5
/ means esi-=5
- means esi–
+ means esi++

In the keyfile, you will have a series of these symbols to make esi equal to username[i], then a comma, then username[i]. Some caveats are:

a symbol repeated > 5 times makes the key invalid. ie ***** is not allowed.
after every iteration, esi = esi/username[i]

So a valid key for a user lundeen is (with no white space):

****+-****+-****+-****+-****+-*+++,l****+-****+-****+-****+
-****+-*++,u****+-****+-****+-****+-***++++,n****+-****+-
****+-****+-*++++,d****+-****+-****+-****+-**+,e****+-****+-
****+-****+-**+,e****+-****+-****+-****+-***+++++,n

Easy as pie.

Here is my disassembly with the notes I made as I figured out these facts. My comments are hashed #

080484d4 <main>:
80484d4: 8d 4c 24 04 lea 0×4(%esp),%ecx
80484d8: 83 e4 f0 and $0xfffffff0,%esp
80484db: ff 71 fc pushl 0xfffffffc(%ecx)
80484de: 55 push %ebp
80484df: 89 e5 mov %esp,%ebp
80484e1: 57 push %edi
80484e2: 56 push %esi
80484e3: 53 push %ebx
80484e4: 51 push %ecx
80484e5: 81 ec 28 10 00 00 sub $0×1028,%esp

#Get the username, return to %eax
80484eb: e8 f0 fe ff ff call 80483e0 <getuid@plt>
80484f0: 83 ec 0c sub $0xc,%esp

#call getpwuid with %eax as an arg returns a struct <password>
80484f3: 50 push %eax
80484f4: e8 a7 fe ff ff call 80483a0 <getpwuid@plt>

80484f9: 89 85 d8 ef ff ff mov %eax,0xffffefd8(%ebp)

80484ff: 5e pop %esi
8048500: 5f pop %edi

#push what %eax is pointing to, the username
8048501: ff 30 pushl (%eax)
8048503: 31 f6 xor %esi,%esi

#Hello, %s, let’s seen what you’ve done so far
8048505: 68 50 87 04 08 push $0×8048750
804850a: e8 c1 fe ff ff call 80483d0 <printf@plt>

804850f: 8b 85 d8 ef ff ff mov 0xffffefd8(%ebp),%eax
8048515: 5b pop %ebx

#%ebx is 0×8048750

8048516: 8d 9d ee ef ff ff lea 0xffffefee(%ebp),%ebx

#%ebx is now 0xbfdcc6a6 or which is a memory address that contains a “”
#This is a negative number
#below, ebx is sort of an iterator. It’s initially set to 1 below

804851c: ff 30 pushl (%eax); the username?
804851e: ff 70 14 pushl 0×14(%eax); working directory
8048521: 68 7f 87 04 08 push $0x804877f ; %s/.key_%s
8048526: 68 01 10 00 00 push $0×1001 ;1001
804852b: 53 push %ebx


#snprintf(*str (ebx), 1001(size),/home/lundeen/.key_lundeen)
804852c: e8 bf fe ff ff call 80483f0 <snprintf@plt>

8048531: 83 c4 18 add $0×18,%esp
8048534: 6a 00 push $0×0
8048536: 53 push %ebx
8048537: e8 c4 fe ff ff call 8048400 <open@plt>

#open(/home/lundeen/.key_lundeen,0)
#the 0 flag indicates read only
#the open function returns a -1 if an error occurs
804853c: 83 c4 10 add $0×10,%esp

#if there is no file or permissions are not set, this cmp is true
804853f: 83 f8 ff cmp $0xffffffff,%eax

8048542: 89 c7 mov %eax,%edi
8048544: ba 01 00 00 00 mov $0×1,%edx
8048549: c6 85 d3 ef ff ff 20 movb $0×20,0xffffefd3(%ebp)
8048550: c7 85 d4 ef ff ff 00 movl $0×0,0xffffefd4(%ebp)
8048557: 00 00 00

#if -1 != eax meaning if there is no error, jump to 804863c
804855a: 0f 85 dc 00 00 00 jne 804863c <main+0×168>
#else jump to 8048682
8048560: e9 1d 01 00 00 jmp 8048682 <main+0x1ae>

—->jmp if there is more than the end of file in the file
#From below, if there is something in the file, jump to here
#eax should be 0 to begin with

#move the last character on the file to %dl then the eax
8048565: 8a 55 ef mov 0xffffffef(%ebp),%dl
8048568: 89 d0 mov %edx,%eax
804856a: 88 95 df ef ff ff mov %dl,0xffffefdf(%ebp)
8048570: 83 e8 2a sub $0x2a,%eax

8048573: 3c 05 cmp $0×5,%al

#ja is jump if greater than (unsigned)
#804867d is the end of the program
#if(eax is * + , – . or /)
8048575: 0f 87 02 01 00 00 ja 804867d <main+0x1a9>
804857b: 0f b6 c0 movzbl %al,%eax
#this jump is hit
#%eax is * + , – . / because ja is unsighned
#jum at the 0x0487a4 + eax*4
#x/x6 *0x80487a4 =
#0x80487a4: (*)0x0804858f (+)0×08048585 (,)0x080485bc (-)0x0804859b
#0x80487b4: (.)0x0804867d (/)0x080485a5

#esi seems to be given an initial value of 0
804857e: ff 24 85 a4 87 04 08 jmp *0x80487a4(,%eax,4)
#if the char is +
#esi++
8048585: 46 inc %esi
#compare to the previous byte read 0xfffefd3
8048586: 80 bd d3 ef ff ff 2b cmpb $0x2b,0xffffefd3(%ebp)
804858d: eb 20 jmp 80485af <main+0xdb>

#if the char is *
#esi +=5
804858f: 83 c6 05 add $0×5,%esi
8048592: 80 bd d3 ef ff ff 2a cmpb $0x2a,0xffffefd3(%ebp)
8048599: eb 14 jmp 80485af <main+0xdb>

#if the char is -
#esi–
804859b: 4e dec %esi
804859c: 80 bd d3 ef ff ff 2d cmpb $0x2d,0xffffefd3(%ebp)
80485a3: eb 0a jmp 80485af <main+0xdb>

#if the char is /
#esi -=5
80485a5: 83 ee 05 sub $0×5,%esi
80485a8: 80 bd d3 ef ff ff 2f cmpb $0x2f,0xffffefd3(%ebp)

–>all punctuation ends up here

#if the value above is not the same as the previous byte?
#if it’s not the same jump to the start of the loop below
#AND set ebx (the counter) back to 1. This means you can’t have > 5
#of the same thing in a row
80485af: 75 7a jne 804862b <main+0×157>

80485b1: 43 inc %ebx
80485b2: 83 fb 05 cmp $0×5,%ebx

#if 5 >= ebx (ebx is incremented above and set to 1 below)
#looks like this reads another char and repeats
80485b5: 76 79 jbe 8048630 <main+0x15c>
#else if 5<ebx
#then exit
80485b7: e9 c1 00 00 00 jmp 804867d <main+0x1a9>

#if the char is a comma, we’re done manipulating edi
#break 3
80485bc: 51 push %ecx
80485bd: 8d 4d ef lea 0xffffffef(%ebp),%ecx
80485c0: 6a 01 push $0×1
80485c2: 51 push %ecx
80485c3: 57 push %edi
#read another byte from the file
80485c4: e8 47 fe ff ff call 8048410 <read@plt>
80485c9: 83 c4 10 add $0×10,%esp
80485cc: 48 dec %eax
#eax is now equal to zero if it’s not the end of the file
#if it is the end of the file then end the program
80485cd: 0f 85 aa 00 00 00 jne 804867d <main+0x1a9>

#eax should be the last char just read
80485d3: 0f b6 45 ef movzbl 0xffffffef(%ebp),%eax

#if esi is == to char just read (else exit)
80485d7: 39 f0 cmp %esi,%eax
80485d9: 0f 85 9e 00 00 00 jne 804867d <main+0x1a9>
#eax and ecx have something to do with getpuid(), probably
#pointers to the username
80485df: 8b 85 d8 ef ff ff mov 0xffffefd8(%ebp),%eax
80485e5: 8b 8d d4 ef ff ff mov 0xffffefd4(%ebp),%ecx
80485eb: 8b 10 mov (%eax),%edx
80485ed: 0f be 04 0a movsbl (%edx,%ecx,1),%eax

#if eax isequal to esi else exit
#eax is the current username letter
80485f1: 39 c6 cmp %eax,%esi
80485f3: 0f 85 84 00 00 00 jne 804867d <main+0x1a9>

80485f9: 41 inc %ecx
80485fa: 89 8d d4 ef ff ff mov %ecx,0xffffefd4(%ebp)

#if there are no more letters in the username
8048600: 80 3c 0a 00 cmpb $0×0,(%edx,%ecx,1)
8048604: 75 14 jne 804861a <main+0×146>
8048606: 52 push %edx
8048607: 8d 45 ef lea 0xffffffef(%ebp),%eax
804860a: 6a 01 push $0×1
804860c: 50 push %eax
804860d: 57 push %edi
804860e: e8 fd fd ff ff call 8048410 <read@plt>
#break 5
8048613: 83 c4 10 add $0×10,%esp
#break 4
8048616: 85 c0 test %eax,%eax
#break 2
#if eax is not equal to zero then end program
#if there are no more stuff in the file
8048618: 75 63 jne 804867d <main+0x1a9>

#else
804861a: ba 0a 00 00 00 mov $0xa,%edx
#break 3
804861f: 89 f0 mov %esi,%eax
8048621: 89 d1 mov %edx,%ecx
8048623: 31 d2 xor %edx,%edx
8048625: f7 f1 div %ecx
8048627: 89 c6 mov %eax,%esi
#edx ->0; ecx-> 10; eax ->esi
#esi = esi/10
8048629: eb 05 jmp 8048630 <main+0x15c>
#this may needs to be hit at least once every five times so the loop can repeat
804862b: bb 01 00 00 00 mov $0×1,%ebx
8048630: 8a 85 df ef ff ff mov 0xffffefdf(%ebp),%al
8048636: 88 85 d3 ef ff ff mov %al,0xffffefd3(%ebp)

—->
#jump here if the file exists
804863c: 50 push %eax
804863d: 8d 45 ef lea 0xffffffef(%ebp),%eax
#%eax is 0xbfdcd6a7 which is the buffer where the previous byte will be stored

#the read function
8048640: 6a 01 push $0×1
8048642: 50 push %eax

#eax is 0xbfa20767
8048643: 57 push %edi
8048644: e8 c7 fd ff ff call 8048410 <read@plt>
#read(file discriptor(6),0xbfa20767 or whatever(buffer),size(1 byte))
#number of bytes read is returned, 0 indicates eof

#eax is now the size of the number of bytes returned
#the value read is stored in (hopefully 0xbfdcd6a7)

8048649: 83 c4 10 add $0×10,%esp
804864c: 48 dec %eax
#numbytes read–, it should probably be zero at this point, and ZF is set

#if the file was empty or an eof marker was received continue
#else je is true, so jump on up
804864d: 0f 84 12 ff ff ff je 8048565 <main+0×91>

8048653: 8b 95 d8 ef ff ff mov 0xffffefd8(%ebp),%edx
8048659: 8b 8d d4 ef ff ff mov 0xffffefd4(%ebp),%ecx
804865f: 8b 02 mov (%edx),%eax
8048661: 31 d2 xor %edx,%edx
8048663: 80 3c 08 00 cmpb $0×0,(%eax,%ecx,1)
8048667: 75 19 jne 8048682 <main+0x1ae>
8048669: 83 ec 0c sub $0xc,%esp
804866c: 68 8a 87 04 08 push $0x804878a
8048671: e8 3a fd ff ff call 80483b0 <puts@plt>
8048676: 31 d2 xor %edx,%edx
8048678: 83 c4 10 add $0×10,%esp
804867b: eb 05 jmp 8048682 <main+0x1ae>
804867d: ba 02 00 00 00 mov $0×2,%edx

8048682: 8d 65 f0 lea 0xfffffff0(%ebp),%esp
8048685: 89 d0 mov %edx,%eax
8048687: 59 pop %ecx
8048688: 5b pop %ebx
8048689: 5e pop %esi
804868a: 5f pop %edi
804868b: c9 leave
804868c: 8d 61 fc lea 0xfffffffc(%ecx),%esp
804868f: c3 ret

After figuring out how it works, it really is easy to make a keygen. I wrote this in C++

#include <fstream>
#include <iostream>
#include <string>
using namespace std;

#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>


int main()
{
string username = getlogin();
cout<<username<<endl;
string key = “”;

int esi = 0;
for(int i=0; i<username.length(); i++)
{
int num = 0;
num += esi;
for(;num < username[i]-20; num+=20){
key += “****+-”;}
for(;num < username[i]-5; num+=5){
key += “*”;}
for(;num < username[i]; num++){
key += “+”;}

key = key + “,” + username[i];

esi = username[i]/10;
}

string writefile = “/home/” + username + “/.key_” + username;
//cout<<writefile<<endl;
ofstream fout;
fout.open(writefile.c_str());
fout<<key;
fout.close();

return 0;
}

Conclusions

This was a lot of fun! I think I’ll try to do one of these from crackmes.de more often. A little hard though. This was rated at 1 which is the easiest, and I think 1 should be easier. cm1 definitely took me a long time to figure out. I’d say this is more like a 3 (the ranking is out of 10).

Boot Ubuntu into Runlevel 3

I have servers that I like to boot into runlevel 3 with. Here’s how in Ubuntu from a regular desktop install.

I guess I should clarify.

First of all, I kind of accidently installed gnome on my firewall. I got the alternate cd and just clicked on install – not install text only. I don’t really care – I have the disk space. I could reinstall, but nah. What I don’t want to do is run in level 2. I’d rather boot into text since 99% of the time that’s all I’ll use. If I ever do need gnome or X I can always startx.

Naturally, I went to /etc/inittab (which didn’t exist before) and created the following:

id:3:initdefault

This was fairly stupid of me. Since Edgy, Ubuntu has been into this upstart thing which actually kicks ass. The above did actually work (it boots into level 3) but a better way would have been to modify the script in /etc/events.d/ which handles all the old inittab stuff.

So we’re done, right? No way jose, cuz in ubuntu, runlevel 3 happens to look a heck of a lot like runlevel 2.

So go into /etc/rc3.d/. These are links to your sysV /etc/init.d/ scripts. You should see a lot of Sxxblah links. The ‘S’ stands for start and the number stands for the order things are run. To not start these, you probably want to move some things (like gdm, X, your nvidia drivers) to start with a ‘K’ for kill. The number can probably stay the same, since that’s just the order that things are executed in. So something like:

# mv S10xserver-xorg-input-wacom K10xserver-xorg-input-wacom
# mv S13gdm K13gdm
# mv S20nvidia-kernel K20nvidia-kernel

You can probably move a lot of these. I’m probably not using bluetooth on my server, for example. If you ever do happen to need it, just change to runlevel 2 with init.

Next time you boot, you should now get a text login. Good job.

Follow

Get every new post delivered to your Inbox.