shinobe179の日記

@shinobe179 の技術メモ・やらかし録

ここ最近のいいねまとめ(2020/06/30)

はじめに

ここ最近のいいねまとめました。

セキュリティ業界に就職したいなら

MVP。全ての某書読者は朝晩声に出して読むべき。

脅威インテリジェンスの教科書

以前読んだ『インテリジェンス駆動型インシデントレスポンス』とセットで、あらためてインプットしたい。

t.co

AWS 認定セキュリティ専門知識対策本

出るらしいです。たぶん買う。

www.amazon.co.jp

技術を知らないマネージャー

自分にあって相手にないものが自分の価値。資格だけエンジニアとかも叩かれがちだけど、分母だけが大きくなっていくのは相対的に自分の価値が高まることになるので、そういう観点からは(独善的だとは思うけど)割と好ましく思っています。

バグバウンティを狙う際には、絶対にルールを読みましょう。

はい。

Tsunami 試してみた

デモとは言え、出力される情報が結構心許ないか気がする。 CVE 番号とかが出てくれるとすごく嬉しいんだけど。これなら OpenVAS のほうがいいかなと思ったけど、 OpenVAS に比べて手軽に使えるのが魅力か。

NTT フレッツ光における通信速度などの現状について、背景や仕組みから正しく理解する 2020

既知の内容っぽかったけど解像度が段違いだった。本題から外れるけど、「引っ越しで光配線方式をツモりたい」は参考にしたいところ。

htn.to

ハニーポットまとめ

構築したい気持ちと、構築したところでちゃんとお世話しないだろうなという気持ちがある。

github.com

AWS 認定ソリューションアーキテクト プロフェッショナル 対策本

買った。

www.amazon.co.jp

tcpdump チートシート

ええやん。

https://cdn.comparitech.com/wp-content/uploads/2019/06/tcpdump-cheat-sheet.jpg

(おわり)

【VirtualBox】ゲスト OS を放置してたら DHCP で割り当てられる IP アドレスが消えていたときの対処

はじめに

ゲスト OS の Linux を放置しているといつの間にかインターネットに接続できなくなっていて、 ip aip r すると NAT インターフェイスがなくなってデフォゲがなくなっていることがあります。 sudo systemctl restart network-manager とか sudo ifconfig eth0 downup とかしても解決しないので、 ゲスト OS を再起動していました。

結論

ゲスト OS で sudo dhclient したら、無事 IP アドレス/デフォゲ共々復活しました。リース期限切れなのか、はたまた別の理由なのかは不明です。

参考(「linux ipconfig renew」でググったら見つけた): https://www.atchfactory.com/mvt/cat2/linux/ipconfig-renew-ubuntu.html

【Python】テキストファイルを開いて for で読み込むときは改行コードに注意しましょう(自戒

はじめに

あまりに初歩的過ぎて書くまでもないと思ったけど、そういうのだからこそ(自分のために)残しておく価値があると言い聞かせて書く。とは言え初歩的過ぎて凹んでいるので Twitter に流すのはやめておこうと思う。

結論

テキストファイルを for 文で 1 行ずつ読み込むときは改行コードがくっついてくるので必要に応じて処理する。

発端

CTF for Beginners 2020 の Spy に挑戦している最中、事前情報として提示された社員名簿 (employee.txt) を 1 行ずつ for 文で読み込んでログインを試行するスクリプトを組んだ。

import requests
import string
import json

URL = 'https://spy.quals.beginners.seccon.jp/'
with open('employees.txt') as f:
    for u in f:
        data = {
            'name': u,
            'password': ''
        }
        r = requests.post(URL, data=data)
        print(f'status: {r.status_code}, user: {u}, time: {r.text}') # 所要時間だけパースするのが面倒なので、 HTML ごとテキストに書き出して別途 grep した

見立てが正しければ、システムにアカウントが存在する場合とそうでない場合とで、ログイン処理にかかる時間に差が生じるはずだが、有意な差は得られなかった。しかし、手作業で 1 アカウントずつ確認していったら有意差が確認できた。結局、 flag は手作業頼りで取った。

原因

flag を取った後に data を print したら以下のようになっていた。 \n が含まれているせいで全アカウント存在しないものとして扱われたから、有意差が得られなかったということが分かった。

$ python3 enumerate.py
{'name': 'Arthur\n', 'password': ''}
{'name': 'Barbara\n', 'password': ''}
{'name': 'Christine\n', 'password': ''}
...

対策

rstrip()\n を除去すればよい。そもそも予想通りでない時点で print デバッグすればよかったし、 VSCode とか IDE を使っていればすぐに気づけた気がする。

import requests
import string
import json

URL = 'https://spy.quals.beginners.seccon.jp/'
with open('employees.txt') as f:
    for u in f:
        data = {
            'name': u.rstrip(),
            'password': ''
        }
        r = requests.post(URL, data=data)
        print(f'status: {r.status_code}, user: {u}, time: {r.text}') 

参考

qiita.com

【プログラミング】AtCoder ABC164-D の解法に関するメモ

はじめに

1 年以上ぶりの ABC は C 完でした。 D の理解にすごく苦労したので、メモを残します。

アプローチ

たくさんの解説記事を拝読しましたが、皆さん editorial と同じアプローチのようで、それを踏襲しました。大まかには以下のような感じです。

  • 1つ目の for 文で、 s の各区間の mod 2019 の結果の数を mods に保存する。
  • 2つ目の for 文で、 mods に格納された mod 2019 の結果が同一になる組み合わせの数を足し上げる。

atcoder.jp

s = input()[::-1]
v = 0
mods = [ 0 for i in range(2019) ]
mods[0] = 1
d = 1
 
for c in s:
    v += int(c) * d
    v %= 2019
    d *= 10
    d %= 2019
    mods[v] += 1
 
ans = 0
for n in mods:
    ans += (n * (n - 1)) // 2
 
print(ans)

なんで 10 と 2019 が互いに素だと、/ 10n を無視してよくなるの?

ここからは、私が個人的に理解できなかった部分の補足です。

この部分については、どの解説記事にも特に解説がなかったと思います。解説するまでもないことなのかもしれませんが、数学弱者が直感的にこの結果に辿り着くのは厳しかった……。

Twitterで明け方までうんうん唸っていたら、解説記事を投稿してくださっていたタムログさんが解説してくださいました。

※タムログさんの解説記事はこちら

タムログさんの例にのっかると、 2019 / 3 の mod 2019 を計算するときに、分母を無視すると 2019 ≡ 0 (mod 2019) ですが、分母を考慮に入れると 2019 / 3 = 693 ですから、 693 !≡ 0 (mod 2019)となって、結果が変わってしまう(分子だけだと合同だけど、分母込みだと合同でなくなる)わけです。これがなぜ起こるかというと、分母が 2019 と互いに素でない値だと、2019 の因数を除してしまうからですね。

以下は、分子を 20190 、分母を 10 もしくは 3 としたときの、それぞれの分母なし・ありでの合同式の真偽を確認したメモです。最後のほう、 6930 に見えますが 6730 です。

f:id:befs_anne:20200502200149j:plain

なんで mods[0] = 1 するの?

前述したような処理で解が求められるのは、「(区間A - 区間B) % 2019 ≡ 0 (mod 2019)」であるかどうかを、「区間A ≡ 区間B (mod 2019)」であることを検証することで求められるからです。各区間の mod 2019 の結果の中にはもちろん 0 も含まれるわけですが、区間 A が s[len(s-1)]、つまり s の右端の値を含む場合、区間 B は 0 ということになります。この場合の区間 B ついても区間のひとつとして扱い、0 ≡ 0 (mod 2019) としてカウントしなければ、仮に s の右端の値を含む区間 A の mod 2019 が 0 だった場合に、組み合わせの数が正しく計算されません。

なんで v %= 2019 するの? mod の結果は別で保存したほうがよくない?

n 回目のループで mod しようがしまいが、 n+1 回目のループでの v %= 2019 の結果には影響を及ぼしません(mod 2019 の結果に再度 mod 2019 しても値は変わらない)。計算効率の観点から見ると、v に mod した値を使うことで、後述の d %= 2019 の処理と同様効率を高めていることになると言えそうです。

なんで d %= 2019 するの?していいの?

これをしないと、ループ毎に d が 10 ずつ増えていくので、そのまま乗じてしまうと桁数がかなり大きくなってしまって v %= 2019 の処理に時間がかかってしまうんですね(実際、これがないと TLE になりました)。これをして結果に影響が及ばないかどうかについては、「(3 * 10000) % 2019」 の結果と 「(3 * 1924) % 2019」(※) の結果が、どちらも 1734 であることを以て証明できている気がします。

※…10000 % 2019 = 1924

さいごに

緑コーダー目指して、解けない問題に真剣に向き合ってみました。ふつうに延べ 1 日くらい費やしました。しんどかったです。数学やアルゴリズムの基礎理解が乏しいので厳しい戦いが続きそうですが、強くなるためにやっていこうと思います。

【プログラミング】複数キー、異順ソート(AtCoder ABC128-B)

はじめに

目から鱗だったのでメモします。異順ソートという言葉はないと思いますが便宜上名付けました。

問題

市名 点数 が複数個渡されるので、市名は昇順、かつ同じ市名なら点数は降順にして、番号(渡された順に1から採番)を出力する。

atcoder.jp

ポイント

各キーで昇順/降順が異なるソートをどのようにおこなうか?

私の実装

以下の記事を参考に、優先度が低いものから2回ソートした。

qiita.com

atcoder.jp

n = int(input())
rs = [ [i+1, input().split()] for i in range(n) ]

rs = sorted(rs, key=lambda a:int(a[1][1]), reverse=True)
rs = sorted(rs, key=lambda a:a[1][0])

for r in rs:
    print(r[0])

解説によると

https://img.atcoder.jp/abc128/editorial.pdf

問題文に合うようにレストランを並べるには、C++ でいう pair 型を利用すると簡単に実装できます。具 体的には、pair<pair<string,int>,int> の配列を用意し、レストランの情報を格納します。

ここまでは自力で辿り着いた。

first.second (2 番目のソート基準) に int 型で点数の −1 倍を入れ (点数が高い順に並べたいので)、

本来なら点数は降順でソートしなければならないが、-1倍することで市名と同じ昇順に揃えられるようにしてソートを1回で済ませてしまう、ということ。この発想はなかった。

試してみた

かなりシンプルな実装になった。sorted()のkeyを使ってキーを指定することをしないので、pair内の値の順番も重要になる。

atcoder.jp

n = int(input())
rs = []
 
for i in range(n):
    s,p = input().split()
    p = int(p)
    rs.append([[s, -p], i+1])
 
for r in sorted(rs):
    print(r[1])

柔軟な発想。。

『実践 bashによるサイバーセキュリティ対策』はセキュリティに興味があってbashに慣れていない人にすすめたい

はじめに

『実践 bashによるサイバーセキュリティ対策』を読みました。

公式: www.oreilly.co.jp

内容

第Ⅰ部では、bash正規表現の基礎的な使い方、セキュリティの CIA (機密性、完全性、可用性) + αとサイバーキルチェーンの考え方をさらっとやります。

第Ⅱ、Ⅲ、Ⅳ部では、第Ⅰ部で学んだ基礎を踏まえて、「セキュリティ関連のタスクをいかにして bash でこなすか」を、豊富なサンプルと共に示してくれています。

なんで bash …?

これについては 1 章で早々に触れていて、「技術的に見てもスクリプト言語として見てもクロスプラットフォームである」から、「セキュリティ業務における理想のテクノロジと位置付けている」としています。大体どんな Linux でも使える上に、最近じゃ Windows でも使えちゃいますからね。セキュリティの文脈に限らず、bash が便利な道具のひとつであることには、私も異論ありません。

紹介されているコマンドは LPIC レベル 1 相当のものがほとんど

だいたい以下のような顔ぶれです。もちろんこれが全てではないです。

Linux 触り始めた頃に欲しかった

正直、普段から業務で bash を駆使している方にとっては、読んでいて目から鱗が落ちることはそれほどなさそうです。

この本のいいところは、そういう中・上級者を満足させるようなニッチな情報ではなくて、基本的なコマンドに「このコマンドで、セキュリティ関係のこういうタスクをこなせます」という分かりやすい実例がついていることにあると思っています。grep を使ってログ検索とか、 curl を使って Web クローリングとか。練習に使えるサンプルコードも提供されています。

github.com

ゆえにこの本は初~中級者向けの本だと思っていて、漠然とセキュリティエンジニアに憧れていたものの Linux のこと全く分からなかった数年前の私にこの本があったら、テンションブチ上がってたこと間違いなしです。あの頃の私に必要だったのは、あずき色のお堅い参考書ではなく、ヘビがあしらわれたいかつい表紙のわりに、例が豊富で親切なこの本だったのです。

まとめ

我ながら「そんなやついねえだろ」というタイトルだったんですが、よくよく思い返してみれば自分がそうだったという話でした。もし同じような方がいらっしゃったらお手に取ってみてください。ボリュームもそれほどではないので、テンションブチ上げでこなしているうちに、あっという間に bash が苦じゃなくなるはずです。

【Python】リストや辞書を複製するときは copy() しなきゃいけないし、それらに値として更にリストや辞書が含まれているなら copy.deepcopy() しましょう(自戒

はじめに

はまったのでメモします。

リストや辞書を代入で複製しようとするとハマる

a_dict の構造をそのまま b_dict でも流用して、値だけ b に変えたれ~と思ったとき、代入によってそれをしようとすると、 a_dict['id'] まで b に変わってしまいます。調べるに、どうやらこれが参照渡しというやつらしい。基本情報で出てきた(落ちたけど

>>> a_dict = {'id': 'a'}
>>> a_dict
{'id': 'a'}
>>> b_dict = a_dict
>>> b_dict
{'id': 'a'}
>>> b_dict['id'] = 'b'
>>> b_dict
{'id': 'b'}
>>> a_dict
{'id': 'b'} # ここは a のままでいてほしい
>>>

解決策: copy() を使う

copy() を使うことで、参照渡しでなく値渡しをしましょう。

>>> a_dict = {'id': 'a'}
>>> a_dict
{'id': 'a'}
>>> b_dict = a_dict.copy()
>>> b_dict
{'id': 'a'}
>>> b_dict['id'] = 'b'
>>> b_dict
{'id': 'b'}
>>> a_dict
{'id': 'a'}
>>>

copy() で複製しようとしたリストや辞書の値に、リストや辞書が含まれているとハマる

今度は a_dict['pattern'] に配列を加えてから、 copy() で b_dict を作る。で、配列の内容を b っぽいものに変更すると、 a_dict['pattern'] も b っぽい内容に……なったはずなんだけど、ならない。 Python のバージョンが上がって(以下の REPL は 3.8.1) 、挙動が変わったのかな?

>>> a_dict = {'id': 'a', 'pattern': ['a', 'A']}
>>> a_dict
{'id': 'a', 'pattern': ['a', 'A']}
>>> b_dict = a_dict.copy()
>>> b_dict
{'id': 'a', 'pattern': ['a', 'A']}
>>> b_dict['id'] = 'b'
>>> b_dict['pattern'] = ['b', 'B']
>>> b_dict
{'id': 'b', 'pattern': ['b', 'B']}
>>> a_dict
{'id': 'a', 'pattern': ['a', 'A']} # a_dict['pattern'] も ['b', 'B'] になっちゃってた気がするんだが

……まあいいや、なったとして。それはガワの辞書は copy() によって値渡しできてるけど、中身のリストや辞書は参照渡しされているからみたいです。 Python のリファレンス中ではそれぞれ「浅いコピー」「深いコピー」と呼ばれていて、今回みたいにリストや辞書(クラスなども)が入れ子になっているオブジェクトを「複合オブジェクト」と呼ばれています。

docs.python.org

解決策: copy.deepcopy() を使う

import copy して、 b_dict = copy.deepcopy(a_dict) すると解消します。

...

コピーって、本当に奥が深いですネ!

shinobe179拝