2023FCTF
热身赛
Web
ViewSource
一道简单的前端题,根据提示ViewSource,ctrl+u
查看源代码,这里网页代码被加密过了,直接往下看发现可疑段落
分析逻辑,用户输入your_flag
,如果your_flag
与my_flag
相同,则弹窗my_flag
提供两种做法
既然my_flag
变量在js代码中定义,那么我们就能用控制台把它输出
ctrl+s
保存网页源代码用编辑器打开
修改代码逻辑,如果用户输入的your_flag
与实际flag不相等则弹窗my_flag
,保存,打开html文件往输入框里随便输个什么提交
javaDeserialize-1
点开题目,
javaDeserialize-2
filechecker_mini
打开题目,让我们上传一个文件:
桌面上随便丢了个php文件进去提交看看会有啥情况:
判断文件类型,(MIME绕过预定)
附件下载下来先看源码:
index.html:
可以看出该网页使用模块渲染将result值渲染进index对应位置,那么就看下后端代码app.py:
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
| from flask import Flask, request, render_template, render_template_string from waitress import serve import os import subprocess
app_dir = os.path.split(os.path.realpath(__file__))[0] app = Flask(__name__) app.config['UPLOAD_FOLDER'] = f'{app_dir}/upload/'
@app.route('/', methods=['GET','POST']) def index(): try: if request.method == 'GET': return render_template('index.html',result="ヽ(=^・ω・^=)丿 ヽ(=^・ω・^=)丿 ヽ(=^・ω・^=)丿")
elif request.method == 'POST': f = request.files['file-upload'] filepath = os.path.join(app.config['UPLOAD_FOLDER'], f.filename)
if os.path.exists(filepath) and ".." in filepath: return render_template('index.html', result="Don't (^=◕ᴥ◕=^) (^=◕ᴥ◕=^) (^=◕ᴥ◕=^)") else: f.save(filepath) file_check_res = subprocess.check_output( ["/bin/file", "-b", filepath], shell=False, encoding='utf-8', timeout=1 ) os.remove(filepath) if "empty" in file_check_res or "cannot open" in file_check_res: file_check_res="wafxixi ฅ•ω•ฅ ฅ•ω•ฅ ฅ•ω•ฅ" return render_template_string(file_check_res)
except: return render_template('index.html', result='Error ฅ(๑*д*๑)ฅ ฅ(๑*д*๑)ฅ ฅ(๑*д*๑)ฅ')
if __name__ == '__main__': serve(app, host="0.0.0.0", port=3000, threads=1000, cleanup_interval=30)
|
上面大家都用render_template()
就你爱用render_template_string()
是吧(指指点点,一眼模板注入,那么我们就希望file_check_res
里有我们能够执行的rce代码。file_check_res
哪来的?倒退往上看。
1 2 3 4 5 6 7 8
| f.save(filepath) file_check_res = subprocess.check_output( ["/bin/file", "-b", filepath], shell=False, encoding='utf-8', imeout=1 ) os.remove(filepath)
|
先保存filepath这样一个文件,subprocess.check_output(command)
返回Linux命令行输出,然后再把filepath文件删除,那么这里的file_check_res
就是file -b {filepath}
的结果。往上看filepath其实就是将上传文件目录的绝对路径和该文件的文件名拼接起来来标定用户上传的这个文件在容器中的绝对位置。逻辑搞明白了,现在的重点就在于如何对一个文件使用file -b
命令后返回值中能回显我们所期望的值。动手操作下flie命令,发现其不会输出文件的内容只会输出其类型,
代码中的-b参数作用:
所以在文件名上动手脚的想法也破灭了(悲。
卡住了,向大佬博客寻求帮助,去guthub查找file命令源码。第一个仓库点开。
点开tests里面是各种针对该file
命令的测试结果
这8个分别分别是在文本中写入bash脚本的4种情况和对应的用file
命令执行的输出结果,可以看出如果文本内容为#!/usr/bin
开头的那么输出结果中会显示文本中的其他内容。
本地做测试:创建一个文本文件修改内容如下
测试结果如下:
显而易见,输出可控,可以进行模板渲染。新建一个文本文件内容如下,上传文件
存在ssti漏洞,开始利用,调用os模块
调用popen()方法。
上传文件,获取flag。
Misc
png
附件下载下来是一张png图片,
010打开划拉到最底部发现冗余数据,部分flagFCTF{To
,
010跑PNGTemplate.bt脚本报错,左下提示CRC不匹配,说明修改了高宽却没有修改CRC导致读取报错,图片宽高很可能被修改过。(或者放入kali中无法打开)
进行一个高度的改,将高度640改为700
文件头数据块IHDR包含的第一部分数据就是图片宽高,分别对应第二行中的第一组四个字节和第二组四个字节,掏出计算器算算高度拉长点。
得到另一部分flag:_the_flawless_
然后就是做这道题时比较懵逼的一个地方了,招最后一部分flag。先开起zsteg看看能拿到什么吧。查到了之前找到的FCTF{To
,但是愣是没发现最后一部分flag,如果有LSB隐写那zsteg也应该能淦出来才对呀?
由于过于依赖工具,死磕这条路坚信不存在lsb隐写。愣是没用Stegsolve手搓,到处找,查IDAT块、翻EXIF信息…最后还得感谢ixout手搓LSB出来。
呜呜呜呜呜呜呜呜呜呜。
最后三段flag拼接起来得到flag。
呜呜呜呜呜呜呜呜呜呜。
cet6
一道基础的USB取证题,但是不太常规。。。
zip套娃
第一层:
binwalk无法分离,不是伪加密。没给其他条件明文攻击也不太行,那就爆破试试吧(。
ARCHPR开起来,攻击方式字典,选的是kali字典。跑了一段时间后成功拿到第一层密码。
第二层:
依然先丢到kali里binwalk试下,分离成功,伪加密
分离出来的东西多了个0.zip打开其实就是第二层的包不过问题不大,直接看第三层的压缩包
第三层:
打开压缩包看到了一个支点.txt文件,同时第二层解压出来后也有一个支点.txt,支点.txt文件大小大于12字节,大胆猜测是明文攻击。
WinRAR将泄密出来的文件压缩为zip,开始明文攻击,然后就。。。
相信不止我一个人遇到这种情况。。。这里我们忽略了一些细节,加密文件是通过什么方式压缩的呢?不同压缩软件采用的压缩算法也会不同,自然会出现不匹配的情况,这里多尝试几次,鼠标右键发送到压缩文件可行,开始明文攻击
等待了一段时间之后拿到秘钥(btw这一坨是啥。。。)
第四层:
字典、明文、伪加密,各种姿势都试过了,打开压缩包看一眼,分散成这么多小文件,大胆猜测CRC碰撞
EXIF查看一下压缩文件数据,CRC32、字节数等等
大致整理下6个文本文件的CRC32、字节数:
文件名 |
CRC32 |
字节数 |
1.txt |
0x92716b7c |
5 |
2.txt |
0x1ab6bb72 |
4 |
3.txt |
0xfcf21afd |
3 |
4.txt |
0x89a155cb |
5 |
5.txt |
0x2d09a3d6 |
6 |
6.txt |
0xe3f20a9d |
2 |
1-3字节的脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ''' 1byte '''
import binascii import string
def crack_crc(): print('-------------Start Crack CRC-------------') crc_list = [0xda6fd2a0, 0xf6a70, 0x70659eff, 0x862575d] comment = '' chars = string.printable for crc_value in crc_list: for char1 in chars: char_crc = binascii.crc32(char1.encode()) calc_crc = char_crc & 0xffffffff if calc_crc == crc_value: print('[+] {}: {}'.format(hex(crc_value),char1)) comment += char1 print('-----------CRC Crack Completed-----------') print('Result: {}'.format(comment))
if __name__ == '__main__': crack_crc()
|
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
| ''' 2bytes '''
import binascii import string
def crack_crc(): print('-------------Start Crack CRC-------------') crc_list = [0xe3f20a9d] comment = '' chars = string.printable for crc_value in crc_list: for char1 in chars: for char2 in chars: res_char = char1 + char2 char_crc = binascii.crc32(res_char.encode()) calc_crc = char_crc & 0xffffffff if calc_crc == crc_value: print('[+] {}: {}'.format(hex(crc_value),res_char)) comment += res_char print('-----------CRC Crack Completed-----------') print('Result: {}'.format(comment))
if __name__ == '__main__': crack_crc()
|
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
| ''' 3bytes ''' import binascii import string
def crack_crc(): print('-------------Start Crack CRC-------------') crc_list = [0x92716b7c, 0x1ab6bb72, 0xfcf21afd, 0x89a155cb, 0x2d09a3d6, 0xe3f20a9d] comment = '' chars = string.printable for crc_value in crc_list: for char1 in chars: for char2 in chars: for char3 in chars: res_char = char1 + char2 + char3 char_crc = binascii.crc32(res_char.encode()) calc_crc = char_crc & 0xffffffff if calc_crc == crc_value: print('[+] {}: {}'.format(hex(crc_value),res_char)) comment += res_char print('-----------CRC Crack Completed-----------') print('Result: {}'.format(comment))
if __name__ == '__main__': crack_crc()
|
4-6字节的使用theonlypwner工具,修改供选字符
1 2
| permitted_characters = set( map(ord, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_{}!'))
|
FTCF{
Y0u_
_z1p_
master
拼接起来就是FTCF{Y0u_4re_z1p_master!}
使用theonlypwner时出了点问题,最开始忘记了flag中必含的”{“”}”字符,导致怎么都跑不出来,还是看了眼hint想起来应该修改脚本中的供选字符。如果感叹号是在4-6字节的那些文件的话肯定也跑不出来了,考虑问题还是不够周到。
(大佬的博客:https://www.cnblogs.com/ECJTUACM-873284962/p/9884416.html)
(CTFwiki:https://ctf-wiki.org/misc/archive/zip/#_7)
正式赛
Web
Misc
Puzzle
附件如图所示:
一整张图构成极其简单,想要藏什么数据的话大概率只能是IDAT块隐写了,pngcheck查看下
9个IDAT块,块长度没有隐藏什么特殊信息,一张正常的png图片的IDAT块的前面的块应该都是填充满且相同大小的,这个check结果显然就不正常,感觉每一个块都单独成图。试着删除块。
tweakpng:
准备删IDAT块,九个块就拷贝九份先。删块的时候不要把IEND块删了,这是png格式的结尾标识。
每个IDAT块单独成一张图片,已经很明显了,是一张二维码。
自己对于二维码结构的认知只有三个定位块(悲),所以瞎拼,然后都没法扫(,根据学长放的hint:
归纳就是三个定位块的周围一个像素块的一圈必须是空白的,且相互之间有黑白像素块交叉分布的定时标志,根据特征拿到ps里拼了下
扫一下,拿到flag:
FCTF{n1ce_puzzl3}
自信音游人
附件是给的曲目选自阿卡伊的だいあるのーと。