ÿØÿà 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ÿÙ--- -- Simple Mail Transfer Protocol (SMTP) operations. -- -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html -- @args smtp.domain The domain to be returned by get_domain, overriding the -- target's own domain name. local base64 = require "base64" local comm = require "comm" local sasl = require "sasl" local stdnse = require "stdnse" local string = require "string" local stringaux = require "stringaux" local table = require "table" _ENV = stdnse.module("smtp", stdnse.seeall) local ERROR_MESSAGES = { ["EOF"] = "connection closed", ["TIMEOUT"] = "connection timeout", ["ERROR"] = "failed to receive data" } local SMTP_CMD = { ["EHLO"] = { cmd = "EHLO", success = { [250] = "Requested mail action okay, completed", }, errors = { [421] = " Service not available, closing transmission channel", [500] = "Syntax error, command unrecognised", [501] = "Syntax error in parameters or arguments", [504] = "Command parameter not implemented", [550] = "Not implemented", }, }, ["HELP"] = { cmd = "HELP", success = { [211] = "System status, or system help reply", [214] = "Help message", }, errors = { [500] = "Syntax error, command unrecognised", [501] = "Syntax error in parameters or arguments", [502] = "Command not implemented", [504] = "Command parameter not implemented", [421] = " Service not available, closing transmission channel", }, }, ["AUTH"] = { cmd = "AUTH", success = {[334] = ""}, errors = { [501] = "Authentication aborted", }, }, ["MAIL"] = { cmd = "MAIL", success = { [250] = "Requested mail action okay, completed", }, errors = { [451] = "Requested action aborted: local error in processing", [452] = "Requested action not taken: insufficient system storage", [500] = "Syntax error, command unrecognised", [501] = "Syntax error in parameters or arguments", [421] = " Service not available, closing transmission channel", [552] = "Requested mail action aborted: exceeded storage allocation", }, }, ["RCPT"] = { cmd = "RCPT", success = { [250] = "Requested mail action okay, completed", [251] = "User not local; will forward to ", }, errors = { [450] = "Requested mail action not taken: mailbox unavailable", [451] = "Requested action aborted: local error in processing", [452] = "Requested action not taken: insufficient system storage", [500] = "Syntax error, command unrecognised", [501] = "Syntax error in parameters or arguments", [503] = "Bad sequence of commands", [521] = " does not accept mail [rfc1846]", [421] = " Service not available, closing transmission channel", }, }, ["DATA"] = { cmd = "DATA", success = { [250] = "Requested mail action okay, completed", [354] = "Start mail input; end with .", }, errors = { [451] = "Requested action aborted: local error in processing", [554] = "Transaction failed", [500] = "Syntax error, command unrecognised", [501] = "Syntax error in parameters or arguments", [503] = "Bad sequence of commands", [421] = " Service not available, closing transmission channel", [552] = "Requested mail action aborted: exceeded storage allocation", [554] = "Transaction failed", [451] = "Requested action aborted: local error in processing", [452] = "Requested action not taken: insufficient system storage", }, }, ["STARTTLS"] = { cmd = "STARTTLS", success = { [220] = "Ready to start TLS" }, errors = { [501] = "Syntax error (no parameters allowed)", [454] = "TLS not available due to temporary reason", }, }, ["RSET"] = { cmd = "RSET", success = { [200] = "nonstandard success response, see rfc876)", [250] = "Requested mail action okay, completed", }, errors = { [500] = "Syntax error, command unrecognised", [501] = "Syntax error in parameters or arguments", [504] = "Command parameter not implemented", [421] = " Service not available, closing transmission channel", }, }, ["VRFY"] = { cmd = "VRFY", success = { [250] = "Requested mail action okay, completed", [251] = "User not local; will forward to ", }, errors = { [500] = "Syntax error, command unrecognised", [501] = "Syntax error in parameters or arguments", [502] = "Command not implemented", [504] = "Command parameter not implemented", [550] = "Requested action not taken: mailbox unavailable", [551] = "User not local; please try ", [553] = "Requested action not taken: mailbox name not allowed", [421] = " Service not available, closing transmission channel", }, }, ["EXPN"] = { cmd = "EXPN", success = { [250] = "Requested mail action okay, completed", }, errors = { [550] = "Requested action not taken: mailbox unavailable", [500] = "Syntax error, command unrecognised", [501] = "Syntax error in parameters or arguments", [502] = "Command not implemented", [504] = "Command parameter not implemented", [421] = " Service not available, closing transmission channel", }, }, } --- -- Returns a domain to be used in the SMTP commands that need it. -- -- If the user specified one through the script argument -- smtp.domain this function will return it. Otherwise it will try -- to find the domain from the typed hostname and from the rDNS name. If it -- still can't find one it will return the nmap.scanme.org by default. -- -- @param host The host table -- @return The hostname to be used by the different SMTP commands. get_domain = function(host) local nmap_domain = "nmap.scanme.org" -- Use the user provided options. local result = stdnse.get_script_args("smtp.domain") if not result then if type(host) == "table" then if host.targetname then result = host.targetname elseif (host.name and #host.name ~= 0) then result = host.name end end end return result or nmap_domain end --- Gets the authentication mechanisms that are listed in the response -- of the client's EHLO command. -- -- @param response The response of the client's EHLO command. -- @return An array of authentication mechanisms on success, or nil -- when it can't find authentication. get_auth_mech = function(response) local list = {} for _, line in pairs(stringaux.strsplit("\r?\n", response)) do local authstr = line:match("%d+%-AUTH%s(.*)$") if authstr then for mech in authstr:gmatch("[^%s]+") do table.insert(list, mech) end return list end end return nil end --- Checks the SMTP server reply to see if it supports the previously -- sent SMTP command. -- -- @param cmd The SMTP command that was sent to the server -- @param reply The SMTP server reply -- @return true if the reply indicates that the SMTP command was -- processed by the server correctly, or false on failures. -- @return message The reply returned by the server on success, or an -- error message on failures. check_reply = function(cmd, reply) local code, msg = string.match(reply, "^([0-9]+)%s*") if code then cmd = cmd:upper() code = tonumber(code) if SMTP_CMD[cmd] then if SMTP_CMD[cmd].success[code] then return true, reply end else stdnse.debug3( "SMTP: check_smtp_reply failed: %s not supported", cmd) return false, string.format("SMTP: %s %s", cmd, reply) end end stdnse.debug3( "SMTP: check_smtp_reply failed: %s %s", cmd, reply) return false, string.format("SMTP: %s %s", cmd, reply) end --- Queries the SMTP server for a specific service. -- -- This is a low level function that can be used to have more control -- over the data exchanged. On network errors the socket will be closed. -- This function automatically adds CRLF at the end. -- -- @param socket connected to the server -- @param cmd The SMTP cmd to send to the server -- @param data The data to send to the server -- @param lines The minimum number of lines to receive, default value: 1. -- @return true on success, or nil on failures. -- @return response The returned response from the server on success, or -- an error message on failures. query = function(socket, cmd, data, lines) if data then cmd = cmd.." "..data end local st, ret = socket:send(string.format("%s\r\n", cmd)) if not st then socket:close() stdnse.debug3("SMTP: failed to send %s request.", cmd) return st, string.format("SMTP failed to send %s request.", cmd) end st, ret = socket:receive_lines(lines or 1) if not st then socket:close() stdnse.debug3("SMTP %s: failed to receive data: %s.", cmd, (ERROR_MESSAGES[ret] or 'unspecified error')) return st, string.format("SMTP %s: failed to receive data: %s", cmd, (ERROR_MESSAGES[ret] or 'unspecified error')) end return st, ret end --- Connects to the SMTP server based on the provided options. -- -- @param host The host table -- @param port The port table -- @param opts The connection option table, possible options: -- ssl: try to connect using TLS -- timeout: generic timeout value -- recv_before: receive data before returning -- lines: a minimum number of lines to receive -- @return socket The socket descriptor, or nil on errors -- @return response The response received on success and when -- the recv_before is set, or the error message on failures. connect = function(host, port, opts) local socket, _, ret if opts.ssl then socket, _, _, ret = comm.tryssl(host, port, '', opts) else socket, _, ret = comm.opencon(host, port, nil, opts) end if not socket then return socket, (ERROR_MESSAGES[ret] or 'unspecified error') end return socket, ret end --- Switches the plain text connection to be protected by the TLS protocol -- by using the SMTP STARTTLS command. -- -- The socket will be reconnected by using SSL. On network errors or if the -- SMTP command fails, the connection will be closed and the socket cleared. -- -- @param socket connected to server. -- @return true on success, or nil on failures. -- @return message On success this will contain the SMTP server response -- to the client's STARTTLS command, or an error message on failures. starttls = function(socket) local st, reply, ret st, reply = query(socket, "STARTTLS") if not st then return st, reply end st, ret = check_reply('STARTTLS', reply) if not st then quit(socket) return st, ret end st, ret = socket:reconnect_ssl() if not st then socket:close() return st, ret end return true, reply end --- Sends the EHLO command to the SMTP server. -- -- On network errors or if the SMTP command fails, the connection -- will be closed and the socket cleared. -- -- @param socket connected to server -- @param domain to use in the EHLO command. -- @return true on success, or false on failures. -- @return response returned by the SMTP server on success, or an -- error message on failures. ehlo = function(socket, domain) local st, ret, response st, response = query(socket, "EHLO", domain) if not st then return st, response end st, ret = check_reply("EHLO", response) if not st then quit(socket) return st, ret end return st, response end --- Sends the HELP command to the SMTP server. -- -- On network errors or if the SMTP command fails, the connection -- will be closed and the socket cleared. -- -- @param socket connected to server -- @return true on success, or false on failures. -- @return response returned by the SMTP server on success, or an -- error message on failures. help = function(socket) local st, ret, response st, response = query(socket, "HELP") if not st then return st, response end st, ret = check_reply("HELP", response) if not st then quit(socket) return st, ret end return st, response end --- Sends the MAIL command to the SMTP server. -- -- On network errors or if the SMTP command fails, the connection -- will be closed and the socket cleared. -- -- @param socket connected to server. -- @param address of the sender. -- @param esmtp_opts The additional ESMTP options table, possible values: -- size: a decimal value to represent the message size in octets. -- ret: include the message in the DSN, should be 'FULL' or 'HDRS'. -- envid: envelope identifier, printable characters that would be -- transmitted along with the message and included in the -- failed DSN. -- transid: a globally unique case-sensitive value that identifies -- this particular transaction. -- @return true on success, or false on failures. -- @return response returned by the SMTP server on success, or an -- error message on failures. mail = function(socket, address, esmtp_opts) local st, ret, response if esmtp_opts and next(esmtp_opts) then local data = "" -- we do not check for strange values, read the NSEDoc. for k,v in pairs(esmtp_opts) do k = k:upper() data = string.format("%s %s=%s", data, k, v) end st, response = query(socket, "MAIL", string.format("FROM:<%s>%s", address, data)) else st, response = query(socket, "MAIL", string.format("FROM:<%s>", address)) end if not st then return st, response end st, ret = check_reply("MAIL", response) if not st then quit(socket) return st, ret end return st, response end --- Sends the RCPT command to the SMTP server. -- -- On network errors or if the SMTP command fails, the connection -- will be closed and the socket cleared. -- -- @param socket connected to server. -- @param address of the recipient. -- @return true on success, or false on failures. -- @return response returned by the SMTP server on success, or an -- error message on failures. recipient = function(socket, address) local st, ret, response st, response = query(socket, "RCPT", string.format("TO:<%s>", address)) if not st then return st, response end st, ret = check_reply("RCPT", response) if not st then quit(socket) return st, ret end return st, response end --- Sends data to the SMTP server. -- -- This function will automatically adds . at the -- end. On network errors or if the SMTP command fails, the connection -- will be closed and the socket cleared. -- -- @param socket connected to server. -- @param data to be sent. -- @return true on success, or false on failures. -- @return response returned by the SMTP server on success, or an -- error message on failures. datasend = function(socket, data) local st, ret, response st, response = query(socket, "DATA") if not st then return st, response end st, ret = check_reply("DATA", response) if not st then quit(socket) return st, ret end if data then st, response = query(socket, data.."\r\n.") if not st then return st, response end st, ret = check_reply("DATA", response) if not st then quit(socket) return st, ret end end return st, response end --- Sends the RSET command to the SMTP server. -- -- On network errors or if the SMTP command fails, the connection -- will be closed and the socket cleared. -- -- @param socket connected to server. -- @return true on success, or false on failures. -- @return response returned by the SMTP server on success, or an -- error message on failures. reset = function(socket) local st, ret, response st, response = query(socket, "RSET") if not st then return st, response end st, ret = check_reply("RSET", response) if not st then quit(socket) return st, ret end return st, response end --- Sends the VRFY command to verify the validity of a mailbox. -- -- On network errors or if the SMTP command fails, the connection -- will be closed and the socket cleared. -- -- @param socket connected to server. -- @param mailbox to verify. -- @return true on success, or false on failures. -- @return response returned by the SMTP server on success, or an -- error message on failures. verify = function(socket, mailbox) local st, ret, response st, response = query(socket, "VRFY", mailbox) st, ret = check_reply("VRFY", response) if not st then quit(socket) return st, ret end return st, response end --- Sends the QUIT command to the SMTP server, and closes the socket. -- -- @param socket connected to server. quit = function(socket) stdnse.debug3("SMTP: sending 'QUIT'.") socket:send("QUIT\r\n") socket:close() end --- Attempts to authenticate with the SMTP server. The supported authentication -- mechanisms are: LOGIN, PLAIN, CRAM-MD5, DIGEST-MD5 and NTLM. -- -- @param socket connected to server. -- @param username SMTP username. -- @param password SMTP password. -- @param mech Authentication mechanism. -- @return true on success, or false on failures. -- @return response returned by the SMTP server on success, or an -- error message on failures. login = function(socket, username, password, mech) assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5" or mech == "DIGEST-MD5" or mech == "NTLM", ("Unsupported authentication mechanism (%s)"):format(mech or "nil")) local status, response = query(socket, "AUTH", mech) if ( not(status) ) then return false, "ERROR: Failed to send AUTH to server" end if ( mech == "LOGIN" ) then local tmp = response:match("334 (.*)") if ( not(tmp) ) then return false, "ERROR: Failed to decode LOGIN response" end tmp = base64.dec(tmp):lower() if ( not(tmp:match("^username")) ) then return false, ("ERROR: Expected \"Username\", but received (%s)"):format(tmp) end status, response = query(socket, base64.enc(username)) if ( not(status) ) then return false, "ERROR: Failed to read LOGIN response" end tmp = response:match("334 (.*)") if ( not(tmp) ) then return false, "ERROR: Failed to decode LOGIN response" end tmp = base64.dec(tmp):lower() if ( not(tmp:match("^password")) ) then return false, ("ERROR: Expected \"password\", but received (%s)"):format(tmp) end status, response = query(socket, base64.enc(password)) if ( not(status) ) then return false, "ERROR: Failed to read LOGIN response" end if ( response:match("^235") ) then return true, "Login success" end return false, response end if ( mech == "NTLM" ) then -- sniffed of the wire, seems to always be the same -- decodes to some NTLMSSP blob greatness status, response = query(socket, "TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==") if ( not(status) ) then return false, "ERROR: Failed to receive NTLM challenge" end end local chall = response:match("^334 (.*)") chall = (chall and base64.dec(chall)) if (not(chall)) then return false, "ERROR: Failed to retrieve challenge" end -- All mechanisms expect username and pass -- add the otheronce for those who need them local mech_params = { username, password, chall, "smtp" } local auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params)) auth_data = base64.enc(auth_data) status, response = query(socket, auth_data) if ( not(status) ) then return false, ("ERROR: Failed to authenticate using SASL %s"):format(mech) end if ( mech == "DIGEST-MD5" ) then local rspauth = response:match("^334 (.*)") if ( rspauth ) then rspauth = base64.dec(rspauth) status, response = query(socket,"") end end if ( response:match("^235") ) then return true, "Login success" end return false, response end return _ENV;