Remote Security Random Tips
News & Ads

Reverse Engineering Linux

black hat Linux application cracking. What for it is? Primarily the application run can be understund. Not all Linux software is free (Macintos ? :). That could be for someone a challange and he can “converte” the application on freeware.
This experience can be used on other systems (Windows). With this knowledge you will be able to precisely tune applications and gain control on new boxes. 

What will be our topic:
– decompilation
– static file analysis
– dynamic file analysis
– process tracing/patching
– ELF

Necessary knowledge:
– assembler AT&T / Intel
– C
– linux memory management

Difficulty level:
3/5 ###00

We will work with Damn Vulnerable Linux (DVLinux) Live distribution is for perfecting the knowledge of computer safety and is available in ZIP c. 1GB. The package includes even files *.vmx for VMware Player (virtualisation platform that is free for *nix even for windows). That will give us an uniformed environment with already prepared progrmas and tasks. Particulary we will look at 2 crackmes contained in DVL.

DVL is not necessary for when working with this article. Any linux with needed programs is sufficient. Crackmes can be downloaded separately.

We won’t need Xserver at all. So after login we will do shell modification.

$ splash -s
$ killall gpm
$ gpm -m /dev/mouse -t ps2

By this we will take away the boot splash screen and correct the mouse range. If you can’t get along without X, you can choose KDE or FluxBox. Generaly speking it is better to stay in text mode. You will realize this when runing DVL in virtual machine (it wil save the source).

Cyrex LinuxCrackme1

Easy crackme for beginners. Your first step in reverse engineering. Open the dir where the crackme is and run it.

$ cd /dvl/crackmes_package/level_01/Cyrex_LinuxCrackme1/
$ ./crackme
-[ Linux CrackMe (Level:2) by cyrex ]-
-[ TODO: You have to get the valid Password ]-
Enter Password: asd
-[ Ohhhh, your skills are bad try again later ]-

For this beginners crackme we can assume that the password has a static character saved in the application. We will look for static strings.

$ strings crackme

-[ Linux CrackMe (Level:2) by cyrex ]-
-[ TODO: You have to get the valid Password ]-
Enter Password:
47ghf6fh37fbgbgj
-[ Good, You’re ready to begin linux reversing ]-
-[ Ohhhh, your skills are bad try again later ]-

I think that “47ghf6fh37fbgbgj” is the password. And it realy is. That was easy but the reality is different. Lets do a small static analyse for verifying the password. After displaying the function “main” with minimal knowledge of assembler we can tell what is happening.

$ objdump -d crackme | grep “<main>”: -A 50
08048450 <main>:
8048450: 55 push %ebp
8048451: 89 e5 mov %esp,%ebp
8048453: 83 ec 28 sub $0×28,%esp
8048456: 83 c4 f4 add $0xfffffff4,%esp
8048459: 68 20 86 04 08 push $0×8048620
; it inserts “pointer” on stack
; on string “-[ Linux CrackMe (Level:2) by cyrex ]-”
804845e: e8 f9 fe ff ff call 804835c <printf@plt>
; and calls function printf that will write it out
; it goes on repeating other strings
8048463: 83 c4 10 add $0×10,%esp
8048466: 83 c4 f4 add $0xfffffff4,%esp
8048469: 68 60 86 04 08 push $0×8048660
; -[ TODO: You have to get the valid Password ]-
804846e: e8 e9 fe ff ff call 804835c <printf@plt>
8048473: 83 c4 10 add $0×10,%esp
8048476: 83 c4 f4 add $0xfffffff4,%esp
8048479: 68 90 86 04 08 push $0×8048690
; Enter Password:
804847e: e8 d9 fe ff ff call 804835c <printf@plt>
8048483: 83 c4 10 add $0×10,%esp
; creates space for 16 bit local variable
8048486: 83 c4 f8 add $0xfffffff8,%esp
8048489: 8d 45 e0 lea 0xffffffe0(%ebp),%eax
; in order to function scanf acquire parameters it is necessary to put them on stack in inverse order
804848c: 50 push %eax
; first the variable address where the input is saved
804848d: 68 a1 86 04 08 push $0x80486a1
; then string “%s” that serves as a format for reading
8048492: e8 95 fe ff ff call 804832c <scanf@plt>
; scanf(“%s”,(char *)eax)
; calling scanf and opens the value from input onto address in eax = 0xffffffe0(%ebp)
8048497: 83 c4 10 add $0×10,%esp
804849a: 83 c4 f8 add $0xfffffff8,%esp
; function strcmp calling is being prepared
804849d: 68 a4 86 04 08 push $0x80486a4
; on stack “pointer” on the right password
80484a2: 8d 45 e0 lea 0xffffffe0(%ebp),%eax
80484a5: 50 push %eax
; on stack “pointer” on opened value from stdin
80484a6: e8 71 fe ff ff call 804831c <strcmp@plt>
; strcmp((char *)eax,”47ghf6fh37fbgbgj”)
; control if input and password are matching
80484ab: 83 c4 10 add $0×10,%esp
80484ae: 89 c0 mov %eax,%eax
80484b0: 85 c0 test %eax,%eax
80484b2: 75 12 jne 80484c6 <main+0×76>
; further on we don’t need to know anything else because we went throu the whole verification and we know it
; index on password 0x80486a4
80484b4: 83 c4 f4 add $0xfffffff4,%esp

Simple evidence.

$ objdump -s crackme | grep -A 1 80486a0
80486a0 00257300 34376768 66366668 33376662 .%s.47ghf6fh37fb
80486b0 6762676a 00000000 00000000 00000000 gbgj…………

Ballmann CheckPasswd

We will use the crackme from DVL. Download version is a bit different.
http://www.datenterrorist.de/devel/check-password
A more difficult crackme.

$ cd /dvl/crackmes_package/level_01/Ballmann_checkpasswd/
$ ./check-password
I need a password!
$ ./check-password asd
Wrong password!

Now we know where to write the password. We have deduced that the password is being transfered as an argument. To show that it is realy true do the static analyse initialisation of the process.

$ objdump -d ./check-password | less

080484df <main>:
; int main(int argc, char *argv[])

; 0×8(%ebp) includes value of the argument argc funktion main.
; here is being checked if the program was run with some arguments
80484fb: 83 7d 08 01 cmpl $0×1,0×8(%ebp)
; if(1 != argc) go to password check otherwise continue
80484ff: 75 18 jne 8048519 <main+0x3a>
; will write a error message and ends
8048501: c7 04 24 6d 86 04 08 movl $0x804866d,(%esp)
8048508: e8 63 fe ff ff call 8048370 <puts@plt>
804850d: c7 04 24 01 00 00 00 movl $0×1,(%esp)
8048514: e8 77 fe ff ff call 8048390 <exit@plt>

Lets make it clear. It’s not a rule that 0×8(%ebp) contains the argc value. In this case it simply has it. Actually the initialization sections which have been inserted by compilator GCC are being called before fuction main. The very first section is called _start. All these sections are defined in the file header (ELF format).

Running the program goes as follows: file is loaded by the operating system in the memory, does a checking (what it si, whose it is etc.). If everything is OK it saves on the stack all necessary process related information including argument call. The first argument is always the process name but we are interested in arguments that are joining after the first one. In the memory it looks like this.

0×08048000
program (executable part)
data (strings,values etc.)
static variable
free space for malloc call, etc.
stack
arguments (including program name)
setting values
program name
NULL
0xBFFFFFFF

and a detail of bottom part of the graph

argc
argv[0]
argv[1]

argv[argc-1]
NULL
env[0]
env[1]

env[n]
NULL

Back to crackme. We know where to write the password. Now we have to find its value. Use the same procedure as already used at the first crackme – string print-out in the program.

$ strings ./check-password | less

djjk/aWmbQkO6
I need a password!
Wrong password!
Accepted password!
/bin/csh

It looks like the same situation as in the first crackme but it is different. We will do a dynamic analyse with the program ltrace which catches the dynamic calls.

$ ltrace ./check-password asd
__libc_start_main(0x80484df, 2, 0xbfec1cb4, 0x804856c, 0x80485bd <unfinished …>
strncpy(0xbfec1bdc, 0xbfec273b, 20, 0, 0) = 0xbfec1bdc
crypt(0xbfec1bdc, 0x804865c, 20, 0, 0) = 0xb7ed7120
puts(“Wrong password!”Wrong password!
) = 16
exit(1, 0, 0xbfec1c28, 0×8048587, 0xb7ea5ffc <unfinished …>
+++ exited (status 1) +++

Note strncpy and crypt calls. Strncpy is called with two indicators on stack (viz diagram). In the source code it probably looked like this:
strncpy(pass,argv[1],20);
Next is called the crypt with first argument same as mel strncpy and the second is the indicator on some string in the section data. Lets see on what it is roughly pointing to.

$ objdump -s ./check-password | grep 8048654
8048654 03000000 01000200 646a0064 6a6a6b2f ……..dj.djjk/

The calling looks about like this:
crypt(pass,”dj”);
You can try this function sideways and use the same arguments that we got from the analyse. Then you will find out that the string that the function crypt returns is very similar to the mysterious string djjk/aWmbQkO6. That could be the control hash.

Go throu the whole application only in assembleru and you will find out that there is nothing more to solve.

$ objdump -d ./check-password | less

In main there is argument control, at the right number the function check is called where the hash is counted for the password transfered by application argument and is compared with string
djjk/aWmbQkO6 in the section .data. If the hashes are consistent they run /bin/csh

The question is what’s behind the hash djjk/aWmbQkO6. It could be cracked by brute force method but that would take ages. Other possibility is to patch a file and replace a hash with another. This is OK but even better solution would be patching the application image in the memory.

“Loader” draws the application into the memory and after corecting it runs it. And the effect is the same and there is no damage. This method is applicable for shellcode injecting and other “good ideas”.

#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <string.h>

#define _BIN “check-password” /* name of crackme file */
#define _SALT “dj” /* addition to hash calculation */
#define _W 4 /* sizeof(long) that returns ptrace */
#define _PLEN 4 /* number of long that close hash */

int main(int argc, unsigned char *argv[]){

if(argc<2){
fprintf(stderr,”usage: %s passwordn”,argv[0]);
return 0;
}

int pid;
/* fork viz. `man fork` */
if((pid = fork()) == 0){
/*
set up procces tracing on yourself(child). with this when calling exec you get
signal SIGTRAP, to be able to change application data as a parent before the calling
procces runs with the help of exec
*/
ptrace(PTRACE_TRACEME, 0,NULL,NULL);
/* giving back control */
execl(_BIN, _BIN, argv[1], NULL);
} else {
int status;
/* wait for the child to change the status (gets SIGTRAP when calling exec) */
wait(&status);
/* if the child is not alive, something is wrong and that’s the end */
if(WIFEXITED(status))
return -1;
/* child received SIGTERM and we can trace it */
if(WIFSTOPPED(status)){
/* hash of the new password */
char * pass = (char *)crypt(argv[1],_SALT);
printf(“new password hash: %sn”,pass);
/* create a new block which replaces original hash */
char pload[_W*_PLEN];
strcpy(pload,pass);
/* two last values are strange and do not belonge to hash */
pload[(_W*_PLEN)-2] = 0×49;
pload[(_W*_PLEN)-1] = 0×20;
/* set up address and hash length */
long *p = (long *)pload, *addr = (long *)0x0804865f;
long data, limit = (long)(addr + _PLEN);
/* rewrite oroginal hash with the new one */
printf(“[i] run patchn”
“0xADDRESS ASCII HEXDUMP ASCII HEXDUMPn…n”);
while(addr < (long *)limit){
/* read data(long) from process(child) */
data = ptrace(PTRACE_PEEKDATA,pid, addr ,NULL);
printf(“%p: %4.4s %x < %4.4s %xn”,addr, &data, data, p,*p);
/* rewrites hash in child with new hash */
ptrace(PTRACE_POKEDATA, pid, addr, *p);
addr++; p++;
}
printf(“…n[i] starting child processnn”);
/* stop buging child */
ptrace(PTRACE_DETACH,pid,0,0);
/* and wait till it dies */
wait(NULL);
}
}

return(1);
}

has to be compiled with -lcrypt

$ gcc loader.c -o loader -lcrypt;
$ ./loader airdump
new password hash: djfwob3BbChAg
[i] run patch
0xADDRESS ASCII HEXDUMP ASCII HEXDUMP

0x804865f: djjk 6b6a6a64 < djfw 77666a64
0×8048663: /aWm 6d57612f < ob3B 4233626f
0×8048667: bQkO 4f6b5162 < bChA 41684362
0x804866b: 6 20490036 < g 20490067

[i] starting child process

Accepted password!
$

Similar Posts: