PyChart のPEP 8(コーディング規約)チェックを強化してみた(Mac版)

最近たまにPythonのプログラムを作成することがあるのですが、そんな中、PEP 8 というPythonのコーディング規約があることを知りました。

そこで PEP 8 を試しに使ってみようと思い調べていたところ、pythonなどの開発環境であるPyChart には、すでにそのチェック機能が実装されているとのことでした。

しかしながらそのチェック(警告)の設定がデフォルトでは「Weak Warning」となっており、あまり目立たなくなっていた為、試しに「Error」となるように設定変更してみました。

1.PyCharmの「Preference」から設定変更

PyCharmの「Preference」を選択すると、以下のポップアップ画面が表示されますので、その中から下図の通り、

「Editor」-「Inspections」-「PEP 8 coding style violation」

と選択していきます。

PyCharm – Preference

すると Severity を設定するところがありますので、「Weak Warning」から「Error」 に変えてみました。(「Warning」でもよかったですが。。)

2.自作のpythonをチェックしてみる

1.の設定変更後、PyChart の編集画面にて自作の pythonを表示させてみたところ、下図の赤枠の通り、右上にエラーが11件あることが表示されていました。

PyChart – PEP 8 エラー警告

さらにその赤枠の部分をクリックすると、画面下に新しいウィンドウが開き、エラーの一覧が表示されました。

大半が “,(カンマ)” や “#(コメント)”の後ろに「スペース」が無いというものでしたが、複数の人間で共同開発する場合は、こういった規約に従うことが重要なのだと思います。

CISSP の CPE を稼ぐ(2023〜2024)自分のセキュリティブログを申請したら簡単に10ポイントゲットできました

2023年にはいってから、それまでISC2のサイトで提供されていた Professional Magazine が News and Insights に変わってしまい、CPE ポイントを稼ぎづらくなってしまいました。(ここは個人的な感想ですが。)

その為、約1時間の視聴で1ポイント稼げる Webinarsを頼りにしてきましたが、インターネットを調べていると、自分が書いたセキュリティ関連のブログでも申請できる(しかも10ポイント!)ようなので、さっそくこのサイトで書いたブログについて申請してみました。

1.申請カテゴリの選択

ISC2のサイトにログイン後、CPE の申請画面から該当するブログの作成日付を選びます。

すると次に申請カテゴリを登録する画面が表示されますので、赤枠で囲った通り申請しました。

ISC2 – Add New CPE – Category

2.ブログ情報の詳細入力

1.で「Writing, Researching, Publishing」を選択し、下にスクロールするとタイトルなどを入れるフィールドが出てきますので、1つ1つ入れていきます。

なお、Publisher の部分は個人ブログなのでどう書こうかすこし悩みましたが、インターネットの情報を参考にして、ブログのURLを記載しておきました。
Credits の部分は、もちろん10ポイントにしました!

ISC2 – Add New CPE – Details

その後はいつも通り Domain を適当に選んで申請しました。

3.無事申請が受付られました

申請するとすぐに以下の赤枠のとおり通り受け付けられました。
残り3ヶ月で13ポイントくらい稼がないといけなかったので、この10ポイントは大きいですね。

ISC2 – CPE List

<参考サイト>

・ブログ記事を書いてCPEを申請する(晴耕雨読)https://tex2e.github.io/blog/security/cpe-credit-blo

スクレイピング:指定した文字と文字の間のhtmlデータを全て取り出す

タイトルの通り、何かのhtml タグではなく、ブラウザに表示される文字列のスタート部分とエンド部分を指定して html データを抜き出してみました。

1.Pythonプログラム

# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import requests
import re
from bs4 import BeautifulSoup

# Set the day before yesterday's date
beforeyesterday = datetime.now() - timedelta(days=17)
beforeyesterday_str = beforeyesterday.strftime("%Y/%m/%d")

# URL of the top page
url = "https://www.xxx.jp/"・・・スクレイピングサイト"/"付き
domain ="https://www.xxx.jp"・・・スクレイピングサイト"/"無し

# start_string と end_string の間のストリングを取得する
start_string = "○○の一例:"
end_string = "○○○した場合は、○○○までご連絡ください。"

# Get the HTML content of the top page
response = requests.get(url)
html = response.content.decode("utf-8")

# Use BeautifulSoup to parse the HTML content
soup = BeautifulSoup(html, "html.parser")

# Find the parent element with the "○○○情報" title
security_info = soup.find('h1', string="○○○情報").parent
#print(security_info)

# Find all <h3> tags within the parent element
h3_tags = security_info.find_all('h3')

filtered_links = [tag.a['href'] for tag in h3_tags if tag.a is not None and beforeyesterday_str in tag.a.text]

for link in filtered_links:
    if link.startswith('http'):
        print(link)
    else:
        link = domain + link
        print(link)

    # トップページから取得した指定日の記事を読み込む
    response = requests.get(link)
    html = response.content.decode("utf-8")

    # BeautifulSoupを使ってHTMLを解析
    # soup = BeautifulSoup(html, 'html.parser')
    # article = soup.find('article', class_="nc-content-list")
    # print(article)

    # texts_p = [c.get_text() for c in article.find_all('p')]
    # print(texts_p)

    # Split the target text on the start and end strings and take the middle part
    target_text = html.split(start_string)[1].split(end_string)[0].split("</p>")[0]

    print(target_text)

    # 改行コードを空文字列に置換して一つのテキストにする
    target_text = target_text.replace('\n', '')

    # <br />タグを区切り文字として順番に配列に入れる
    result_array = [text for text in target_text.split('<br />') if text]

    # 結果の出力
    print(result_array)

2.プログラムのポイント

ChatGPTと相談しながらつくったのではっきりわかってないところはありますが^^、大体以下の意味合いかと思います。

タグとストリングで検索し親タグを含めたデータを取得

# この方法<p>タグなどには使えないかもしれません。

27行目:security_info = soup.find(‘h1’, string=”○○○”).parent
これは”○○○”というストリングが含まれる<h1>タグを見つけて、その1つ上位層のタグまでのデータをsecurity_infoに渡してくれているようです。

31行目:h3_tags = security_info.find_all(‘h3’)
21行目のsecurity_infoから<h3>タグの部分だけ抜き出してh3_tagsに入れてくれているようです。

33行目:filtered_links = [tag.a[‘href’] for tag in h3_tags if tag.a is not None and beforeyesterday_str in tag.a.text]
ここやたら長いですが、結果的には
h3_tagsからbeforeyesterday_strが含まれるhrefのリンクデータのみ抜き出し、filtered_linksに入れてくれているようです。

スタートとエンドの文字列を指定して抜き出し、お尻の不要なデータを削除

57行目:target_text = html.split(start_string)[1].split(end_string)[0].strip().split(“</p>”)[0]
ここも長いですが、
htmlに読み込まれているWebページのデータについて、start_string から end_stringまでのデータを抜き出してくれているようです。
また、split(“</p>”)[0]をつけることで、お尻にくっついていた</p>タグ以降を削除しています。

3.実行例

以下、実際にプログラムを実行した際の出力結果です。

https://web.xxx.jp/faqs/xxxx
○○○のサイズがクォータ制限に達しました<br />○○○のストレージ容量が少ない<br />~ 受信メールエラー<br />○○○のストレージがいっぱいです、○○○が一時停止されています
['○○○のサイズがクォータ制限に達しました', '○○○のストレージ容量が少ない', '~ 受信メールエラー', '○○○のストレージがいっぱいです、○○○が一時停止されています']

スクレイピング:Webページの指定したH5タグの配下にあるデータを抽出する

WebページのHTMLの指定した<h5>タグの配下の、<div>タグの中のデータを取得するプログラムを作成しましたので、記録として残しておきます。

<h5 class="alert_h5">○○○の件名</h5>
<div class="dit_a">
<p>【重要】(○○○)<br>
【重要・緊急】○○○のお知らせ<br>
【○○○】○○○等の確認について<br>
<br>
※○○○<br></font></p>
</div>

1.作成したPythonプログラム

以下作成したサンプルプログラムです。

# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import requests
import re
from bs4 import BeautifulSoup

# Set the day before yesterday's date
beforeyesterday = datetime.now() - timedelta(days=15)
beforeyesterday_str = beforeyesterday.strftime("%Y%m%d")
mail_line_pattern = "From: \"[a-zA-Z0-9_.+-]+.+[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+\""
mail_pattern = "^[0-9a-zA-Z_.+-]+@[0-9a-zA-Z-]+\.[0-9a-zA-Z-.]+$"
env_mail_pattern = "<+[0-9a-zA-Z_.+-]+@[0-9a-zA-Z-]+\.[0-9a-zA-Z-.]+>"
subject_line_pattern = "Subject:"

# Initialize an empty list to store the articles
articles_beforeyesterday = []
text_beforeyesterday = []
link_beforeyesterday = []
mail_list = []
email_list = []
env_email_list = []
subject_list = []
title_list = []

# URL of the top page
url = "https://www.xxx.jp/news/"
domain ="http://www.xxx.jp"

# Get the HTML content of the top page
response = requests.get(url)
html = response.content.decode("utf-8")

# Use BeautifulSoup to parse the HTML content
soup = BeautifulSoup(html, "html.parser")

# Find all <a href> elements in the HTML
for a in soup.find_all("a",href=re.compile(beforeyesterday_str)):
    if a in articles_beforeyesterday:
        print("duplicated")
    else:
        text=a.getText()
        link=a.get("href")
        articles_beforeyesterday.append(a)
        text_beforeyesterday.append(text)
        link_beforeyesterday.append(link)

print(link_beforeyesterday)

for link in link_beforeyesterday:
    # Get the HTML content of the top page

    if link.startswith('http'):
        print(link)
    else:
        link = domain + link
        print(link)

    response = requests.get(link)
    html = response.content.decode("utf-8")

    # Use BeautifulSoup to parse the HTML content
    soup = BeautifulSoup(html, "html.parser")

    h5_tags = soup.find_all('h5', class_='alert_h5')

    for tag in h5_tags:
        if tag.get_text() == '○○○の件名':
            print(tag.find_next('p').get_text())

2.プログラムの解説

今回のポイントは2つあります。

データが指定した文字で始まっているか確認

1つ目は53 から57行目になります。

「if link.startswith(‘http’):」でlinkに入っているデータが’http’で始まっている場合、printしているだけですが、始まっていない場合、”http://www.xxx.jp”というデータを頭にくっつけています。
 これは今回対象となったサイトのリンクがいきなりディレクトリで始まっていた為です。

指定したデータが含まれるタグ配下のデータを取得

2つ目のポイントは最後の4行になります。

 「○○○の件名」が含まれる “h5″タグを見つけて、その配下の “p”タグで囲まれた文字列をピックアップしています。

3.実行結果

このプログラムの実行結果(出力)例は以下の通りです。

duplicated
['/news/xxxxxx_20230728.html', 'http://www.xxx.jp/news/xxxxx_20230728.html']
http://www.xxx.jp/news/xxxxxx_20230728.html
【重要】(○○○)
【重要・緊急】○○○のお知らせ
【○○○】○○○等の確認について

※○○○
http://www.xxx.jp/news/xxxxxx_20230728.html
【重要】(○○○)
【重要・緊急】○○○のお知らせ
【○○○】○○○等の確認について

※○○○

スクレイピング:Webページからhrefの中に指定した文字が含まれる記事を抜き出し、その記事から必要なデータを抽出する

指定したサイトから一昨日の日付を<a href>のリンクに持つ記事をピックアップし、その記事の中からメールアドレスとメール件名を抽出する pythonをChatGPT code interpreterの手も借りながら作成しましたので記録として残しておきます。

1.作成したpythonコード

最終的に作成したpythonコードは以下のとおりです。

# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import requests
import re
from bs4 import BeautifulSoup

# Set the day before yesterday's date
beforeyesterday = datetime.now() - timedelta(days=2)
beforeyesterday_str = beforeyesterday.strftime("%Y%m%d")
# mail_line_pattern = "From: \"[a-zA-Z0-9_.+-]+.+[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+\""
mail_line_pattern = "From:"
mail_pattern = "^[0-9a-zA-Z_.+-]+@[0-9a-zA-Z-]+\.[0-9a-zA-Z-.]+$"
env_mail_pattern = "<+[0-9a-zA-Z_.+-]+@[0-9a-zA-Z-]+\.[0-9a-zA-Z-.]+>"
subject_line_pattern = "Subject:"

# Initialize an empty list to store the articles
articles_beforeyesterday = []
text_beforeyesterday = []
link_beforeyesterday = []
mail_list = []
email_list = []
env_email_list = []
subject_list = []
title_list = []

# URL of the top page
url = "https://www.xxx.jp/news/"・・・スクレイピングするサイトのURL

# Get the HTML content of the top page
response = requests.get(url)
html = response.content.decode("utf-8")

# Use BeautifulSoup to parse the HTML content
soup = BeautifulSoup(html, "html.parser")

# Find all <a href> elements in the HTML
for a in soup.find_all("a",href=re.compile(beforeyesterday_str)):
    if a in articles_beforeyesterday:
        print("duplicated")
    else:
        text=a.getText()
        link=a.get("href")
        articles_beforeyesterday.append(a)
        text_beforeyesterday.append(text)
        link_beforeyesterday.append(link)

print(articles_beforeyesterday)

for link in link_beforeyesterday:
    # Get the HTML content of the top page
    response = requests.get(link)
    html = response.content.decode("utf-8")

    # Use BeautifulSoup to parse the HTML content
    soup = BeautifulSoup(html, "html.parser")
    print(link)
    mail_list=soup.find_all(string=re.compile(mail_line_pattern))
    
    for mail in mail_list:
        # email = re.findall(mail_line_pattern, mail.replace('\n', ''))
        # ヘッダーメールアドレスが日本語の場合もあるので、以下に修正
        # lstrip()・・・左端の文字(\n)を削除
    # strip(mail_line_pattern)・・・"From:"を削除
        # split('<')[0]・・・"<"以降を削除
        # replace('"', '')・・・ダブルクォーテーションを削除
        email = mail.lstrip().strip(mail_line_pattern).split('<')[0].replace('"', '')
        env_email = re.findall(env_mail_pattern, mail.replace('\n', ''))
        email_list.append(email)
        env_email_list.append(env_email)
    print(email_list)
    print(env_email_list)

    subject_list=soup.find_all(string=re.compile(subject_line_pattern))
    for title in subject_list:
        email_title_line = title.replace('\n', '')
        email_title = email_title_line.replace('Subject: ', '')
        title_list.append(email_title)
    print(title_list)

    # 配列の初期化
    mail_list = []
    email_list = []
    env_email_list = []
    subject_list = []
    title_list = []

2.pythonコード解説

指定した文字列が含まれるhref のリンクをピックアップ

38行目:for a in soup.find_all(“a”,href=re.compile(beforeyesterday_str)):
ここで、reライブラリを利用し一昨日の日付が href のリンクの中に含まれているものを抜き出しています。

指定した文字列で始まる行をピックアップ

59行目:mail_list=soup.find_all(string=re.compile(mail_line_pattern))
では、タグを指定せず、soup に取り込まれた HTMLページからmail_line_patternの正規表現に合致するものを抜き出しています。

文字列からいろいろ削除する

67行目:email = mail.lstrip().strip(mail_line_pattern).split(‘<‘)[0].replace(‘”‘, ”)
コメントも書いていますが、元々の文字列からいろいろな方法でデータを削除し、必要なものだけにしています。

3.実行結果

なお、この時の実行結果は以下の通りです。

% python3 scraping.py
python3 info-tech-center.py
duplicated
[<a href="https://www.xxx.jp/news/20230621xxxxxx.html">○○メールに関する注意喚起a</a>]
https://www.xxx.jp/news/20230621xxxxxx.html
[['From: "xx.xxx.ac.jp"'], ['From: "xx.xxx.ac.jp"'], ['From: "Postmaster@xx.xxx.ac.jp"']]
[['<xxxx-xxxxxxxx@xxx.xxxxxxx.ne.jp>'], ['<xxx@xxxxxxxxxxx.co.jp>'], ['<xxxx@xx.uec.ac.jp>']]
['xxxx@xx.uec.ac.jp 電子メール通知', 'メール認証 xxxx@xx.uec.ac.jp', 'メールボックスのストレージがいっぱいです。アカウントは停止されています。確認が要求されました']

<参考サイト>

・PythonとBeautiful Soupでスクレイピング(Qiita)(https://qiita.com/itkr/items/513318a9b5b92bd56185)
・Python配列のループ処理(Qiita)(https://qiita.com/motoki1990/items/d06fc7559546a8471392)
・図解!Python BeautifulSoupの使い方を徹底解説!(select、find、find_all、インストール、スクレイピングなど)(https://ai-inter1.com/beautifulsoup_1/#st-toc-h-19)
・Pythonで文字列を抽出(位置・文字数、正規表現)(note.nkmk.me)(https://note.nkmk.me/python-str-extract/#_12)
・サルにも分かる正規表現入門(https://userweb.mnet.ne.jp/nakama/)
・Pythonで特定の文字以降を削除する(インストラクターのネタ帳)(https://www.relief.jp/docs/python-get-string-before-specific-character.html)
・【Python】文字列を「〜で始まる/終わる/〜が含まれる」で抽出する方法(てっくらぶ)(https://www.tecrab.com/articles/python-starts-ends-with-in.html)
・[Python コピペ] 文字列内のダブルクォーテーション(”)、シングルクォーテーション(’)の削除方法(資産運用を考えるブログ)(https://kabu-publisher.net/index.php/2021/10/31/16/python_double_single1/)

ChatGPT Code interpreterを使って直接スクレイピングはできないけど、スクレイピングするpythonは作成できる

ChatGPT Code interpreterはインターネットに繋がっていないので直接 Webサイトのスクレイピングコードを作成することはできないと言われています。

ただし、以下のような質問と、WebサイトからダウンロードしたHTMLを添付してあげることにより、<ほぼ>そのまま動作するpythonコードを作成してもらうことが可能です。

添付ファイルのWebサイトにアクセスし、トップページから特定の日付の記事のタイトルとリンクをデータを抜き出すpythonコードを作成してください。
なお、日付は自動的に昨日の日付を設定し、添付のファイルは以下のURLにあるものとしてください。https://www.xxxx.com/news/

以下、Code interpreterにて作成してもらった pythonプログラムです。

後半は手直しが必要な状態ですが、もう少しきちんと指示をしてあげればそのまま動作するpythonを作ってくれるのではなかと思います。

from datetime import datetime, timedelta
import requests
from bs4 import BeautifulSoup

# Set yesterday's date
yesterday = datetime.now() - timedelta(days=1)

# Initialize an empty list to store the articles
articles_yesterday = []

# URL of the top page
url = "https://www.xxxx.jp/news/"

# Get the HTML content of the top page
response = requests.get(url)
html = response.text

# Use BeautifulSoup to parse the HTML content
soup = BeautifulSoup(html, "html.parser")

# Find all <li> elements in the HTML
for li in soup.find_all("li"):
    # Find the <time> element in the <li> element
    time_element = li.find("time")
    if time_element:
        # Extract the date from the "datetime" attribute of the <time> element
        date_str = time_element["datetime"].split("T")[0]
        date = datetime.strptime(date_str, "%Y-%m-%d")

        # Check if the date of the article is yesterday
        if date == yesterday:
            # Find the <a> element in the <li> element
            a_element = li.find("a")
            if a_element:
                # Extract the title and the link of the article
                title = a_element.text
                link = a_element["href"]
                articles_yesterday.append((title, link))

# Print the articles of yesterday
for title, link in articles_yesterday:
    print(f"Title: {title}")
    print(f"Link: {link}\n")

AbuseIPDBのSplunk用AddonであるAbuseIPDB Checkを使ってみた

タイトルのとおり、SplunkのAddonとして提供されているAbuseIPDB Check をSplunkにインストールして軽く使ってみましたので、その時の操作をここに記録しておきます。

1.Addonのダウンロード

以下のサイトからAbuseIPDB Check の Addonをダウンロードできますので、Splunkサイトにログインの上、ダウンロードします。

https://splunkbase.splunk.com/app/4903

2.Addonのインストール

Splunkダッシュボードの左上にある「Appの管理」(歯車マーク)をクリックし、Appの管理画面の右上にある「ファイルからAppをインストール」から、1.でダウンロードした tgzファイルをインストールします。

3.config.jsonファイルをコピー&編集

Macの場合、/Applications/Splunk/etc/apps/abuseipdb_check フォルダにAppの本体がインストールされます。

さらにその配下の defaultフォルダにconfig.json ファイルが置かれていますので、 defaultフォルダと同じ階層にある localフォルダにコピーします。

cd /Applications/Splunk/etc/apps/abuseipdb_check/default/
cp config.json ../local

そして、localフォルダにコピーしたconfig.jsonファイルを編集します。

config.jsonファイルを vi などで開くと以下の通り書かれていますので、「yourkeyhere」の部分の AbuseIPDBサイトの自分のアカウントからとってきた API Keyに置き換えます。

{
    "abuseip": [
            {
                    "api_key": "yourkeyhere"
            }
    ]
}

4.Splunkの再起動

3.まで実施したら以下のコマンドでSplunkを再起動します。

/Applications/Splunk/bin/splunk restart

5.AbuseIPDB Check App確認

Splunk が再起動したら、再度「Appの管理」を確認すると、以下の通りAbuseIPDB Checkが登録されているはずです。

Splunk – Appの管理

6.abuseipコマンドを試してみる

これで AbuseIPDB Check Appの abuseip コマンドが利用できるようになっているはずですので、以前対応した Virus Totalの vt4splunk と同じ要領で指定したIPの評価をAbuseIPDBからとってきます。

| makeresults
| eval testip="8.8.8.8"
| abuseip ipfield=testip
Splunk – abuseip 実行例

なお、Virus Totalと同時に評価をとってくることも可能なようです。

| makeresults
| eval testip="8.8.8.8"
| abuseip ipfield=testip
| vt4splunk ip=testip
Splunk – abuseip & vt4splunk 同時実行例

ちょっとテーブルの体裁を整えてみたのがこちら。

sourcetype=stream:http
| vt4splunk ip=dest_ip
| abuseip ipfield=dest_ip
| table _time,dest_ip, AbuseConfidence, vt_detections,vt_total_engines,vt_reputation
Splunk – abuseip & vt4splunk 同時実行例2

いい感じにAbuseIPDB と Virus Total の評価結果が表示できていると思います。

<参考サイト>

splunkbase AbuseIPDB Check(https://splunkbase.splunk.com/app/4903)
AbuseIPDB Splunk+AbuseIPDB(https://www.abuseipdb.com/splunk)

Virus TotalのSplunk用Addonであるvt4splunkを使ってみた(ハッシュ編)

Virus TotalのSplunk用Addonであるvt4splunkを使ってファイルハッシュの調査をしてみたのでここに記録しておきます。

1.ハッシュ値の準備

vt4splunk経由で確認するファイルのハッシュ値を準備します。

今回は、MISPにて収集した IoCデータの中から以下の赤枠のハッシュ値でテストしてみることにしました。

MISP – IoCサンプル1

なお、MISPではこのハッシュはまず、Payload delivery というタイプで2019-04-10 に配布されており、その後、md5 のタイプで 2019-04-12に配布されているようです。

MISP – IoCサンプル2

2.vt4splunkにハッシュデータを直接渡す

それでは先ほどのMISPから取り出したハッシュをvt4splunkを使って評価してみます。

Search画面から以下のコマンドを打つと、testhashで指定したファイルハッシュに対する Virus Totalでの評価結果を検索結果の一部として表示することができるようになります。

Splunk vt4splunk addon

これをみるとこのハッシュが最初に報告されたのが、2019-04-12 となっています。

ほぼ同時期ですが、Payload delivery というタイプでMISPを確認しておけば、2日早くこのファイルをハッシュにて検知できていたようですね。

MISP が ThreatFox から集めてきたIoCに C2 サイトかどうかの記載があるか確認してみた。

ThreatFox から収集した イベントの一例です。

misp – ThreatFox

イベントには複数の IoC が登録されていますが、それぞれの IoC についてどのマルウェアの C2 なのかが記載されています。

misp – ThreatFox

OpenCTI のAPIにPythonから Accessし、登録されたindicatorを表示できるか確認してみた

先日構築したOpenCTIに登録された indicatorにAPIからアクセスできるか確認してみました。

できれば指定した indicator をピンポイントで検索できればと思っていますが、その前に取り合えず、APIから登録した indicator をリストで表示させることができたので、ここに記録しておきます。

なお、APIの使い方については以下のサイトに記載されています。

https://github.com/OpenCTI-Platform/client-python/blob/master/docs/client_usage/getting_started.rst

1.pycti のインストール

もしOpenCTIの環境に pycti を入れていないようであれば、以下の要領でインストールしておきましょう。

sudo apt install python3-pip
pip3 install pycti

2.Indicatorの登録

冒頭で紹介したサイトにサンプルが掲載されていますので、それをすこしいじって OpenCTI に indicator を登録します。

おもに以下の太字の部分を環境に合わせて修正すれば大丈夫です。

from dateutil.parser import parse
from pycti import OpenCTIApiClient
from stix2 import TLP_GREEN

# OpenCTI API client initialization
opencti_api_client = OpenCTIApiClient("http://localhost:8080", "<ローカルに立てたOpenCTIのAPI Keyを設定>")

# Define an OpenCTI compatible date
date = parse("2023-07-16").strftime("%Y-%m-%dT%H:%M:%SZ")

# Get the OpenCTI marking for stix2 TLP_GREEN
TLP_GREEN_CTI = opencti_api_client.marking_definition.read(id=TLP_GREEN["id"])

# Use the client to create an indicator in OpenCTI
indicator = opencti_api_client.indicator.create(
    name="C2 server of the new campaign",
    description="This is the C2 server of the campaign",
    pattern_type="stix",
    pattern="[IPv4-Addr:value = '100.172.180.181']",
    x_opencti_main_observable_type="IPv4-Addr",
    valid_from=date,
    update=True,
    markingDefinitions=[TLP_GREEN_CTI["id"]],
)

上記プログラムを「create_indicator.py」という名前で保存して、以下の通り OpenCTIのサーバ上で実行します。

$ python3 create_indicator.py 
INFO:pycti.entities:Listing Threat-Actors with filters null.
INFO:pycti.entities:Reading Marking-Definition {marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da}.
INFO:pycti.entities:Creating Indicator {C2 server of the new campaign}.
OpenCTI – indicatorリスト

3.Indicatorのリスト表示

それでは次に、OpenCTI に登録した Indicator をリストで表示してみます。
プログラムについては以下のサイトのものを参考にカスタマイズしています。

https://github.com/OpenCTI-Platform/client-python/blob/master/examples/get_all_indicators_using_pagination.py

以下、カスタマイズ後のプログラムです。
実行時に「pattern」や「description」などを表示するようにしています。

from pycti import OpenCTIApiClient

# Variables
api_url = "http://localhost:8080"
api_token = "ローカルに立てたOpenCTIのAPI Keyを設定>"

# OpenCTI initialization
opencti_api_client = OpenCTIApiClient(api_url, api_token)

# Get all reports using the pagination
custom_attributes = """
    id
    pattern_type
    pattern
    created
    description
"""

final_indicators = []
data = {"pagination": {"hasNextPage": True, "endCursor": None}}
while data["pagination"]["hasNextPage"]:
    after = data["pagination"]["endCursor"]
    if after:
        print("Listing indicators after " + after)
    data = opencti_api_client.indicator.list(
        first=50,
        after=after,
        customAttributes=custom_attributes,
        withPagination=True,
        orderBy="created_at",
        orderMode="asc",
    )
    final_indicators += data["entities"]

for indicator in final_indicators:
    print("[" + indicator["created"] + "] " + indicator["pattern"] + ", " + indicator["description"])

そしてこれが実行結果です。

$ python3 get_all_indicators.py
INFO:pycti.entities:Listing Threat-Actors with filters null.
INFO:pycti.entities:Listing Indicators with filters null.
[2023-07-17T00:58:42.733Z] [domain-name:value = 'www.5z8.info'], This is the C2 server of the campaign
[2023-07-17T01:18:55.912Z] [IPv4-Addr:value = '100.172.180.180'], This is the C2 server of the campaign
[2023-07-17T01:34:48.208Z] [IPv4-Addr:value = '100.172.180.181'], This is the C2 server of the campaign

indicator やそのdescriptionがきちんと表示されています。