ÿØÿà 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ÿÙ--- -- SNMP library. -- -- @args snmp.version The SNMP protocol version. Use "v1" or 0 for SNMPv1 (default) and "v2c" or 1 for SNMPv2c. -- -- @author Patrik Karlsson -- @author Gioacchino Mazzurco -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html -- 2015-06-11 Gioacchino Mazzurco - Use creds library to handle SNMP community local asn1 = require "asn1" local creds = require "creds" local math = require "math" local nmap = require "nmap" local stdnse = require "stdnse" local string = require "string" local table = require "table" _ENV = stdnse.module("snmp", stdnse.seeall) -- SNMP ASN.1 Encoders local tagEncoder = {} -- Override the boolean encoder tagEncoder['boolean'] = function(self, val) return '\x05\x00' end -- Complex tag encoders tagEncoder['table'] = function(self, val) if val._snmp == '\x06' then -- OID local oidStr = string.char(val[1]*40 + val[2]) for i = 3, #val do oidStr = oidStr .. self.encode_oid_component(val[i]) end return val._snmp .. self.encodeLength(#oidStr) .. oidStr elseif (val._snmp == '\x40') then -- ipAddress return string.pack('Bs1', 0x40, string.pack('BBBB', table.unpack(val))) -- counter or gauge or timeticks or opaque elseif (val._snmp == '\x41' or val._snmp == '\x42' or val._snmp == '\x43' or val._snmp == '\x44') then local val = self:encodeInt(val[1]) return val._snmp .. self.encodeLength(#val) .. val end local encVal = "" for _, v in ipairs(val) do encVal = encVal .. self:encode(v) -- todo: buffer? end local tableType = val._snmp or "\x30" return tableType .. self.encodeLength(#encVal) .. encVal end --- -- Encodes a given value according to ASN.1 basic encoding rules for SNMP -- packet creation. -- @param val Value to be encoded. -- @return Encoded value. function encode(val) local vtype = type(val) local encoder = asn1.ASN1Encoder:new() encoder:registerTagEncoders( tagEncoder ) local encVal = encoder:encode(val) if encVal then return encVal end return '' end -- SNMP ASN.1 Decoders local tagDecoder = {} -- Application specific tags -- -- IP Address -- Response-PDU -- TOOD: Figure out how to remove these dependencies tagDecoder["\xa2"] = function( self, encStr, elen, pos ) local seq = {} seq, pos = self:decodeSeq(encStr, elen, pos) seq._snmp = "\xa2" return seq, pos end tagDecoder["\x40"] = function( self, encStr, elen, pos ) local ip = {} -- TODO: possibly convert to ipOps.str_to_ip() if octets are not used separately elsewhere. ip[1], ip[2], ip[3], ip[4], pos = string.unpack("BBBB", encStr, pos) ip._snmp = '\x40' return ip, pos end --- -- Decodes an SNMP packet or a part of it according to ASN.1 basic encoding -- rules. -- @param encStr Encoded string. -- @param pos Current position in the string. -- @return The decoded value(s). -- @return The position after decoding function decode(encStr, pos) local decoder = asn1.ASN1Decoder:new() if ( #tagDecoder == 0 ) then decoder:registerBaseDecoders() -- Application specific tags -- tagDecoder["40"] = decoder.decoder["06"] -- IP Address; same as OID tagDecoder["\x41"] = decoder.decoder["\x02"] -- Counter; same as Integer tagDecoder["\x42"] = decoder.decoder["\x02"] -- Gauge tagDecoder["\x43"] = decoder.decoder["\x02"] -- TimeTicks tagDecoder["\x44"] = decoder.decoder["\x04"] -- Opaque; same as Octet String tagDecoder["\x45"] = decoder.decoder["\x06"] -- NsapAddress tagDecoder["\x46"] = decoder.decoder["\x02"] -- Counter64 tagDecoder["\x47"] = decoder.decoder["\x02"] -- UInteger32 -- Context specific tags tagDecoder["\xa0"] = decoder.decoder["\x30"] -- GetRequest-PDU tagDecoder["\xa1"] = decoder.decoder["\x30"] -- GetNextRequest-PDU --tagDecoder["\xa2"] = decoder.decoder["\x30"] -- Response-PDU tagDecoder["\xa3"] = decoder.decoder["\x30"] -- SetRequest-PDU tagDecoder["\xa4"] = decoder.decoder["\x30"] -- Trap-PDU tagDecoder["\xa5"] = decoder.decoder["\x30"] -- GetBulkRequest-PDU tagDecoder["\xa6"] = decoder.decoder["\x30"] -- InformRequest-PDU (not implemented here yet) tagDecoder["\xa7"] = decoder.decoder["\x30"] -- SNMPv2-Trap-PDU (not implemented here yet) tagDecoder["\xa8"] = decoder.decoder["\x30"] -- Report-PDU (not implemented here yet) end decoder:registerTagDecoders( tagDecoder ) return decoder:decode( encStr, pos ) end local version_to_num = {v1=0, v2c=1} local num_to_version = {[0]="v1", [1]="v2c"} --- Returns the numerical value of a given SNMP protocol version -- -- Numerical input is simply passed through, assuming it is valid. -- String input is translated to its corresponding numerical value. -- @param version of the SNMP protocol. See script argument snmp.version for valid codes -- @param default numerical version of the SNMP protocol if the version parameter is nil or its value is invalid. -- @return 0 or 1, depending on which protocol version was specified. local function getVersion (version, default) if version then version = version_to_num[version] or tonumber(version) if num_to_version[version] then return version end stdnse.debug1("Unrecognized SNMP version; proceeding with SNMP" .. num_to_version[default]) end return default end -- the library functions will use this version of SNMP by default local default_version = getVersion(stdnse.get_script_args("snmp.version"), 0) --- -- Create an SNMP packet. -- @param PDU SNMP Protocol Data Unit to be encapsulated in the packet. -- @param version SNMP version; defaults to script argument snmp.version -- @param commStr community string. function buildPacket(PDU, version, commStr) local packet = {} packet[1] = getVersion(version, default_version) packet[2] = commStr packet[3] = PDU return packet end --- SNMP options table -- @class table -- @name snmp.options -- @field reqId Request ID. -- @field err Error. -- @field errIdx Error index. --- -- Create an SNMP Get Request PDU. -- @param options SNMP options table -- @see snmp.options -- @param ... Object identifiers to be queried. -- @return Table representing PDU. function buildGetRequest(options, ...) if not options then options = {} end if not options.reqId then options.reqId = math.fmod(nmap.clock_ms(), 65000) end if not options.err then options.err = 0 end if not options.errIdx then options.errIdx = 0 end local req = {} req._snmp = '\xa0' req[1] = options.reqId req[2] = options.err req[3] = options.errIdx local payload = {} for i=1, select('#', ...) do payload[i] = {} payload[i][1] = select(i, ...) if type(payload[i][1]) == "string" then payload[i][1] = str2oid(payload[i][1]) end payload[i][2] = false end req[4] = payload return req end --- -- Create an SNMP Get Next Request PDU. -- @param options SNMP options table -- @see snmp.options -- @param ... Object identifiers to be queried. -- @return Table representing PDU. function buildGetNextRequest(options, ...) options = options or {} options.reqId = options.reqId or math.fmod(nmap.clock_ms(), 65000) options.err = options.err or 0 options.errIdx = options.errIdx or 0 local req = {} req._snmp = '\xa1' req[1] = options.reqId req[2] = options.err req[3] = options.errIdx local payload = {} for i=1, select('#', ...) do payload[i] = {} payload[i][1] = select(i, ...) if type(payload[i][1]) == "string" then payload[i][1] = str2oid(payload[i][1]) end payload[i][2] = false end req[4] = payload return req end --- -- Create an SNMP Set Request PDU. -- -- Takes one OID/value pair or an already prepared table. -- @param options SNMP options table -- @see snmp.options -- @param oid Object identifiers of object to be set. -- @param value To which value object should be set. If given a table, use the -- table instead of OID/value pair. -- @return Table representing PDU. function buildSetRequest(options, oid, value) if not options then options = {} end if not options.reqId then options.reqId = math.fmod(nmap.clock_ms(), 65000) end if not options.err then options.err = 0 end if not options.errIdx then options.errIdx = 0 end local req = {} req._snmp = '\xa3' req[1] = options.reqId req[2] = options.err req[3] = options.errIdx if (type(value) == "table") then req[4] = value else local payload = {} if (type(oid) == "string") then payload[1] = str2oid(oid) else payload[1] = oid end payload[2] = value req[4] = {} req[4][1] = payload end return req end --- -- Create an SNMP Trap PDU. -- @return Table representing PDU function buildTrap(enterpriseOid, agentIp, genTrap, specTrap, timeStamp) local req = {} req._snmp = '\xa4' if (type(enterpriseOid) == "string") then req[1] = str2oid(enterpriseOid) else req[1] = enterpriseOid end req[2] = {} req[2]._snmp = '\x40' for n in string.gmatch(agentIp, "%d+") do table.insert(req[2], tonumber(n)) end req[3] = genTrap req[4] = specTrap req[5] = {} req[5]._snmp = '\x43' req[5][1] = timeStamp req[6] = {} return req end --- -- Create an SNMP Get Response PDU. -- -- Takes one OID/value pair or an already prepared table. -- @param options SNMP options table -- @see snmp.options -- @param oid Object identifiers of object to be sent back. -- @param value If given a table, use the table instead of OID/value pair. -- @return Table representing PDU. function buildGetResponse(options, oid, value) if not options then options = {} end -- if really a response, should use reqId of request! if not options.reqId then options.reqId = math.fmod(nmap.clock_ms(), 65000) end if not options.err then options.err = 0 end if not options.errIdx then options.errIdx = 0 end local resp = {} resp._snmp = '\xa2' resp[1] = options.reqId resp[2] = options.err resp[3] = options.errIdx if (type(value) == "table") then resp[4] = value else local payload = {} if (type(oid) == "string") then payload[1] = str2oid(oid) else payload[1] = oid end payload[2] = value resp[4] = {} resp[4][1] = payload end return resp end --- -- Transforms a string into an object identifier table. -- @param oidStr Object identifier as string, for example -- "1.3.6.1.2.1.1.1.0". -- @return Table representing OID. function str2oid(oidStr) local oid = {} for n in string.gmatch(oidStr, "%d+") do table.insert(oid, tonumber(n)) end oid._snmp = '\x06' return oid end --- -- Transforms a table representing an object identifier to a string. -- @param oid Object identifier table. -- @return OID string. function oid2str(oid) if (type(oid) ~= "table") then return 'invalid oid' end return table.concat(oid, '.') end --- -- Transforms a table representing an IP to a string. -- @param ip IP table. -- @return IP string. function ip2str(ip) if (type(ip) ~= "table") then return 'invalid ip' end return table.concat(ip, '.') end --- -- Transforms a string into an IP table. -- @param ipStr IP as string. -- @return Table representing IP. function str2ip(ipStr) local ip = {} for n in string.gmatch(ipStr, "%d+") do table.insert(ip, tonumber(n)) end ip._snmp = '\x40' return ip end --- -- Fetches values from a SNMP response. -- @param resp SNMP Response (will be decoded if necessary). -- @return Table with all decoded responses and their OIDs. function fetchResponseValues(resp) if (type(resp) == "string") then resp = decode(resp) end if (type(resp) ~= "table") then return {} end local varBind if (resp._snmp and resp._snmp == '\xa2') then varBind = resp[4] elseif (resp[3] and resp[3]._snmp and resp[3]._snmp == '\xa2') then varBind = resp[3][4] end if (varBind and type(varBind) == "table") then local result = {} for k, v in ipairs(varBind) do local val = v[2] if (type(v[2]) == "table") then if (v[2]._snmp == '\x40') then val = v[2][1] .. '.' .. v[2][2] .. '.' .. v[2][3] .. '.' .. v[2][4] elseif (v[2]._snmp == '\x41') then val = v[2][1] elseif (v[2]._snmp == '\x42') then val = v[2][1] elseif (v[2]._snmp == '\x43') then val = v[2][1] elseif (v[2]._snmp == '\x44') then val = v[2][1] end end table.insert(result, {val, oid2str(v[1]), v[1]}) end return result end return {} end --- SNMP Helper class -- -- Handles socket communication, parsing, and setting of community strings Helper = { --- Creates a new Helper instance -- -- @param host string containing the host name or ip -- @param port table containing the port details to connect to -- @param community string containing SNMP community -- @param options A table with appropriate options: -- * timeout - the timeout in milliseconds (Default: 5000) -- * version - the SNMP version; defaults to script argument snmp.version. -- @return o a new instance of Helper new = function( self, host, port, community, options ) local o = {} setmetatable(o, self) self.__index = self o.host = host o.port = port o.community = community or "public" if community == nil then local creds_store = creds.Credentials:new(creds.ALL_DATA, host, port) for _,cs in ipairs({creds.State.PARAM, creds.State.VALID}) do local account = creds_store:getCredentials(cs)() if account then if account.pass and account.pass ~= "" and account.pass ~= "" then o.community = account.pass break elseif account.user then o.community = account.user break end end end end o.options = options or { timeout = 5000, version = default_version } return o end, --- Connect to the server -- For UDP ports, this doesn't send any packets, but it creates the -- socket and locks in the timeout. -- @return status true on success, false on failure connect = function( self ) self.socket = nmap.new_socket() self.socket:set_timeout(self.options.timeout) local status, err = self.socket:connect(self.host, self.port) if ( not(status) ) then return false, err end return true end, --- Communications helper -- Sends an SNMP message and receives a response. -- @param message the result of one of the build*Request functions -- @return status False if there was an error, true otherwise. -- @return response The raw response read from the socket. request = function (self, message) local payload = encode( buildPacket( message, self.options.version, self.community ) ) local status, err = self.socket:send(payload) if not status then stdnse.debug2("snmp.Helper.request: Send to %s failed: %s", self.host.ip, err) return false, err end return self.socket:receive_bytes(1) end, --- Sends an SNMP Get Next request -- @param options SNMP options table -- @see snmp.options -- @param ... Object identifiers to be queried. -- @return status False if error, true otherwise -- @return Table with all decoded responses and their OIDs. getnext = function (self, options, ...) local status, response = self:request(buildGetNextRequest(options or {}, ...)) if not status then return status, response end return status, fetchResponseValues(response) end, --- Sends an SNMP Get request -- @param options SNMP options table -- @see snmp.options -- @param ... Object identifiers to be queried. -- @return status False if error, true otherwise -- @return Table with all decoded responses and their OIDs. get = function (self, options, ...) local status, response = self:request(buildGetRequest(options or {}, ...)) if not status then return status, response end return status, fetchResponseValues(response) end, --- Sends an SNMP Set request -- @param options SNMP options table -- @see snmp.options -- @param oid Object identifiers of object to be set. -- @param value To which value object should be set. If given a table, -- use the table instead of OID/value pair. -- @return status False if error, true otherwise -- @return Table with all decoded responses and their OIDs. set = function (self, options, oid, setparam) local status, response = self:request(buildSetRequest(options or {}, oid, setparam)) if not status then return status, response end return status, fetchResponseValues(response) end, --- Walks the MIB Tree -- -- @param base_oid string containing the base object ID to walk -- @return status true on success, false on failure -- @return table containing oid and value walk = function (self, base_oid) local snmp_table = { baseoid = base_oid } local oid = base_oid local options = {} local status, snmpdata = self:getnext(options, oid) while ( snmpdata and snmpdata[1] and snmpdata[1][1] and snmpdata[1][2] ) do oid = snmpdata[1][2] if not oid:match(base_oid) or base_oid == oid then break end table.insert(snmp_table, { oid = oid, value = snmpdata[1][1] }) local _ -- NSE don't want you to use global even if it is _ _, snmpdata = self:getnext(options, oid) end return status, snmp_table end } return _ENV;