|
|
import gradio as gr |
|
|
import re |
|
|
|
|
|
def extract_email(text): |
|
|
"""Вытаскивает email""" |
|
|
match = re.search(r'[\w\.-]+@[\w\.-]+', text) |
|
|
return match.group(0) if match else None |
|
|
|
|
|
def analyze_email(headers, body): |
|
|
score = 0 |
|
|
findings = [] |
|
|
|
|
|
|
|
|
if re.search(r"SPF.*FAIL", headers, re.IGNORECASE): |
|
|
score += 10 |
|
|
findings.append("<li>SPF FAIL: Сервер не разрешен для этого домена (+10)</li>") |
|
|
|
|
|
if re.search(r"DKIM.*FAIL", headers, re.IGNORECASE): |
|
|
score += 10 |
|
|
findings.append("<li>DKIM FAIL: Подпись DKIM фальшивая (+10)</li>") |
|
|
|
|
|
from_match = re.search(r"^From:(.*)", headers, re.MULTILINE | re.IGNORECASE) |
|
|
return_match = re.search(r"^Return-Path:(.*)", headers, re.MULTILINE | re.IGNORECASE) |
|
|
|
|
|
if from_match and return_match: |
|
|
from_email = extract_email(from_match.group(1)) |
|
|
return_email = extract_email(return_match.group(1)) |
|
|
|
|
|
if from_email and return_email and from_email != return_email: |
|
|
score += 5 |
|
|
findings.append(f"<li>Отправитель ({from_email}) не совпадает с Return-Path ({return_email}) (+5)</li>") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
keywords_urgency = [ |
|
|
"заблокирован", "срочно", "удаление", "ограничен", "suspension", "urgent", "immediate", |
|
|
"остановлен", "удален", "удалён", "удалена", "остановлена", "ограничена", "заморожена", |
|
|
"заморожен", "suspended", |
|
|
|
|
|
"немедленно", "аккаунт будет заблокирован", "блокировка счета", "блокировка аккаунта", |
|
|
"служба безопасности" |
|
|
] |
|
|
|
|
|
|
|
|
keywords_action = [ |
|
|
"verify", "update", "login", "вход", "пароль", "password", "аутентификация", |
|
|
"authentication", "войдите", "требуется", "confirm", "confirmation", |
|
|
|
|
|
"подтвердите", "подтвердить", "данные карты", "конфиденциальная информация", |
|
|
"подтвердите личность", "перевод средств", "поддержка банка" |
|
|
] |
|
|
|
|
|
|
|
|
keywords_greed = [ |
|
|
"freespin", "фриспин", "casino", "казино", "lottery", "1xbet", "лотерея", "бонус", |
|
|
"распродажа", "бесплатно", "раздача", "подарок", "гифт", "giveaway", "выигрыш", |
|
|
"скидка", "prize", "gift", "win", "победитель", "winner", "поздравляем", "congratulations", |
|
|
|
|
|
"вы выиграли", "приз", "выбран случайным образом" |
|
|
] |
|
|
|
|
|
body_lower = body.lower() |
|
|
|
|
|
|
|
|
found_urgency = False |
|
|
for word in keywords_urgency: |
|
|
if word in body_lower: |
|
|
|
|
|
|
|
|
score += 2 |
|
|
findings.append(f"<li>Психологическое давление: '{word}' (+2)</li>") |
|
|
|
|
|
|
|
|
for word in keywords_action: |
|
|
if word in body_lower: |
|
|
score += 1 |
|
|
findings.append(f"<li>Запрос данных/действий: '{word}' (+1)</li>") |
|
|
|
|
|
|
|
|
for word in keywords_greed: |
|
|
if word in body_lower: |
|
|
score += 2 |
|
|
findings.append(f"<li>Приманка/Выигрыш: '{word}' (+2)</li>") |
|
|
|
|
|
|
|
|
ip_links = re.findall(r'https?://(?:[0-9]{1,3}\.){3}[0-9]{1,3}', body) |
|
|
if ip_links: |
|
|
score += 5 |
|
|
findings.append(f"<li>IP в ссылке: ({ip_links[0]}) (+5)</li>") |
|
|
|
|
|
suspicious_tlds = [".xyz", ".top", ".info", ".club", ".shop", ".site"] |
|
|
for tld in suspicious_tlds: |
|
|
if tld in body_lower: |
|
|
if re.search(rf"\w{tld}/\w", body_lower) or re.search(rf"\w{tld}\b", body_lower): |
|
|
score += 2 |
|
|
findings.append(f"<li>Подозрительный домен '{tld}' (+2)</li>") |
|
|
|
|
|
|
|
|
if score == 0: |
|
|
verdict_text = "🟢 Уровень угрозы: НИЗКИЙ. Явных признаков фишинга не обнаружено." |
|
|
findings_html = "Угроз не найдено." |
|
|
elif score < 5: |
|
|
verdict_text = f"🟡 Уровень угрозы: СРЕДНИЙ. Счет: {score}." |
|
|
findings_html = f"<ul>{''.join(findings)}</ul>" |
|
|
else: |
|
|
verdict_text = f"🔴 Уровень угрозы: ВЫСОКИЙ. Счет: {score}." |
|
|
findings_html = f"<ul>{''.join(findings)}</ul>" |
|
|
|
|
|
return str(score), verdict_text, findings_html |
|
|
|
|
|
|
|
|
headers_input = gr.Textbox(label="1. Служебные заголовки", lines=5, placeholder="Вставьте заголовки (SPF, DKIM, From...)") |
|
|
body_input = gr.Textbox(label="2. Текст письма", lines=10, placeholder="Вставьте текст письма...") |
|
|
|
|
|
score_output = gr.Textbox(label="Итоговый счет угрозы", type="text", max_lines=1) |
|
|
verdict_output = gr.Textbox(label="Финальный вердикт", type="text", max_lines=3) |
|
|
findings_output = gr.HTML(label="Детализация обнаруженных угроз") |
|
|
|
|
|
|
|
|
examples_list = [ |
|
|
|
|
|
["SPF: FAIL\nFrom: Bank <security@bank.com>\nReturn-Path: hacker@evil.ru", |
|
|
"Ваш аккаунт будет заблокирован через 24 часа. Срочно подтвердите данные карты по ссылке: http://secure-login-update.ru"], |
|
|
|
|
|
|
|
|
["SPF: PASS\nDKIM: PASS\nFrom: University <edu@uni.ru>\nReturn-Path: edu@uni.ru", |
|
|
"Уважаемый студент, напоминаем о необходимости оплатить обучение до 25 числа. Оплату можно произвести через личный кабинет университета."], |
|
|
|
|
|
|
|
|
["SPF: PASS\nFrom: Promo <promo@shop.com>", |
|
|
"Только сегодня! Вы выбраны среди 100 счастливчиков! Вы выиграли приз! Заберите свой приз на странице акции: https://promo-super-win.shop"] |
|
|
] |
|
|
|
|
|
gr.Interface( |
|
|
fn=analyze_email, |
|
|
inputs=[headers_input, body_input], |
|
|
outputs=[score_output, verdict_output, findings_output], |
|
|
title="Email Security Analyzer (Практика 3). Нуритдинов Марат (USE-26)", |
|
|
description="Инструмент для оценки фишинга.", |
|
|
examples=examples_list |
|
|
).launch() |