CTF-PWN暑期刷题记录
放一波暑假的刷题记录
以下题目来源:ctfshow pwn入门
☁️ ✴︎📩 🩹
Test_your_nc 01~05
01 pwn0
🚩flag:ctfshow{f3fb4340-bbae-4ef1-ac35-e7668329b410}
💡hint:签到
题目描述:用户名为 ctfshow 密码 为 123456 请使用 ssh软件连接
虚拟机镜像 用户名为ctfshow 密码是ctfshow
给了一个shell。
获取flag:
ls / |
02 pwn1
🚩flag:ctfshow{4dd710b0-23c0-4a5e-bcb8-909ec3aa4e81}
💡hint:基础分析
使用nc pwn.challenge.ctf.show 28107连接上靶机,即可拿到flag。
程序直接执行了后门函数
03 pwn2
🚩flag:ctfshow{5c2dc31f-35e8-4df8-ba49-155c8529e01b}
💡hint:基础分析
题目描述:给你一个shell,这次需要你自己去获得flag
环境中提示用户手动执行后门函数获得flag。
cat /ctfshow_flag |
04 pwn03
🚩flag:ctfshow{d0593e57-6c08-4666-b6f3-d35718fb6f59}
💡hint:基础分析
题目描述:哪一个函数才能读取flag?

system()是 Linux 系统调用,可以直接执行 shell 命令。cat /ctfshow_flag会读取/ctfshow_flag文件的内容并输出到终端。
05 pwn4
🚩flag:ctfshow{0b4bf2e8-ec76-480f-8ffb-453184a07ea9}
💡hint:反编译 自动化路径求解
题目描述:或许需要先得到某个神秘字符

现在需要按照规范的流程来分析。
先checksec:
checksec是一个用于检查二进制文件安全防护机制的工具,它能快速分析 ELF 可执行文件或共享库的安全属性,包括:
- NX(堆栈不可执行):防止 shellcode 在栈上运行;
- PIE(地址随机化):使代码段加载地址随机化,增加漏洞利用难度;
- RELRO(重定位只读):保护 GOT 表不被篡改(Full RELRO 最安全);
- Canary(栈保护):检测栈溢出攻击;
- Fortify:对危险函数(如
strcpy)进行编译时加固。
运行checksec --file=程序名即可查看防护状态,帮助评估漏洞利用的可行性。

可以看到是64位保护全开。
使用ida进行查看

大致流程如下:
首先将字符串 “CTFshowPWN” 复制到 s1 变量中。
接着,使用 puts 函数输出字符串 “find the secret !”。
紧接着,通过 __isoc99_scanf 函数从用户输入中读取一个字符串到 s2 变量中。
最后,通过 strcmp 函数比较 s1 和 s2 的内容是否相同。如果相同,则调用 execve_func 函 数。
跟进execve_func():

可以看到将字符串 “/bin/sh” 赋值给 argv 变量。
然后,将 v2 和 v3 初始化为 0。
通过调用 execve 系统调用来执行 /bin/sh shell。
因此这里的execve_func也就是我们所谓的一个后门函数了
所以根据主函数,我们需要输入的字符是:
CTFshowPWN
然后按照常规流程来获取flag即可。

06~35为前置基础系列
06 pwn5
🚩flag:ctfshow{0b4bf2e8-ec76-480f-8ffb-453184a07ea9}
💡hint:计组原理
题目描述:运行此文件,将得到的字符串以ctfshow{xxxxx}提交。
如:运行文件后 输出的内容为 Hello_World
提交的flag值为:ctfshow{Hello_World}
注意添加可执行权限:

添加外壳之后是ctfshow{Welcome_to_CTFshow_PWN}
07 pwn6
🚩flag:ctfshow{114514}
💡hint:计组原理
题目描述:立即寻址方式结束后eax寄存器的值为?
首先执行文件,结果跟上一题一样。

我们再来看源代码。
section .data |
看懂注释,就是11+114504-1=114514
即flag:ctfshow{114514}
08 pwn7
🚩flag:ctfshow{0x36D}
💡hint:计组原理
题目描述:寄存器寻址方式结束后edx寄存器的值为?
题目文件相比还是和上一个题目一样。看到上一个题目的代码:
; 寄存器寻址方式 |
故flag:ctfshow{0x36D}
内存中一般大写(?)
09 pwn8
🚩flag:ctfshow{0x80490E8}
💡hint:计组原理
题目描述:直接寻址方式结束后ecx寄存器的值为?
section .data |
注意这里是msg的地址,不是msg本身。
ida中查看得到:0x80490E8
相关资料:
x86 汇编寻址方式详解
在你的代码中,展示了多种 x86 汇编寻址方式。以下是每种方式的详细说明和示例:
1. 立即寻址(Immediate Addressing)
特点:操作数直接编码在指令中(常数)。
用途:用于赋值或算术运算。
示例:
mov eax, 11 ; eax = 11(直接赋值) |
2. 寄存器寻址(Register Addressing)
特点:操作数是寄存器。
用途:快速寄存器间数据传递。
示例:
mov ebx, 0x36d ; ebx = 0x36d |
3. 直接寻址(Direct Addressing)
特点:操作数是内存地址(符号或绝对地址)。
用途:访问全局变量或静态数据。
示例:
mov ecx, msg ; ecx = msg的地址(如 0x8049000) |
4. 寄存器间接寻址(Register Indirect Addressing)
特点:操作数是寄存器指向的内存地址。
用途:通过指针访问内存。
示例:
mov esi, msg ; esi = msg的地址 |
⚠️ 注意:[] 表示解引用(类似 C 语言的 *)。
5. 寄存器相对寻址(Register Relative Addressing)
特点:寄存器 + 偏移量访问内存。
用途:访问数组或结构体成员。
示例:
mov ecx, msg ; ecx = msg的地址 |
相当于 eax = msg[4](假设 msg 是字符数组)。
6. 基址变址寻址(Base-Index Addressing)
特点:基址寄存器 + 变址寄存器 * 比例因子。
用途:高效访问多维数组或复杂数据结构。
示例:
mov ecx, msg ; ecx = 基址(msg地址) |
相当于 eax = msg[edx*2](假设每个元素占2字节)。
7. 相对基址变址寻址(Relative Base-Index Addressing)
特点:基址寄存器 + 变址寄存器 * 比例因子 + 偏移量。
用途:灵活访问内存中的复杂结构。
示例:
mov ecx, msg ; ecx = 基址 |
相当于 eax = msg[8 + edx*2 - 6]。
8. 系统调用中的寻址应用
你的代码最后使用了 直接寻址 传递字符串地址:
mov eax, 4 ; sys_write 系统调用 |
这里 msg 是直接寻址,而 edx 是立即寻址。
寻址方式对比表
| 寻址方式 | 语法示例 | 等效 C 语言 | 典型用途 |
|---|---|---|---|
| 立即寻址 | mov eax, 11 |
eax = 11; |
常数赋值 |
| 寄存器寻址 | mov edx, ebx |
edx = ebx; |
寄存器间数据传递 |
| 直接寻址 | mov ecx, msg |
ecx = &msg; |
访问全局变量 |
| 寄存器间接寻址 | mov eax, [esi] |
eax = *esi; |
指针解引用 |
| 寄存器相对寻址 | mov eax, [ecx+4] |
eax = *(ecx + 4); |
数组/结构体访问 |
| 基址变址寻址 | mov eax, [ecx+edx*2] |
eax = *(ecx + edx*2); |
多维数组访问 |
| 相对基址变址寻址 | mov eax, [ecx+edx*2-6] |
eax = *(ecx + edx*2 - 6); |
复杂数据结构访问 |
为什么需要多种寻址方式?
- 灵活性:适应不同数据结构(如数组、链表、结构体)。
- 效率:基址变址寻址可减少指令数量(如循环遍历数组)。
- 可读性:直接映射高级语言的内存访问模式。
常见问题
Q1: 为什么 mov eax, [msg] 和 mov eax, msg 结果不同?
mov eax, msg:将msg的地址存入eax。mov eax, [msg]:将msg地址处的 值 存入eax(解引用)。
Q2: 如何选择寻址方式?
- 访问简单变量 → 直接寻址
- 通过指针访问 → 寄存器间接寻址
- 数组遍历 → 基址变址寻址
掌握这些寻址方式是理解汇编和逆向工程的基础! 🚀
10 pwn9
🚩flag:ctfshow{0x636C6557}
💡hint:计组原理 汇编基础
题目描述:寄存器间接寻址方式结束后eax寄存器的值为?
文件片段:
msg db "Welcome_to_CTFshow_PWN", 0 |



11 pwn10
🚩flag:ctfshow{ome_to_CTFshow_PWN}
💡hint:计组原理 汇编基础
题目描述:寄存器相对寻址方式结束后eax寄存器的值为?
section .data |
; 寄存器相对寻址方式 |
这里将msg的地址(0x80490E8)+ 4 处所执向的地址的值赋给eax
0x80490E8+4=0x80490EC
ctfshow{ome_to_CTFshow_PWN}
12 pwn11
🚩flag:ctfshow{ome_to_CTFshow_PWN}
💡hint:计组原理 汇编基础
基址变址寻址方式结束后的eax寄存器的值为?
section .data |
; 基址变址寻址方式 |
这里将msg的地址(0x80490E8) 复制给ecx
将2复制给edx,edx=2
eax=ecx+2*edx
=0x80490E8+4
=0x80490EC
ctfshow{ome_to_CTFshow_PWN}
13 pwn12
🚩flag:ctfshow{ome_to_CTFshow_PWN}
💡hint:计组原理 汇编基础
相对基址变址寻址方式结束后eax寄存器的值为?
section .data |
; 相对基址变址寻址方式 |
ecx=0x80490E8
edx=1
ecx=ecx+8
=0x80490E8+8
=0x80490F0
eax=ecx+edx*2-6
=0x80490F0-4
=0x80490EC
ctfshow{ome_to_CTFshow_PWN}
14 pwn13
🚩flag:ctfshow{hOw_t0_us3_GCC?}
💡hint:二进制文件基础
gcc -o flag flag.c |

15 pwn14
🚩flag:ctfshow{01000011_01010100_01000110_01110011_01101000_01101111_01110111_00001010}
💡hint:二进制文件基础
请你阅读以下源码,给定key为”CTFshow”,编译运行即可获得flag
|
所以需要先写入key文件,然后编译运行文件。

16 pwn15
🚩flag:ctfshow{@ss3mb1y_1s_3@sy}
💡hint:二进制文件基础
编译汇编代码到可执行文件,即可拿到flag
这段代码是一个使用 x86 汇编语言编写的程序,用于在标准输出上打印一串特定格式的字符串。 要将这段代码编译为可执行文件,使用汇编器和链接器进行以下步骤:
使用以下命令将汇编代码编译为目标文件:
nasm -f elf flag.asm -o flag.o |
使用以下命令将目标文件链接为可执行文件:
ld -m elf_i386 -o flag flag.o |
运行此文件:
./flag |
17 pwn16
🚩flag:ctfshow{daniuniuda}
💡hint:二进制文件基础
使用gcc将其编译为可执行文件
.s 文件是汇编语言源文件的一种常见扩展名。它包含了使用汇编语言编写的程序代码。汇编语言是一种低级编程语言,用于直接操作计算机的指令集架构。 .s 文件通常由汇编器(Assembler)处
理,将其转换为可执行文件或目标文件。
可以使用 gcc 命令直接编译汇编语言源文件(.s文件)并将其链接为可执行文件。 gcc 命令具有适用于多种语言的编译器驱动程序功能,它可以根据输入文件的扩展名自动选择适当的编译器和链接器。
gcc -o flag flag.s |
18 pwn17
🚩flag:ctfshow{8f284eaf-6fc1-4a4c-92ee-eca8a266d137}
💡hint:二进制文件基础 Linux基础命令的拼接
有些命令好像有点不一样?
不要一直等,可能那样永远也等不到flag
;cat /ctf* |
为什么Payload能工作?
- 分号
;的作用:
- 在Linux中,
;用于分隔多个命令,无论前一个命令是否成功。 - 例如:
command1 ; command2会依次执行command1和command2。
- 通配符
\*的作用:
cat /ctf*会匹配所有以/ctf开头的文件(如/ctfshow_flag)。
- 输入长度限制(10字节):
;cat /ctf*(9字节)和;/bin/sh(8字节)均满足长度限制。
19 pwn18
🚩flag:ctfshow{02c7e220-25e0-45bf-a1e1-5f58604d9cc9}
💡hint:二进制文件基础 Linux基础命令的拼接 逆向分析
仔细看看源码,或许有惊喜
假作真时真亦假,真作假时假亦真
int __fastcall main(int argc, const char **argv, const char **envp) |
假作真时真亦假,输入9是假,那么就是真flag。
20 pwn19
🚩flag:ctfshow{37005897-483d-4287-aff4-8f5ab17ba35a}
💡hint:二进制文件基础 逆向分析
关闭了输出流,一定是最安全的吗?
打开环境前先分析反编译源码:
int __fastcall main(int argc, const char **argv, const char **envp) |
我们可以使用了 exec 函数来执行 sh命令,并使用 1>&0来进行输出重定向。这个命令将标准输出重定向到标准输入,实际上就是将命令的输出发送到后续命令的输入。
具体来说, 1>&0 中的 1 表示标准输出, 0 表示标准输入。通过将标准输出重定向到标准输入,可以实现将命令的输出作为后续命令的输入。这样可以在执行 sh命令后,进入一个交互式的Shell环境, 可以在该环境中执行命令并与用户进行交互。
也可以直接exec cat /ctf* 1>&0 将 cat /ctf*命令的输出发送到标准输入,实际上就是将命令的输 出再次输出到屏幕上。
21 pwn20
🚩flag:ctfshow{1_1_0x600f18_0x600f28}
💡hint:Linux安全机制
提交ctfshow{【.got表与.got.plt是否可写(可写为1,不可写为0)】,【.got的地址】,【.got.plt的地址】}
例如 .got可写.got.plt表可写其地址为0x400820 0x8208820
最终flag为ctfshow{1_1_0x400820_0x8208820}
若某个表不存在,则无需写其对应地址
如不存在.got.plt表,则最终flag值为ctfshow{1_0_0x400820}
RELRO(Relocation Read-Only)是一种ELF二进制保护机制,用于防止攻击者篡改动态链接过程中的关键内存区域(如GOT表)。它分为三种模式:
- No RELRO:GOT/PLT完全可写,极易被攻击;
- Partial RELRO:GOT部分只读(延迟绑定仍可写),中等防护;
- Full RELRO:启动时立即解析所有符号并完全锁定GOT为只读,提供最强保护但会略微增加启动时间。
使用checksec可查看保护状态,安全场景建议开启Full RELRO(编译选项:-Wl,-z,relro -Wl,-z,now)。
先checksec一下:
checksec --file=pwn |

没有RELRO保护,故.got和.got.plt都存在。
查看表的地址:
readelf -S pwn |
故:ctfshow{1_1_0x600f18_0x600f28}
22 pwn21
🚩flag:ctfshow{0_1_0x600ff0_0x601000}
💡hint:Linux安全机制
checksec --file=pwn |
部分防护。

查看表的地址:
readelf -S pwn |

ctfshow{0_1_0x600ff0_0x601000}
23 pwn22
🚩flag:ctfshow{0_0_0x600fc0}
💡hint:Linux安全机制
checksec --file=pwn |

Full RELRO:启动时立即解析所有符号并完全锁定GOT为只读,提供最强保护但会略微增加启动时间。
readelf -S pwn |
在写.got表的时候就会抛出异常,而.got.plt不存在 故flag:ctfshow{0_0_0x600fc0}
24 pwn23
🚩flag:ctfshow{d9d1e78b-1dab-44d8-a705-6c8346051804}
💡hint:缓冲区溢出漏洞
用户名为 ctfshow 密码 为 123456 请使用 ssh软件连接
ssh ctfshow@题目地址 -p题目端口号 |
int __cdecl main(int argc, const char **argv, const char **envp) |
首先,程序尝试打开名为”/ctfshow_flag”的文件,并将文件指针赋值给 stream变量。如果打开文件失败(文件不存在或无法访问),程序输出错误消息并终止。
如果成功打开文件,程序使用 fgets 函数从文件中读取最多64个字符到名为 flag 的缓冲区。
程序输出提示消息: “How to input?”。
如果程序运行时传入了命令行参数( argc大于1),则调用 ctfshow 函数,并将第一个命令 行参数作为参数传递给该函数。
ctfshow 函数很简单,它接受一个字符串参数 src ,并使用 strcpy 函数将该字符串复制到名 为 dest 的缓冲区中。然后,它返回指向 dest缓冲区的指针。
这里仅仅是为了演示当未开启Canary保护时,输入字符串长度超过了 dest 缓冲区的大小,这可能导致缓冲区溢出漏洞。

25~29 为栈溢出
25 pwn35
🚩flag:ctfshow{d5502ece-4e3a-45ec-9e5c-2c7f9ca78a09}
💡hint:栈溢出
正式开始栈溢出了,先来一个最最最最简单的吧
ctfshow{d5502ece-4e3a-45ec-9e5c-2c7f9ca78a09}

checksec --file=pwn |

32位程序开启NX,部分开启RELRO 32位
IDA查看main函数:
int __cdecl main(int argc, const char **argv, const char **envp) |
打开”/ctfshow_flag” 文件,读取其中的内容,并根据命令行参数决定打印不同的消息。如果命令行 参数个数小于等于 1 ,则提示用户重试,否则调用 ctfshow 函数处理用户输入的命令行参数,并输出相关消息。
跟进ctfshow函数:
char *__cdecl ctfshow(char *src) |
char dest: 声明一个名为 dest 的字符变量。 return strcpy(&dest, src): 使用 strcpy 函 数将 src 字符串复制到 dest 字符数组中,并返回指向 dest 的指针。 strcpy函数这个函数是一个典 型的可以用来利用溢出的函数。所以我们可以在这里进行栈溢出。
注意到signal(11, (__sighandler_t)sigsegv_handler);函数
当发生 对存储的无效访问时,会把stderr打印输出,即将flag的值打印输出
那么我们直接输入超长数据就会溢出,程序就会崩溃进而打印出flag
26 pwn36
🚩flag:ctfshow{8197d563-2f34-41d7-94b0-a2ae5fe8d0f0}
💡hint:栈溢出
存在后门函数,如何利用?
先来checksec
checksec --file=pwn |

32位保护仅部分开启RELRO,同时注意到有可读可写可执行的段
ida查看main函数:
int __cdecl main(int argc, const char **argv, const char **envp) |
跟进ctfshow函数:
char *ctfshow() |
使用 gets 函数从标准输入读取一行字符串,并将其存储在 s 数组中。然后,返回指向 s 的指针。 gets 函数是非常不安全的,容易导致缓冲区溢出漏洞。因为它无法限制输入的长度,可能会超出 s 数组的容量,导致覆盖栈上的其他数据或执行任意代码。这也是明显的栈溢出漏洞,s距ebp仅有0x28,而gets不限制输入长度。
from pwn import * |
运行脚本即可得到flag。
27 pwn37
🚩flag:ctfshow{00b37148-e0b5-422c-9a92-0fbe1310dc78}
💡hint:栈溢出
32位的 system(“/bin/sh”) 后门函数给你
from pwn import * |
溢出覆盖返回地址再输入后门函数地址即可控制程序的执行流程
28 pwn38
🚩flag:ctfshow{3fc5ea88-fa41-45c3-b57c-861618063a48}
💡hint:栈溢出
64位的 system(“/bin/sh”) 后门函数给你
from pwn import * |
思路和上一个题目类似。
ls |

29 pwn39
🚩flag:ctfshow{4a15a146-0370-4287-b0b1-a047a5779ae1}
💡hint:栈溢出
32位的 system(); “/bin/sh”
漏洞函数ctfshow()。
from pwn import * |

30~33为格式化字符
30 pwn91
🚩flag:ctfshow{2d4ff392-5150-475d-a7c1-443e845a4b4d}
💡hint:格式化字符串
开始格式化字符串了,先来个简单的吧

32位关闭PIE,部分开启RELRO
ida查看main函数
int __cdecl main(int argc, const char **argv, const char **envp) |
可以看到当daniu = 6 的时候即可获的一个shell
跟进ctfshow函数:
unsigned int ctfshow() |
可以看到这里的ptintf(s)明显的存在格式化字符串漏洞
daniu的地址为: 0x804B038

可以看到daniu在bss段,测一下格式化字符串的偏移,可以看到偏移为7
from pwn import * |
通过shell获取flag。
31 pwn92
🚩flag:ctfshow{4f5e810f-72b6-447f-8a30-a74822609048}
💡hint:格式化字符串
可能上一题没太看懂?来看下基础吧
ida分析函数,得知使用用户输入的格式化字符串将 s 输出,如果需要获取flag,仅仅需要使用 %s 输出flag字符串即可获取flag
from pwn import * |
32 pwn93
🚩flag:ctfshow{cc14b02c-70f9-461e-aa67-96516a0834a0}
💡hint:格式化字符串
emmm,再来一道基础原理?
int __fastcall main(int argc, const char **argv, const char **envp) |
跟进菜单:
int menu() |
获取flag的地方在case 7 exit0()函数,跟进后发现其实是一个后门函数:
unsigned __int64 exit0() |
也就是说读取用户输入的时候输入7即可获得flag。
from pwn import * |
33 pwn94
🚩flag:ctfshow{cc14b02c-70f9-461e-aa67-96516a0834a0}
💡hint:格式化字符串
好了,你已经学会1+1=2了,接下来继续加油吧
int __cdecl __noreturn main(int argc, const char **argv, const char **envp) |
跟进ctfshow():
void __noreturn ctfshow() |
进入一个循环,然后其中有明显的格式化字符串漏洞, 看到程序中有system函数,还是可以用格式化字符串漏洞任意写的,将printf_got 指针指向的地址改为 system_plt,再printf(),就相当于 system()了,如果我们再发送 “/bin/sh\x00”,作为其参数,就能getshell了。
from pwn import * |
34~37为堆管理器
34 pwn135
🚩flag:ctfshow{6f51b075-d479-4231-aada-f294823c75fc}
💡hint:堆管理器
malloc、calloc、realloc 的作用
| 函数 | 作用 | 关键区别 |
|---|---|---|
malloc(size) |
分配 size 字节的未初始化内存,内容随机(可能含敏感数据)。 |
最快,但需手动初始化。 |
calloc(n, size) |
分配 n * size 字节的初始化为零的内存(适合数组/结构体)。 |
比 malloc 慢,但安全。 |
realloc(ptr, new_size) |
调整已分配内存块的大小(可扩大/缩小),原数据保留(新部分未初始化)。 | 若 ptr 为 NULL,等价于 malloc。 |
一句话总结:malloc 分配未初始化内存,calloc 分配并清零内存,realloc 调整已有内存块大小。
本题输入4拿到flag。
35 pwn136
🚩flag:ctfshow{bc477bcf-c4ce-4e20-870e-72aa123b7703}
💡hint:堆管理器
如何释放堆?
free堆块的原理
当调用
free(ptr)时,堆管理器会执行以下操作:
- 检查指针有效性:确保
ptr是由malloc/calloc/realloc分配的合法地址,防止双重释放(Double Free)或非法释放。- 标记为未使用:将该内存块标记为
free状态(通常通过修改块头部的元数据,如size和in_use标志位)。- 合并空闲块:若相邻内存块也是空闲的,合并它们以减少碎片(
coalescing)。- 加入空闲链表:将释放的块插入
free list(如glibc的bin),供后续malloc重用,可能触发brk/mmap调整堆空间。关键点:
free不会立即归还内存给操作系统,而是由堆管理器缓存复用,存在 Use-After-Free (UAF) 漏洞风险。
输入4获得flag。
36 pwn137
🚩flag:ctfshow{31e71893-ad61-424b-85d7-c3e01878746d}
💡hint:堆管理器
sbrk and brk example
sbrk()和brk()是用于管理堆内存的系统调用,**brk()直接设置堆的结束地址(break指针)到指定位置,从而扩展或收缩堆空间;sbrk()则是相对调整,通过增量(正数扩展、负数收缩)移动break指针**,并返回调整前的地址。两者共同维护堆的动态内存分配,malloc等库函数底层依赖它们操作内存,但过度碎片化可能导致性能问题。
37 pwn138
🚩flag:ctfshow{3c16a2a8-a843-4cae-9eab-511d82ed8f09}
💡hint:堆管理器
Private anonymous mapping example
当用户申请内存过大时,ptmalloc2会选择通过mmap()函数创建匿名映射段供用户使用,并通过unmmap()函数进行回收。
当用户申请的内存超过
mmap_threshold(默认 128KB,可通过mallopt调整)时,ptmalloc2会绕过堆区管理,直接调用mmap()创建独立的匿名内存映射段(无文件关联,权限为可读写),该段与堆区隔离,减少碎片化影响;释放时调用munmap()立即将内存归还操作系统,避免堆缓存带来的延迟或内存泄漏风险。这种机制适合大块内存分配,但频繁使用可能引发缺页中断开销,且地址随机化(ASLR)会降低局部性效率。
38 pwn24
🚩flag:ctfshow{f615f3c6-b99a-4590-ac95-1f7baf5b974b}
💡hint:Linux安全机制
你可以使用pwntools的shellcraft模块来进行攻击
pwntools的shellcraft模块是一个用于快速生成各种架构(如x86、ARM、MIPS等)的shellcode的工具库,它提供了预定义的汇编代码片段(如执行
/bin/sh、系统调用、文件读写等),用户可通过简单调用(如shellcraft.sh())自动适配目标平台,无需手动编写底层汇编。其核心作用是简化漏洞利用开发,尤其在栈溢出、ROP攻击等场景中,能一键生成可靠的逆向Shell、反弹Shell或权限提升代码,支持动态调整参数(如寄存器、系统调用号),并自动处理字节对齐和NULL字节过滤,大幅提升PWN题或真实漏洞利用的效率与可移植性。
from pwn import * |
32位仅部分开启RELRO保护
可以看到存在一个RWX权限的段,即可读可写可执行的段
直接IDA查看main函数发现有一个ctfshow函数,但是无法跟进 那么就直接看汇编了:
- 函数开始时进行一些栈操作,保存寄存器的值。
- 调用 __x86_get_pc_thunk_bx 函数,获取当前的指令位置并存储在 ebx 寄存器中。
- 分配 0x84 字节的空间用于缓冲区,存储用户输入的数据。
- 调用 read 函数,从标准输入读取数据,并存储到缓冲区。
- 调用 puts 函数,将缓冲区的内容打印到标准输出。
- 通过调用 call eax 指令,以 eax 寄存器的值作为函数指针,跳转到缓冲区中存储的地址执 行。
- 之后是一些清理工作和函数返回的准备操作。
这题题目提示了可以使用 pwntools的shellcraft模块进行攻击
shellcraft 模块是 pwntools 库中的一个子模块,用于生成各种不同体系结构的 Shellcode。 Shellcode 是一段以二进制形式编写的代码,用于利用软件漏洞、执行特定操作或获取系统权限。
shellcraft 模块提供了一系列函数和方法,用于生成特定体系结构下的 Shellcode。
from pwn import * # 导入 pwntools 库 |
39 pwn25
🚩flag:ctfshow{3a6ba94e-fcd7-4427-be46-28c0402728a7}
💡hint:Linux安全机制
开启NX保护,或许可以试试ret2libc
NX保护(No-eXecute)
NX保护是一种内存保护机制,由CPU和操作系统共同实现,通过将数据段(如栈、堆)标记为不可执行(
READ/WRITE但无EXECUTE权限),阻止攻击者在这些区域直接执行恶意代码(如shellcode)。其核心目的是防御栈溢出等漏洞利用技术,迫使攻击者转向更复杂的利用方式(如ROP)。现代编译器默认开启NX(通过-z noexecstack选项),可用checksec工具检测程序是否启用该保护。
ret2libc(Return-to-libc)
ret2libc是一种绕过NX保护的漏洞利用技术,通过覆盖返回地址跳转到libc库中的函数(如
system、execve),而非直接执行shellcode。攻击步骤通常为:1) 泄露libc基地址(利用puts或printf);2) 计算目标函数(如system("/bin/sh"))的真实地址;3) 构造ROP链调用该函数。这种技术依赖程序动态链接libc的特性,无需注入代码,但需处理地址随机化(ASLR)和参数传递问题。
from pwn import * |
40 pwn 26
🚩flag:ctfshow{3a6ba94e-fcd7-4427-be46-28c0402728a7}
💡hint:Linux安全机制
设置好 ASLR 保护参数值即可获得flag
为确保flag正确,本题建议用提供虚拟机运行
提取码为: show
ASLR(Address Space Layout Randomization,地址空间布局随机化)是一种安全防护机制,由操作系统在程序运行时动态随机化内存布局(如栈、堆、libc库的基地址),使得攻击者难以预测关键数据或代码的绝对地址(如
system函数、/bin/sh字符串),从而阻止基于固定地址的漏洞利用(如栈溢出、ROP链)。ASLR通过增加攻击者定位目标的难度,有效防御大多数内存攻击,但若结合信息泄露漏洞(如格式化字符串、UAF)获取随机化后的地址,仍可能被绕过。现代操作系统(如Linux、Windows)默认全局开启ASLR,可通过cat /proc/sys/kernel/randomize_va_space查看配置(2表示完全启用)。
可以将该文件的值设置为以下几个选项之一来控制 ASLR 的行为:
- 0:关闭 ASLR,内存布局不随机化。
- 1:启用 ASLR,但只有堆和栈是随机化的。
- 2:启用完整的 ASLR,所有内存段(包括堆、栈、共享库等)都是随机化的。
使用命令设置 ASLR 的级别 :
echo "0" > /proc/sys/kernel/randomize_va_space |
flag is :ctfshow{0x400687_0x400560_0x603260_0x7ffff7fd64f0}