{"id":30,"date":"2013-04-11T01:36:44","date_gmt":"2013-04-11T06:36:44","guid":{"rendered":"https:\/\/adamkuj.net\/blog\/?p=30"},"modified":"2021-05-11T09:01:03","modified_gmt":"2021-05-11T14:01:03","slug":"u-turn-reverse-traceroute-using-scapy","status":"publish","type":"post","link":"https:\/\/adamkuj.net\/blog\/2013\/04\/11\/u-turn-reverse-traceroute-using-scapy\/","title":{"rendered":"U-Turn: Reverse Traceroute Using Scapy"},"content":{"rendered":"<p><a href=\"https:\/\/adamkuj.net\/blog\/wp-content\/uploads\/2013\/04\/u-turn.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/adamkuj.net\/blog\/wp-content\/uploads\/2013\/04\/u-turn-275x300.jpg\" alt=\"Thug means never having to say you're sorry..\" width=\"183\" height=\"200\" class=\"alignright size-medium wp-image-51\" srcset=\"https:\/\/adamkuj.net\/blog\/wp-content\/uploads\/2013\/04\/u-turn-275x300.jpg 275w, https:\/\/adamkuj.net\/blog\/wp-content\/uploads\/2013\/04\/u-turn.jpg 276w\" sizes=\"auto, (max-width: 183px) 100vw, 183px\" \/><\/a><\/p>\n<p>I&#8217;ve always wanted to write a reverse-traceroute system. However, writing my own Linux NetFilter module was daunting &#8211; I&#8217;m not a C programmer, let alone a kernel hacker. Then I came across <a title=\"Scapy\" href=\"http:\/\/www.secdev.org\/projects\/scapy\/doc\/\">Scapy<\/a>.<\/p>\n<p>While intercepting and mangling the packets in userspace does add some artificial latency, it is a good proof-of-concept. And of course, it supports both IPv4 and IPv6:<\/p>\n<p><!--more--><\/p>\n<p>[python collapse=&#8221;true&#8221; title=&#8221;uturn.py&#8221; language=&#8221;true&#8221;]<\/p>\n<p>#! \/usr\/bin\/env python<br \/>\nfrom scapy.all import *<br \/>\nimport nfqueue<br \/>\nimport asyncore<br \/>\nfrom socket import AF_INET, AF_INET6<br \/>\nimport struct<br \/>\nimport threading<\/p>\n<p>def uturn_4(query_orig):<\/p>\n<p># if the packet is expiring at this hop, send a TTL exceeded message<br \/>\n# this shouldn&#8217;t result in the traceroute ending<br \/>\n# (that&#8217;s usually indicated by a port-unreachable message for UDP\/TCP traceroutes<br \/>\n#  or an echo-reply for ICMP traceroutes)<br \/>\nif query_orig[IP].ttl is 1:<br \/>\ninflectionpoint = IP(src=query_orig[IP].dst, dst=query_orig[IP].src)\/ICMP(type=11,code=0)\/IPerror(str(query_orig))<br \/>\nsend(inflectionpoint, verbose=0)<br \/>\nsys.stdout.flush()<br \/>\nreturn 1<\/p>\n<p># copy query_orig to query_tr_probe, so we can manipulate it<br \/>\nuturn_tr_probe = copy.deepcopy(query_orig)<\/p>\n<p># delete the IP checksum so it can be re-created<br \/>\ndel uturn_tr_probe[IP].chksum<\/p>\n<p># decrement the TTL by one to make up for the inflection point<br \/>\nuturn_tr_probe[IP].ttl -= 1<\/p>\n<p># swap src and dst IP&#8217;s before sending<br \/>\nuturn_tr_probe[IP].src = query_orig[IP].dst<br \/>\nuturn_tr_probe[IP].dst = query_orig[IP].src<\/p>\n<p># send query_tr_probe, and get the ICMP ttl-exceeded response as uturn_tr_icmp<br \/>\nuturn_tr_icmp=sr1(uturn_tr_probe, timeout=1, nofilter=0, filter=&#8221;icmp[0] = 11 and icmp[1]=0&#8243;, verbose=0)<\/p>\n<p># if we got a response back from the sr1() function,<br \/>\n# and the response was an ICMP packet&#8230;<br \/>\nif uturn_tr_icmp is not None and IPerror in uturn_tr_icmp:<br \/>\n# copy the ICMP response to uturn_response<br \/>\nuturn_response = copy.deepcopy(uturn_tr_icmp)<\/p>\n<p># delete the IP checksum so it can be automatically re-created<br \/>\ndel uturn_response[IP].chksum<\/p>\n<p># replace the IPerror message with the contents of the original packet<br \/>\nuturn_response[IPerror] =  IPerror(str(query_orig))<\/p>\n<p># rewrite the dstIP to be that of the original query<br \/>\nuturn_response[IP].dst = query_orig[IP].src<\/p>\n<p># send our u-turned ICMP TTL exceeded message<br \/>\nsend(uturn_response, verbose=0)<\/p>\n<p>sys.stdout.flush()<br \/>\nreturn 1<\/p>\n<p>def uturn_6(query_orig):<\/p>\n<p># if the packet is expiring at this hop, send a HLIM exceeded message<br \/>\n# this shouldn&#8217;t result in the traceroute ending<br \/>\n# (that&#8217;s usually indicated by a port-unreachable message for UDP\/TCP traceroutes<br \/>\n#  or an echo-reply for ICMP traceroutes)<br \/>\nif query_orig[IPv6].hlim is 1:<br \/>\ninflectionpoint = IPv6(src=query_orig[IPv6].dst, dst=query_orig[IPv6].src)\/ICMPv6TimeExceeded(type=3,code=0)\/IPerror6(str(query_orig))<br \/>\nsend(inflectionpoint, verbose=0)<br \/>\nsys.stdout.flush()<br \/>\nreturn 1<\/p>\n<p># copy query_orig to query_tr_probe, but swap src\/dst values<br \/>\nuturn_tr_probe = copy.deepcopy(query_orig)<\/p>\n<p># decrement the TTL by one to make up for the inflection point<br \/>\nuturn_tr_probe[IPv6].hlim -= 1<\/p>\n<p># swap src and dst IP&#8217;s before sending<br \/>\nuturn_tr_probe[IPv6].src = query_orig[IPv6].dst<br \/>\nuturn_tr_probe[IPv6].dst = query_orig[IPv6].src<\/p>\n<p># send query_tr_probe, and get the ICMPv6 ttl-exceeded response as uturn_tr_icmp<br \/>\nuturn_tr_icmp=sr1(uturn_tr_probe, timeout=1, nofilter=0, filter=&#8221;ip6[40] = 3&#8243;, verbose=0)<\/p>\n<p># if we got a response back from the sr1() function,<br \/>\n# and the response was an ICMPv6 Time Exceeded packet&#8230;<br \/>\nif uturn_tr_icmp is not None and ICMPv6TimeExceeded in uturn_tr_icmp:<br \/>\n# copy the ICMP response to uturn_response<br \/>\nuturn_response = copy.deepcopy(uturn_tr_icmp)<\/p>\n<p># delete the ICMP checksum so it can be automatically re-created<br \/>\n# note: scapy docs (i.e. ls(ICMPv6TimeExceeded) lists this is chksum<br \/>\n# but in reality, it is cksum &#8212; appears to be a typo\/bug in scapy<br \/>\ndel uturn_response[ICMPv6TimeExceeded].cksum<\/p>\n<p># replace the IPerror6 message with the contents of the original packet<br \/>\nuturn_response[IPerror6] =  IPerror6(str(query_orig))<\/p>\n<p># rewrite the dstIP to be that of the original query<br \/>\nuturn_response[IPv6].dst = query_orig[IPv6].src<\/p>\n<p># send our u-turned ICMP TTL exceeded message<br \/>\nsend(uturn_response, verbose=0)<\/p>\n<p>sys.stdout.flush()<br \/>\nreturn 1<\/p>\n<p>def cb(payload):<br \/>\ndata = payload.get_data()<br \/>\n# check which IP version we&#8217;re working with<br \/>\nif IP(data).version == 4:<br \/>\npkt = IP(data)<br \/>\nthread.start_new_thread(uturn_4, (pkt,))<br \/>\nelif IPv6(data).version == 6:<br \/>\npkt = IPv6(data)<br \/>\nthread.start_new_thread(uturn_6, (pkt,))<\/p>\n<p>payload.set_verdict(nfqueue.NF_DROP)<br \/>\nreturn 1<\/p>\n<p># stolen from somebody on stackoverflow<br \/>\nclass AsyncNfQueue(asyncore.file_dispatcher):<br \/>\n&#8220;&#8221;&#8221;An asyncore dispatcher of nfqueue events.<\/p>\n<p>&#8220;&#8221;&#8221;<\/p>\n<p>def __init__(self, cb, nqueue=0, family=AF_INET, maxlen=5000, map=None):<br \/>\nself._q = nfqueue.queue()<br \/>\nself._q.set_callback(cb)<br \/>\nself._q.fast_open(nqueue, family)<br \/>\nself._q.set_queue_maxlen(maxlen)<br \/>\nself.fd = self._q.get_fd()<br \/>\nasyncore.file_dispatcher.__init__(self, self.fd, map)<br \/>\nself._q.set_mode(nfqueue.NFQNL_COPY_PACKET)<\/p>\n<p>def handle_read(self):<br \/>\nself._q.process_pending(5)<\/p>\n<p># We don&#8217;t need to check for the socket to be ready for writing<br \/>\ndef writable(self):<br \/>\nreturn False<\/p>\n<p>async_queue = AsyncNfQueue(cb)<br \/>\nasyncore.loop(1) # time out of 1 second<\/p>\n<p>[\/python]<\/p>\n<p>The Python\/Scapy script gets its data from the Linux NFQUEUE. I use the following script to generate the iptables\/ip6tables rules to divert incoming traceroutes to NFQUEUE:<\/p>\n<p>[bash collapse=&#8221;true&#8221; title=&#8221;firewall-traceroute.sh&#8221;]<\/p>\n<p>#!\/bin\/bash<\/p>\n<p># generate ipv4 chains<br \/>\nfor i in {2..32}<br \/>\ndo<br \/>\nprintf &#8220;iptables -N TR%03d\\n&#8221; $i<br \/>\nprintf &#8220;iptables -A TR%03d -m recent &#8211;remove &#8211;name hop%03d\\n&#8221; $i $((i-1))<br \/>\nprintf &#8220;iptables -A TR%03d -m recent &#8211;set &#8211;name hop%03d -m ttl &#8211;ttl-eq %d -j NFQUEUE\\n&#8221; $i $i $i<br \/>\ndone<\/p>\n<p># first ipv4 hop input rule<br \/>\nprintf &#8220;iptables -A INPUT -m recent &#8211;set &#8211;name hop001 -m ttl &#8211;ttl-eq 1 -j NFQUEUE\\n&#8221;<\/p>\n<p># subsequent ipv4 hop intput rules<br \/>\nfor i in {2..32}<br \/>\ndo<br \/>\nprintf &#8220;iptables -A INPUT -m recent &#8211;name hop%03d &#8211;rcheck &#8211;seconds 5 -j TR%03d\\n&#8221; $((i-1)) $i<br \/>\ndone<\/p>\n<p># generate ipv6 chains<br \/>\nfor i in {2..32}<br \/>\ndo<br \/>\nprintf &#8220;ip6tables -N TR%03d\\n&#8221; $i<br \/>\nprintf &#8220;ip6tables -A TR%03d -m recent &#8211;remove &#8211;name hop%03d\\n&#8221; $i $((i-1))<br \/>\nprintf &#8220;ip6tables -A TR%03d  -m recent &#8211;set &#8211;name hop%03d -m hl &#8211;hl-eq %d -j NFQUEUE\\n&#8221; $i $i $i<br \/>\ndone<\/p>\n<p># first ipv6 hop input rule<br \/>\nprintf &#8220;ip6tables -A INPUT -m recent &#8211;set &#8211;name hop001 -m hl &#8211;hl-eq 1 -j NFQUEUE\\n&#8221;<\/p>\n<p># subsequent ipv6 hop intput rules<br \/>\nfor i in {2..32}<br \/>\ndo<br \/>\nprintf &#8220;ip6tables -A INPUT -m recent &#8211;name hop%03d &#8211;rcheck &#8211;seconds 5 -j TR%03d\\n&#8221; $((i-1)) $i<br \/>\ndone<\/p>\n<p>[\/bash]<\/p>\n<p>Due to <a href=\"http:\/\/tools.ietf.org\/html\/bcp38\">BCP 38<\/a> filtering, many hops after the inflection point are filtered (those networks are blocking packets spoofed from the interface addresses of their routers). Like all traceroute tests, you need to know how to <a href=\"http:\/\/www.nanog.org\/meetings\/nanog47\/abstracts.php?pt=MTQ0MiZuYW5vZzQ3&amp;nm=nanog47\">interpret the results<\/a>. However, the tool can still be interesting and useful. Here are some examples:<\/p>\n<p>[code title=&#8221;example 1&#8243; collapse=&#8221;true&#8221; highlight=&#8221;17&#8243;]<br \/>\ntraceroute to adamkuj.net (216.139.253.42), 60 hops max, 60 byte packets<br \/>\n1  217.147.213.238 (217.147.213.238)  0.410 ms<br \/>\n2  veX.glb1-bb-r2.nexellent.net (77.245.28.34)  0.297 ms<br \/>\n3  ve1.zrh2-bb-r1.nexellent.net (77.245.29.1)  0.379 ms<br \/>\n4  ge-0-1-8-11.zur11.ip4.tinet.net (213.200.70.105)  0.705 ms<br \/>\n5  xe-2-2-2.fra21.ip4.tinet.net (89.149.183.73)  7.868 ms<br \/>\n6  as6461.ip4.tinet.net (77.67.73.34)  7.155 ms<br \/>\n7  xe-4-2-0.mpr1.cdg11.fr.above.net (64.125.22.197)  19.552 ms<br \/>\n8  xe-3-3-0.mpr1.lhr2.uk.above.net (64.125.24.85)  20.236 ms<br \/>\n9  xe-5-2-0.cr1.dca2.us.above.net (64.125.26.21)  99.282 ms<br \/>\n10  xe-4-0-0.cr1.iah1.us.above.net (64.125.31.245)  130.019 ms<br \/>\n11  xe-0-0-0.cr2.iah1.us.above.net (64.125.30.66)  131.137 ms<br \/>\n12  xe-0-1-0.mpr2.aus1.us.above.net (64.125.27.201)  133.523 ms<br \/>\n13  208.185.23.230.t01811-01.above.net (208.185.23.230)  133.315 ms<br \/>\n14  xe-3-3.dist-rtr-01.aus.us.siteprotect.com (216.139.253.66)  132.644 ms<br \/>\n15  xe-50.core-sw-01.aus.us.siteprotect.com (216.139.253.50)  133.324 ms<br \/>\n16  voodoo.adamkuj.net (216.139.253.42)  252.609 ms<br \/>\n17  vrid-26.core-sw.aus.us.siteprotect.com (216.139.253.33)  495.446 ms<br \/>\n18  xe-3-4.dist-rtr-01.aus.us.siteprotect.com (216.139.253.49)  462.565 ms<br \/>\n19  xe-1-3.brdr-rtr-01.aus.us.siteprotect.com (216.139.253.65)  462.311 ms<br \/>\n20  xe-1-2.brdr-rtr-02.aus.us.siteprotect.com (216.139.253.78)  445.989 ms<br \/>\n21  xe-5-2-0.edge3.Dallas1.Level3.net (4.59.112.37)  478.398 ms<br \/>\n22  *<br \/>\n23  *<br \/>\n24  *<br \/>\n25  *<br \/>\n26  *<br \/>\n27  *<br \/>\n28  *<br \/>\n29  ae-6-6.car1.Zurich1.Level3.net (4.69.133.230)  596.192 ms<br \/>\n30  NEXELLENT-A.car1.Zurich1.Level3.net (213.242.67.138)  593.508 ms<br \/>\n31  ve2.glb2-db-sw1.nexellent.net (77.245.28.35)  588.519 ms<br \/>\n32  *<br \/>\n33  *<br \/>\n34  *<br \/>\n&#8230;<br \/>\n[\/code]<\/p>\n<p>[code title=&#8221;example 2&#8243; collapse=&#8221;true&#8221; highlight=&#8221;18&#8243;]<br \/>\ntraceroute to adamkuj.net (2606:8200:0:1a:0:d88b:fd2a:2), 60 hops max, 80 byte packets<br \/>\n1  2a02:298:80:1337::1 (2a02:298:80:1337::1)  20.203 ms<br \/>\n2  vl19.cr1-sdf2.uk.uksolutions.net (2a02:298:0:1::25)  0.329 ms<br \/>\n3  te2-1.cr1-lon1.uk.uksolutions.net (2a02:298:0:1::16)  4.330 ms<br \/>\n4  xe-5-3-1-core0.tcsov.uk.as6908.net (2a01:450:2::31:1)  4.238 ms<br \/>\n5  lo0-core0.thest.uk.as6908.net (2a01:450::1:3)  4.477 ms<br \/>\n6  xe-0-0-0-5.r02.londen03.uk.bb.gin.ntt.net (2001:728:0:5000::59)  4.766 ms<br \/>\n7  po-5.r00.londen03.uk.bb.gin.ntt.net (2001:728:0:2000::5d)  119.543 ms<br \/>\n8  2001:438:ffff::407d:e49 (2001:438:ffff::407d:e49)  4.397 ms<br \/>\n9  *<br \/>\n10  *<br \/>\n11  *<br \/>\n12  *<br \/>\n13  2001:438:ffff::407d:1bc9 (2001:438:ffff::407d:1bc9)  120.184 ms<br \/>\n14  2001:438:fffe::51e (2001:438:fffe::51e)  123.732 ms<br \/>\n15  xe-3-3.dist-rtr-01.aus.us.siteprotect.com (2606:8200::fd42)  122.332 ms<br \/>\n16  2606:8200::fd32 (2606:8200::fd32)  128.577 ms<br \/>\n17  voodoo.adamkuj.net (2606:8200:0:1a:0:d88b:fd2a:2)  221.822 ms<br \/>\n18  2606:8200:0:1a::2 (2606:8200:0:1a::2)  421.865 ms<br \/>\n19  2606:8200::fd31 (2606:8200::fd31)  479.377 ms<br \/>\n20  xe-1-3.brdr-rtr-01.aus.us.siteprotect.com (2606:8200::fd41)  414.999 ms<br \/>\n21  2001:438:fffe::51d (2001:438:fffe::51d)  441.163 ms<br \/>\n22  *<br \/>\n23  *<br \/>\n24  *<br \/>\n25  2001:438:ffff::407d:cde (2001:438:ffff::407d:cde)  435.620 ms<br \/>\n26  ae-1.r20.dllstx09.us.bb.gin.ntt.net (2001:418:0:2000::d9)  446.681 ms<br \/>\n27  ae-3.r20.asbnva02.us.bb.gin.ntt.net (2001:418:0:2000::2cd)  476.665 ms<br \/>\n28  ae-0.r21.asbnva02.us.bb.gin.ntt.net (2001:418:0:2000::e)  466.413 ms<br \/>\n29  ae-2.r23.amstnl02.nl.bb.gin.ntt.net (2001:418:0:2000::1b2)  558.141 ms<br \/>\n30  ae-1.r03.amstnl02.nl.bb.gin.ntt.net (2001:728:0:2000::142)  557.867 ms<br \/>\n31  xe-0-0-0-23.r03.amstnl02.nl.ce.gin.ntt.net (2001:728:0:5000::26)  568.745 ms<br \/>\n32  *<br \/>\n33  *<br \/>\n34  *<br \/>\n35  lo0-core0.tcsov.uk.as6908.net (2a01:450::1:5)  569.270 ms<br \/>\n36  2a01:450:2::31:a (2a01:450:2::31:a)  560.775 ms<br \/>\n37  te9-1.cr1-sdf2.uk.uksolutions.net (2a02:298:0:1::15)  574.273 ms<br \/>\n38  vl19.cr1-ndf1.uk.uksolutions.net (2a02:298:0:1::26)  564.310 ms<br \/>\n39  *<br \/>\n40  *<br \/>\n41  *<br \/>\n&#8230;<br \/>\n[\/code]<\/p>\n<p>[code title=&#8221;example 3&#8243; collapse=&#8221;true&#8221; highlight=&#8221;24&#8243;]<br \/>\ntraceroute to adamkuj.net (216.139.253.42), 60 hops max, 60 byte packets<br \/>\n1  82.147.0.13 (82.147.0.13)  0.277 ms<br \/>\n2  te2-4.cr1-the.uk.6dg.co.uk (217.10.157.110)  5.589 ms<br \/>\n3  xe-4-3-1-core0.thest.uk.as6908.net (62.149.52.21)  5.554 ms<br \/>\n4  ae2-core0.thnor.uk.as6908.net (62.149.50.170)  5.717 ms<br \/>\n5  ae1-core1.thnor.uk.as6908.net (62.149.50.214)  7.703 ms<br \/>\n6  sl-crs2-lon-0-0-0-0.sprintlink.net (213.206.156.149)  6.307 ms<br \/>\n7  sl-crs1-lon-0-2-0-0.sprintlink.net (213.206.129.152)  6.653 ms<br \/>\n8  213.206.131.22 (213.206.131.22)  6.078 ms<br \/>\n9  ae-52-52.csw2.London1.Level3.net (4.69.139.120)  111.191 ms<br \/>\n10  ae-58-223.ebr2.London1.Level3.net (4.69.153.137)  110.796 ms<br \/>\n11  ae-41-41.ebr1.NewYork1.Level3.net (4.69.137.66)  109.873 ms<br \/>\n12  4.69.201.66 (4.69.201.66)  111.744 ms<br \/>\n13  ae-1-100.ebr1.Washington12.Level3.net (4.69.143.213)  109.478 ms<br \/>\n14  ae-6-6.ebr1.Atlanta2.Level3.net (4.69.148.105)  110.850 ms<br \/>\n15  ae-63-63.ebr3.Atlanta2.Level3.net (4.69.148.241)  109.592 ms<br \/>\n16  ae-7-7.ebr3.Dallas1.Level3.net (4.69.134.21)  112.102 ms<br \/>\n17  ae-63-63.csw1.Dallas1.Level3.net (4.69.151.133)  109.723 ms<br \/>\n18  ae-1-60.edge3.Dallas1.Level3.net (4.69.145.8)  111.042 ms<br \/>\n19  HOSTWAY-COR.edge3.Dallas1.Level3.net (4.59.112.38)  138.118 ms<br \/>\n20  xe-1-2.brdr-rtr-01.aus.us.siteprotect.com (216.139.253.77)  124.940 ms<br \/>\n21  xe-3-3.dist-rtr-01.aus.us.siteprotect.com (216.139.253.66)  123.130 ms<br \/>\n22  xe-50.core-sw-01.aus.us.siteprotect.com (216.139.253.50)  126.718 ms<br \/>\n23  voodoo.adamkuj.net (216.139.253.42)  168.105 ms<br \/>\n24  vrid-26.core-sw.aus.us.siteprotect.com (216.139.253.33)  442.778 ms<br \/>\n25  xe-3-4.dist-rtr-01.aus.us.siteprotect.com (216.139.253.49)  458.994 ms<br \/>\n26  xe-1-3.brdr-rtr-01.aus.us.siteprotect.com (216.139.253.65)  456.953 ms<br \/>\n27  xe-0-3-0.mpr2.aus1.us.above.net (208.185.23.229)  470.776 ms<br \/>\n28  *<br \/>\n29  *<br \/>\n30  *<br \/>\n31  *<br \/>\n32  *<br \/>\n33  *<br \/>\n34  *<br \/>\n35  *<br \/>\n36  *<br \/>\n37  te3-2-0-cr0.nik.nl.as6908.net (81.20.64.42)  577.000 ms<br \/>\n38  *<br \/>\n39  *<br \/>\n40  *<br \/>\n41  *<br \/>\n42  ae1-core0.sdsds.uk.as6908.net (62.149.50.105)  615.305 ms<br \/>\n43  62.149.52.10 (62.149.52.10)  569.237 ms<br \/>\n44  vl19-te2-2.cr1-sdn.uk.6dg.co.uk (217.10.157.142)  570.466 ms<br \/>\n45  *<br \/>\n46  *<br \/>\n47  *<br \/>\n48  *<br \/>\n&#8230;<br \/>\n[\/code]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve always wanted to write a reverse-traceroute system. However, writing my own Linux NetFilter module was daunting &#8211; I&#8217;m not a C programmer, let alone a kernel hacker. Then I came across Scapy. While intercepting and mangling the packets in userspace does add some artificial latency, it is a good proof-of-concept. And of course, it [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[4,15,3,2],"class_list":["post-30","post","type-post","status-publish","format-standard","hentry","category-networking-tools","tag-linux","tag-python","tag-scapy","tag-traceroute"],"_links":{"self":[{"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/posts\/30","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/comments?post=30"}],"version-history":[{"count":28,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/posts\/30\/revisions"}],"predecessor-version":[{"id":321,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/posts\/30\/revisions\/321"}],"wp:attachment":[{"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/media?parent=30"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/categories?post=30"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/tags?post=30"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}