CTF | DASCTF May & BJDCTF3rd 部分WriteUp


引言

poster

哇呜,转眼间一个月又过去了。这个月事情挺多的,当然也比较懒,一直咕着没来更新博客。

前两天水了一下 DASCTF 五月赛 & BJDCTF3rd,这次不是新生赛了,还是和企业合办的,于是难度大的亚批,打得都自闭了。(没理由吹新生赛了嘤嘤嘤。

有一种好久没打 CTF 的感觉,这次上手挺生疏的(?

题目看上去挺多的,确实也是,然而就不怎么会做嘛。(摊手

题目

官方问题汇总文档见这里。(包括 hint 等)

这次主要来看看 Misc 题吧,其他的随意看看好了。(反正也不会做

比赛结束后题目环境已经关了,随便写写8.



Reverse

一闪一闪亮晶晶~(请将flag经过md5后提交)

hint1: 完整的图案可以扫一扫

hint2: http://taqini.space/2020/04/30/shell-output-control-code

hint3: 此题有多种解法,不一定要硬把flag逆出来

这题本来是想逆向解的,就是他用了不少输出控制码,看了一下挺复杂的。

可以扫一扫,明确就是二维码之类的了。

一闪一闪的,突然想到截图然后叠加就好了呀!

断开连接后再一看,他的实现是一屏一屏刷的,每一次记录都保留在终端里,直接终端里复制结果,再写个 jio 本就好了呀!

Exp:

python大法好呀!

我这里分别复制了10次闪烁的结果到文件里,考虑10次应该够了。

图形是25*25的,用一个矩阵来存是0还是1.

之后再转换到 [0, 255] uint8 类型,用 PIL 作图好了。

# -*- coding:utf-8 -*-

from PIL import Image
import numpy as np

table = np.zeros((25, 25), dtype=int)
# print(table)

for i in range(1, 11):
    with open('{}.txt'.format(i), 'r', encoding='utf-8') as fin:
        row = 0
        while row < 25:
            line = fin.readline()
            print(line)
            for j in range(0, 49, 2):
                if line[j] == 'x':
                    table[row][j//2] = 1
            row += 1


a = [[0 if b == 0 else 255 for b in row] for row in table]
qrcode = Image.fromarray(np.uint8(np.array(a)))
qrcode.show()
qrcode.save('qrcode.png')

这里图片反色了还有点小啊哈哈哈。问题不大,放大来扫就是了。

qrcode

扫码得到

BJD{TW1NKLE_TW1NKLE_L1TTLE_5TAR}

真是一闪一闪亮晶晶~

MD5 后得到

e1eded5a7f630a2d02d3fe17b786919a

哇好耶,这题好难得啊,是 17 个做出来的之一唉。



Misc

Questionnaire

叮~ 您有一份调查问卷~ 请查收~ https://forms.gle/Vmzt99LazrtXsRLM9

flag格式:32位字符串

关于问卷问题答案:请使用英文或拼音作答。

问卷第七题hint

离中国科学院大学最近的长城

答案为长城名字的拼音,例如:

Badaling

答对每题都会提示flag的一部分

Flag一共7部分

除了大学和公园外

其余题目都是拼音

这是什么签到题???

最开始以为是填了之后放flag的,发现并不是。

填了几个发现错误提示,和 TaQini 师傅 py 了一下,果然是一个题给一部分 flag,最后连起来。(不过到最后也放 hint 了哈哈哈

借助百度识图、搜狗识图、谷歌识图,好不容易才整明白是啥。。(感觉找个老北京人要快得多呢2333

图片分别对应着:

BIT 北理工 d41d

好邻居 8cd9

稻香村 8f00b2

jingweizhai 04e9

Jingshan Park 景山公园 8009

chaoyang 朝阳 98ecf8

hefangkou 河防口 427e

(最后这个长城查了半天,搜索引擎告诉我怀柔区的长城是慕田峪(确实)。。后来和 TaQini 师傅聊到说这是个鲜为人知的长城……

连起来得到 flag:

d41d8cd98f00b204e9800998ecf8427e

P.S.: 后面查不出来在网页里一搜,发现还是谷歌的套路,是用 JavaScript 传入的数据。里面就有匹配的逻辑了。

(还是 F12 大法好呀!

js

var FB_PUBLIC_LOAD_DATA_ = [null,["Let's see how well you know our Capital.",[[1790362738,"What is the name of this university?",null,0,[[336510603,null,0,null,[[4,302,["Beijing Institute of Technology|BIT"]
,"d41d"]
]
]
]
,null,null,null,null,[["1vV5T8FOS13NOQDji-xYIynLwsUMXcV8aatxUWP6ljvfz-w",null,[740,416,0]
]
]
]
,[603160739,"What is the name of the store?",null,0,[[539054317,null,0,null,[[4,302,["Haolinju|haolinju"]
,"8cd9"]
]
]
]
,null,null,null,null,[["1x4dT2M6J3EbaiVZ37ssMVunsnsB2UMCM6g4LCHyhlHJu-Q",null,[740,416,0]
]
]
]
,[488094967," What BRAND is this food?",null,0,[[1465781074,null,0,null,[[4,302,["Daoxiangcun|daoxiangcun"]
,"8f00b2"]
]
]
]
,null,null,null,null,[["1lH3bwgs28QoVKcUYhtzoqAcacmh4n4CHyWjGQen4RiE3Jw",null,[375,458,1]
]
]
]
,[1097246628,"Which RESTAURANT are the ducks coming from? ",null,0,[[353762320,null,0,null,[[4,302,["Jingweizhai|jingweizhai"]
,"04e9"]
]
]
]
,null,null,null,null,[["11ym4QgB0WEymoJXlmFy7FTC5Eyd5rV1adBbw6vWN5PmXvw",null,[740,555,0]
]
]
]
,[1916058196,"Which PARK is this?",null,0,[[901636349,null,0,null,[[4,302,["Jingshan|jingshan"]
,"8009"]
]
]
]
,null,null,null,null,[["16pfH3k5-5kDo-Rb9BxeKRvx0S-Qy4IgUdlX8iJ0AUOBIwQ",null,[740,554,0]
]
]
]
,[1044111735,"Which DISTRICT is the No.3 of Beijing?","The restaurant in question4 is in this Distric",0,[[1620980704,null,0,null,[[4,302,["Chaoyang|chaoyang"]
,"98ecf8"]
]
]
]
,null,null,null,null,[["1VbfGqSSHlM9D_HY1TsENa6rle3axBYbtKdyHS_klYDLG5g",null,[740,371,0]
]
]
]
,[1877231084,"Which part of the Great Wall is this?","In Huairou Distric",0,[[1337434564,null,0,null,[[4,302,["Hefangkou|hefangkou"]
,"427e"] //...


/bin/cat 2

[CAUTION] cats as numerous as stars are coming… 解出的答案经md5后提交~

2005205ec4f1a07a3e9

有两种喵喵,还是动图唉,远着看就是二维码嘛!(果然是 TaQini 师傅出的题

首先把宽度压缩到原来一半,本来想 python 写个jio本的,PIL 感觉有点麻烦还不大会。

于是一波骚操作重新上个色好了。(你肯定想不到我是在 word 中处理的吧,嘿嘿嘿

PS 也行,Stegsolve 里 gif 截取一帧然后丢去取某通道某 bit 信息/随机上色/etc. 都行。

重新上色

扫码得到

[email protected]~Mia0~m!aO~m1a0~~~

喵喵喵喵喵喵~

md5加密得到32位flag。

9b84eb9e7107ffafebeb1000e8c05322

(看到还真有手搓二维码的 tql



test your nc

183.129.189.60:10118,10119,10120,10121,10122,10123

hint1: /f1a9.bak 你看到了嘛?

hint2: 用df命令看看硬盘总共多大,再看看flag多大。

hint3: 系统命令不够使?试试万能的python

先来看看 /f1a9.bak

$ cat /f1a9.bak
#!/usr/bin/python
#coding=utf-8

from random import randint
from uuid import uuid4

flag = [hex(i)[2:-1] for i in uuid4().fields]

f=open('/home/ctf/flag','w')
f.write('🐀')
f.seek(1024*1024*1024)
f.write('flag{')
offset = 1024
for i in flag:
    f.seek(1024*1024*1024*randint(offset,offset+2048))
    offset += 2048
    f.write(i)
f.write('}')
f.seek(1024*1024*1024*randint(offset,offset+2048))
f.write('good job!')
f.close()

这里的uuid正好就查了一下。

UUID: 通用唯一标识符 ( Universally Unique Identifier ), 对于所有的UUID它可以保证在空间和时间上的唯一性. 它是通过MAC地址, 时间戳, 命名空间, 随机数, 伪随机数来保证生成ID的唯一性, 有着固定的大小( 128 bit ). 它的唯一性和一致性特点使得可以无需注册过程就能够产生一个新的UUID. UUID可以被用作多种用途, 既可以用来短时间内标记一个对象, 也可以可靠的辨别网络中的持久性对象.

python 的 uuid 模块提供UUID类和函数uuid1(), uuid3(), uuid4(), uuid5() 来生成1, 3, 4, 5各个版本的UUID ( 需要注意的是: python中没有uuid2()这个函数). 对uuid模块中最常用的几个函数总结如下:

  1. uuid.uuid1([node[, clock_seq]]) : 基于时间戳

    使用主机ID, 序列号, 和当前时间来生成UUID, 可保证全球范围的唯一性. 但由于使用该方法生成的UUID中包含有主机的网络地址, 因此可能危及隐私. 该函数有两个参数, 如果 node 参数未指定, 系统将会自动调用 getnode() 函数来获取主机的硬件地址. 如果 clock_seq 参数未指定系统会使用一个随机产生的14位序列号来代替.

  2. uuid.uuid3(namespace, name) : 基于名字的MD5散列值

    通过计算命名空间和名字的MD5散列值来生成UUID, 可以保证同一命名空间中不同名字的唯一性和不同命名空间的唯一性, 但同一命名空间的同一名字生成的UUID相同.

  3. uuid.uuid4() : 基于随机数

    通过随机数来生成UUID. 使用的是伪随机数有一定的重复概率.

  4. uuid.uuid5(namespace, name) : 基于名字的SHA-1散列值

    通过计算命名空间和名字的SHA-1散列值来生成UUID, 算法与 uuid.uuid3() 相同.

(来自网络)

看来是在 1024*1024*1024*randint(offset,offset+2048) 的随机位置写入了flag。

再来看看硬盘大小和文件大小。

$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
overlay         16197544 2011556  14169604  13% /
tmpfs              65536       0     65536   0% /dev
tmpfs             507956       0    507956   0% /sys/fs/cgroup
/dev/vda1       16197544 2011556  14169604  13% /etc/hosts
shm                65536       0     65536   0% /dev/shm
tmpfs             507956       0    507956   0% /proc/acpi
tmpfs             507956       0    507956   0% /proc/scsi
tmpfs             507956       0    507956   0% /sys/firmware
$ ls -al
total 64
drwxr-x--- 1 root ctf            4096 May 22 06:40 .
drwxr-xr-x 1 root root           4096 May 19 03:09 ..
-rwxr-x--- 1 root ctf             220 May  5  2019 .bash_logout
-rwxr-x--- 1 root ctf            3771 May  5  2019 .bashrc
-rwxr-x--- 1 root ctf             807 May  5  2019 .profile
-rw-r----- 1 root ctf  16150150774793 May 22 06:40 flag
-rw-r----- 1 root ctf              23 May 19 11:05 readme

硬盘这么小,怎么flag文件这么大(16TB+ ???),佛了。

这个 nc 有15s的时间限制,还有执行内存限制之类的。

试了一下没有stringsgrep 出不来 flag。(被kill了?

考虑到 hint 中提示可以用py来整。环境中只有py2.

python -c "open('./flag','r').read()"

这样会炸内存。

考虑分块读取,然而好像又有限制。佛了。


20200605 更新:

看了看出题的 TaQini 师傅的出题笔记,这里顺便参考一下。

之所以生成的 flag 如此之大,是因为生成的脚本中的 seek 函数在将文件偏移的指针往后移动一段之后,在写入的有效数据(flag)之间留下了一个个由 \x00 组成的空洞(hole),这些 hole 是不占用(不分配)磁盘空间的。因此题目中用df看硬盘空间还是很小的。

而用lseek函数能跳过 hole 去读取数据。

可以用 man lseek 来看看函数手册。

off_t lseek (int fd, off_t offset, int whence);

The lseek() function repositions the offset of the open file associated with the file descriptor fd to the argument offset according to the directive whence.

fd:文件描述符;

offset: 移动文件位置的偏移数,以字节为单位;

whence 为系统定义的常量,可能的值有SEEK_SETSEEK_CURSEEK_ENDSEEK_DATASEEK_HOLE

SEEK_SET
The offset is set to offset bytes. 文件读写位置从文件开始往后移动offset个字节。

SEEK_CUR
The offset is set to its current location plus offset bytes. 文件读写位置从当前位置开始移动offset个字节。offset可以为负值。

SEEK_END
The offset is set to the size of the file plus offset bytes. 文件读写位置从文件结尾移动offset个字节。offset可以为负值。

这里要用到的是 whence 参数为 SEEK_DATA,man 中还对空洞(hole)的原理作了解释。

Seeking file data and holes

​ Since version 3.1, Linux supports the following additional values for whence:

SEEK_DATA
Adjust the file offset to the next location in the file greater than or equal to offset containing data. If offset points to data, then the file offset is set to offset.

SEEK_HOLE
Adjust the file offset to the next hole in the file greater than or equal to offset. If offset points into the middle of a hole, then the file offset is set to offset. If there is no hole past offset, then the file offset is adjusted to the end of the file (i.e., there is an implicit hole at the end of any file).

In both of the above cases, lseek() fails if offset points past the end of the file.

These operations allow applications to map holes in a sparsely allocated file. This can be useful for applications such as file backup tools, which can save space when creating backups and preserve holes, if they have a mechanism for discovering holes.

For the purposes of these operations, a hole is a sequence of zeros that (normally) has not been allocated in the underlying file storage. However, a filesystem is not obliged to report holes, so these operations are not a guaranteed mechanism for mapping the storage space actually allocated to a file. (Furthermore, a sequence of zeros that actually has been written to the underlying storage may not be reported as a hole.) In the simplest implementation, a filesystem can support the operations by making SEEK_HOLE always return the offset of the end of the file, and making SEEK_DATA always return offset (i.e., even if the location referred to by offset is a hole, it can be considered to consist of data that is a sequence of zeros).

大概也就是说,这些空洞是零序列,它们通常在底层文件存储中没有分配空间。

SEEK_DATA 是跳过 hole 寻找下一个数据,SEEK_HOLE是跳过数据寻找下一个 hole。

在 python 中的 os 模块也有lseek这个函数,于是可以用来写exp了。

考虑到没有环境了,这里利用/f1a9.bak中的内容在本地生成了一个 flag,而后参照 TaQini 师傅的脚本来读取 flag。

首先看一下 flag 有多大。

# ls -al
total 56
drwxr-xr-x 2 root root           4096 6月   5 22:06 ./
drwxr-xr-x 8 root root           4096 6月   5 21:48 ../
-rw-r--r-- 1 root root 15007689474057 6月   5 21:48 flag

在命令行可以用 python -c <command> 来执行 py 语句,多行的话用三个引号引起来就完事了(我也才知道命令行也可以这样多行的嘤嘤嘤)

Exp:(在命令行执行)

python -c """
import os
fd = os.open('/home/ctf/flag',0)
off = 0
flag = ''
while off < 15007689474057:
    off = os.lseek(fd,off,3) # SEEK_DATA
    off += 1
    a = os.read(fd,1)
    if a != '\x00':
        flag += a
print flag
"""

nc_flag

即可得到flag了!


这里还有另一种方法,那就是利用 grep 大法

看到有师傅说,用 grep -z 可以单行语句出 flag,不过没有具体给出如何实现,于是就自己试了一下。

看了看 grep --helpman grep

-z, –null-data 一个 0 字节的数据行,但不是空行

-z, –null-data
Treat the input as a set of lines, each terminated by a zero byte (the ASCII NUL character) instead of a newline. Like the -Z or –null option, this option can be used with commands like sort -z to process arbitrary file names.

-z就是说把零字符看成是输入的分行标志,可以理解是把 \x00 当成分行符,正好就符合这题的意思了,妙呀!

试了一阵子,发现可以这样来读取 flag 文件:

grep -z ".\+" flag
# Or
grep -z "." flag

nc_flag2

想不到吧,grep 还能这么用呢。



babyweb

flag格式:把BJD{this_is_flag}转md5,提交md5值(小写)

hint: 第三组密码中的某一位,以下图的解密结果为准:

百度图片搜出来的密码表是Q,以原著为准 改成w,再根据图片的提示改成W;如果还提交失败就是其他位有解错的。

有个flag.zip,里面有个图片密码加密了。

这题网页上看到那个 password_is_here 之间的空字符还觉得奇怪,做题的时候就没想到是零宽度字符隐写(Zero-Width Space Steganography)……原来还有用到网页上的。

解密网站参考:

http://zero.rovelast.com/

https://offdev.net/demos/zwsp-steg-js

GitHub 仓库参考:

zwsp-steg-py (基于 python 实现)

zwsp-steg-js (基于 JavaScript 实现)

解出来密码是zerowidthcharactersinvisible

拿去解压图片,发现文件倒序了。

(留坑,后面接着复现)



manual

请使用ssh连接靶机(用户名为ctf,密码详见欢迎信息)———— 歡迎使用BJD3rd帮助手册————「manual一下,你就知道」

183.129.189.60:10125,10126,10127,10128,10129,10130

hint0 试试limux的man命令,看看与本题的man文档有何不同

hint0.5 关于登录密码,试试emoji网站的分享功能

hint1 本题无需提权,请仔细看看根目录下的文件

hint2 据说会ps的师傅都做出来了

hint3 f1a9.py的独白:我的真实身份是web server

这题有毒,做了一天没做出来……大概写写吧。

ssh login

% ssh [email protected] -p 10128

Welcome to BJD3rd Games ~

🐀🐾🌴🚜🍋🐊🍇🐂🍓🎑🐈🐟💁🚟🍗

The above login passwd is encrypted.

leads:
 - http://emoji.taqini.space
 - suika

Try to figure out where is your

         #           #####   #####  
######  ##     ##   #     # #     #
#      # #    #  #  #     #       #
#####    #   #    #  ######    ###  
#        #   ######       #    #    
#        #   #    # #     #         
#      ##### #    #  #####     #    

p.s. Maybe you have lots of xiaowenhao after login,
     I will help u look up the manual pages of flag.

Now, input passwd to start the game:

http://emoji.taqini.space 是 TaQini 师傅部署的 codemoji 加解密网站。

suika在日语中是 西瓜 的意思,看来这就是 key 了。

随意试着加密了一个,在分享界面浏览器抓包得到分享链接形式为

http://taqini.space/codemoji/share.html?data=eyJtZXNzYWdlIjoi8J%2BNi%2FCfjonwn4y38J%2BOkfCfjYvwn46J8J%2BMt%2FCfjpHwn42L8J%2BOifCfjLfwn46RIiwia2V5Ijoi8J%2BNiSJ9

data 参数 urldecode + base64 解码后得到

{"message":"🍋🎉🌷🎑🍋🎉🌷🎑🍋🎉🌷🎑","key":"🍉"}

于是想到修改message参数构造 payload 如下。

{"message":"🐀🐾🌴🚜🍋🐊🍇🐂🍓🎑🐈🐟💁🚟🍗","key":"🍉"}

base64 encode + urlencode:

eyJtZXNzYWdlIjoi8J%2BQgPCfkL7wn4y08J%2BanPCfjYvwn5CK8J%2BNh%2FCfkILwn42T8J%2BOkfCfkIjwn5Cf8J%2BSgfCfmp%2Fwn42XIiwia2V5Ijoi8J%2BNiSJ9

构造分享链接得到:

http://taqini.space/codemoji/share.html?data=eyJtZXNzYWdlIjoi8J%2BQgPCfkL7wn4y08J%2BanPCfjYvwn5CK8J%2BNh%2FCfkILwn42T8J%2BOkfCfkIjwn5Cf8J%2BSgfCfmp%2Fwn42XIiwia2V5Ijoi8J%2BNiSJ9

拿去解码。

codemoji

得到ssh连接密码:

C0dEmOj!so4UnNy

man flag

还别说,挺好看的233.

flag

看了 Help 才发现这是 w3m。还带右键菜单的哇!

w3m是个开放源代码的文字式网页浏览器。

w3m支持表格、框架、SSL连线、颜色。如果是在适当的terminal上,甚至还支持“inline image”。 这个软件通常尽量呈现出网页本来的编排。

“w3m”这名字是来自“WWW-wo-Miru”,就是日文的“看WWW”之义。

哇是真的浏览器唉,还可以上我博客(雾),虽然解析不了中文嘤嘤嘤。

blog

借助快捷键U分别访问givemeflaggetflag

givemeflag

getflag

HISTORY 里给了个网址 https://www.jianshu.com/p/ac26b4506a38

讲的是自定义man手册

查看/etc/manpath.config文件。

manpath.config

对比一下好像并没有什么区别(???

manpath

V 可以查看文件,但是找不到 f1a9.py

Option 里可以改配置用外部浏览器打开,考虑是反弹 shell。

(不过么有 curl,不会了……嘤嘤嘤

(看大佬们是用 perl 整的。

这题后面还有好几步……有空再复现8.

最后是一个 http://999.taqini.space 太极?会玩。



PY me

请提交md5(“BJD{xxxx}”);

使用nc连接; 尽量用后面的端口,10020端口不太稳定

183.129.189.60:10020,10021,10022,10023,10024,10026

flag格式为md5(BJD{.*})上交md5值

使用nc连接靶机,建议用后面端口号

hint1: 所有没有使用hint()中给出的函数做出题目的方法都是非预期,如果您是非预期请报告我们,所以 从哪里入手应该懂了吧?

hint2: if( #s#o#m#e#t#h#i#n#g##I##w#o#n#t##t#e#l#l##y#o#u# ) 这过滤了全部字母和数字,当然还过滤了其他符号,需要自己fuzz(socket pwntool等)

hint3: python几?

打错字了,是result,不影响做题

题目给了 hint 说过滤了全部字母和数字,当然还过滤了其他符号

试了发现不仅仅如此,常用的一下函数也被过滤了,比如 ossysopenlambdasystemexecevalbuiltins等等。

函数名称用字符串拼接可以绕过,但不知道用什么方法能绕过来调用函数了。

看了下 dir(),大概有这几个 False, __builtins__ , hint, rceMe

题目中的 if False,直接令 False=True 就行,毕竟 python 里都是对象 object 2333.

本来想改 str 的,但怎么样都绕不过 if( #s#o#m#e#t#h#i#n#g#_#I#_#w#o#n#t#_#t#e#l#l#_#y#o#u# ) 这部分判断。

利用报错知道了源码文件名称为 bjdpyjail.py,不过好像没啥用。

考虑用字符来绕过,手动试验发现包括但不限于 ^.; 被过滤了。

说到这难受死了,有空得学学 pwntools,直接控制台手码代码太jr难受了,还不能快捷调用上一条执行命令,无法接受。

这么说来,异或 ^ 不行,但取反 ~ 可以,< >也可。

查了一下网上有关于 PHP 的字符绕过,然而在 python 里不能直接对字符本身取反,要先取ord,然后想了半天都想不到该怎么绕了……

(摊手,等复现

赛后看到大佬找到了奇妙的绕过方法,把源码读取出来了。(详见 这里

vars(locals()['_'+'_buil'+'tins_'+'_'])["op"+"en"]('bjdpyjail.py', 'r').read()

读取源码

预期解详见出题师傅 Y1ng 师傅的这一篇:

[译]PlaidCTF中一道PyJail逃逸题目分析



Pwn

TaQiniOJ-0

欢迎来到TaQini的C语言新手训练营

(没有附件,盲打)

183.129.189.60:10075,10076,10077,10078,10079,10080

taqinioj

oj2

这里告诉了flag所在目录为 /home/ctf/flag

好久没写C了,突然都忘记文件操作了。被 python 的open惯坏了唉。

试了一下,还有 defender 唉。flag被过滤了。

#include <stdio.h>
#include <string.h>
int main(){   
    FILE *fp;
    char name[20]="/home/ctf/fl";
    char tmp[1000]={0};
    printf("Hello TaQini");
    strcat(name,"ag");
    printf("%s\n", name); 
    fp=fopen(name,"r");
    fscanf(fp, "%s", tmp);
    printf("%s", tmp);
    fclose(fp);

    return 0;
}
@

defender

发现好像敏感词不仅仅是flag,还有home啥的……然而赛后才发现,气死我了。

可以sprintf来拼接字符串。

(想起来某次做项目给单片机编程就靠这玩意拼接字符串,被 python 惯坏的我写的难受死了。

sprintf(&name, "%s%s%s%s%s", "/hom", "e","/ct", "f/f", "lag");

当然还可以直接用字符数组赋值的方法。

char name[15] = { '/','h','o','m','e','/','c','t','f','/','f','l','a','g','\0' };

赛后群里有大佬给出了如下的非预期解,太佩服了!

#include "/home/ctf/fl\\
ag"

非预期解

哇呜,用报错来弹flag!虽然之前写错语法也报错了,但这脑洞真没想到。果然是 OJ 大佬。

同样漏洞可用于另一个 OJ 题。



Crypto

bbcrypto

# -*- coding:utf-8 -*-
import A,SALT
from itertools import *

def encrypt(m, a, si):
    c=""
    for i in range(len(m)):
        c+=hex(((ord(m[i])) * a + ord(next(si))) % 128)[2:].zfill(2)
    return c
if __name__ == "__main__":
    m = 'flag{********************************}'
    a = A
    salt = SALT
    assert(len(salt)==3)
    assert(salt.isalpha())
    si = cycle(salt.lower())
    print("明文内容为:")
    print(m)
    print("加密后的密文为:")
    c=encrypt(m, a, si)
    print(c)
    #加密后的密文为:
    #177401504b0125272c122743171e2c250a602e3a7c206e014a012703273a3c0160173a73753d

通过assert可以知道salt是三个字母组成的。

cycle返回一个迭代器 iterator,每一次调用 next 返回下一个字符。

需要根据 flag{} 解出ASALT,然后再代回去反解 flag 中的字符。取模的话想不到别的方法应该也是爆破解。

考虑是爆破来解A& SALT,然而我a的爆破范围太大了设到了 1000 然后就没整出来……((

赛后看人家大佬解出来A是57,saltahh

(之后再复现8



小结

最终的解题情况如下。

解题情况

Web 的小红花这么多人解出来,啊是我太菜了。题目把出网流量封了,弹不了shell,赛后看群里大佬们是用时间盲注怼出来的。

总之,这次题目质量感觉挺高的,难度是真的大得亚批,两天打下来每天都在自闭……

这次做题思路比较乱,可能也比较佛系,一下看这题一下看那题,噢原来都不会做。(群里萌新又做出来了???打扰了。

然后还有大佬两周学会 pwn 然后做出了一堆题,这么容易入门的嘛,心动了。(算了8)



CyberChef

为了便于打 CTF,前天晚上做题途中自己搭了个 CyberChef,我才不会说之前一直想搭但一直没整呢。

CyberChef

网址在这里:https://chef.miaotony.xyz/

不带各种页面统计,不收集隐私信息,欢迎来玩来 BAKE 呀!

简而言之,它就是可以处理包括但不限于各种编码各种加密各种转换的一个在线工具,可以把一系列处理流程放进 菜谱 Recipe 里,然后 BAKE 就出结果啦!

下面是官方的介绍。

The Cyber Swiss Army Knife - a web app for encryption, encoding, compression and data analysis

The Cyber Swiss Army Knife

CyberChef is a simple, intuitive web app for carrying out all manner of “cyber” operations within a web browser. These operations include simple encoding like XOR or Base64, more complex encryption like AES, DES and Blowfish, creating binary and hexdumps, compression and decompression of data, calculating hashes and checksums, IPv6 and X.509 parsing, changing character encodings, and much more.

The tool is designed to enable both technical and non-technical analysts to manipulate data in complex ways without having to deal with complex tools or algorithms. It was conceived, designed, built and incrementally improved by an analyst in their 10% innovation time over several years.

官方 GitHub 仓库在这里

大概就这样了8

(溜


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