一命速通 Python 基础 的文章封面
返回渗透 Wiki
起点与方法 渗透 Wiki

一命速通 Python 基础

面向渗透学习的 Python 基础路线:语法、文件处理、网络请求、目录扫描、端口扫描、Shell 基础和脚本工程化。

Python 基础不是为了背语法,而是为了在渗透测试里把重复动作自动化。

你会遇到很多碎活:整理 URL、处理字典、批量请求、检查端口、拼接路径、读写结果、对扫描输出做二次筛选、把一堆命令回显整理成报告。Python 的价值就在这里:它是安全学习里最趁手的胶水语言。

这篇文章的目标很明确:

  • 先掌握 Python 的核心语法。
  • 再把语法放进渗透场景里理解。
  • 最后写几个小工具:目录扫描器、端口扫描器、Shell/TTY 基础示例。

所有示例都默认只用于自己的靶场、授权环境和本地实验。不要对未授权目标做扫描、爆破或批量请求。

先跑起来

确认 Python 版本:

python --version
python3 --version

Windows 上可能是 python,Linux 和 macOS 上常见是 python3

新建实验目录:

mkdir python-pentest-basic
cd python-pentest-basic

写一个 main.py

message = "Python is a pentest glue language"
print(message)

运行:

python main.py

渗透学习里不要一上来追求复杂工具。先让脚本跑起来,再慢慢给它加输入、输出、错误处理和结果保存。

变量和类型

变量就是给数据起名字。

target = "http://127.0.0.1:8000"
port = 8000
is_alive = True
timeout = 3.0

print(target, port, is_alive, timeout)

常见类型:

类型示例渗透场景
str"admin"URL、路径、参数、响应文本
int80端口、状态码、计数
float2.5超时时间、延迟
boolTrue是否命中、是否可访问
NoneNone暂无结果、空值

写安全脚本时,你经常会把扫描目标、状态码、响应长度、标题、匹配结果放进变量里。

url = "http://127.0.0.1:8000/admin"
status_code = 200
content_length = 1024
found = status_code == 200 and content_length > 0

print(f"{url=} {status_code=} {found=}")

f-string 是 Python 最舒服的字符串格式化方式。以后输出结果、拼接报告、打印调试信息都会用到。

字符串处理

安全脚本里有大量文本处理:URL、路径、请求参数、字典、命令输出。

raw = "  /Admin/Login  "

path = raw.strip().lower()
print(path)

常用方法:

text = "GET /admin/login HTTP/1.1"

print(text.startswith("GET"))
print("/admin" in text)
print(text.split(" "))
print(text.replace("HTTP/1.1", "").strip())

把路径拼成 URL:

base_url = "http://127.0.0.1:8000"
path = "/admin"

url = f"{base_url.rstrip('/')}/{path.lstrip('/')}"
print(url)

rstrip('/')lstrip('/') 很实用。它能避免拼出这种路径:

http://127.0.0.1:8000//admin

分支判断

分支用来根据结果做决定。

status_code = 403

if status_code == 200:
    print("可能存在可访问页面")
elif status_code in (301, 302):
    print("发生跳转,需要继续观察 Location")
elif status_code == 403:
    print("存在路径,但当前无权限")
elif status_code == 404:
    print("路径不存在")
else:
    print("需要人工判断")

在渗透场景里,状态码不能机械理解。

  • 200 不一定有漏洞,只代表请求成功。
  • 403 反而可能说明路径存在。
  • 302 可能跳到登录页。
  • 500 可能是错误处理暴露,也可能只是服务异常。

Python 脚本只负责帮你筛线索,真正的判断还要靠上下文。

循环

循环用来批量处理目标。

paths = ["/admin", "/login", "/backup.zip"]

for path in paths:
    print(f"checking {path}")

带序号:

paths = ["/admin", "/login", "/backup.zip"]

for index, path in enumerate(paths, start=1):
    print(index, path)

过滤空行:

raw_paths = ["/admin", "", "   ", "/login"]

for raw_path in raw_paths:
    path = raw_path.strip()
    if not path:
        continue
    print(path)

continue 的意思是跳过本轮循环。处理字典文件时特别常用,因为字典里经常会有空行、注释和无效内容。

列表、字典、集合

列表适合存一组有顺序的数据。

ports = [22, 80, 443, 8080]
ports.append(3306)

print(ports[0])
print(ports[-1])

字典适合存结构化结果。

result = {
    "url": "http://127.0.0.1:8000/admin",
    "status": 403,
    "length": 512,
    "note": "路径可能存在,但需要权限",
}

print(result["url"])
print(result.get("title", "no title"))

集合适合去重。

raw_hosts = ["example.com", "test.local", "example.com"]
hosts = set(raw_hosts)

print(hosts)

渗透脚本常见的数据结构:

findings = [
    {"url": "http://127.0.0.1:8000/admin", "status": 403},
    {"url": "http://127.0.0.1:8000/login", "status": 200},
]

for item in findings:
    print(item["url"], item["status"])

你可以把它理解成一个轻量版结果表。

函数

函数用来把一段动作命名。

def build_url(base_url: str, path: str) -> str:
    return f"{base_url.rstrip('/')}/{path.lstrip('/')}"


print(build_url("http://127.0.0.1:8000/", "/admin"))

为什么要写函数?

  • 代码复用。
  • 逻辑更清楚。
  • 后续更容易测试。
  • 出问题时更容易定位。

例如判断状态码:

def classify_status(status_code: int) -> str:
    if status_code == 200:
        return "open"
    if status_code in (301, 302):
        return "redirect"
    if status_code == 403:
        return "forbidden"
    if status_code == 404:
        return "missing"
    return "unknown"


print(classify_status(403))

函数名要写得像一句话。以后你回来看代码,不用每行都读,就知道它大概在干什么。

文件读写

渗透里经常要读字典、读目标列表、写扫描结果。

准备一个 paths.txt

/admin
/login
/backup.zip

读取:

from pathlib import Path

wordlist = Path("paths.txt")
paths = []

for line in wordlist.read_text(encoding="utf-8").splitlines():
    path = line.strip()
    if path:
        paths.append(path)

print(paths)

写结果:

from pathlib import Path

results = [
    "http://127.0.0.1:8000/admin 403",
    "http://127.0.0.1:8000/login 200",
]

Path("result.txt").write_text("\n".join(results), encoding="utf-8")

pathlib 比手搓字符串路径舒服很多。写脚本时优先用它。

异常处理

网络请求会失败,文件可能不存在,编码可能不对,目标可能超时。

不要让脚本一遇到错误就崩掉。

from pathlib import Path

try:
    content = Path("missing.txt").read_text(encoding="utf-8")
except FileNotFoundError:
    print("文件不存在")
else:
    print(content)

网络场景里更常见:

import socket

try:
    socket.create_connection(("127.0.0.1", 8000), timeout=2)
except TimeoutError:
    print("连接超时")
except OSError as error:
    print(f"连接失败:{error}")
else:
    print("端口可连接")

异常处理不是为了吞掉错误,而是为了让脚本能继续跑,并把失败原因记录下来。

简单匹配

复杂正则可以后面再学。刚开始写渗透脚本,先把简单匹配用熟。

url = "http://127.0.0.1:8000/admin/login"

print("/admin" in url)
print(url.startswith("http://"))
print(url.endswith("/login"))

筛选路径:

paths = ["/", "/admin", "/login", "/api/debug", "/assets/app.js"]

for path in paths:
    if "admin" in path or path.startswith("/api"):
        print(path)

很多小脚本不需要一开始就上正则。instartswithendswithsplitstrip 已经能解决大量入门问题。

JSON 和 CSV

脚本输出最好是可继续处理的数据。

JSON 适合结构化结果:

import json

findings = [
    {"url": "http://127.0.0.1:8000/admin", "status": 403, "length": 512},
    {"url": "http://127.0.0.1:8000/login", "status": 200, "length": 2048},
]

with open("findings.json", "w", encoding="utf-8") as file:
    json.dump(findings, file, ensure_ascii=False, indent=2)

CSV 适合表格:

import csv

findings = [
    {"url": "http://127.0.0.1:8000/admin", "status": 403, "length": 512},
    {"url": "http://127.0.0.1:8000/login", "status": 200, "length": 2048},
]

with open("findings.csv", "w", encoding="utf-8", newline="") as file:
    writer = csv.DictWriter(file, fieldnames=["url", "status", "length"])
    writer.writeheader()
    writer.writerows(findings)

如果你以后要把结果导入表格、报告或其他脚本,JSON 和 CSV 会很省心。

网络请求

Python 标准库能发请求,但安全脚本里常用 requests

安装:

python -m pip install requests

最小请求:

import requests

url = "http://127.0.0.1:8000"
response = requests.get(url, timeout=5)

print(response.status_code)
print(len(response.text))
print(response.headers.get("Server"))

加上 User-Agent:

import requests

headers = {
    "User-Agent": "Neuroblue-Lab/1.0",
}

response = requests.get("http://127.0.0.1:8000", headers=headers, timeout=5)
print(response.status_code)

渗透测试里发请求要克制:

  • 设置超时。
  • 控制频率。
  • 记录目标范围。
  • 不要对未授权站点批量跑脚本。

实战案例一:简单目录扫描器

场景:你在本地靶场或授权测试环境里,想快速检查一些常见路径是否存在。

准备 paths.txt

/
/admin
/login
/backup.zip
/api/debug

安装依赖:

python -m pip install requests

dir_scan.py

import time
from pathlib import Path

import requests

base_url = "http://127.0.0.1:8000"
interesting_status = {200, 301, 302, 401, 403}


def build_url(base_url: str, path: str) -> str:
    return f"{base_url.rstrip('/')}/{path.lstrip('/')}"


for line in Path("paths.txt").read_text(encoding="utf-8").splitlines():
    path = line.strip()
    if not path or path.startswith("#"):
        continue

    url = build_url(base_url, path)

    try:
        response = requests.get(url, timeout=3, allow_redirects=False)
    except requests.RequestException as error:
        print(f"[ERR] {url} {error}")
        continue

    status = response.status_code
    length = len(response.content)

    if status in interesting_status:
        print(f"[{status}] {url} len={length}")

    time.sleep(0.1)

运行:

python dir_scan.py

这个脚本很简单,但已经足够说明 Python 在渗透里的价值:

  • 读字典。
  • 拼 URL。
  • 发请求。
  • 过滤有价值的状态码。
  • 控制请求频率。

它不是替代 dirsearchffufgobuster 这类成熟工具,而是帮你理解目录扫描的底层逻辑。

实战案例二:简单端口扫描器

场景:你在自己的靶场或授权内网实验环境里,想快速检查几个端口是否能连通。

port_scan.py

import socket

host = "127.0.0.1"
ports = [22, 80, 443, 8000, 8080]


def is_open(host: str, port: int, timeout: float = 1.5) -> bool:
    try:
        with socket.create_connection((host, port), timeout=timeout):
            return True
    except OSError:
        return False


for port in ports:
    state = "open" if is_open(host, port) else "closed"
    print(f"{host}:{port} {state}")

运行:

python port_scan.py

这不是替代 nmap,只是让你理解端口扫描的本质:尝试建立 TCP 连接,成功就是可连,失败就记录失败。

真正做扫描时优先使用成熟工具,并且确认授权范围。

实战案例三:Python Shell 与 TTY 基础

Shell 的本质是一个进程,它接收输入,执行命令,再把输出返回给你。Python 经常出现在两个位置:

  • 在本地或靶场里启动一个命令解释器,帮助你理解 Shell 是什么。
  • 在已经授权、已经拿到基础命令执行的环境里,改善交互体验。

先看本地示例。Linux / macOS 上可以写:

import subprocess

subprocess.run(["/bin/sh"])

Windows 上可以写:

import subprocess

subprocess.run(["cmd.exe"])

运行:

python shell_demo.py

如果你在授权靶场里已经获得了一个很简陋的 Linux Shell,经常会看到交互不好用:方向键异常、没有补全、不能正常清屏。此时可以尝试用 Python 申请一个伪终端:

python3 -c 'import pty; pty.spawn("/bin/bash")'

这一步不是“黑魔法”,只是让当前交互更像正常终端。这里不展开反连 Shell、免杀、持久化之类内容。初学阶段先理解输入、输出、进程和终端,比背一堆 payload 更重要。

命令行参数

脚本写死变量只能自己临时用。加上命令行参数后,它才像一个工具。

最小 argparse 示例:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("target")
parser.add_argument("-p", "--port", type=int, default=80)
args = parser.parse_args()

print(args.target)
print(args.port)

运行:

python tool.py 127.0.0.1 -p 8080

以后你写任何安全脚本,都建议先想清楚:

  • 目标从哪里输入?
  • 字典从哪里输入?
  • 结果输出到哪里?
  • 超时和延迟怎么控制?
  • 是否需要安静模式或详细模式?

这比代码本身更像“工具设计”。

子进程调用

有时你需要从 Python 调用系统命令,比如在授权环境里调用 ping 或读取工具输出。

import subprocess

result = subprocess.run(
    ["ping", "-c", "1", "127.0.0.1"],
    text=True,
    capture_output=True,
)

print(result.returncode)
print(result.stdout)
print(result.stderr)

注意两点:

  • 优先传列表,不要拼接字符串。
  • 不要把用户输入直接塞进 shell。

危险写法:

import subprocess

user_input = "127.0.0.1; whoami"
subprocess.run(f"ping -c 1 {user_input}", shell=True)

这类写法本身就是命令注入的风险来源。学 Python 的同时,也是在理解漏洞是怎么被写出来的。

虚拟环境

不同项目依赖不同,建议每个项目一个虚拟环境。

python -m venv .venv

Windows:

.venv\Scripts\activate

Linux / macOS:

source .venv/bin/activate

安装依赖:

python -m pip install requests
python -m pip freeze > requirements.txt

以后别人复现你的脚本:

python -m pip install -r requirements.txt

写安全工具不要把全局 Python 环境弄乱。虚拟环境是最基本的工程卫生。

渗透脚本的基本规范

一个能长期使用的小脚本,至少应该做到:

  • 有清楚的授权边界说明。
  • 参数不要写死。
  • 设置网络超时。
  • 失败要记录原因。
  • 输出结构化结果。
  • 不要默认高并发。
  • 不要默认破坏性操作。
  • 代码里不要硬编码真实密码、Token、Cookie。

你可以把脚本分成四层:

输入参数 -> 执行动作 -> 整理结果 -> 输出文件

只要这四层清楚,脚本就不会很快变成一团乱麻。

学习路线

建议顺序:

  1. 变量、类型、分支、循环。
  2. 列表、字典、集合、字符串。
  3. 函数、异常、文件读写。
  4. pathlibjsoncsv、简单字符串匹配。
  5. requestssocketargparse
  6. 写三个小工具:目录扫描器、端口扫描器、Shell/TTY 基础示例。
  7. 把自己的靶场复盘过程自动化一小块。

不要为了学 Python 而学 Python。最好每学一个知识点,就问它能不能解决一个真实小问题。

比如:

  • 字符串:清洗 URL 和路径。
  • 列表:保存目标集合。
  • 字典:保存扫描结果。
  • 文件:读取字典和输出报告。
  • 简单匹配:筛选路径、URL 和响应片段。
  • 网络请求:做授权范围内的批量验证。
  • 参数解析:把脚本变成命令行工具。

练习任务

给自己几个小任务:

  1. 写一个脚本,读取 targets.txt,去重后输出。
  2. 写一个脚本,读取 URL 列表,只保留 https:// 开头的。
  3. 写一个脚本,检查本地靶场的 /admin/login/api 是否存在。
  4. 写一个脚本,检查 127.0.0.1228080008080 是否可连。
  5. 给目录扫描器加 CSV 输出。
  6. 给目录扫描器加 --only-status 200,403 参数。
  7. 把脚本执行结果整理成 Markdown 表格。

这些任务很小,但它们会把 Python 基础和渗透工作流慢慢接起来。

结尾

Python 在渗透学习里的意义不是“我会一门编程语言”,而是“我能把重复劳动变成工具”。

先把基础语法用在真实场景里:读文件、发请求、解析文本、保存结果。等这些动作稳定之后,再去学并发、异步、爬虫框架、漏洞利用脚本和更复杂的安全工具开发,才不会漂在空中。

一开始写得慢没关系。你每写出一个能解决真实问题的小脚本,都会让后面的学习轻一点。