前言
上上周看到了这个 XCTF 的高校战“疫”网络安全分享赛,觉得有点意思可以玩一下。
于是上周末就水了一下,然而啥也不会菜到爆炸,都没几道题会做……(后面还补作业瞎折腾去了
最近看了看大佬们的 WP,估计在他们眼里都是签到题吧,啥也不说直接丢一个 payload 搞定走人(awsl。
不过还是有点感受或者其他方面的收获之类的,顺便来学点东西,随便写一点吧。
官方 WP: 点这里下载 (版权归官方所有)
好像好多环境都关了。
(话说这篇文章丢 draft 里好久了唉,再不发就要长草了。
MISC
2019-nCoV
真·签到
flag{shijiejiayou}
武汉加油!中国加油!世界加油!
简单MISC
压缩包里有一个flag.zip
(加密) 和一个photo.jpg
(打不开)。
photo.jpg
经过提取得到一个zip文件。
里面有一个 ctf.txt
文件,里面是
./.--./../-.././--/../-.-./.../../-/..-/.-/-/../---/-./---/..-./..-/-./../...-/./.-./.../../-/-.--/.--/.-/.-.
一看是摩斯密码,解密得到
EPIDEMICSITUATIONOFUNIVERSITYWAR
(嗯 EPIDEMIC SITUATION OF UNIVERSITY WAR 嘛。高校战疫啦。
作为密码,解压flag.zip
文件,得到flag.txt
VGgxc19pc19GbGFHX3lvdV9hUkVfcmlnSFQ=
base64解密得到flag:
Th1s_is_FlaG_you_aRE_rigHT
隐藏的信息
压缩包里有个二维码.jpg
,以及 纯数字.zip
。
其中压缩包加密了。
发现二维码被扣掉了三个定位脚而且反色了,于是补全
扫码得到(可能补得不是很好,但发现手机 QQ 的扫码真的强
flag{this_is_also_not_flag} 解压密码不在这里0.0!
假的flag!发现并不能解压。
同时在十六进制编辑器里看到最后有一段字符串:TOGETYOURFLAG
。(有点懵,好像没啥用的亚子
再看回压缩包,根据文件名 纯数字
,于是暴力跑了一波,然而并不行。
于是想到伪加密,果然如此。
解压得到隐藏的信息.wav
,音频内容大概就是 我的武汉尽快好起来
之类的,挺应景的,也挺励志的。
在音频最后发现了比较微弱的拨号声音,看了看频域,发现了如下的频谱。(在最前面和最后面都有这样的信号)
每个拨号声由两个频率叠加而成。
想起来刚放寒假的时候有个学弟还问过拨号的原理,那时候还答不上来,查了半天没找到合适的资料。
这次再去谷歌查了一下,发现原来这叫做 DTMF 信号。
双音多频信号(Dual-Tone Multi-Frequency, DTMF),电话系统中电话机与交换机之间的一种用户信令,最常用于发拨号时送被叫号码。
双音多频的拨号键盘是4×4的矩阵,每一行代表一个高频,每一列代表一个低频。每按一个键就发送一个高频和低频的正弦信号组合,比如’1’相当于697和1209赫兹(Hz)。交换机可以解码这些频率组合并确定所对应的按键。
(来自维基百科)
1209 Hz | 1336 Hz | 1477 Hz | 1633 Hz | |
---|---|---|---|---|
697 Hz | 1 | 2 | 3 | A |
770 Hz | 4 | 5 | 6 | B |
852 Hz | 7 | 8 | 9 | C |
941 Hz | * | 0 | # | D |
本来想利用网络上在线的解码工具,然而发现网站不是上不去就是停止服务了。(噗
那还是自己来吧。
对于每一位号码分别查看频谱,根据最接近的低频和高频信号对应于表格内的数字/字母。(挺佩服直接就听出来的大佬
得到音频最后面这几个数字为 5618521
,最前面的几个为 18748
。
凑成 flag{187485618521}
交上去,不对!
难道两部分反了?于是倒过来再试,不行。
难道要倒叙?不行。
难道是字母?于是转成键盘上1-0对应着下面的一行字母,试了试,不行。
难道是哪个数字搞错了???再确认了一遍,队友也确认了一遍,就是这个啊(黑脸。
Base64 解码一下,不对不可能。
于是卡在这里了。
看到别人 WP 的时候发现蠢死了,想到 base64 解码没想到反过来编码……
然而人家
二维码
里给提示了,只是提示不在结尾而已。呐,USEBASE64TOGETYOURFLAG.
下次得先用
strings
来看看……
于是 flag 就是 flag{MTg3NDg1NjE4NTIx}
。
找资料的时候发现了有大佬在单片机上实现 DTMF 软解码的。
在线生成某种特定音调的网站:http://onlinetonegenerator.com/dtmf.html
其他一些资料:
另一个在线 DTMF 生成工具 DTMF Tone Generator
一个在线 DTMF 解码工具 Detect DTMF Tones(好像上不去)
GitHub项目:
DTMF_Detector 📞 Using Matlab to simulate Dual-Tone Multi-Frequency (DTMF) of telephone
DTMF-Detection-Goertzel-Algorithm- DTMF detection system developed on AVR Atmega128 board based on Goertzel algorithm
武汉加油
发现附件只有一张图片↓。
首先祝武汉加油,中国加油,向医护人员致敬。
发现了文件拼接了一个 RAR 文件,foremost 提取不出来,懒得开 Ubuntu 用 binwalk 了,于是直接手动提取出压缩包。
得到一个flag.exe
.
什么?MISC 题还要 RE???
然而看了一下发现解不了,好像加壳了。
随便猜了一下和武汉加油相关的 flag 交上去,都不对……
看了大佬们的 WP 发现是 flag{zhong_guo_jia_you}
… 中国加油!
(佩服直接猜出来的)
原来图片里还隐写了密码,用 steghide 爆破提取出
flag.txt
,可以得到密码' 武 汉 加 油 !
,输入到flag.exe
就可以得到 flag。(emmm
ez_mem&usb
MISC 是真的杂……
captured.pcap
抓包文件,可以在一堆流量中提取出一个上传文件的页面upload_file.php
。
从提交的 form 里可以提取出一个 data.zip
.
压缩包里是一个 data.vmem
,噫一个虚拟机内存镜像。
懵了,没玩过内存取证的题目唉(好菜啊
看题目后面应该还需要提取 USB 的数据,大概 flag 在 USB 数据里吧。
后面有空再学学 volatility 内存取证。
Web
sqlcheckin
就是输入用户名和密码登录那种,用 PHP 的 PDO 来连接数据库。用户名需要为admin
。
毕竟是 checkin 题,感觉应该不会太难。然而试了试常用的万能密码发现并没有效果,都被 Deny 了。
然后队友在网上搜到了原题—— 湖南省第三届大学生信息安全水赛WriteUp.
'-0-'
可以代替'or'1
绕过。于是构造参数如下即可。
username=admin&password='-0-'
立个 flag,后面好好学一下 SQL(
webtmp
源码:
import base64
import io
import sys
import pickle
from flask import Flask, Response, render_template, request
app = Flask(__name__)
class Animal:
def __init__(self, name, category):
self.name = name
self.category = category
def __repr__(self):
return f'Animal(name={self.name!r}, category={self.category!r})'
def __eq__(self, other):
return type(other) is Animal and self.name == other.name and self.category == other.category
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == '__main__':
return getattr(sys.modules['__main__'], name)
raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def restricted_loads(s):
return RestrictedUnpickler(io.BytesIO(s)).load()
def read(filename, encoding='utf-8'):
with open(filename, 'r', encoding=encoding) as fin:
return fin.read()
@app.route('/', methods=['GET', 'POST'])
def index():
if request.args.get('source'):
return Response(read(__file__), mimetype='text/plain')
if request.method == 'POST':
try:
pickle_data = request.form.get('data')
if b'R' in base64.b64decode(pickle_data):
return 'No... I don\'t like R-things. No Rabits, Rats, Roosters or RCEs.'
else:
result = restricted_loads(base64.b64decode(pickle_data))
if type(result) is not Animal:
return 'Are you sure that is an animal???'
correct = (result == Animal(secret.name, secret.category))
return render_template('unpickle_result.html', result=result, pickle_data=pickle_data, giveflag=correct)
except Exception as e:
print(repr(e))
return "Something wrong"
sample_obj = Animal('mmm','aaaa')
pickle_data = base64.b64encode(pickle.dumps(sample_obj)).decode()
return render_template('unpickle_page.html', sample_obj=sample_obj, pickle_data=pickle_data)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
(可能有些地方被我不小心改了
看到 pickle 想到了反序列化,之前只听说过 PHP 有反序列化漏洞,这里才知道原来 python 也有。
这次正好就来了解一下 pickle 反序列化吧。
后面再记一下这一块的笔记吧(大概率咕咕咕
可以用 pickletools 这个反汇编工具来帮助理解 pickle 代码,即opcode
。
比如这里把name
设成 'mmm'
,category
设为 'aaaa'
,而后生成 pickle,可以看到各个命令的含义。
$ python3 -m pickletools -a ./data
0: \x80 PROTO 3 Protocol version indicator.
2: c GLOBAL '__main__ Animal' Push a global object (module.attr) on the stack.
19: q BINPUT 0 Store the stack top into the memo. The stack is not popped.
21: ) EMPTY_TUPLE Push an empty tuple.
22: \x81 NEWOBJ Build an object instance.
23: q BINPUT 1 Store the stack top into the memo. The stack is not popped.
25: } EMPTY_DICT Push an empty dict.
26: q BINPUT 2 Store the stack top into the memo. The stack is not popped.
28: ( MARK Push markobject onto the stack.
29: X BINUNICODE 'name' Push a Python Unicode string object.
38: q BINPUT 3 Store the stack top into the memo. The stack is not popped.
40: X BINUNICODE 'mmm' Push a Python Unicode string object.
48: q BINPUT 4 Store the stack top into the memo. The stack is not popped.
50: X BINUNICODE 'category' Push a Python Unicode string object.
63: q BINPUT 5 Store the stack top into the memo. The stack is not popped.
65: X BINUNICODE 'aaaa' Push a Python Unicode string object.
74: q BINPUT 6 Store the stack top into the memo. The stack is not popped.
76: u SETITEMS (MARK at 28) Add an arbitrary number of key+value pairs to an existing dict.
77: b BUILD Finish building an object, via __setstate__ or dict update.
78: . STOP Stop the unpickling machine.
highest protocol among opcodes = 2
看了半天反序列化漏洞,大部分都是利用__reduce__
方法来实现任意代码的执行。
如果没有输出的话,可以反弹 shell 到自己的服务器上,利用网站访问日志来得到需要的信息。
(这时我看了看服务器的 nginx log,发现日常被扫,常见的 admin、phpmyadmin 目录,甚至还有想跨站执行 rm -rf /
的……
话说回来,当我写好 __reduce__
方法 post 上去之后,发现被拒绝了。不过代码里也有体现。
然而想要将 callable 对象执行都必须要调用 reduce
,在 pickle 中都会涉及 R
这个字母。
于是乎不能在服务器上通过执行自己的代码来获取 flag。
后面突然想到,你不让我 RCE,我可以改掉你的 secret
中的 name
和 category
啊!
这里要手搓 pickle 了。当时的思路是调用 secret
模块的内容对 Animal 进行实例化。然而比较菜,不知道为啥出了点问题。
后来又想到读取 secret 文件读取属性信息进而获取 flag,不过没有具体的思路,于是到这里就卡住了。
后来看到大佬的 WP,发现果然是**直接改掉这个
secret
**。不过具体不是调用
secret
模块,而是直接在__main__
下建一个自己的secret
类,设置自己的属性,覆盖掉 import 进来的secret
。再利用自己设置的属性信息去校验就完事了。payload = b"\x80\x03c__main__\nsecret\n} (X\x04\x00\x00\x00nameX\x03\x00\x00\x00233X\x08\x00\x00\x00categoryX\x03\x00\x 00\x00233ub0c__main__\nAnimal\n)\x81} (X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00233X\x08\x00\x00\x00categoryX\x03\ x00\x00\x00233ub."
另一个 payload:
b"c__main__\nsecret\np0\n0g0\n(}(S'name'\nS'asd'\ndtbg0\n(}(S'category'\nS'123'\ndtb\x80\x04\x95=\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06Animal\x94\x93\x94)\x81\x94}\x94(\x8c\x04name\x94\x8c\x03asd\x94\x8c\x08category\x94\x8c\x03123\x94ub." # base64 Y19fbWFpbl9fCnNlY3JldApwMAowZzAKKH0oUyduYW1lJwpTJ2FzZCcKZHRiZzAKKH0oUydjYXRlZ29yeScKUycxMjMnCmR0YoAElT0AAAAAAAAAjAhfX21haW5fX5SMBkFuaW1hbJSTlCmBlH2UKIwEbmFtZZSMA2FzZJSMCGNhdGVnb3J5lIwDMTIzlHViLg==
下面这个链接里有关于 pickle 及其反序列化漏洞的更完整的介绍。
🐍 Security Issues in Python Pickle
(Scientific Network needed
What is serialization, How to serialize data, Pickle in CTF Challenges, Implementation of Pickle, Vulnerabilities in Wild, Mitigation strategies && Best practices…
里面讲到了几个例题
2019-SJTU-Pickle
2019-SJTU-Pickle-Revenge-Back
2018-HITB-Python-Revenge
2019-SJTU-Pickle-Revenge (←这个题就和这里这题几乎一个套路
不允许R
,他这里的注入思路也是调用 secret
模块的内容对 Animal 进行实例化,有点想不明白我写的哪里出问题了(
他还进一步实现了对文件的读取。最后的结论是,放弃使用pickle
。毕竟太危险了呀!
Extensive Reading:
Else
其他的题大概看了看。。不过没什么思路或者有思路不知道怎么实现,好菜啊……
(怎么大佬们动不动随手就一个 payload 搞定了嘤嘤嘤
小结
再回过头来看这次 CTF 的比赛名称,高校战“疫”网络安全分享赛。
还是挺有意思的吧。主题结合了当下的热点,题目也比较应景,也挺正能量的。
COVID-19 面前,不仅仅需要中国努力,更需要全世界一起行动起来,共同与疫情进行较量。
这是整个人类共同面临的困难和挑战,从中也更能意识到人类命运共同体的含义了。
武汉加油,中国加油,世界加油!
再看 CTF,感觉涉及的面不是一般的广,更多的是想来学点东西吧,你看人家大佬都比你厉害呢。
其中的 MISC 题经常需要不小的脑洞,比如 武汉加油、隐藏的信息 之类的题目就是呢。
或许这就是CTF的魅力吧!
可能文中有的表述不大严谨,不要太在意(
这篇其实写的也很水
(溜