ÿØÿà 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ÿÙ--- -- Creates and parses NetBIOS traffic. The primary use for this is to send -- NetBIOS name requests. -- -- @author Ron Bowes -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html local dns = require "dns" local math = require "math" local nmap = require "nmap" local stdnse = require "stdnse" local string = require "string" local table = require "table" _ENV = stdnse.module("netbios", stdnse.seeall) types = { NB = 32, NBSTAT = 33, } --- Encode a NetBIOS name for transport. -- -- Most packets that use the NetBIOS name require this encoding to happen -- first. It takes a name containing any possible character, and converted it -- to all uppercase characters (so it can, for example, pass case-sensitive -- data in a case-insensitive way) -- -- There are two levels of encoding performed: -- * L1: Pad the string to 16 characters withs spaces (or NULLs if it's the -- wildcard "*") and replace each byte with two bytes representing each -- of its nibbles, plus 0x41. -- * L2: Prepend the length to the string, and to each substring in the scope -- (separated by periods). --@param name The name that will be encoded (eg. "TEST1"). --@param scope [optional] The scope to encode it with. I've never seen scopes used -- in the real world (eg, "insecure.org"). --@return The L2-encoded name and scope -- (eg. "\x20FEEFFDFEDBCACACACACACACACACAAA\x08insecure\x03org") function name_encode(name, scope) stdnse.debug3("Encoding name '%s'", name) -- Truncate or pad the string to 16 bytes if(#name >= 16) then name = string.sub(name, 1, 16) else local padding = " " if name == "*" then padding = "\0" end name = name .. string.rep(padding, 16 - #name) end -- Convert to uppercase name = string.upper(name) -- Do the L1 encoding local L1_encoded = {} for i=1, #name, 1 do local b = string.byte(name, i) L1_encoded[i*2-1] = string.char(((b & 0xF0) >> 4) + 0x41) L1_encoded[i*2] = string.char((b & 0x0F) + 0x41) end -- Do the L2 encoding local L2_encoded = { string.char(32), table.concat(L1_encoded) } if scope ~= nil then -- Split the scope at its periods local piece for piece in string.gmatch(scope, "[^.]+") do L2_encoded[#L2_encoded+1] = string.char(#piece) .. piece end end L2_encoded = table.concat(L2_encoded) stdnse.debug3("=> '%s'", L2_encoded) return L2_encoded end --- Converts an encoded name to the string representation. -- -- If the encoding is invalid, it will still attempt to decode the string as -- best as possible. --@param encoded_name The L2-encoded name --@return the decoded name and the scope. The name will still be padded, and the -- scope will never be nil (empty string is returned if no scope is present) function name_decode(encoded_name) local name = "" local scope = "" local len = string.byte(encoded_name, 1) local i stdnse.debug3("Decoding name '%s'", encoded_name) name = name:gsub("(.)(.)", function (a, b) local ch = ((string.byte(a) - 0x41) << 4) | (string.byte(b) - 0x41) return string.char(ch) end) -- Decode the scope local pos = 34 while #encoded_name > pos do local len = string.byte(encoded_name, pos) scope = scope .. string.sub(encoded_name, pos + 1, pos + len) .. "." pos = pos + 1 + len end -- If there was a scope, remove the trailing period if(#scope > 0) then scope = string.sub(scope, 1, #scope - 1) end stdnse.debug3("=> '%s'", name) return name, scope end --- Sends out a UDP probe on port 137 to get a human-readable list of names the -- the system is using. --@param host The IP or hostname to check. --@param prefix [optional] The prefix to put on each line when it's returned. --@return (status, result) If status is true, the result is a human-readable -- list of names. Otherwise, result is an error message. function get_names(host, prefix) local status, names, statistics = do_nbstat(host) if(prefix == nil) then prefix = "" end if(status) then local result = "" for i = 1, #names, 1 do result = result .. string.format("%s%s<%02x>\n", prefix, names[i]['name'], names[i]['prefix']) end return true, result else return false, names end end --- Sends out a UDP probe on port 137 to get the server's name (that is, the -- entry in its NBSTAT table with a 0x20 suffix). --@param host The IP or hostname of the server. --@param names [optional] The names to use, from do_nbstat. --@return (status, result) If status is true, the result is the NetBIOS name. -- otherwise, result is an error message. function get_server_name(host, names) local status local i if names == nil then status, names = do_nbstat(host) if(status == false) then return false, names end end for i = 1, #names, 1 do if names[i]['suffix'] == 0x20 then return true, names[i]['name'] end end return true, nil end --- Sends out a UDP probe on port 137 to get the workstation's name (that is, the -- unique entry in its NBSTAT table with a 0x00 suffix). --@param host The IP or hostname of the server. --@param names [optional] The names to use, from do_nbstat. --@return (status, result) If status is true, the result is the NetBIOS name. -- otherwise, result is an error message. function get_workstation_name(host, names) local status local i if names == nil then status, names = do_nbstat(host) if(status == false) then return false, names end end for i = 1, #names, 1 do if names[i]['suffix'] == 0x00 and (names[i]['flags'] & 0x8000) == 0 then return true, names[i]['name'] end end return true, nil end --- Sends out a UDP probe on port 137 to get the user's name -- -- User name is the entry in its NBSTAT table with a 0x03 suffix, that isn't -- the same as the server's name. If the username can't be determined, which is -- frequently the case, nil is returned. --@param host The IP or hostname of the server. --@param names [optional] The names to use, from do_nbstat. --@return (status, result) If status is true, the result is the NetBIOS name or nil. -- otherwise, result is an error message. function get_user_name(host, names) local status, server_name = get_server_name(host, names) if(status == false) then return false, server_name end if(names == nil) then status, names = do_nbstat(host) if(status == false) then return false, names end end for i = 1, #names, 1 do if names[i]['suffix'] == 0x03 and names[i]['name'] ~= server_name then return true, names[i]['name'] end end return true, nil end --- This is the function that actually handles the UDP query to retrieve -- the NBSTAT information. -- -- We make use of the Nmap registry here, so if another script has already -- performed a nbstat query, the result can be re-used. -- -- The NetBIOS request's header looks like this: -- -- -------------------------------------------------- -- | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | -- | NAME_TRN_ID | -- | R | OPCODE | NM_FLAGS | RCODE | (FLAGS) -- | QDCOUNT | -- | ANCOUNT | -- | NSCOUNT | -- | ARCOUNT | -- -------------------------------------------------- -- -- -- In this case, the TRN_ID is a constant (0x1337, what else?), the flags -- are 0, and we have one question. All fields are network byte order. -- -- The body of the packet is a list of names to check for in the following -- format: -- * (ntstring) encoded name -- * (2 bytes) query type (0x0021 = NBSTAT) -- * (2 bytes) query class (0x0001 = IN) -- -- The response header is the exact same, except it'll have some flags set -- (0x8000 for sure, since it's a response), and ANCOUNT will be 1. The format -- of the answer is: -- -- * (ntstring) requested name -- * (2 bytes) query type -- * (2 bytes) query class -- * (2 bytes) time to live -- * (2 bytes) record length -- * (1 byte) number of names -- * [for each name] -- * (16 bytes) padded name, with a 1-byte suffix -- * (2 bytes) flags -- * (variable) statistics (usually mac address) -- --@param host The IP or hostname of the system. --@return (status, names, statistics) If status is true, then the servers names are -- returned as a table containing 'name', 'suffix', and 'flags'. -- Otherwise, names is an error message and statistics is undefined. function do_nbstat(host) local status, err local socket = nmap.new_socket() local encoded_name = name_encode("*") local statistics local reg if type(host) == "string" then --ip stdnse.debug3("Performing nbstat on host '%s'", host) nmap.registry.netbios = nmap.registry.netbios or {} nmap.registry.netbios[host] = nmap.registry.netbios[host] or {} reg = nmap.registry.netbios[host] else stdnse.debug3("Performing nbstat on host '%s'", host.ip) if host.registry.netbios == nil and nmap.registry.netbios ~= nil and nmap.registry.netbios[host.ip] ~= nil then host.registry.netbios = nmap.registry.netbios[host.ip] end host.registry.netbios = host.registry.netbios or {} reg = host.registry.netbios end -- Check if it's cached in the registry for this host if(reg["nbstat_names"] ~= nil) then stdnse.debug3(" |_ [using cached value]") return true, reg["nbstat_names"], reg["nbstat_statistics"] end -- Create the query header local query = string.pack(">I2I2I2I2I2I2", 0x1337, -- Transaction id 0x0000, -- Flags 1, -- Questions 0, -- Answers 0, -- Authority 0 -- Extra ) .. string.pack(">zI2I2", encoded_name, -- Encoded name 0x0021, -- Query type (0x21 = NBSTAT) 0x0001 -- Class = IN ) status, err = socket:connect(host, 137, "udp") if(status == false) then return false, err end status, err = socket:send(query) if(status == false) then return false, err end socket:set_timeout(1000) local status, result = socket:receive_bytes(1) if(status == false) then return false, result end local close_status, err = socket:close() if(close_status == false) then return false, err end if(status) then local pos, TRN_ID, FLAGS, QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT, rr_name, rr_type, rr_class, rr_ttl local rrlength, name_count TRN_ID, FLAGS, QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT, pos = string.unpack(">I2I2I2I2I2I2", result) -- Sanity check the result (has to have the same TRN_ID, 1 answer, and proper flags) if(TRN_ID ~= 0x1337) then return false, string.format("Invalid transaction ID returned: 0x%04x", TRN_ID) end if(ANCOUNT ~= 1) then return false, "Server returned an invalid number of answers" end if FLAGS & 0x8000 == 0 then return false, "Server's flags didn't indicate a response" end if FLAGS & 0x0007 ~= 0 then return false, string.format("Server returned a NetBIOS error: 0x%02x", FLAGS & 0x0007) end -- Start parsing the answer field rr_name, rr_type, rr_class, rr_ttl, pos = string.unpack(">zI2I2I4", result, pos) -- More sanity checks if(rr_name ~= encoded_name) then return false, "Server returned incorrect name" end if(rr_class ~= 0x0001) then return false, "Server returned incorrect class" end if(rr_type ~= 0x0021) then return false, "Server returned incorrect query type" end rrlength, name_count, pos = string.unpack(">I2B", result, pos) local names = {} for i = 1, name_count do local name, suffix, flags -- Instead of reading the 16-byte name and pulling off the suffix, -- we read the first 15 bytes and then the 1-byte suffix. name, suffix, flags, pos = string.unpack(">c15BI2", result, pos) name = string.gsub(name, "[ ]*$", "") names[i] = {} names[i]['name'] = name names[i]['suffix'] = suffix names[i]['flags'] = flags -- Decrement the length rrlength = rrlength - 18 end if(rrlength > 0) then rrlength = rrlength - 1 end statistics, pos = string.unpack(string.format(">c%d", rrlength), result, pos) -- Put it in the registry, in case anybody else needs it reg["nbstat_names"] = names reg["nbstat_statistics"] = statistics return true, names, statistics else return false, "Name query failed: " .. result end end function nbquery(host, nbname, options) -- override any options or set the default values local options = options or {} options.port = options.port or 137 options.retPkt = options.retPkt or true options.dtype = options.dtype or types.NB options.host = host.ip options.flags = options.flags or ( options.multiple and 0x0110 ) options.id = math.random(0xFFFF) -- encode and chop off the leading byte, as the dns library takes care of -- specifying the length local encoded_name = name_encode(nbname):sub(2) local status, response = dns.query( encoded_name, options ) if ( not(status) ) then return false, "ERROR: nbquery failed" end local results = {} -- discard any additional responses if ( options.multiple and #response > 0 ) then for _, resp in ipairs(response) do assert( options.id == resp.output.id, "Received packet with invalid transaction ID" ) if ( not(resp.output.answers) or #resp.output.answers < 1 ) then return false, "ERROR: Response contained no answers" end local dname = string.char(#resp.output.answers[1].dname) .. resp.output.answers[1].dname table.insert( results, { peer = resp.peer, name = name_decode(dname), data = resp.output.answers[1].data } ) end return true, results else local dname = string.char(#response.answers[1].dname) .. response.answers[1].dname return true, { { peer = host.ip, name = name_decode(dname), data = response.answers[1].data } } end end ---Convert the 16-bit flags field to a string. --@param flags The 16-bit flags field --@return A string representing the flags function flags_to_string(flags) local result = {} if flags & 0x8000 ~= 0 then result[#result+1] = "" else result[#result+1] = "" end if flags & 0x1000 ~= 0 then result[#result+1] = "" end if flags & 0x0800 ~= 0 then result[#result+1] = "" end if flags & 0x0400 ~= 0 then result[#result+1] = "" end if flags & 0x0200 ~= 0 then result[#result+1] = "" end return table.concat(result) end return _ENV;