强网杯 2025
0x00 前言
一般路人,划水这一块
0x01 题解
flag-market
通过对oflag的输入能覆盖掉上面的You are so parsimonious,这样就有bss段上的格式化字符串了。接着利用这个把exit.got该成main,就制造出了无限次格式化字符串漏洞。
原本程序的逻辑是碰到{之后结束循环,然后通过 memset 来清除残留的 flag ,我们直接把 memset.got 写成 puts.plt即可
from pwn import * |
bph
沙箱
line CODE JT JF K |
一开始的输入token没有b'\x00'截断,所以可以泄露栈上内容
堆地址,libc,栈地址3选1。申请堆块这里
这里因为没对输入的size做检测,所以有一个任意地址写,但只能写一个0,找到了很久之前的一个非常相似的题目whctf2017-stackoverflow 参考他的做法,往stdin的_IO_buf_base的结尾写上0,下一次输入就能从_IO_read_end 开始写入内容,再次修改_IO_buf_base 和 _IO_buf_end的内容,即可实现libc的任意地址写。接着劫持stdout,需要libc.sym['setcontext'] + 61 来栈迁移,libc.sym['setcontext'] + 294 恢复rbp
from pwn import * |
ez_stack (复现版)
沙箱,没有o啊
line CODE JT JF K |
就一个read,我还以为是srop或者ret2dl
显然两个都不是,后者依赖pop rdi等gadget,前者依赖syscall以及对rax的设置。所以得学学各种奇淫技巧。
首要目标是泄露libc,只要能泄露出来就什么都好说。先是把栈迁移到bss段,利用_start 函数,它会把libc_start_main 压入栈。就是做格式化字符串经常用的那个,实际上这个东西附近有个这样的函数__libc_print_version
稍微跟进一下jmp的地方
有个系统调用号为1的syscall,没错就是write。所以只要想办法到这里,寄存器的设置自然不成问题,都在来之前的read之前就设置好了,我们可以通过这个__libc_print_version来泄露libc
至于怎么把libc start main写成libc_print_version,我们需要下面这三段gadget来配合,第一个gadget主要作用在第一行以及最后一行ret,因为我们可以控制rbp,所以也可以控制edx
2.同理
3.把dh加给bl(rbx的最低一个字节)
只要让 libc_start_main 的地址+ 0xe0即可
泄露完libc了,该怎么把flag读出来呢?
注意到题目给的dockerfile
FROM ubuntu:22.04 |
flag是来自环境变量的,实际上程序启动的时候环境变量会留在栈上,类似这样
所以我们可以通过environ 来泄露栈地址,然后泄露对应环境变量地址的地址,然后再泄露就行
from pwn import * |
本地的效果大概是这样
远程没打印出flag的话就调调地址什么的,懒得弄了



