当前位置:网站首页>2022 Google CTF SEGFAULT LABYRINTH wp
2022 Google CTF SEGFAULT LABYRINTH wp
2022-07-06 17:31:00 【xyzmpv】
不要问我为什么双休来打CTF 问就是活该:)
著名的Google CTF。去年有看见一位师傅画了一整张纸的流程图做了一道逆向,今年来自己尝(自)试(虐)一下。
当然pwn是不可能pwn的,只能做道misc维持生活这样子。很幸运在第一天做出来了SEGFAULT LABYRINTH这道Misc,交题的排名也比较不错(当然第二天就摸了)。这里来分享一下我的wp。
逻辑分析
首先整个程序主体基本上只有一个main函数,逆向起来并不费劲。
一开始申请一个mmap块用来放指针,地址固定(这个地址最后会给你)。然后进入while循环。
程序的主体逻辑就在这个while(1)的循环里面。先读一个随机数,再取这个随机数第一字节的低4位并把高4位清零(实际上这个随机数真正用到的只有这低4位)。
每一个循环都会再申请16个mmap块并将其地址按顺序写入v5指向的空间。这个过程中会用随机数在0-16中间抽奖,只有抽中的那个块才有权限(当然也可能脸黑,抽到1或者2 权限只有3或6)。
第一次的v5是一开始申请的那个mmap块的地址,每一次循环都会将v5赋值为本次循环抽中的的那个有权限的块的地址(否则接下来没法继续写地址)
也就是说,整个过程相当于一个分叉树的生长过程,每一次挑选一个随机节点,在这个节点上生长出16个子节点,以此类推
仔细看看可以知道,涉及flag的部分在if(!--v4)这个判断里面,由于v4最初是10,所以实际上得跑到第10次循环才会进入逻辑,而此时已经初始化了16^10个节点(地址)。在这个分支中,会将flag读到最后一个有权限的节点处。

示意图如下所示:

然后,分配一块有RWX权限的段(并在段头放上定制的shellcode),输出Welcome to the Segfault Labyrinth,设置保护

再让你覆盖ptr[0]并读入长度为ptr[0] % (4096 - v16)字节的shellcode并执行
注意两点:
- 如前所述,shellcode的开头已经给定好了,而且总长度不得超过4096
- 执行shellcode时参数为
mmap_addr_1,也就是分叉树头结点地址
写死的shellcode开头段(特意留下了rdi)

沙盒保护

解题思路
本质上来说题目的要求就是遍历这个分叉树并找到最后一个节点,再将其打印出来。
看保护可以发现可以用的系统调用只有以上几个了。第一反应直接用shellcraft生成shellcode或写C再编译,抠下来PIC。
但实际调试还发现,不能使用栈否则会直接SEGFAULT(这应该也是题目名称的来源)。在这种情况下,基本不太可能通过以上两种方式实现,于是只能手搓shellcode了
一开始的想法是直接取空节点的值然后cmp 0,但调试中发现空节点的权限是0,直接取值会崩掉。后面有师傅提出来使用mmap去测试节点权限,结果也是会崩掉。
最后的解决办法非常简单粗暴——直接通过write遍历打印每个节点。对于非空节点,返回值rax是长度,而空节点返回值则为负数。通过控制write长度+cmp rax,长度+条件跳转的方法,就可以实现对这一分叉树的遍历。
最后搓出来的exp如下:
# -*- coding:utf-8 -*-
from pwn import *
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#p = process('./challenge')
p = remote('segfault-labyrinth.2022.ctfcompetition.com', 1337)
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222)
#e = ELF()
#gdb.attach(p,'''
#b* $rebase(0x14E8)
#c
#si 16
#si 5
#''')
#local_file =
#libc =
#elf = ELF(local_file)
se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
rl = lambda :p.recvline()
ru = lambda delims :p.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr))
payload=asm('mov r15,rdi') #保存头节点地址
payload+=asm('mov rsi,[rdi]') #取头节点指向的第一个地址
payload+=asm('mov rdi,1')
payload+=asm('mov rdx,0x40')
payload+=asm('mov rax,1')
payload+=asm('syscall') #构造write
payload+=asm('add r15,8') #默认不通过校验,头节点指针顺移
payload+=asm('mov rdi,r15')
payload+=asm('cmp rax,0x40')
payload+=asm('jnz $-37') #若不通过,则前期准备完成,直接跳回第二条汇编
payload+=asm('sub r15,8') #通过,先复位头节点指针,再迭代指针
payload+=asm('mov r15,[r15]')
payload+=asm('mov rdi,r15')
payload+=asm('jmp $-49')#返回第二条汇编,继续循环
sa('Welcome to the Segfault Labyrinth',p64(len(payload)))
se(payload)
p.interactive()
小结
- 这个办法自我感觉还是不错的,通过write的报错和返回值进行遍历判断,同时也直接能打印出flag,而且长度只有14条汇编
- pwntools汇编的向上跳转跟正常汇编器一样,需要先计算出前面的指令长度再写
- 一开始逆错了,以为
v5始终是开头给的那个mmap地址,调试了一下发现没法直接找到flag,而且内存有一堆mmap块,这才反应过来 - 感谢做题过程中参与头脑风暴的两位师傅,提供了很多宝贵的建议。第一次在这种级别比赛做出题而且交题的顺位比较不错,还是很开心的。
pwn是不可能pwn的,只能做点misc维持生活这样子
边栏推荐
- [牛客] [NOIP2015]跳石头
- Zabbix 5.0:通过LLD方式自动化监控阿里云RDS
- 【JVM调优实战100例】04——方法区调优实战(上)
- Provincial and urban level three coordinate boundary data CSV to JSON
- Fastdfs data migration operation record
- The MySQL database in Alibaba cloud was attacked, and finally the data was found
- Deep learning framework TF installation
- Eventbus source code analysis
- Implementation principle of waitgroup in golang
- Taro 小程序开启wxml代码压缩
猜你喜欢
![[牛客] B-完全平方数](/img/bd/0812b4fb1c4f6217ad5a0f3f3b8d5e.png)
[牛客] B-完全平方数

第六篇,STM32脉冲宽度调制(PWM)编程

界面控件DevExpress WinForms皮肤编辑器的这个补丁,你了解了吗?
![[case sharing] basic function configuration of network loop detection](/img/d8/a367c26b51d9dbaf53bf4fe2a13917.png)
[case sharing] basic function configuration of network loop detection
![[HFCTF2020]BabyUpload session解析引擎](/img/db/6003129bc16f943ad9868561a2d5dc.png)
[HFCTF2020]BabyUpload session解析引擎

Force buckle 1037 Effective boomerang

「精致店主理人」青年创业孵化营·首期顺德场圆满结束!

Part V: STM32 system timer and general timer programming

《安富莱嵌入式周报》第272期:2022.06.27--2022.07.03

JTAG debugging experience of arm bare board debugging
随机推荐
There is an error in the paddehub application
from .cv2 import * ImportError: libGL.so.1: cannot open shared object file: No such file or direc
Meet in the middle
Install Firefox browser on raspberry pie /arm device
「笔记」折半搜索(Meet in the Middle)
golang中的atomic,以及CAS操作
Asset security issues or constraints on the development of the encryption industry, risk control + compliance has become the key to breaking the platform
NEON优化:性能优化经验总结
LLDP兼容CDP功能配置
身体质量指数程序,入门写死的小程序项目
How to evaluate load balancing performance parameters?
Lldp compatible CDP function configuration
Deeply explore the compilation and pile insertion technology (IV. ASM exploration)
Supersocket 1.6 creates a simple socket server with message length in the header
ESP Arduino (IV) PWM waveform control output
Atomic in golang, and cas Operations
Dell筆記本周期性閃屏故障
Batch obtain the latitude coordinates of all administrative regions in China (to the county level)
Taro2.* 小程序配置分享微信朋友圈
第三方跳转网站 出现 405 Method Not Allowed