LINUX.ORG.RU

Не приходит ответ на SYN-запрос

 , ,


0

1

Уже пару дней бьюсь…

Это исходник скрипта.

Интересующий фрагмент:

logger.info("start")

src_ip = get_local_ip()
src_port = random.randint(30000, 50000)

logger.debug("local ip address   : %s", src_ip)
logger.debug("local port         : %d", src_port)

dst_ip = socket.gethostbyname(args.address)
dst_port = args.port

logger.debug("remote ip address  : %s", dst_ip)
logger.debug("remote port        : %d", dst_port)

ip_header = IPHeader(
    src_ip,
    dst_ip,
    total_len=60,
    # ident=secrets.randbits(16),
    flags=IPHeader.Flags.MF,
)

# чексумму подставит роутер,если ее не указать
ip_header.check = checksum(ip_header.pack())

options = [
    (TCPOption.Kind.MSS, bytes.fromhex("05b4")),  # 1460
    (TCPOption.Kind.SACK_PERMIT,),
    # Не знаю что в TSVal поставить
    (TCPOption.Kind.TS, secrets.randbits(32).to_bytes(4) + b"\0\0\0\0"),
    (TCPOption.Kind.NOP,),
    # (TCPOption.Kind.WIN_SCALE, 7),
    (TCPOption.Kind.WIN_SCALE, 0),
]

tcp_header = TCPHeader(
    src_port,
    dst_port,
    seq_num=secrets.randbits(32),
    flags=TCPHeader.Flags.SYN,
    data_off=10,  # 40 bytes,
    options=options,
)

tcp_header.check = checksum(tcp_header.pack())

syn_packet = ip_header + tcp_header
logger.debug(syn_packet)

data = syn_packet.pack()
logger.debug("packet data : %s", data.hex(" "))
logger.debug("packet size : %d", len(data))
assert len(data) == ip_header.total_len


try:
    # socket.IPPROTO_TCP чтобы сниффить
    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
except PermissionError as ex:
    raise RuntimeError("root priveleges are required")

# просим не добавлять ip заголовки при отправке (для socket.IPPROTO_RAW не нужно)
# s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# ip.dst == 178.248.233.6 and tcp.flags.syn == 1 (www.linux.org)
# ip.dst == 85.119.149.3 ?
# ip.dst == 93.184.216.34 (example.com)

if 0 == (n_bytes := s.sendto(data, (ip_header.dst_ip, 0))):
    raise RuntimeError("packet was not sent")


logger.debug("bytes sent: %d", n_bytes)
response, addr = s.recvfrom(65535)

logger.debug("response : %s", response.hex(" "))

WireShark’ом я вижу, что запрос отправляется на сервер, НО ОТВЕТА не приходит.

Я проверял чексуммы рассчитываются вроде правильно, добавил всякие таймстемпы:

INFO:__main__:start
DEBUG:__main__:local ip address   : 192.168.0.104
DEBUG:__main__:local port         : 46986
DEBUG:__main__:remote ip address  : 178.248.233.6
DEBUG:__main__:remote port        : 443
DEBUG:__main__:Compound(items=[IPHeader(src_ip='192.168.0.104', dst_ip='178.248.233.6', version=4, ihl=5, tos=0, total_len=60, ident=0, flags=<Flags.MF: 2>, frag_off=0, ttl=64, protocol=6, check=56748), TCPHeader(src_port=46986, dst_port=443, seq_num=3635193239, ack_num=0, data_off=10, reserved=0, flags=<Flags.SYN: 2>, window=32120, check=5903, urg_ptr=0, options=[(<Kind.MSS: 2>, b'\x05\xb4'), (<Kind.SACK_PERMIT: 4>,), (<Kind.TS: 8>, b'r2\t\xf2\x00\x00\x00\x00'), (<Kind.NOP: 1>,), (<Kind.WIN_SCALE: 3>, 0)])])
DEBUG:__main__:packet data : 45 00 00 3c 00 00 40 00 40 06 dd ac c0 a8 00 68 b2 f8 e9 06 b7 8a 01 bb d8 ac a5 97 00 00 00 00 a0 02 7d 78 17 0f 00 00 02 04 05 b4 04 02 08 0a 72 32 09 f2 00 00 00 00 01 03 03 00
DEBUG:__main__:packet size : 60
DEBUG:__main__:bytes sent: 60

Вроде пакет правильно собирает. Но куда ответ приходит? Я пробовал без TCP опций отправлять, пробовал Ethernet заголовки добавлять (ну чтобы как в WireShark). У меня идея по SYN-ASC спалить открытый порт, но у меня получился только SYN-флуд… Я упустил какой-то момент. Кто может подсказать какой? ю

Ответ на: комментарий от peregrine

там чексумма tcp пакета неверная, может потому и не приходит. для ip haeder она правильно считается, а для остального нет. сраный питон и его сранные числа

rtxtxtrx ★★
() автор топика

Раз уж ты взял пистон, возьми уже Scapy и не насилуй себе мозг.

>>> p = IP(dst="178.248.233.6") / TCP(sport=31337, dport=80, flags='S')
>>> r = sr1(p)
Begin emission:
Finished sending 1 packets.
....................................*
Received 37 packets, got 1 answers, remaining 0 packets
>>> r
<IP  version=4 ihl=5 tos=0x28 len=44 id=14598 flags=DF frag=0 ttl=57 proto=tcp chksum=0xc087 src=178.248.233.6 dst=172.16.0.7 |<TCP  sport=www_http dport=eldim seq=735151389 ack=1 dataofs=6 reserved=0 flags=SA window=5792 chksum=0x1153 urgptr=0 options=[('MSS', 536)] |<Padding  load='\x00\x00' |>>>

Смари, я ЛОР попинговал.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 2)
Ответ на: комментарий от hateyoufeel

Ты не понимаешь смысл затеи - это изучение протокола, а питон этот как язык для быстрого прототипирования с которого потом можно переписать все на чем-то более быстром. Там просто чексума tcp-пакета неправильно считалось, потому что я по привычке трахаюсь в глаза и дальше первого предложения абзаца не читаю

rtxtxtrx ★★
() автор топика
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от rtxtxtrx

а питон этот как язык для быстрого прототипирования

Я вот это самое «быстрое прототипирование» никогда не понимал. Быстрее все пишешь на том языке, который знаешь лучше всего, ИМХО.

смысл затеи - это изучение протокола

А что ты там хочешь «изучить»? Не проще взять tcpdump/wireshark и разглядывать работу протоколов, сколько хочешь?

pathfinder ★★★★
()
Ответ на: комментарий от ValdikSS

Ответ может не приходить из-за того, что пакет не уходит в сеть или уходит совсем не туда.

Сначала tcpdump-ом нужно убедиться, что пакет ушел и все адреса (включая mac-адрес назначения) правильные.

vel ★★★★★
()
Ответ на: комментарий от vel

Я забил на этот скрипт… Неделя прошла, решил всеже его доделать. Сейчас исправил ошибку в расчете чексумы TCP-пакета (я там неправильно его считал):

https://gist.github.com/s3rgeym/89477852a8a63dd789da91b5ec63c83c

Сам скрипт я с нуля переписал, чтобы не 100500 строк было, где я досканально все проверяю и по честному разбираю…

Я сейчас смотрю WireShark. SYN - ACK приходит, но сам скрипт зависает:

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
n = s.sendto(packet, (args.address, 0))
logger.debug("bytes sent: %d", n)
assert n > 0

# Доходит до сюда и не отдает мне ответ
response, addr = s.recvfrom(1024)
logger.debug(addr)
logger.debug("raw response: %s", response.hex(" "))

Короче хз откуда его читать, учитывая что исходный порт я ставлю от балды, наверное, надо перехватывать все пакеты (socket.IPPROTO_TCP)

rtxtxtrx ★★
() автор топика
Ответ на: комментарий от rtxtxtrx

raw-сокет должен получать все.

Запусти программу через strace. Если программа не получит ответный пакет, значит пакет грохнуло ядро на приёме. Через «netstat -s» можно посмотреть не увеличиваются ли ошибки.

А если пакет в программу пришел, то значит засада где-то в питонячей части.

Для начала, не надо ничего посылать, нужно открыть сокет и в цикле читать. И в это время с этой же машины сделай куда-нибудь соединение. Если ты не увидишь ни одного пакета, значит нужно начать внимательно изучать документацию.

vel ★★★★★
()
Ответ на: комментарий от vel

Да оно только так и работает:

def normalize_ports(ports: list[str]) -> set[int]:
    rv = set()
    for x in ports:
        try:
            a, b = map(int, x.split("-", 1))
            rv.update(range(a, b + 1))
        except ValueError:
            rv.add(int(x))
    return rv


WELL_KNOWN_PORTS = frozenset(
    [
        110,
        143,
        21,
        22,
        2222,
        23,
        25,
        3306,
        443,
        465,
        5432,
        587,
        5900,
        6379,
        80,
        8080,
        8443,
        9000,
        993,
        995,
    ]
)


class NameSpace(argparse.Namespace):
    address: str
    ports: list[str]
    debug: bool
    timeout: float


def parse_args(
    argv: list[str] | None,
) -> tuple[argparse.ArgumentParser, NameSpace]:
    parser = argparse.ArgumentParser()
    parser.add_argument("address", type=socket.gethostbyname)
    parser.add_argument(
        "-p",
        "--port",
        dest="ports",
        nargs="+",
        help="port or range of ports",
    )
    parser.add_argument("-t", "--timeout", type=float, default=15.0)
    parser.add_argument("-d", "--debug", action="store_true", default=False)
    args = parser.parse_args(argv, namespace=NameSpace())
    return parser, args


def main(argv: list[str] | None = None) -> None:
    _, args = parse_args(argv)

    if args.debug:
        logging.basicConfig()
        logger.setLevel(logging.DEBUG)

    ports = normalize_ports(args.ports) if args.ports else WELL_KNOWN_PORTS

    local_ip = get_local_ip()

    sniff_t = threading.Thread(
        target=sniff,
        args=(local_ip, args.address, ports),
    )

    sniff_t.daemon = True
    sniff_t.start()

    with socket.socket(
        socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW
    ) as s:
        for port in ports:
            src_port = random.randint(30000, 50000)
            packet = make_syn_packet(local_ip, src_port, args.address, port)
            n = s.sendto(packet, (args.address, 0))
            logger.debug("bytes sent: %d", n)

    sniff_t.join(args.timeout)


def sniff(local_ip: str, target_ip: str, ports: set[int]) -> None:

    with socket.socket(
        socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP
    ) as s:
        while 1:
            res, addr = s.recvfrom(65535)

            if addr[0] != target_ip:
                continue

            src_ip = socket.inet_ntoa(res[12:16])

            if src_ip != target_ip:
                continue

            dst_ip = socket.inet_ntoa(res[16:20])

            if dst_ip != local_ip:
                continue

            src_port = int.from_bytes(res[20:22])
            # dst_port = int.from_bytes(res[22:24])

            # Это самый быстрый способ проверить открыт ли порт
            if res[33] == 0x012 and src_port in ports:
                print(src_ip, src_port)


if __name__ == "__main__":
    sys.exit(main())

Так-то он нормально порты перебирает:

❯ sudo ./scan_ports.py github.com
140.82.121.3 22
140.82.121.3 443
140.82.121.3 80
rtxtxtrx ★★
() автор топика
Последнее исправление: rtxtxtrx (всего исправлений: 2)
Ответ на: комментарий от tyamur

Все приходит. И порты проверяет. Оно работает. Просто странно как-то, что последовательно нельзя все делать типа сокетом отправил syn и тем же сокетом прочитал syn-ack. Можно только перехватывать все входящие пакеты и искать среди них syn-ack

anonymous
()