msantos / procket

Erlang interface to low level socket operations
http://blog.listincomprehension.com/search/label/procket
BSD 3-Clause "New" or "Revised" License
283 stars 80 forks source link

Sending TCP SYN packet from procket #25

Closed varnerac closed 8 years ago

varnerac commented 8 years ago

Hey,

I am trying to send a TCP SYN packet for a SYN scan from procket using pkt. I am on OS X and I'm using the rebar3 branch of procket and running the code as root. I'm mimicking this C code: http://www.binarytides.com/tcp-syn-portscan-in-c-with-linux-sockets/

-module(raw_syn).

-include_lib("pkt/include/pkt.hrl").

-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

-define(IP_HDRINCL, 3).

make_ipv4_syn() ->
    IPID = rand:uniform(16#ffff) - 1,
    SeqNo = rand:uniform(16#ffffffff) - 1,
    TCPHdr = #tcp{dport = 8080,
                  seqno = SeqNo,
                  syn = 1,
                  win = 5840}, % header size 20 bytes
    IPv4Hdr = #ipv4{id = IPID,
                    df =  1,
                    len = 40, % 20 for TCPHdr and 20 for IPv4HDR
                    saddr = {127, 0, 0, 1},
                    daddr = {127, 0, 0, 1}}, % header size 20 bytes
    IPCheckSum = pkt:makesum([IPv4Hdr, TCPHdr, <<>>]),
    I = pkt:ipv4(IPv4Hdr#ipv4{sum=IPCheckSum}),
    T = pkt:tcp(TCPHdr),
    <<I/binary, T/binary>>.

-ifdef(TEST).
%% based on http://www.binarytides.com/tcp-syn-portscan-in-c-with-linux-sockets/
make_ipv4_syn_test() ->
    Packet  = make_ipv4_syn(),
    {ok, Socket} = procket:open(0, [{protocol, tcp}, {type, raw}, {family, inet}]),
    ok = procket:setsockopt(Socket, ?IPPROTO_IP, ?IP_HDRINCL, <<1:32/native>>),
    ok = procket:write(Socket, Packet),
    ok.
-endif.

I receive **error:{badmatch,{error,enotconn}} from ok = procket:write(Socket, Packet). Is there something simple I am missing?

msantos commented 8 years ago

Use procket:sendto/4. Similar to an unconnected datagram packet, since the OS doesn't know where the packet is being sent, we have to provide a struct sockaddr_in.

Here's a version of your code I tested on linux:

-module(raw_syn).

-export([
        make_ipv4_syn/0,
        make_ipv4_syn_test/0
    ]).

-include_lib("pkt/include/pkt.hrl").

-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

-define(IP_HDRINCL, 3).

make_ipv4_syn() ->
    IPID = rand:uniform(16#ffff) - 1,
    SeqNo = rand:uniform(16#ffffffff) - 1,
    TCPHdr = #tcp{dport = 8080,
                  seqno = SeqNo,
                  syn = 1,
                  win = 5840}, % header size 20 bytes
    IPv4Hdr = #ipv4{id = IPID,
                    df =  1,
                    len = 40, % 20 for TCPHdr and 20 for IPv4HDR
                    saddr = {127, 0, 0, 1},
                    daddr = {127, 0, 0, 1}}, % header size 20 bytes
    IPCheckSum = pkt:makesum([IPv4Hdr, TCPHdr, <<>>]),
    I = pkt:ipv4(IPv4Hdr#ipv4{sum=IPCheckSum}),
    T = pkt:tcp(TCPHdr),
    <<I/binary, T/binary>>.

-define(IPPPROTO_TCP, 6).
%% based on http://www.binarytides.com/tcp-syn-portscan-in-c-with-linux-sockets/
make_ipv4_syn_test() ->
    Packet  = make_ipv4_syn(),
    IPPROTO_TCP = case os:type() of
        {unix,linux} -> procket:ntohs(?IPPROTO_TCP);
        _ -> ?IPPROTO_TCP
    end,
    {ok, Socket} = procket:open(0, [{protocol, IPPROTO_TCP}, {type, raw}, {family, inet}]),
    ok = procket:setsockopt(Socket, ?IPPROTO_IP, ?IP_HDRINCL, <<1:32/native>>),
    Sockaddr_in = <<
        (procket:sockaddr_common(?PF_INET, 16))/binary,
        8080:16,    % destination port
        127,0,0,1,  % destination IPv4 address
        0:64        % pad
        >>,
    procket:sendto(Socket, Packet, 0, Sockaddr_in).

Sending the SYN:

$ sudo tcpdump -n -s0 -X -i lo port 8080
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
17:32:53.585891 IP 127.0.0.1.0 > 127.0.0.1.8080: Flags [S], seq 2076729285, win 5840, length 0
        0x0000:  4500 0028 28ec 4000 4006 13e2 7f00 0001  E..((.@.@.......
        0x0010:  7f00 0001 0000 1f90 7bc8 5fc5 0000 0000  ........{._.....
        0x0020:  5002 16d0 0000 0000                      P.......

The source port is 0 because we didn't set it in make_ipv4_syn/0. The other quirk is that Linux requires the protocol number to be in host byte order while, if I remember correctly, the BSDs use network byte order. Thanks for reminding me about the rebar3 branch, I forgot to merge it!