day1
pwn
verifmt
a powerful verifier bro,that’s the Verifmt
analysis
保护全开,glibc2.39环境,题目给了源码 | |
%后跟数字会直接报错,但是根据
*的方式来增加偏移,并且由于是无符号无法向前泄漏*是一个提取参数的占位符,利用合理的payload,我们就可以合理的占掉args的参数同时仍然保证控制%和读取的参数数量仍然在限定数字内
可以注入\0字符绕过verify,然后用hhn覆盖\0
问题是如果只用
%*进行参数弹出我们后面的r9 r10寄存器为nil,format限制的个数也会使得leak的地址是nil,因此我们需要%*c来占取掉6个参数,这样我们最后一个%p便可以打印出栈上地址指针
并且据此我们可以写出进行写入的函数write_byte(addr,val): | |
最后写入的时候我们通过泄漏出来的栈指针来指向到返回地址进行写入,因为有while所以有足够的写入机会,只要单字节一个个写入地址即可
exp
不知道为什么就本地docker不能通,明明libc拉的就是容器里的
| |
StackPrelude
一首前奏曲(Prelude),准备好迎接不可能的栈溢出挑战吧
analysis
对源码进行分析
| |
这一题的关键在于要如何在让远程在能够大量leak数据的情况下仍能继续进行交互,这里所建立的socket服务器一次只能处理一个请求,服务器会再接受到我们发送的数据以后然后send发送回来
可以简单看一下计算机网络
第一种想法是利用TCP协议的半关闭特性,我们在四次挥手的时候首先发送一个客户端的半闭FIN包,此时不是正常关闭连接使得服务器由于MSG_WAITALL返回0从而关闭cfd,但是这种条件下我们要关闭输入的包,即便能leak数据我们也无法继续交互因此只能pass了
第二种想法则是利用中断信号。这里使用OOB(OUT-OF-BAND),在send的时候使用urgent byte从而实现服务器端触发SIGURG信号,由于这是一个异步信号,会打断recv函数,而send依然会返回相同长度的数据并且我们仍然可以继续交互!
交互和exp
先讲讲题目如何进行交互调试
| |
以上是我的脚本,在本地调试的时候首先打开程序和pwndbg并建立一个io ,然后再打开一个新的进程进行连接和交互
| |
然后就可以在打开了pwndbg的接口进行交互了
调试在我们发送数据后打开recv的时候在第二步可以发现如下情况
socket使用fd 4作为固定的交互句柄,如果我们想要在打开shell后进行正常的靶机交互,此处我们还需要尝试使用dup2,将程序交互的fd句柄duplicate到stdout上(或者其他stdin stderr),否则得不到回显,进行的
system("/bin/sh")也会直接 | |
tips:
需要注意,当我们要与docker靶机进行交互的时候,不能使用回环地址lo而是要使用本机映射的docker内网IP交互,原因在于使用OOB
docker在映射端口的时候,通常在我们的机子上做了两件事:
- 设置
iptables规则:将流量从宿主机端口导向 Docker 桥接网络。 - 启动
docker-proxy进程:docker-proxy或类似的组件(如userland-proxy)在宿主机上运行,监听宿主机端口,并将流量转发到容器的内部 IP 和端口。 而如果我们启动回环地址lo,内核发现目标地址是127.0.0.1,可能会对 TCP 帧进行优化,跳过校验和等。
数据不会像普通流量一样经历应用->传输->网络->链路,而是在网络层被捕获。docker-proxy在处理回环流量不会完整维护TCP帧和Flags,因此导致OOB攻击在设置URG指针的时候由于基于TCP帧标志被优化而失败,触发不到recv的中断而泄漏数据失败,因此我们必须使用Docker的桥接网络接口
day2
pwn
Stack_Impromptu
The world impossible is not in my dictionary
analysis
保护全开首先审计源码
main.cpp:
| |
当有一个新连接cfd创建的时候,会正常调用pthread_create,并在使用完后就用pthread_detach将其回收
很明显,是一个多线程的挑战,我们不可能直接尝试在一个线程中leak任何内存的内容,关键在于server_main和server_read
| |
server_read有栈溢出并且会不断被调用,但是就像上一题,这一题可没有MSG_WAITALL来让我们使用中断信号leak信息了
pthread多线程,所有线程都会在一个共同的进程空间内运行,并共享全局变量、堆空间、文件描述符。每个线程仅拥有独立的栈(Stack)!
这道题我在比赛的时候完全没有思路,严格要求输入的格式和数据大小导致不知道如何实现泄漏,AI也只让我尝试能不能用数据去覆盖到其他线程的内存空间,而且多线程调试也没那么简单,主要是要考虑时序上的交互问题
幸运的是比赛后discord里看到个位解的一位师傅的hint:
那么swap fd具体要怎么做..?—>我们不是有pthread吗!
如果我们能通过多线程但是覆盖掉指定线程的fd,实现fd的劫持,那么是否能实现copy fd并重定向输出,最后实现内存信息的泄漏呢?
fd劫持
我们先尝试使用两个线程来查看是否真的可以劫持fd,创建两个线程
在线程栈2中查看栈底,实际空间如上图,我们完全有足够的空间去覆盖fd
[rbp-0x68]处的qword指针,再二次解析到存储的fd,所以我们实际要写入0x7c的0后最后覆盖掉fd如此一来,最后到close的时候实际关闭的则是进程1的fd了
堵塞的绕过
而因为我们实际关闭的是线程2,实际上再次分配的时候仍然是供给线程2的fd而不是复用的4 所以在尝试leak数据前必须要创建出4个线程,最后才能出现两个线程复用fd的情况
leak
这里需要使用到一个TCP重置攻击
的方法
由于我们再次获得到了fd=4,而实际上原本最开始创建的线程仍然没有关闭,因此此时会直接向对方发送一个RST包强制关闭连接,而此时第四个线程会再次接手fd=4(极短时间),相当于实现
ROP
之后要如何利用溢出进行ROP以及如何交互?想法其实类似上文:
- 创建线程,利用覆盖关闭掉一个fd
- 再次创建线程,此时我们便可以拿到我们需要的fd以及对应的线程控制权
- 通过标准输入输出进行交互,因此我们基本获取远程shell
exp
| |


















