ÿØÿà JFIF    ÿÛ „ !.%+&8&+/1555$;@;4?.451 4,$,44444444444414444444444444444444444444444444444444ÿÀ  á á" ÿÄ     ÿÄ ?    !1AQaq"2‘¡±ÁðBRbrÑá#‚’¢²3S CñÿÄ   ÿÄ !    !1QAa‘2ÿÚ   ? 5˜Z¯V¦cø)›t/? z¨±>Õ5€¶‹Á¤·¼z¼Ü¬+ñ®v¤¨_ˆR­BFn©—˜ý®ç̝P8gýt·ÉSTŦˆìät?þé¼íìN/Þa)ì–í6ô… Ï¿øÃj´¿KÇü]ÿ ªô¹-eKànëÕHTx}ýSÜ›ÿ ”7Ø×&µ<¦  ¥ÑO¶[Ù¯ä¨ÞÃÿ PZ-¬;#õ|•oaÿ ©CìÞz3˜öː/¤­ñTûIØ}š^ mÓ%ªxˆ¥ÉŸu=Z+ISe¿45™¼u;ú&WØ÷€æßQ™®{|íx*TC“#ZŠìZ§²‹ 6pv…³¿¡äª*áZÐ%ÒOáˆo"x«OHk w±æ+¬V(kMúŸ5Vö«$ ÁrÏbàb57/luR ¸ÑÛj Òµì`Мq­û žICÀÊ•©4€Âcà¨Ï€O´<èÐ:›ù(Ë^L8þ‘ÍÌ#¸Ð_Ì©ÙK(Öz 4¬û+¸;ü’V’84‘¬ÃŽ:[â‡ÔÌáõp¢~§ªlæ£ö{®G>J¼"°‡7¯ÆÉèßû ‹É‹§ÁòÃýâßî ^ƾÙõ‹×óH#«LP½ïX=xÑÍ$|W?•~• îëÔ©ª‹ {ÝT…Kÿ ”hûâá)J*ö˜–ÔU;iÇ€/ ÆþjóZ\ýwØ=Ìm ºèËL9 ýèÆð/¨’¥öo=nË.%Îì ŽÕ¯È|{Oj²ƒE6e/ßdÄõ²Ìâ1O®ò×TsəԸhOMýíMˆ¿¼H˜l²,7Â¥#MF/Úf°Ö½± ¸–dr‹NýÊ íjqx{œÉ ä-È ¦ øÄër¨q°ð †nцýÑÄÆ’mä…n<0È™;ÁÝá¯ÁZƒ7FÀmì­ É&9ˆîéi¶ùN§Y• ÃZãAâ?•‡©‰ , ó¾IŸŠc1 4â&y­&pŠ­6;M À 0¹qç»p.á …ŸÅáK@%6·y6ƒ‰3?”úºŽ‰éX5ªPT §µ!=Mž«Ú½‹ÅgÂSâÉaþÓoö–¯ÁÔìR>5éÿ üs¶ÆUcÌ kÇR ]ÿ ù¬¼«VŽ;Â|‡~¢¦”ÏŰæ {L™Õ°Óv¹ò¸írޡעCÃ!íVÕ {¶»sŒNPg/ "uÕbkm²“$ďå¿é¹§°½æz¯6 †s¿!s–wÚÝ“™Œ °.ûj>·+™Òa…©Œ&rÝÎtÛë긪Ît’LAVp%c Úý[ÄzJ¾ÇàXXç@˜ó<êL]·T˜¾¥1Ó©V‡g´æ½¦Ý@¹óø!_@´ÞâSÁ —S3™•& ]@JHÚý©ZŽ €×æÔr»Áf!‡yÞ4Mv*èÓã_{‘åóUuљØ«Oïé*®EvÑ Œ÷‡U \"㪒ÍK+À 4“M¡ï:0¥5í!'<@î´”>Ç»&Z–ïCCV˜Ì5Šo&îhè.žû |ÓK©h$s6KìŒëã)¹hI¦GïOåóI;ììü#É$Š0…Ææ¥TØ.5­¾gn´ “ÂÖ\:hœ89G)J@„}œ:’Ò{/Š"¦_Æ×7Æ3VÇŠÊa]ÚŒÙ€Ä–=®uÁßâACZƒ§§£ Qnâ:«,×{tyø¬iÛcœÜÄ€H½ÄÍCk´÷šß .W'b¤Íåh]÷€=,Žv×cÚEÚHXJX¶îo¨FÒtèöŸ>ªª6[J®Fµ£sGÁeqõfe\íjÒÐïÄÐGˆe1Ø‹.Ø”‘Ëuø Y­ˆÜ ŽG|zùªüMpDnQWÄ”%JŠ™)â*p@Örš«ÕT2Ð%ˆG#ª„ ·¤!°ŸOTÂT¸aÚ%4&h™LµšØüÐ.F¿²ÐÞ_Ç‚¾ÅÃaÜ÷09Æ q€öy˜v‡85õN÷]¬äѼóS{°_MެúÔ#°Ç¸0åÞè2ëôPcvÆw9®ií1Ä8F™˜à‰´+‰Ik1òÝ7“Ñ×ÒsÝ\x‚h`ÞÑ`ó"|µEcý£n˜h`}GÞ !±ù²Ápü²ß6 0ïi󜵩SÈÇ7˜-ÕURO˜¦´f$ªž-Í6(œ}<„ éc øs]ŽŽ„*—¾ ìdŽ„)méª\¿êÎIg¾ØÞ~I#C/¼¼´EÁÈŽi8“©õådô·>euä ƒ'Ê×लR1ÉJE1ÐAát`t;ÇР%Ý<‡¥„ÍÆ`×Oyó)õiI€ñQaŸ4Ûù\áàaÃÔ¹HÃu¹*k€¦<„e S‡&õÏ B!ŽhüÞ`yj}mªf×\¿ Ç~æ­9‡û\՞Ǖg²1Žû5V7 !àöšm° c`ܬøÇìµÒ'P"?…´Ö,"§^•õލsÔ)6˜sæéÍR¼ ò|Sl”‹7 nPW Gòú÷½§O¯‡„l¡kSÞŒr½PÊ@æ¢pŽ-mÿ #Ÿ˜Àº¶Áä¦;ïÔæ$1££`“Õ>„—·ž)ßð³ñ#Ï Ô$¶œ‰ÊE‹À;÷º ¯«P:Ñ”8–IÊtpÞ3ª“>ê“þës4ò2OÏÕ­±zô†Õ§‰.÷ä¸;¿˜“'œ›žª}«Œ{ª±Ì 9ÔóÞÕ‡0 $íWV3Üì¬ —@kÝ4@¿r¼±½¬™›?øØæ´'Áé®CË3-g$˜ö‡×auÚi´Žp/êÛ æF›Ú2v‹ã¿¿,nB1̨ƃqÞa5͝@&Æû“él÷ \C²½UÍc ¯k×¢U ÖéQå™—-r wô ÞÏ<Ò=&=ÿ Ôê Òêˈt,i—;LîÜ á¸*ÚÃ1$êL•LÍ <É)ýÐà’ ;F™{ƒ™˜€&'}‚ãÄK`¡ÞT@I;®žZóè‚s’7®°›+§O­Åq©é»²9<Ô J ¼9O’HL»Ùïì¸rk¼Ž_ý‘TŸu[²ßÚŒ·ü÷B%¯E ŸÔX5êO´ Ç•€’I0 ÉJX` ñ¹õ%;µŸD‘«´€àwÒ™U ûئžÖö\×®×´8 ½‡ºÐÆÓ§?Àkmœ=;d5*@-ì0F Rªýš[Ü6âö̃ڸr*KA9· u*µæ£?U¸Âêí†8@¦X4 e-ò„0s{ HâUpU?¼mñRa°®a%Ð'tÉ×’\¾ÊÉ]t›h>·(Ë@R¼¡Ãt h}’O÷au<+nT…Ö…MӐ??Óe95 q>í/;&JSû °¯ÊéÞ øƒ*Ã2½Ài&:nôUl=¾¿5eˆ3”ñc|Ú2V”>„»&eE;«ÚäC p¢Û úy 9š[ŒÌx¼擼A&DåÒ¯ˆ¤ÀÌ;"˜ ÏQä¸åhÊ}Ûq«Û0WžÒ|»€ø®öCm5•\ÇÀ§Pe3£]0ÃàLDÉ‰1øªxjgwT‚÷¿LΨK‹›ùs—xˆÜ±µ kæ¸f‰‰ÜGk/LÛØ6d9ò¶ùA{ƒA3š/¬D¬khÓk‰`˜"㯒r¿±Óã jx‡°e}<Ñø\3y:'À•/h½Í€Ç4~g ?Û(¼]v‘ªlKÎâ~?O‚W%{Ì:“'©úNq¾›úo(X’¥¯ˆ nFê{Ç€ü?º'ë ø‹ì Þ09ŒÌç9Æ —ËC`j@ÓÄ(+a‹un¸#ÂꟋ{K`‘ÑÍÍ'à´»/Û,KW;Þ4²þð ï Nm|~fGÏ(…³Ã)«1ö­Õ ¥‡¨©ƒÃ™ü-s=à=U66Ï«Ýc蓦W¹íž®›nÔ%êÇìŒ<#Ü×84ån®Ð ÒåOC` ñânÑs‡¢ç 1õ%Îhì½Ã½® e:ݼUZo™`  ÅZŸŒÊ«ê1ÏÄo$q¹Þ€©ˆhÐÉä¯ñ[!…Ú˜àJ:x2$Íß&PåT£6ç— ‡Í*4Ýšçjÿ ‰É nófÐ ó(L5C•åÆ\rMÒ@ò }y-W}™üýVù—ú¢=Ù”c®‘< M ž ´Phr ¦©TD ‘ù.$´÷O‡‘V2Æò.=IUŒ=ž‡â¬i™aþÓåÙ?òUø'ØÖ•.~* šTŒ!•-×áºTâ®ä#õü'´ eýlYÅÓeÕKÂrT"CÚ@u!Óxƒ{š3€}1¿(r}%«nËamjÑ%ÑNEò v ˜à  σöK³,*º.àzù¨™Ó ÚçâU¦*¿ 9{%Ö¹ njûdaXöb) kÛÆ±ûÓ\°M7ˆÂ=û›ç¿Ã‚­V»Cg–8ÙêE- j)k$º`Ã-ùEýeBÆÇ]c¡°ñty&Òd0nõ'¡W+ƒ*|–øµFa\GQªEAÔp5\Ǽ·¼Ç8·õ -â§Ú[ ‡ uZeÖ 3}×d'+¹:ð+K†Û®s!Ï$úe€<Û”x)1»a­¡LC]¸µík…ÚàA»AYº{†ªS[¦5HÒ7ù --,ísòDØ€èk ÞÀîÜ ò@â( ËNˆë›4ô½•/¦o‡€Û7 ê•ÆêòðÜy'Án½µ á˜ݦ ndeo…[ì¶Ê,¥R³Ä=À±—–ß;£™´ñSâ*g§”ïaið‘Jå~™ÓÞ ß³Õ¢»8x埒²52>AÊb&-÷\7´éÄù€T˜,w;3{ï˜k…à¹ÄqÀ«œ{€\ ˆ¾[´¨јr &Úé„Ívˆ±8†¿]|¬ņ4I×pÞS1ÈÖz‰#Ìv‡G!YNògñ:màTz¢Ý1ô©^O=~ë|5Bã™ç•¼µõ•bÆ@úÕS¬ÈŒ#¬zünrŸ û” Z²•èðV"ÁHÚý©wÝ €7¼Ìu1hÑa3Éä û f$o¿É ™Ú›ÝçnpÒ3äÌ3†Í§,Äï]$‰/pê †«À¼¸e9­Æê_C]žƒ·ý·frÁN«, E=›Çq -‰öŒ:aÏ¿±í&£Í:-} 84‘ÿ eƒQÑeëSsuiA ³g㟥ú£?ÿ ʼn*”“÷aühe:ÊWa@ÒÞk±eØ] F Ô—r.åä˜ @ö¥ªZoÐýYL·¥S²G/‡ñ <~*ZÆ´è>JlòàÛÆ½ÿ 窘ìGN¢:I®KšJp/`íIÁÀõ#Ä-€ö­šµŒoF4|ÆQØÆ@Ì|£Ô…¢À{9˜è½Üó›€ôYÒÎYsið;ís¤€à²ˆ‚4qÉVŒI$ ‰"° æµ8cXGjœˏ¡Aâý•ËÜ¢ûï e·çLx']á"oÅÎê3¯Ç—¹”ó0nå‚âg{Œñ> S´˜îè°g238‚ãköÝfÚd´6Ò€;ò÷±¢™¼›º ¢Æ'¥Ðx'e¬ç ]bÈÆV¢ó‹kýBO ðÊâ$Ÿ!×T 3Mýמ žìٍàÌü‘8÷€àæØ8æ©6‰©L´«…oãpð„~Çk‰!ñ;‹”ÛžÍ àž±z Ÿôû øŸÝužÏ;ÿ #|u6™Þ¬ÚˆÐõA4¶â|ôl|Ê2ŽÇ¤ÝÅÇY.<#Aí.k§hóF‚”Y; M½Ö4hŸ4&›­¿tès´%FìL¥£Ãk‰ÇT¤haÁ¤ÚxfÉ`ÑìË›>i 3t‚:,–+^÷´–{Û–Nxi"x‘Ûg î¨>¥Õ܁ùZH,2Û“:8xÊ¢Çí9.É-Ìâã-=çjwµS˜dütžçwýGòú®®ûº_ˆýx$–¡ãøO EÚÛÏ÷R„×w+3£Á£öUMyR²¹âŒ°š›¸Ñãò9§Ó_Dl+Ùßc›úšGÅÌc†Ž!Ko=¶.‘Îÿ c²(2®V mª.ÿ ¹B›¹å ù„öŸSV>™ü¯$y:G¢Z×àøúdî¹û­·ýÇ´:•c LÍõi_‹ö+ÎæGÊè>OŠ•äž´§Þ{X}¨1ÚTc›»Qþ•êô°t¿OP?eæ~É{5]•ÙR£r5†nZ\ã@ &îJõ ¾àC°þV>fé¥/ü5ñÊIº_é5 ;e­h<@ Ä&æÃëE%;X,ÒãÆÞ`Oò¦kŸm#˜!ÀyÄ¢| óLšò¥Ä` ¶R=|ÈCâh5ò3DˆïF†ðÒ#ÅìÛœ?¸yhBãœí ZxßÎÄhºRK„`Þödvײ™ÀÈÑÒgŒuY w³%†ƒÓzõ ÖÏp‚dH®¦A´ù§»ÓÇMæ~)ˆð‡û:ù&Ä •vGD´À n ݇¼Ö8Fö óáà£~Ë¥x`oK|Ä?fxiØü%pìR>éò+Û±éÎ>núlFŤ'tq8LZÏvÃ?„¡ß±È⽆¯³íü@x|PöUäèØã¡ð‚ŒAìÏ"vÍwóŸÍ{ ý0.z È•Ö{,N¡£¡ŸKÕÙž>Ýœþ ÍÀ°<×EA!Å‚D™IúOÍ¡>ôG}Â` ÍßkÜL™Ž Þð™ {IøF²¹òQ3&!ÃÂÞz.d&Ï-sH¸,Ôõ˜ŽP€ 77ˆÝ¼ÊëÜw =cÕ Ú,ØÐ5ÎYÐ)ì´öœgŒ[¤ßv㙑8心>h]§µháYš£²ºÑ.{Ï7Sð•?´~×SÃKýJÛ˜ ™Íäiúu<µX¶1õ^kâçIÑ£sZ4h>j*ÔšD:4­¿_ ÷¸ Õxæÿ ¸?Mù _•­ÊÐ ä ÷ý ÑwL œ­ïnTkÛUÍN©ë:¦fV ¶ÜÔÜMªÅâA½–¿R×TXš-%iTÊT•‡Ù‚JôϐZxWÑè‰f‰òG º ×Õû2aZ7OU3[“×AT–ÞŒ…-‘¤”Ì ì&(ˆ¿­•ƒkï’:ðY¦W‘ Å)“†‘˜³Åtcø˜ñTÂwÚÇ4|üLÇªí–v- qˆèU qPE.†â‘˜µ Æ,ÐÅs]8¾„oúÑ i>ÜxxÈó)ƒ ´æÁâØ$À‰vžŸf$Ž |ãw;ÀÁIJ»b` {¦Ó¤Ú$©YÀ‘n@Óïž«9J¼êG m¤ ܯ¹ÌW4€ÐÒÅÛ‡#褕Ÿn-?í|с¥÷Ú¹¬'´ÞÜ9ÓK `hê£SÄSà?7—Wí_´…óB›»:=Ãïq`<8ñÓŒÑlú2d¬ê³£hÖ[l|$vÝro~'R®‰§°ñmY ͧäP |PUª¹·:3Œ[Û{Xÿ ºâ@‚W–Äé u‚ ¯´*=íή.pûÒdt @G‰¬ s¸ ëÉücr ÞæÑ¨Ê@>¤¢Ö±. Þ'¯°ÌME[YéïĵÂCå½ Ué©Áû'Ê9%eÔðNU”ë‘ÌsD3/®+UI˜9h.WC”빓$#:pz:YÓ ¿xž* ³$Í +$kñAŠ‹†¢ Uê>¸)_š¬÷©ßAÂÔb9ÇU ¯¾á•9¯ÏÏ÷O÷¼¼Fähal1‰3Ì[Ïr•´UCksNÐ] R‘¸¥H+§Šé†c©vÖÞ0iÓ76s†î!§=ß ¼~Ô'°Ãmäoäš³ªøi1úÉ)³yV8 CLÄØÁ‘WYïi€H6ÖÑiámø^ÈY´°Ñ7¥Û*—Ñ©L«Qƒï—Ùrÿ ›£Ð*š¸ˆL©ˆ$ˆ ÷¾D§9È®«qbqC)–ˆïv´çñsÑVT­Ø, <àïºÀO«Jý·õ àfPìð .wFšir´þ’2_Y *Æ€x\« ì€9š@ Ž|F⇥ˆkZ@hÖÄ0t¿-<“‹qµ¾*ZL¤Ú)&BJpÓF5=$„at*Zš$’ÑtdûÝRI1 2މ$€$I$#‰SÞ’Hë¬ï;Á$¡t$’`<(ñÇt)$‡Ð.Êf¢X’Kt=Éé$‚ˆªè¢oÝëòI%Rgcª÷ŠyI%¡‰ÿ !ñ)´õ $¤ Ô’IIGÿÙ--- A minimalistic DNS BlackList library implemented to facilitate querying -- various DNSBL services. The current list of services has been implemented -- based on the following compilations of services: -- * http://en.wikipedia.org/wiki/Comparison_of_DNS_blacklists -- * http://www.robtex.com -- * http://www.sdsc.edu/~jeff/spam/cbc.html -- -- The library implements a helper class through which script may access -- the BL services. A typical script implementation could look like this: -- -- -- local helper = dnsbl.Helper:new("SPAM", "short") -- helper:setFilter('dnsbl.inps.de') -- local status, result = helper:checkBL(host.ip) -- ... formatting code ... -- -- -- @author Patrik Karlsson -- local coroutine = require "coroutine" local dns = require "dns" local ipOps = require "ipOps" local nmap = require "nmap" local stdnse = require "stdnse" local stringaux = require "stringaux" local table = require "table" _ENV = stdnse.module("dnsbl", stdnse.seeall) -- Creates a new Service instance -- @param ip host that needs to be checked -- @param mode string (short|long) specifying whether short or long -- results are to be returned -- @param config service configuration in case this service provider -- needs user supplied configuration -- @return o instance of Helper local function service_new (self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } setmetatable(o, self) self.__index = self return o end -- The services table contains a list of valid DNSBL providers -- Providers are categorized in categories that should contain services that -- do DNS blacklist checks for that particular category. -- -- Each service should be stored under a key that specifies the service name -- and should contain: -- ns_type - A table with a record type as key and mode as value -- eg: { ["A"] = "short", ["TXT"] = "long" }. -- If only short queries are supported using A records, this argument may be -- omitted. -- -- resp_parser - A function to parse the response received from -- the DNS query. The function should take two arguments: -- * response - the DNS response received by the server, -- typically a code represented by an IP. -- * mode - a string representing what mode (long|short) that -- the function should parse. If ns_type does not contain -- the TXT record, this argument and check can be omitted. -- When the short mode is used, the function should return a table containing -- the state field, or nil if the IP wasn't listed. When long -- mode is used, the function should return additional information using the -- details field. Eg: -- return { state = "SPAM" } -- short mode -- return { state = "PROXY", details = { -- "Proxy is working", -- "Proxy was scanned" -- } -- long mode -- -- fmt_query - A function responsible for formatting the DNS -- query. When the default format is being used . -- eg: 4.3.2.1.spam.dnsbl.sorbs.net, this function can be omitted. But if -- this function is defined, it must return the query to be executed, -- otherwise the library will assume that the provider needs configuration -- that failed to be provided. -- -- configuration - If the service requires the user to provide -- configurations, this function will have to return a list with the name -- and description of the arguments that provide the configuration/options. -- If this function isn't specified, the library will assume the service -- doesn't require configuration. -- SERVICES = { SPAM = { ["dnsbl.inps.de"] = { -- This service supports both long and short mode ns_type = { ["short"] = "A", ["long"] = "TXT", }, new = service_new, -- Sample fmt_query function, if no function is specified, the library -- will assume that the IP should be reversed add suffixed with the -- service name. fmt_query = function(self) local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") return ("%s.spam.dnsbl.sorbs.net"):format(rev_ip) end, -- This function parses the response and supports both long and -- short mode. resp_parser = function(self, r) local responses = { ["127.0.0.2"] = "SPAM", } if ( ("short" == self.mode and r[1]) ) then return responses[r[1]] else return { state = "SPAM", details = { r[1] } } end end, }, ["spam.dnsbl.sorbs.net"] = { ns_type = { ["short"] = "A" }, new = service_new, resp_parser = function(self, r) return ( r[1] == "127.0.0.6" and { state = "SPAM" } ) end, }, ["bl.nszones.com"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.2"] = "SPAM", ["127.0.0.3"] = "DYNAMIC" } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, ["all.spamrats.com"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.36"] = "DYNAMIC", ["127.0.0.38"] = "SPAM", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, ["list.quorum.to"] = { new = service_new, resp_parser = function(self, r) -- this service appears to return 127.0.0.0 when the service is -- "blocked because it has never been seen to send mail". -- This would essentially return every host as SPAM and we -- don't want that. return ( ( r[1] and r[1] ~= "127.0.0.0" ) and { state = "SPAM" } ) end }, ["sbl.spamhaus.org"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.2"] = "SPAM", ["127.0.0.3"] = "SPAM", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, ["bl.spamcop.net"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.2"] = "SPAM", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, ["l2.apews.org"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.2"] = "SPAM", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, }, PROXY = { ["dnsbl.tornevall.org"] = { new = service_new, resp_parser = function(self, r) if ( "short" == self.mode and r[1] ) then return { state = "PROXY" } elseif ( "long" == self.mode ) then local responses = { [1] = "Proxy has been scanned", [2] = "Proxy is working", [4] = "?", [8] = "Proxy was tested, but timed out on connection", [16] = "Proxy was tested but failed at connection", [32] = "Proxy was tested but the IP was different", [64] = "IP marked as \"abusive host\"", [128] = "Proxy has a different anonymous-state" } local code = tonumber(r[1]:match("%.(%d*)$")) local result = {} for k, v in pairs(responses) do if ( ( code & k ) == k ) then table.insert(result, v) end end return { state = "PROXY", details = result } end end, }, ["ip-port.exitlist.torproject.org"] = { configuration = { ["port"] = "the port to which the target can relay to", ["ip"] = "the IP address to which the target can relay to" }, new = service_new, fmt_query = function(self) if ( not(self.config.port) or not(self.config.ip) ) then return end local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") return ("%s.%s.%s.ip-port.exitlist.torproject.org"):format(rev_ip, self.config.port, self.config.ip) end, resp_parser = function(self, r) local responses = { ["127.0.0.2"] = "PROXY", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, ["tor.dan.me.uk"] = { ns_type = { ["short"] = "A", ["long"] = "TXT", }, new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.100"] = "PROXY", } if ( "short" == self.mode and r[1] ) then return { state = responses[r[1]] } else local flagsinfo = { ["E"] = "Exit", ["A"] = "Authority", ["B"] = "BadExit", ["D"] = "V2Dir", ["F"] = "Fast", ["G"] = "Guard", ["H"] = "HSDir", ["N"] = "Named", ["R"] = "Running", ["S"] = "Stable", ["U"] = "Unnamed", ["V"] = "Valid" } local name, ports, flagsfound = r[1]:match( "N:(.+)/P:([%d,]+)/F:([EABDFGHNRSUV]+)") local flags = {} flags['name'] = "Flags" for k, v in pairs(flagsinfo) do if flagsfound:match(k) then table.insert(flags, v) end end local result = { ("Name: %s"):format(name), ("Ports: %s"):format(ports), flags } return { state = "PROXY", details = result } end end, }, ["http.dnsbl.sorbs.net"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.2"] = "PROXY", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, ["socks.dnsbl.sorbs.net"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.3"] = "PROXY", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, ["misc.dnsbl.sorbs.net"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.4"] = "PROXY", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, } }, ATTACK = { ["dnsbl.httpbl.org"] = { configuration = { ["apikey"] = "the http:BL API key" }, new = service_new, fmt_query = function(self) if ( not(self.config.apikey) ) then return end local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") return ("%s.%s.dnsbl.httpbl.org"):format(self.config.apikey, rev_ip) end, resp_parser = function(self, r) if ( not(r[1]) ) then return end local parts, err = ipOps.get_parts_as_number(r[1]) if ( not(parts) or err ) then -- TODO Should we return failure in the result? stdnse.debug1("The dnsbl.httpbl.org provider failed to return a valid address") return end local octet1, octet2, octet3, octet4 = table.unpack(parts) if ( octet1 ~= 127 ) then -- This shouldn't happen :P stdnse.debug1( "The request made to dnsbl.httpbl.org was considered invalid (%i)", octet1) elseif ( "short" == self.mode ) then return { state = "ATTACK" } else local search = { [0] = "Undocumented", [1] = "AltaVista", [2] = "Ask", [3] = "Baidu", [4] = "Excite", [5] = "Google", [6] = "Looksmart", [7] = "Lycos", [8] = "MSN", [9] = "Yahoo", [10] = "Cuil", [11] = "InfoSeek", [12] = "Miscellaneous" } local result = {} -- Search engines are a special case. if ( octet4 == 0 ) then table.insert(result, ("Search engine: %s"):format( search[octet3])) else table.insert(result, ("Last activity: %i days"):format( octet2)) table.insert(result, ("Threat score: %i"):format( octet3)) local activity = {} activity['name'] = "Activity" -- Suspicious activity if ( (octet4 & 1) == 1) then table.insert(activity, "Suspicious") end -- Harvester if ( (octet4 & 2) == 2) then table.insert(activity, "Harvester") end -- Comment spammer if ( (octet4 & 4) == 4) then table.insert(activity, "Comment spammer") end table.insert(result, activity) end return { state = "ATTACK", details = result } end end, }, ["all.bl.blocklist.de"] = { new = service_new, resp_parser = function(self, r) local responses = { ["127.0.0.2"] = "Amavis", ["127.0.0.3"] = "DDoS", ["127.0.0.4"] = "Asterisk, SIP, VoIP", ["127.0.0.5"] = "Badbot", ["127.0.0.6"] = "FTP", ["127.0.0.7"] = "IMAP", ["127.0.0.8"] = "IRC bot", ["127.0.0.9"] = "Mail", ["127.0.0.10"] = "POP3", ["127.0.0.11"] = "Registration bot", ["127.0.0.12"] = "Remote file inclusion", ["127.0.0.13"] = "SASL", ["127.0.0.14"] = "SSH", ["127.0.0.15"] = "w00tw00t", ["127.0.0.16"] = "Port flood", } if ( "short" == self.mode and r[1] ) then return "ATTACK" else return ( r[1] and responses[r[1]] ) and { state = "ATTACK", details = { ("Type: %s"):format(responses[r[1]]) } } end end, } }, } Helper = { -- Creates a new Helper instance -- @param category string containing a valid DNSBL service category -- @param mode string (short|long) specifying whether short or long -- results are to be returned -- @return o instance of Helper new = function(self, category, mode) local o = { category = category:upper(), mode = mode } assert(category and SERVICES[category:upper()], "Invalid category was supplied, aborting") setmetatable(o, self) self.__index = self return o end, -- Lists all DNSBL services for the category -- @return services table of service names listServices = function(self) local services = {} for name, svc in pairs(SERVICES[self.category]) do if ( svc.configuration ) then local service = {} service['name'] = name for config, description in pairs(svc.configuration) do table.insert(service, ("config: %s.%s - %s"):format( name, config, description)) end table.insert(services, service ) else table.insert(services, name) end end return services end, -- Validates the filter set by setFilter to make sure it contains only -- valid service names. -- @return status boolean, true on success false on failure -- @return err string containing an error message on failure validateFilter = function(self) if ( not(self.filterstr) ) then return true end local all = SERVICES[self.category] self.filter = {} for _, f in pairs(stringaux.strsplit(",%s*", self.filterstr)) do if ( not(SERVICES[self.category][f]) ) then self.filter = nil return false, ("Service does not exist '%s'"):format(f) end self.filter[f] = true end return true end, -- Sets a new service filter to choose only a limited subset of services -- within a category. -- @param filter string containing a comma separated list of service names setFilter = function(self, filter) self.filterstr = filter end, -- Gets a list of filtered services, or all services if no filter is in use -- @return services table containing a list of services getServices = function(self) if ( not(self:validateFilter()) ) then return nil end if ( self.filter ) then local filtered = {} for name, svc in pairs(SERVICES[self.category]) do if ( self.filter[name] ) then filtered[name] = svc end end return filtered else return SERVICES[self.category] end end, doQuery = function(self, ip, name, svc, answers) local condvar = nmap.condvar(answers) local config = {} if ( svc.configuration ) then for key in pairs(svc.configuration) do config[key] = stdnse.get_script_args(("%s.%s"):format(name, key)) end end svc = svc:new(ip, self.mode, config) local ns_type = ( svc.ns_type and svc.ns_type[self.mode] ) and svc.ns_type[self.mode] or "A" local query if ( not(svc.fmt_query) ) then local rev_ip = dns.reverse(ip):match("^(.*)%.in%-addr%.arpa$") query = ("%s.%s"):format(rev_ip, name) else query = svc:fmt_query() end if ( query ) then local status, answer = dns.query(query, {dtype=ns_type, retAll=true} ) answers[name] = { status = status, answer = answer, svc = svc } else stdnse.debug1("Query function returned nothing, skipping '%s'", name) end condvar "signal" end, -- Runs the DNS blacklist check for the given IP against all non-filtered -- services in the given category. -- @param ip string containing the IP address to check -- @return result table containing the results of the BL checks checkBL = function(self, ip) local result, answers, threads = {}, {}, {} local condvar = nmap.condvar(answers) for name, svc in pairs(self:getServices()) do local co = stdnse.new_thread(self.doQuery, self, ip, name, svc, answers) threads[co] = true end repeat for t in pairs(threads) do if ( coroutine.status(t) == "dead" ) then threads[t] = nil end end if ( next(threads) ) then condvar "wait" end until( next(threads) == nil ) for name, answer in pairs(answers) do local status, answer, svc = answer.status, answer.answer, answer.svc if ( status ) then local svc_result = svc:resp_parser(answer) if ( not(svc_result) ) then local resp = ( #answer > 0 and ("UNKNOWN (%s)"):format(answer[1]) or "UNKNOWN" ) stdnse.debug2("%s received %s", name, resp) end if ( svc_result ) then table.insert(result, { name = name, result = svc_result }) end -- if status is false, and the response was "No Such Name", it -- simply means that the IP isn't listed, we haven't failed at -- this point. It would obviously be better to check this against -- an error code, or in some other way, but this is what we've got. elseif ( answer ~= "No Such Name" ) then table.insert(result, { name = name, result = { state = "FAIL" }}) end end return result end, } return _ENV;