周末打了下STCF,有一道wav的misc,尝试了wav隐写的各种方法都失败了,今天看了下wp,学到了一种叫sstv(慢扫描电视)的东西。
慢扫描电视(Slow-scan television)是业余无线电爱好者的一种主要图片传输方法,慢扫描电视通过无线电传输和接收单色或彩色静态图片。
简单点说,就是图片在发送的时候调制成一种哔哔的声音,接收的时候再将这种声音解调成图片。因此需要用到sstv相关的软件
PC:MMSSTV
android: robot36(好像只能接收不能发送)
有了两台设备,我们就可进行图片传输了,例如:从PC传一张“神奈川冲浪里”到手机上

可以看到图片成功传送,但是会受周围环境的影响,因此有一定的失真。
貌似有点扯远了,我们回到这道misc上来。题目文件是一段哔哔的音频,我们直接利用MMSSTV或者robot36进行接收,便可以得到flag

好像有点简单,且上述内容与百万混音都没啥关系,但是当我在搜索类似题目的时候,发现了这道题。题目源文件是一段音乐,具体参考wp
这里有意思的是需要利用干涉消音提取出sstv的信号,这里就要用到AU进行消音,于是顺势学习了一波AU。
1、打开AU,新建多轨工程,导入源文件和从网易云下载的原曲《My Little Pony Theme Song》
可以看到两段音轨非常相似
2、将原曲进行反相
3、播放,robot36接收

顺势学习了一波AU,刚好刚买的音箱也快到了,到时候可以借着连绵阴雨,抚琴一曲,再加上百万混音,岂不美哉!
]]>这道题是栈溢出与堆溢出的结合,利用sprintf函数进行溢出。
1 | ➜ spirit-away checksec ./spirited_away |
32位程序,只开启了NX
survay函数
1 | int survey() |
可以发现实现的是一个留言板的功能,该程序存在两个漏洞:
1 | 当cnt为三位数,例如100时 |
完整脚本
1 | from pwn import * |
这是一道组合溢出的题目,与常见的栈溢出不同,故记录一下。
1 | ➜ babystack checksec ./babystack |
保护全开,既然是栈溢出,那么泄露canary是必不可少的。
main函数
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
首先读取16字节的随机数到栈上,并赋值给bss段上qword_202020。接着进入while循环,函数结束时会判断buf和qword_202020的值。可以发现这里的canary保护和常规的有点不同,因此需要泄露buf的值。
下面进入循环:
login函数
1 | int __fastcall login(const char *a1) |
当is_login==0时会进入login函数,这里首先读取数据到栈上,然后和buf的值进行strncmp判断。这里有两种绕过方法:
1、利用‘\x00’进行截断,因为strlen碰上’\x00’ 和’\x0a’会产生阶段,因此可以直接绕过strncmp的判断
1 | login('\x00'+'a'*0x57) |
2、逐字节进行爆破
1 | def bf(length,s1 = ''): |
copy函数
1 | int __fastcall copy(char *a1) |
当is_login==1时,该函数会将当前栈上读入的数据strcpy到main函数的栈上。
这里的漏洞点在于copy函数和login函数的栈空间相同,而login函数可以读入0x7f个字节,这在copy到main函数栈上时将会产生溢出。
1、利用strncmp爆破出canary的值
2、利用copy函数布置好栈空间,利用strncmpbaopo出libc的地址
3、因为strcpy会有’\x00’阶段,因此无法使用ROP,需要计算出one_gadget一发入魂
4、利用copy函数进行溢出并将返回地址覆盖成one_gadget
完整脚本如下:
1 | from pwn import * |
结果如下

这道题涉及的利用的技巧主要是house of spirit和文件描述符重定向
hos利用场景是可写区域-不可写区域-可写区域,这时可以利用堆中的漏洞进行hos而使中间的不可写区域变得可写。方法是通过伪造堆块使其加入bins中
题目在后半部分关闭stdout和stderr,具体参考Linux反弹shell(一)文件描述符与重定向
除了PIE,其他保护全开。功能如下:
不可写区域为特定值,使用后关闭stdout和stderr1 | unsigned __int64 secret() |
首先是输入name和info,即两处可写区域
1 | .bss:0000000000602060 unk_602060 #name,大小0x20 |
思路如下:
在name和info处伪造好堆块
1 | pay = p64(0) + p64(0x51) |
利用double free造成fastbin attack将bss加入fastbinzhong
1 | secret(1) |
泄露libc,因为got表不可写,因此需要再次利用fastbin attack劫持malloc
1 | dele2() |
完整exp如下
1 | from pwn import * |
edit处存在堆溢出,因此可以overlapping打fastbin attack。add使用的是calloc,可以改写ismmap标志位从而泄露libc
具体方法如下:
ismmap = 11 | add(0x500)#5 |
之后是常规的overlapping打fastbin attack,然后劫持malloc_hook。
完整exp如下:
1 | from pwn import * |
格式化字符串泄露程序基址和libc基址,off by one打unlink
1 | from pwn import * |
比赛没时间打,赛后复现几道题目玩玩。
这道题禁用了write,需要用到栈迁移配合__libc_start_mian来获取syscall以及gadget的多次利用
checksec,No PIE、No canary
1 | [*] '/mnt/hgfs/shared/RCTF/pwn/no_write_attachment/no_write' |
查看程序
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
经典的栈溢出,但是根据题目似乎是禁用了一些函数
seccomp查看沙箱机制
1 | ➜ no_write_attachment seccomp-tools dump ./no_write |
发现只能使用open、read、exit
此时可以想到,将flag读到bss段上,然后逐个字节爆破。那么问题来了,程序中只有read函数,没有open函数。因此可以进行系统调用,但是程序中不存在syscall,这是就需要得到syscall的地址。
我们发现在调用__libc_start_main时会在栈上留下syscall附近的地址,因此首先可以通过栈迁移将栈迁移到已知地址,例如bss段。这里可以利用pop rbp;leave ret进行栈迁移
1 | read_got = elf.got['read'] |
结果如下:

接下需要调用__libc_start_main函数,需要说明的是:
此时__libc_start_main函数的返回值为第一个参数
1 | ##__libc_start_main |
结果如下:

接下来的思路如下:
然后是爆破flag,可以利用__libc_csu_init中的gadget
1 | .text:0000000000400750 loc_400750: ; CODE XREF: __libc_csu_init+54↓j |
具体思路如下:
完整exp如下
1 | from pwn import * |
这道题得难点在于栈帧的控制,需要不断地调试。
这道题存在多处漏洞,负数索引、乘法溢出、堆溢出、off by null。这里利用较为简单的负数索引以及乘法溢出来get shell
64位程序,保护全开。
1 | .data:0000000000004008 off_4008 dq offset off_4008 ; DATA XREF: sub_1260+1B↑r |
程序的所有索引都没有检查负数边界,因此可以直接溢出到0x4008处,索引为-5
隐藏功能6会调用malloc,且大小为0x50,add函数则使用calloc
隐藏功能7有32字节溢出
具体思路如下:
完整exp如下:
1 | from pwn import * |
可以利用乘法溢出修改money
使用mmap进行calloc时,不会进行memset,因此可以泄露libc
]]>题目:KOOBE: Towards Facilitating Exploit Generation of Kernel Out-Of-BoundsWrite Vulnerabilities
会议:USENIX Security ‘20 Summer Quarter Accepted Papers
链接:https://www.usenix.org/conference/usenixsecurity20/presentation/chen-weiteng
KOOBE:Kernel Out-Of-Bound Exploit(内核越界漏洞利用),本文研究的是内核堆内存的越界。
内核保护着操作系统的基础架构,不幸的是,一些内核例如linux内核是用C写的,由于C语言自身的属性会导致一些内存相关漏洞频发。
2006-2018:已修复的安全bug中70%与内存相关。攻击者可以利用这些漏洞进行提权进而获取整个系统的控制权。
syzbot:https://syzkaller.appspot.com/upstream,Linux内核fuzzing的bug显示。
2017.8-2018.9:1216个linux内核漏洞被 syzkaller 发现,平均每天3.42个。毫无疑问会给开发者带来很大的工作量。而这些漏洞有些可以很容易提权,有些则无足轻重,因此需要将这些分开,即给这些bug设定优先级。其中一个办法是利用PoC自动生成一般的内存漏洞利用脚本从而对这些可以利用的漏洞进行评估。当然,内核漏洞不止一种,可以分而治之,UAF漏洞的工作已经有了一定的成果(Fuze),本文研究的是OOB。
OOB:内核越界访问,本文研究的是内核堆内存
挑战:不同的OOB所表现的利用能力(Capability)不同。PoC不能展示所有的漏洞。
Capability: how far, how many, what value
1 | CVE-2016-6187 can overwrite only one single byte(off by one) |
内存属性:函数指针,结构体指针,具体的值等等
本文主要工作:给OOB漏洞分级,自动生成Expliot(AEG)
本文主要贡献:
AEG(Automatic exploit generation)在linux内核的应用具有挑战性。KOOBE专注于性能提取(capability ex-traction )和漏洞评估( exploitability evaluation),这是本文开发堆OOB漏洞的Exploit的关键步骤。给定PoC,触发一个或多个OOB访问,KOOBE生成Exploit从而达到指令指针(IP)劫持。
内核被常用的保护机制所保护。
KASLR:Kernel Address Space Layout Randomization(内核地址空间布局随机化)
SMEP: Supervisor Mode Execution Prevention,禁止内核执行用户空间代码
SMAP: Supervisor Mode Access Prevention,禁止内核访问用户空间
当我们劫持IP之后,这些都可以绕过。
ret2dir:Rethinking kernel isolation,可以绕过SMEP和SMAP
From collision to exploitation: Unleashing use-after-free vulnerabilitiesin linux kernel
KEPLER :无条件地自动将IP控制转化为任意代码执行。
内核OOB漏洞的危害性巨大,实际中内核OOB的研究是一项劳动密集型任务,手动分析花费很多精力还不一定奏效,接下来通过实例详细描述一下OOB漏洞。
CVE-2018-5703:Linux Kernel 4.14.0 ,简化如下:
1 | 1. struct Type1 { ...; }; |
漏洞点:line 12,使用KASAN发现
vulnerable object: 漏洞点
target object: OOB区域
漏洞形成: line 11 -> line 12
咋一看: 好像只能越界写0x08080000000000, 该地址既不属于内核空间也不属于用户空间
再一看: 如果sys_setsockopt()被调用,则可以写任意值(PoC中并未提及,限制漏洞的可利用性, 无公开Exploit)
实际情况: 该漏洞没有公开的exploit,大概是fuzzing发现了咋一看的bug就没有继续进行下去。再一看的漏洞正是本文的KOOBE发现的,并自动生成了可以利用的exploit, 下一章的第三节会详细讲述如何发现的。
exploit简化如下:
1 | 1. for (i = 0; i < N; i++) |
利用过程分为四步:

大部分的漏洞是Fuzzing发现,该漏洞的PoC可以破坏一些内存但是还没达到可利用的层次。因此,此时该漏洞的性能等级较低。KOOBE首先会根据PoC进行性能探测(计算当前性能和探测更高级的性能)。
Linux内核使用的是 slab/slub 分配器,根据size分配相匹配大小的chunk(相同的size所分配的chunk大小相同,例如type1和type3)。被释放的chunk会在堆cache中,因此为了是vulnerable object和target object相邻,首先通过堆喷将堆cache中的内存耗尽。见exploit line1-4。
通过前面的性能概要发现更高级的漏洞,并且布置好了堆内存。下面就需要选择攻击的目标并且写好payload,通常我们可以把攻击目标分成几种:
函数指针,见type3,可以直接控制程序流
数据指针,见type4,可以覆写结构体
非指针:需要具体讨论
1 | reference counter机制 |
这里选择type3,size同vulnerable object。由此看来,收集不同的target object是很有必要的。
例如:CVE-2016-6778
1 | void example1(size) |
明显的off by null漏洞,,这时候如果target object为函数指针就没无法像上面一样控制整个指针,这时候选择RC作为target object就比较合适,因为低字节清零一般会减少RC的值从而使该内存提前释放造成UAF。
在Linux内核中,可以找到2000多个潜在的target object。收集这些object将有助于自动生成exploit。
基于target object调整PoC进而合成exploit。在这个过程,,我们需要绕过高级检测来达到任意代码执行。
类似KASLR、SMAP、SMEP只会让攻击复杂化而不能完全防止攻击。下面简要介绍这三种防护的绕过:
KASLR:信息泄露
SMEP:ROP/JOP
SMAP:physmap spray,用户空间内存映射到physmap,内核可直接访问physmap
最后利用KEPLER把IP劫持转化成任意代码执行。
KEPLER: Facilitating control-flow hijacking primitive eval-uation for linux kernel vulnerabilities
we describe the overview of KOOBE, a novel framework to extract the capabilities of heap OOB-based vulnerabilities and assess their exploitability.

首先进行漏洞分析,利用符号跟踪(symbolic tracing)来总结PoC的性能(capability),然后利用一个或更多的target object来自动确定是否可以利用,如果不能,将触发性能探测(Capability Exploration)来发现新的PoC,然后对新PoC进行分析,直到找到可利用的target object或者timeout。最后利用PoC生成exploit。
给定PoC,KOOBE尝试发现漏洞点(OOB访问处)以及相应的漏洞对象,如下所示
1 | { “vuln_obj”: { |
KASAN: Kernel Address Sanitizer 的缩写,它是一个动态检测内存错误的工具,主要功能是检查内存越界访问和使用已释放的内存等问题。KASAN可以检测的内存异常包括:slab-out-of-bounds/user-after-free/stack-out-of-bounds/global-out-of-bounds等。基于影子内存(shadow memory)和红色区域(red zones)
缺陷:
KOOBE方案:符号追踪结合KASAN,例如: CVE-2017-7184
1 | void example2(i) |
将vul和i都进行符号化,分析符号表达式vul+i/8有可能大于sizeof(TYPE)并且i没有约束时可以确定这是一个漏洞点。
Capability:在本文中,capability表示OOB写的能力,为了量化它,本文做出以下定义:
E:符号执行引擎所支持的所有符号表达式
P:所有路径
Np:可以触发漏洞的路径
Tp={(offpi,lenpi,valpi)|i∈Np∧off,len,val∈E}:OOB可写集
off:how far
len:how many
val:what value
Tpi:OOB可写集的个例
for循环:抽象成一次OOB write
Cp={sizep,Tp,f(p)|sizep∈E}:路径p的性能
size:漏洞对象的大小,size的大小有关target object的个数
f(p)::执行时p的路径约束,例如:line 14 -> line 15
In the motivating example, the capability corresponding tothe original PoC can be expressed as:
Corig={sizeof(Type1),{(offsetof(Type2,option),8,0x08080000000000)},/0}(1)
while the complete capability should be:
Ccomp={sizeof(Type1),{(offsetof(Type2,option),8,val)},{val!=−1}}(2)
when ‘sys_setsockopt’ is invoked before triggering the vul-nerability point.
∀e1,e2∈E, e1<=e2 if e1 is identical to e2 or e1is a constant whose value can be taken in e2
∀p1,p2∈P, Tp1i<=Tp2i if offp1i<=offp2i ∧ lenp1i<=lenp2i ∧ valp1i<=valp2i
∀p1,p2∈P, Cp1<=Cp2 if sizep1<=sizep2 ∧ ∀i∈Np1 Tp1i<=Tp2i
因此:Corig < Ccomp
函数调用:例如memcpy,解决循环问题
直接访问
memcpy(a1,a2,a3)
a1:目的地址,提取出off
a2:源地址,提取出al
a3:长度,作为len
通常,一个漏洞在不同的出发路径上有不同的漏洞点,不同的漏洞点有不同的性能。甚至不同路径触发的相同漏洞点也有不同的性能(如示例)。一般的PoC只有一条触发路径,因此我们需要触发新的路径来扩展性能或者产生新的性能。本文提出一种 capability-guided fuzzing方案来探索新的性能。
Syzkaller :基于覆盖反馈,探索新路径而无法探索新性能
输入:PoC,Cp
方法:突变种子,在覆盖反馈的同时计算Cp,并利用Cp进行反馈(触发OOB)
重点:种子过滤。例如:当value任意时,其它只关于value的种子可以丢弃。
目标约束:target object可以利用的条件,例如:指针必须指向有效地址空间,target object的大小等于vulnerable object 如果利用堆风水(不是必须,为了稳定)。
然后把目标约束和Cp丢进约束求解器进行求解,无解则换下一个目标。具体过程如下:

示例的可利用性评估如下:

同一性能可以重复利用,如CVE-2017-7184,一个OOB可能有多个性能,解决方案-贪心算法,具体如下:

相关定义:

堆喷,堆风水
Syskaller:内核fuzz,主要用在性能探测和利用脚本生成
S2E:二进制符号执行框架,主要用在性能概要和可利用性评估
angr:二进制符号执行分析引擎,主要用在漏洞分析
细节如下:
利用QEMU模式的Syskaller和S2E来进行CGF,Syskaller可以监测内核的内部状态而进行non-crashing fuzzing,避免KASAN的警告来保证持续fuzz(跳过但是记录导致OOB的指令)。缺点是这样可能产生误报导致一些假阳性的漏洞点或者性能。
在可利用性评估中,我们主要考虑三个参数off,len,value并且将它们符号化。相较于符号索引来说,符号执行引擎对符号长度的支持不太友好。
解决方案:利用KLEE和Z3对长度进行迭代约束求解
1 | for i in [0, 10]: |
循环会将阻塞符号执行(fuzzification好像有利用这个方法来阻塞DTA),因为隐式数据流。
SAGE:循环引导和模式匹配
Angr:静态分析
1 | 1. void loop(n)//n = 64 |
处理方案:消除不必要的约束
由于内核的复杂性,复杂的路径约束通常导致约束求解器无法再timeout前完成求解。一些无关的约束可以被松懈或直接忽略。
总共收集2615个目标
数据集:7个CVE+10个Syzbot bug
Ubuntu 16.04 、16G RAM 、Intel(R) Core i7-7700K CPU @ 4.20GHz* 8.

EXP:19 :5,6个新增,其中4个非CVE

动态内存防护措施:KASAN,Valgrind,ASan,MSan等
解决方案 :结合KASAN和符号追踪(污点跟踪和内存探测的超集)
基于覆盖反馈:AFL,Syzkaller,Honggfuzz
结合静态和动态分析的覆盖反馈:Vuzzer
基于梯度下降搜索: Angora
类神经网络:Neuzz
输入状态:Redqueen
Revery: From proof-of-concept toexploitable
相关技术:符号执行以及混合符号执行
用户程序:
APEG:Automatic patch-based exploit genera-tion is possible: Techniques and implications
Automatic generation of control flowhijacking exploits for software vulnerabilities.
Modular synthesis of heap exploits:Windows heap management
Gollum: Modular and greybox exploit generation for heapoverflows in interpreters
Towardsautomated generation of exploitation primitives for webbrowsers
内核:
Unleash-ing use-before-initialization vulnerabilities in the linuxkernel using targeted stack spraying
Fuze: Towards facilitating exploitgeneration for kernel use-after-free vulnerabilities.
Q: Exploit hardening made easy:需要给定任意地址写或者IP劫持基元
Block oriented programming: Au-tomating data-only attacks
KEPLER: Facilitating control-flow hijacking primitive eval-uation for linux kernel vulnerabilities
Heaphopper: Bringing bounded model checking toheap implementation security
是时候学习一下内核pwn了,内核pwn涉及内核以及文件系统的编译,这些类容留到以后再讲,这里先通过几道例题直观感受一下内核pwn。
题目附件
1 | ➜ babyhacker tree -L 1 |
查看启动脚本starvm.sh
1 |
|
开启了kaslr、smep、smap,本地调试可以去掉timeout和添加-s
1 | ➜ babyhacker qemu-system-x86_64 --help |grep gdb |
提取vmlinux,利用extract-vmlinux
1 | ./extract-vmlinx babyhacker/bzImage > babyhacker/vmlinux |
提取文件系统
1 | ➜ babyhacker mkdir core |
查看init发现是空的,于是查看/etc/init.d/rcS
1 | ➜ core bat etc/init.d/rcS |
可以看到添加了内核模块babyhacker.ko,其中dmesg_restrict = 0表示可以直接查看/proc/kallsyms,kptr_restrict=0时,lsmod会直接打印内核地址
1 | ~ $ lsmod |
checksec vmlinux,raw_vmlinux_base = 0xffffffff81000000
1 | ➜ babyhacker checksec vmlinux |
checksec babyhacker.ko,开启了canary
1 | ➜ babyhacker checksec babyhacker.ko |
ida查看babyhacker.ko
查看fop结构体
1 | .data:0000000000000280 fops file_operations <offset __this_module, 0, 0, 0, 0, 0, 0, 0, \ |
发现只定义了babyhacker_ioctl
1 | __int64 __fastcall babyhacker_ioctl(file *file, unsigned int cmd, unsigned __int64 arg) |
64位传参顺序:rdi、rsi、rdx、rcx、r8、r9,然后是栈。这里的rdx1即ioctl的第三个参数
可以看到,当rdx1为负数时,buffersize = v5为rdx1的低两字节可以达到0xffff,于是造成了对栈的越界读写,我们可以泄露canary然后rop
首先把vmlinux的gadgets输出到文件
1 | ➜ babyhacker ROPgadget --binary ./vmlinux > gadgets |
查找想要的gadget
1 | ➜ babyhacker cat gadgets |grep ": pop rdx ; ret$" |
poc.c
1 |
|
然后编译打包
1 | ➜ babyhacker gcc poc.c -static -masm=intel -g -o poc |
进行调试
1 | ➜ babyhacker gdb ./vmlinux -q |
结果如下:

1 | // gcc exp.c -static -masm=intel -g -o exploit |
结果如下

1 | #include <string.h> |
结果如下:

近来很多比赛都有虚拟指令集pwn的题目,漏洞都是常规的漏洞,但是题目还算新颖,有一种计组做实验的感觉。
这类题目主要就是搞清楚指令集的作用,需要对字节、符号、移位等知识有非常清晰的认识,废话不多说,先来一道题目试试。
checksec
1 | [*] '/mnt/hgfs/shared/vmpwn/kindvm/kindvm' |
静态分析,程序流程如下
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
重点关注kindvm_setup()
1 | void *kindvm_setup() |
现在我们来看一下hint
1 | Input your name : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
hint1:name溢出即可,提示name有作用,但不是溢出
1 | ➜ kindvm socat tcp-l:9999,fork exec:./kindvm |
hint2:输入’\x09’,提示filename为”flag.txt”
1 | _DWORD *insn_add() |
因此可以先往reg[0]中写入负数,然后 add reg[0], reg[0]
1 | ➜ kindvm echo -e 'kangel\n\x07\x00\xff\xff\xff\xff\x04\x00\x00' |nc 127.0.0.1 9999 |
hint3:提示有整数溢出
我们现在来看一下指令退出后的函数func_farewell
1 | ssize_t func_farewell() |
banner.txt和name的地址都存放在堆上
1 | pwndbg> x/20wx kc - 8 |
因此 read name -> write it to banner.txt.即可
1 | ➜ kindvm echo -e 'flag.txt\n\x01\x07\xff\xd8\x02\xff\xdc\x07\x06' | nc 127.0.0.1 9999 |
exp如下
1 | from pwn import * |
checksec
1 | [*] '/mnt/hgfs/shared/vmpwn/ciscn_2019_c_virtual/pwn' |
这道题相对上一道题复杂了许多,首先弄清楚程序逻辑
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
如果只看上面,大概可以想到:
1、name = ”/bin/sh\x00”, 利用漏洞将puts@got改成system地址
2、打印栈数据可以泄露一些地址
下面来看一下stack、text、data是如何存放数据的
1 | section_info *__fastcall sub_4013B4(int size) |
结构体如下:
1 |
|
下面再看一下两个操作数据的函数
1 | signed __int64 __fastcall Get(section_info *text_addr, _QWORD *a2) |
Get(addr, a): 从addr从取数据到a中
1 | signed __int64 __fastcall StoreInSection(section_info *a1, __int64 data) |
StoreInSection(a1, data):将data存放到a中
共有7个指令:push、pop、add、sub、mul、div、load、save
push:从stack_addr中取出数据放进data_addr中
1 | _BOOL8 __fastcall do_PUSH(section_info *data_addr, section_info *stack_addr) |
pop:从data_addr中取出数据放进stack_addr中,与push刚好相反
1 | _BOOL8 __fastcall do_POP(section_info *data_addr, section_info *stack_addr) |
add:从data_addr中取出两个数据相加再存入data_addr;sub、mul、div类似
1 | signed __int64 __fastcall do_ADD(section_info *data_addr) |
load:从data_addr中取出数据作为data_addr的索引,再将该索引指向的数据存放到data_addr中,由于没有进行索引的判断,因此可以造成越界读
1 | signed __int64 __fastcall do_LOAD(section_info *data_addr) |
save:从data_addr中取出两个数据,将第二个数据写入第一个数据相关的索引中,存在越界写。
1 | signed __int64 __fastcall do_SAVE(section_info *data_addr) |
例如:利用save将data_addr覆盖成0x400

利用思路:
1、先把got表写入data_addr->section_ptr处:push got_addr;push -3;save
2、load put@got,加上它与system@got的偏移:push 5;load;push offset;add
3、将该地址写入put@got的地址处:push 5;save
exp:
1 | from pwn import * |
checksec
1 | [*] '/mnt/hgfs/shared/vmpwn/easyVM/attachment/EasyVM' |
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
看到case 3,大概可以想到要把free(ptr)修改成system(“/bin/sh”)
先来看指令集,指令集只是定义了一些运算,下面介绍几个主要的
0x80:以下一个指令为索引idx,把下2到5个字节为值传入寄存器a1[idx]
例如:’\x80\x02\x00\x96\xF3\x78’就是a1[2] = 0x78F39600
1 | if ( *(_BYTE *)a1[8] == 0x80u ) |
0x09与0x11:把dword_305C中的值打印出来
1 | if ( *(_BYTE *)a1[8] == 9 ) |
0x53与0x54: 输出一个字节和输入一个字节
1 | if ( *(_BYTE *)a1[8] == 0x53 ) |
0x71接0x76:相当于a1[3] = (_DWORD )(a1[8] + 1);
1 | if ( *(_BYTE *)a1[8] == 0x71 ) |
其实弄清楚&unk_3020中的指令就差不多了,该指令首先给dword_305C赋值,然后对该值进行一系列运算并打印出来,根据该运算特征可以发现是MT19937随机数算法,可以利用Z3约束器进行求解(参考http://ctf.njupt.edu.cn/382.html#EasyVM)
实际上可以不用管这个密文,我们只需要dword_305C已经赋值,然后运行"\x09\x11\x99"即可
这道题可以有多种方法构造任意地址读写
1、利用"\x80\x03"
2、利用"\x71\x76"
具体攻击方法如下:
1、泄露程序基址,得到malloc@got地址
2、泄露malloc的地址,得到libc基址,从而计算free_hook地址和system地址
3、将”/bin/sh\x00”写入ptr,将system@addr写入free_hook
4、case 3 getshell
完整exp
1 | from pwn import * |
https://github.com/PDKT-Team/ctf/tree/master/seccon2018/kindvm#bahasa-indonesia
https://blog.csdn.net/qq_25201379/article/details/83548147
]]>tcache_stashing_unlink_attack是glibc2.29和glibc2.30下的一种新型攻击技巧
该利用原理与unsorted bin attack和house of lore攻击相似,首先来回顾一下它们两个
在glibc2.27/malloc/malloc.c: 3777中
1 | /* remove from unsorted list */ |
没有做任何检查,可以使bck->fd写入libc地址(av)
在glibc2.29/malloc/malloc.c: 3976中
1 | /* remove from unsorted list */ |
做了检查,因此unsorted bin attack在glibc2.29中失效
house of lore利用的是small bin分配时的unlink
参考链接:https://wiki.x10sec.org/pwn/heap/house_of_lore/
在glibc2.23/malloc/malloc.c: 3397中
1 | /* |
从下面的这部分我们可以看出
1 | // 获取 small bin 中倒数第二个 chunk 。 |
如果我们可以修改 small bin 的最后一个 chunk 的 bk 为我们指定内存地址的fake chunk,并且同时满足之后的 bck->fd != victim 的检测,那么我们就可以使得 small bin 的 bk 恰好为我们构造的 fake chunk。也就是说,当下一次申请 small bin 的时候,我们就会分配到指定位置的 fake chunk。
在glibc2.27和2.29中也没有做过多的检查
参考链接:berming
在glibc2.29/malloc/malloc.c: 3664中
1 |
|
可以看到,当small bin不为空而tcache不满时,可以达到与unsorted bin attack和house of lore相同的攻击效果。
看着挺有道理,但是当small bin不为空而tcache不满这两个条件在引入tcache之后似乎有点矛盾。因为我们不能越过Tcache向SmallBin中填入Chunk,也不能越过Tcache从SmallBin中取出Chunk。但是,事情总是充满玄机,这里不得不提calloc和unsorted bin中的last remainder与tcache的爱恨情仇。
1、calloc不会从Tcache拿Chunk,因此可以越过第二条矛盾“不能越过Tcache从SmallBin中取出Chunk”。
2、Unsorted Bin的last remainder基址,当申请的Chunk大于Unsorted Bin中Chunk的大小且其为Unsorted Bin中的唯一Chunk时,该Chunk不会进入Tcache。因此可以越过第一条矛盾“不能越过Tcache向SmallBin中填入Chunk”。
其实calloc与malloc的特性可以方式直接泄露libc,因为当我们使用malloc时会直接从tcache取chunk,tcache中的chunk不存在libc地址,而使用calloc时,会把chunk清零。但是malloc可以泄露堆地址。
下面以gxzyCTF中的twochunk为例
checksec
1 | [*] '/mnt/hgfs/glibc2.29/twochunk/twochunk' |
程序功能较多
1 | result = (signed int)mmap((void *)0x23333000, 0x2000uLL, 3, 34, -1, 0LL); |
在0x23333000出mmap出了一块大小为2000的可读写空间,地址存放在bss段的buf中

在0x23333000处存放name和msg,同时有一次修改和打印name、msg的功能。
使用calloc申请堆块,大小为(0x80, 0x3ff ],即small bin大小的堆块,同时最多申请两个,当size=0x23333可以使用一次malloc(0xE9)
free函数没问题
show函数只能使用一次
edit函数只能使用一次,有32字节溢出
存在后门函数,可以配合修改msg使用,前提是泄露libc地址
因此,这道题的目标就是泄露libc地址,刚好利用类unsorted bin attack可以将libc地址写道任意位置,我们可以写入0x23333000,然后利用打印name、msg函数泄露出来。
首先,我们需要一个用来两个不同大小的chunk,一个未满,一个已满
1 | for i in range(5): |
下面构造两个small bin
1 | #first smallbin |

1 | add(0,0xf0) #为malloc(E9) |

泄露堆地址
1 | add(0,23333) |
tcache stashing unlink
1 | payload = '\x00'*0xf0+p64(0)+p64(0x111)+'\x00'*0x100+p64(0)+p64(0x91)+p64(heap-0x250)+p64(0x23333000-0x10) |
此时堆布局如下

以上过程首先是类house of lore攻击
检查:bck->fd = victim(通过)
结果:bin->bk = bck; bck->fd = bin;
接下来是类unsorted bin attack
检查:无
结果:bin->bk = bck; bck->fd = bin;
攻击之后的效果如下

完整exp如下
1 | from pwn import * |
经典的glibc2.23环境下的堆溢出,程序自带混淆,可以通过简单的黑盒测试确定程序的逻辑。禁掉了execev,可以在栈上构造orw,也可以通过栈迁移打orw。这里利用unlink进行任意地址读写,然后利用environ泄露栈地址,然后在栈上构造orw的ROP链。
通过静态分析可以发现,程序存在add、free、show、edit四个功能。
add:可以malloc大小为[0,0x1000]的堆块,conten并没有直接写进heap,而是放在大小为0x200的bss段中
free:正常free,有清零
show:正常
edit:可以发现edit的大小存放在bss段中
可以发现,edit的大小与堆的大小没有关系,而是等于content的长度,因此可以堆溢出
有show,可以malloc smallbin,便可以泄露libc
1 | sl("name?","kangel") |
有smallbin和堆溢出,可以构造unlink
1 | add(0x100,'2'*0x200)#2 |
堆布局如下:
1 | pwndbg> x/10gx 0x6032e0 |
条件:bck->FD + 0x18 = bck ; bck->BK + 0x10 = bck
效果:bck = *bck - 0x18
可以利用environ进行泄露
1 | environ=libc_base+libc.symbols['environ'] |
计算environ与edit函数中rbp的差值,因为利用edit往栈中写入数据后直接进行rop

直接往rbp中注入rop链即可
完整exp
1 | #coding=utf-8 |
当栈空间不够利用或者无法利用时,我们又需要利用rop的时候,栈迁移可以帮助我们。
下面介绍几种常见的栈迁移方法,持续更新!
double ret就是通过将ebp覆盖成我们构造的fake_ebp ,然后利用leave ; ret这样的gadget将esp劫持到fake_ebp的地址上
假设可以覆盖ebp
1 | esp | | |
第一次leave ; ret
1 | mov esp; ebp |
第二次leave ; ret
1 | mov esp; ebp #esp的值为fake_ebp |
下面以ciscn_2019_es_2为例
checksec
1 | [*] '/mnt/hgfs/pwn/buuoj/es_2_stack_pivot/ciscn_2019_es_2' |
程序漏洞很明显,32位程序下有8字节的栈溢出,可以覆盖到ebp和函数返回地址
1 | int vul() |
利用方式:
1、第一次read和printf可以泄露栈地址
2、第二次read可以进行栈迁移将栈压低,刚好可以利用vul函数的main函数的double ret
vul函数
1 | .text:080485FD leave |
main函数,需要注意的是main函数的返回稍有不同
1 | .text:0804862F mov ecx, [ebp+var_4] |
栈布局
1 | pwndbg> x/12wx $ebp-0x28 |
exp
1 | from pwn import * |
可以直接利用
1 | pop rbp; |
这道题是glibc2.23环境的堆题,包含多种知识与技巧。例如:沙箱函数、overlapping、利用_IO_2_1_stdout_泄漏libc、unsorted bin attack、fastbin attack、setcontext、mprotect、srop等等。
检查保护机制,保护全开
1 | ➜ unctf_orwheap checksec pwn |
ida静态分析

可以发现在add函数处存在off by one。但是限制了size大小(size > 0x67 && size <= 0x3F0),这样我们依然可以进行overlapping。
1 | add(0x68,'0'*0x60) |
此时bin的内容如下
1 | pwndbg> bins |
查看0x55e33cd130f0的chunk
1 | pwndbg> heap 0x55e33cd130f0 |
此时如果有show的功能的话,就可以直接add(0x68,’2’*0x60)从而泄露<main_arena+88>的地址再计算得到libc基址。但这里没有libc基址,于是只好利用_IO_2_1_stdout_泄漏libc。具体操作如下:
1、修改fd的低字节为’\xdd\x25’,让它指向_IO_2_1_stdout_。这里的2需要爆破,概率为1/16
2、利用fastbin attack可以修改_IO_2_1_stdout_的flag字段从而泄露libc
1 | pwndbg> p stdout |
利用脚本如下
1 | delete(0) |
有了libc基址,我们本可以再次利用fastbin attack修改掉malloc_hook为one_gadget直接get shell。但是,ida分析的时候发现存在沙箱函数prctl。可以使用secconp-tools查看
1 | ➜ unctf_orwheap seccomp-tools dump ./pwn |
禁掉execve函数,因此无法直接get shell。这时可以通过orw得到flag。但是没有可用的栈空间,于是我们可以利用setcontext函数调用srop来进行栈迁移,最后call mprotect -> shellcode。
这时候可以利用堆空间来部署SigreturnFrame(),然后将利用fastbin attack将__free_hook的地址改成setcontext。在改写__free_hook之前我们需要利用unsorted bin attack来伪造chunk size。(不得不说,unsorted bin attack确实是打辅助的好手)
1 | #unsorted bin attack |
攻击效果bck->fd = unsorted_chunks(av)
1 | pwndbg> x/10gx 0x7f9fe23c37a8-0x20 |
利用fastbin attack将__free_hook的地址改成setcontext,并布置好SigreturnFrame()
1 | #fastbin attack |
这里要说一下setcontext函数;
1 | int setcontext(const ucontext_t *ucp); |
这个函数的作用主要是用户上下文的获取和设置,可以利用这个函数直接控制大部分寄存器和执行流:
1 | pwndbg> x/80i 0x7ffff7a7bb50 |
这里需要说明的是:
setcontext+53开始用的,不然程序容易崩溃,主要是为了避开fldenv [rcx]这个指令。mov rcx,QWORD PTR [rdi+0xa8]; push rcx即mov rip,QWORD PTR [rdi+0xa8]利用push是保证指向的内存可访问,否则就会crash。这是我们已经把栈迁移到(free_hook) & 0xfffffffffffff000 ,并可以在改地址处写入0x2000字节的数据,下面构造好ROP链即可
完整exp如下:
1 | #coding=utf-8 |
一直对house of系列的利用模棱两可,现在刚好可以利用疫情在家的时间好好梳理一下。
house of spirit(以下简称hos)是 the Malloc Maleficarum 中的一种技术。该技术的核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。
1 |
|
调用一次malloc设置内存
1 | Breakpoint hos.c:11 |
假设存在某个可控的区域,例如栈上
1 | Breakpoint hos.c:18 |
设置fake_chunk的size和next chunk size来绕过检测
1 | Breakpoint hos.c:26 |
假设存在某个漏洞是我们可以free掉fake_chunk
1 | Breakpoint hos.c:30 |
free(a)
1 | Breakpoint hos.c:33 |
malloc(0x30)或malloc(0x38)
1 | Breakpoint hos.c:34 |
成功分配堆块到fake_chunk!
ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理2 * SIZE_SZ,同时也不能大于av->system_mem1 | ----------------------- |
1 | pwndbg> x/40gx $rsp+8 |
name的大小为48,因此可以泄露rbp,可以看到:
money的大小0x40,可以设置fake_chunk size = 0x40以及修改堆地址为fake_chunk的地址(0x7fffffffdc50)
id刚好为nextsize
1 | from pwn import * |
overlapping是一种堆块漏洞利用中相当常见的套路,非常好用,它比较常见的利用条件是off-by-one等堆漏洞。
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/chunk_extend_overlapping-zh
查看stdout结构
1 | pwndbg> p stdout |
当stdout->_flags改变时,可能打印出libc地址,具体参考/usr/include/x86_64-linux-gnu/bits/libio.h
1 | #define _IO_MAGIC 0xFBAD0000 /* Magic number */ |
一种泄露libc的用法是添加_IO_CURRENTLY_PUTTING和 _IO_IS_APPENDING标志位,例如:
1 |
|
运行结果如下:
1 | �����flags: 0xfbad2087 |
realloc_hook的常见利用方式是在使用one_gadget时平衡栈空间。
one_gadget如下:在使用时需要满足一些栈条件
1 | ➜ woodenbox one_gadget ./libc6_2.23-0ubuntu11_amd64.so |
由于调用realloc回去查看realloc_hook是否存在,存在的话会先调用realloc_hook。realloc_hook的地址刚好在malloc_hook上面。常见手段为:在malloc_hook中写入realloc+n,在realloc_hook中写入one_gadget。调用malloc来触发one_gadget。
1 | ➜ woodenbox checksec woodenbox2 |
保护全开,没有show,基本是要想办法泄露libc。程序在change的时候存在堆溢出,因此可以利用overlapping。然后结合unsortedbin来爆破_IO_2_1_stdout_的地址,改变flag来泄露libc,最后将one_gadget写入malloc来getshell。
1 | from pwn import * |
对于堆溢出漏洞(例如off-by-one),常见的打法是overlapping造成double free,然后将one_gadget写入realloc_hook中。如果没有show操作,就可以利用_IO_1_2_stdout来泄漏libc。
]]>两个非fastbin大小的chunk, 如下所示
1 | 0xf251f0:0x00000000000000000x0000000000000021 <-fake_chunk |
其中fake_chunk处于释放状态,即chunk1的RPEV_INUSE=0。
fake_chunk->FD->BK=&fake_chunk,即*(0xf251f0+0x10)+0x18 = 0x6020d0
fake_chunk->BK->FD=&fake_chunk,即*(0xf251f0+0x18)+0x10 = 0x6020d0
chunk->prev_size = fake_chunk->size
攻击结果
1 | pwndbg> x/gx 0x6020d0 |
double free之后一个在fastbin中,一个在smallbin中
1 | pwndbg> bins |
这时候如果我们malloc(0x28),该chunk虽然可以写入,但是属于一个被释放的smallbin,因此可以在构造该chunk为满足unlink的fake_chunk
该程序功能如下:
1、keep secret(malloc)
1 | small serect:malloc(40) |
2、wipe secret(free)
1 | small secret: 未清零,double free |
3、renew secret(edit)
分析如下:
因为每个secret只能malloc一次,因此无法造成经典的fastbin_dup,但是可以造成fastbin_dup_consolidate
1 | add(1, 'a') |
然后修改chunk1构造fake_chunk
1 | f_ptr = 0x6020d0 |
接下来就可以任意读写了,将free@got改为 puts@plt,泄露出libc基址,然后将free@got改为 system
1 | f = p64(0) + p64(atoi_GOT) + p64(puts_GOT) |
地铁没有坐正与坐反,只有起点和终点!
那是一个月黑风高的晚上,窗外刮着不太冷的寒风。我在庆祝新年的喧嚣声中小心翼翼的守护着那独一份的孤独,在百无聊赖之中,我在电脑上敲下了数行文字,用以记录上一年的某些事情,顺便聊以慰藉。就这样,我花了一个小时来记录我的2018,于此同时,我打算用一年的时光来书写我的2019。世事如影,诸君若闲,愿细数与听。
人总向往诗和远方,却被生活种种束缚在了原地。阿磊跟我说:“十八岁计划出门远行,到现在还没有踏出一步。”我说:“不如今年寒假去西藏吧!”于是我们一拍即合,就这样踏上中国海拔最高的那片区域。我们先来到了武汉,顺便找了很久没见的阿峰吃了顿饭。阿峰是个很有意思的人,饭桌上我们都狂笑不止,谈论着往事与将来。
第二天我们坐动车来到了重庆。在阿磊同学的带领下,我们用半天时间品尝了磁器口的麻花,体验2号线的穿楼轻轨,吃了渝大师的中辣火锅,看到了灯光璀璨的洪崖洞。同行的还有多年未见的阿健,我跟阿健说:“雾锁山城山锁雾”,他回道:“山困雾都雾困山”。之后我们去了机场,在机场待了一宿第二天凌晨6点飞往拉萨。重庆是我待过的时间最短的城市,却给我一种玩了很久的感觉。我喜欢它那种古代与现代的完美结合,喜欢它好吃不贵的火锅以及入口即化的麻花。
第一次感受到西藏的与众不同之美便是在飞机上。我们快到西藏的时候正是朝阳出升的时候,然后最绚丽的不是飞在云端观看日出,而是彩虹机翼下那延绵不绝的雪山。我从未见过如此壮丽的大自然,或许是见识短浅,但那种感觉可以让人忘却一切烦恼。在飞机上认识了一位漂亮的藏族姑娘,她跟我讲述的许多当地的饮食于文华。下了飞机之后迎面扑来的是刺穿秋裤的寒气,也许此地别样的热情,连寒气都迫不及待的想与你来一场肌肤之亲。
冬天的西藏其实并没有那么寒冷,只是早晚温差比较大以及日出日落都很晚。我们在玛吉阿米喝了一壶甜茶,在大昭寺门前晒了会太阳,去布达拉宫买了条哈达,坐着大巴在最美的318国道上驰骋,去了湖水蓝如宝石的巴松,在藏民家里吃了牦牛粥喝了酥油茶,去了有“西藏江南”之称的林芝。一切犹如行云流水一般朴实无华且充满乐趣。
之后我们踏上了44小时的绿皮火车远下江南。
浊夜未央。清月渐凉。无睡意、偏倚寒窗。揭衣成被,落地为床。听欢声起,笑声伏,鼾声长。
此生漫漫,皆在奔忙。几时能、闲下空想。乘风而去,踏雪回乡。携云一溪,酒一壶,琴一张。
——《行香子·归途》 我们努力想让生活充实有趣且与众不同,但有些事却是生活中的一些地标,你可以绕弯,可以停留,但是必须经过那里。
这一年的春节最大的乐趣莫过于每晚的夜宵,我和老头小酌一杯之余将白天的剩菜一扫而光,其中我最爱的便是某一家的腊肠。
开学之后便忙着保研的诸多事宜,从资料收集,到关注消息,再到简历投递,每一环节看似自然却扣人心弦。
和学长们打了场国赛,第一天ak题目后我们点了披萨庆祝。
四月份的某天,我那蓄势已久的薰衣草突然在一场暴风之后接二连三的破土而出。一时间竟体会到小学作文中常写的:“我种下一颗种子,终于发出了芽。”
五一我回了趟家,跟家人一起去山里摘了树莓。上一次已是十多年前的事了。
六月份的某一天,我在宿舍看着猛龙打勇士。我知道那一天必将载入NBA史册。
考试结束后和阿九去了镇海角。
去了苏州参加国赛的半决赛。
七月份的某一天,我来到了上海参加上海交通大学的选拔。认识了阿馨,也见到了多年未见的阿彪。
在国赛失利之后,我开始学pwn,开始研究一些真实的漏洞,开始看一些书,开始有计划的刷题,开始用博客记录学习的过程,开始入门CTF。不巧的是,那时候《亲爱的热爱的》正在热播。
后面我去浙大之江实验室实习了一个月,参加了一次护网比赛。
暑假在家待了一个月,这大概是我十多年来,在家连续待得最久的一次。
开学去打了场工控安全竞赛,侥幸拿了二等奖。
有些事情当时会让你觉得一夜成长,过后又云淡风轻。
9月15日
我预约了某照相馆的证件照之后,便一大早去了那里。由于来的太早,我在旁边的咖啡店点了一杯咖啡。我想让自己静下来,但是心却始终高悬不下。前一天晚上几个跟我一起报浙大的同学九推初审都过了,我却还没有结果。我跟阿峰说:“有没有什么能让我心情好一点,我现在很糟。“阿峰一开始不相信我这种人也会心情很糟,后来他推荐我去看脱口秀大会呼兰的段子。呼兰的段子是不错,可是这并不能让我轻松起来。我于是怀着这种心情拍完了证件照,我看了看证件照,白粉棕眉下隐藏的那种淡淡的忧伤还有点小帅,应该仅次于杜小帅了。没过多久,我便收到通过初审的短信。我又去看了两眼证件照,淡淡的忧伤下潜藏的未知的喜悦让小帅之余带点优雅,那时候我想,可以刚一刚杜小帅了。
9月23日
我和阿翼去了紫荆港参加机试。那时的阿翼,手握清华、人大、北航的offer,而我,空有清风,天气太热,连两袖都没有。就这样,它带着100分的机试成绩早早地出了考场,我带着63分的忧伤守候着结束的铃声。我一边埋怨自己为什么不多刷两道pat,一边吐槽着我一个CTF选手为啥要来参加机考,一边又赶紧找老师学长捞一波。一边绝望,一边又抱有希望。一边愤恨,一边又有点窃喜。我不知当时的我是怎样的的心情,只知道那天在酒店点的烧烤还挺香的。
9月24日
我和同行的几个小伙伴去参加面试。面试来了挺多人,大概就是传说中的各路牛鬼蛇神吧。按照我以往的尿性,面试十有八九都是“我跟你不熟,并不想听你叽里呱啦问七问八。”其实主要是,他们老问我一些不会的,让场面一度尴尬,让氛围瞬间如死水一般。果不其然,面试炸了。我只好寄希望于一方有难八方支援。从那一刻起,我的希望逐渐破灭,信心逐渐丧失,只剩下心悬一线。晚上坐在老师的车上,老师跟我说如果失败了你可以参加统考或者出国,我这边都可以帮你,我越听越绝望,不禁陷入了沉思,心随黑夜一般,不知几何,也不知代数。
9月25日
我早早地就醒了。我看了看墙上的电线,又去阳台站了许久。我从未体会那种内心的五味杂陈,这个词语我一般只在小说电影才看到,使用的时候也是那种为赋新词强说愁。我后来又回到了沙发上,在手机的便签上写下:
终于体会到了什么叫孤注一掷的煎熬,早上醒来,思绪就被打乱,无法再安心入眠。看到窗外的朝阳,觉得很美丽,但是不敢多看两眼。
9月26日
这天公布九推结果。我的结果是未录取。那是已是九推尾声,我手上还没有一个offer,意思就是我应该没有学校读了。从一开的壮志满怀到一落千丈只需要仅仅几天,这突然的一切让我猝不及防。我开始茶饭不思,神情恍惚,整个人如同行尸走肉。阿九见证了这一切。我只好将全部希望压在实验室导师身上,导师说,只要有人放弃就争取优先录取我。我打了个电话给辅导员,向他说明的情况,他让我盯紧一点。那一刻,我有燃起了一丝希望。阿九也许不知道这些。
9月27日
我回到了学校,回到了宿舍。我掩饰着内心的不安,与室友谈论着日常。阿仔告诉我可以提前联系一下前面录取的人,看有没有愿意放弃的。我试着做了,那一刻,我完全底下了高傲的头颅,询问他们是否愿意放弃名额。果然这招屡试不爽,一下子就联系到了五六个。我将这个消息告诉实验室的导师,导师也同样欣喜。导师告诉我说,会让学姐在招生办盯着,有人放弃就想办法捞我,我的希望之火又燃起了一些。到了晚上,大家都忙着填志愿。我知道那与我无关,但我依然选择一宿未眠,小心翼翼的守护着我那脆弱而又不堪一击的脆弱之火。
9月28号
早上7点,第一批录取已经结束。然后开始补录,直到下午5点。补录结束,而我的电话一整天都悄然无声。我当时在宿舍阳台上,看着学姐发给我:“补录结束了,我已经尽力了。“那几个字瞬间化作冰冷雨滴,将我的希望之火浇的一丝不剩。我突然内心一阵绞痛,蹲在了地上。当我再站起来的时候,我发现阳台的薰衣草已经枯萎。我跟阿润说我要找工作了,阿润说你即使去工作也不虚的。然后我开始告诉并且说服老头子我要去找工作。老头子并没说太多也没有反对。于是我去找了阿龙,阿龙说你就不要来和我们抢饭碗了,之后我们去喝了点小酒。在路上阿慈打电话来找我聊天,我告诉了他我的决定。喝酒的时候,导师打电话来说问我要不要直博,说有五成机会,我敷衍地答应了。
9月29号
早上醒来,老头子给我发了一大段作为父亲的不忍与承诺,我不知道如何回复。我和室友出门了,这天太阳格外耀眼。出门之前,我把蓄了很久的胡子刮了,突然感到一阵释怀。我不知道是释怀还是躲避,我内心还在想着直博的事。九点左右,上交招生办打来电话,说我前面还有一个同学如果放弃的话就补录到我了,我于是按照她的意思填了志愿。没过一会儿,复试通知下来了。我低头看了看短信,又抬头看了看天空,阳光洒在我的脸上,仿佛自由的雨滴。“有些鸟儿是终究关不住的,它们的羽毛太鲜艳了”。那是内心的自由,我仿佛压抑了很久很久。后来浙大的录取通知来了,我最终去了浙大。
也许正如某位老先生说的:
天将那啥,必先那啥!
有些事是生活的地标,你在达到它之前或许会朴实无华且枯燥地走着两点之间线段最短,或许会波澜壮阔精彩纷呈地静赏陌上花开,或许会绕了一大弯还摔了一两跤。但是不管怎样,到了之后,你还会继续往前走。
后面的的时间我大部分待在了实验室,还记得那时满城桂花飘香。
第一个周末去了骑马射箭,写了一篇《杭秋》。我最喜欢其中一句:
彤叶惹黄花,因羞泛红;清水戏明月,为情相映。
细细品之,至今留有余味。
11月回了趟学校,第一次在火车丢了电脑。而后参加省赛。
12月21,22日,陪阿九考研。
12月26~29日,代表浙大AAA参加xnuca总决赛,苟到了最后一个三等奖。
我们往往都记得那些突然发生的事情,却往往忽略那些一直在坚持的事业。赛博如海,我是沧海一粟。然而:
我最进看了很多教程,也研究了很多的漏洞,感触颇深,现在看来,我亦是有成为世间黑客的潜质!

Thank you, thank you all so very much, thank you to all of you in this room, none of there would be possible without you. Thank you!
这是一道arm菜单题
安装依赖
1 | sudo apt install -y gcc-arm-linux-gnueabi |
启动
1 | qemu-arm -L /usr/arm-linux-gnueabi ./awd7 |
安装gdb-multiarch
1 | sudo apt install gdb-multiarch |
socat启动
1 | socat tcp-l:10005,fork exec:"qemu-arm -L /usr/arm-linux-gnueabi ./awd7",reuseaddr |
python脚本启动
1 | from pwn import * |
gdb调试
1 | gdb-multiarch -q awd7 |
ida打开,发现很明显的缓冲区溢出以及get shell的后门。
]]>If you look for it, I’ve got a sneaky feeling you’ll find that love actually is all around.
1 | 标准输入 即STDIN , 在 /dev/stdin , 一般指键盘输入, shell里代号是 0 |
1 | 系统调用号:4 |
1 | 在32位汇编下相当于: |
1 | RET指令则是将栈顶的返回地址弹出到EIP,然后按照EIP此时指示的指令地址继续执行程序 |
1 | shellcode:"\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80" |
pwntools生成
1 | shellcraft.i386.linux.sh() |
Every time a bell rings, an angel gets his wings.
1 | push esp |
很明显的栈溢出,没有其他可以利用的函数和gadget,只好利用shellcode。因此需要泄露esp的地址,并且payload长度不能超过0x3c,因此需要精心构造shellcode。
1 | from pwn import * |