ÿØÿà 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ÿÙ--- -- PostgreSQL library supporting both version 2 and version 3 of the protocol. -- The library currently contains the bare minimum to perform authentication. -- Authentication is supported with or without SSL enabled and using the -- plain-text or MD5 authentication mechanisms. -- -- The PGSQL protocol is explained in detail in the following references. -- * http://developer.postgresql.org/pgdocs/postgres/protocol.html -- * http://developer.postgresql.org/pgdocs/postgres/protocol-flow.html -- * http://developer.postgresql.org/pgdocs/postgres/protocol-message-formats.html -- -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html -- @author Patrik Karlsson local nmap = require "nmap" local stdnse = require "stdnse" local openssl = stdnse.silent_require "openssl" local string = require "string" local table = require "table" _ENV = stdnse.module("pgsql", stdnse.seeall) -- Version 0.3 -- Created 02/05/2010 - v0.1 - created by Patrik Karlsson -- Revised 02/20/2010 - v0.2 - added detectVersion to automatically detect and return -- the correct version class -- Revised 03/04/2010 - v0.3 - added support for trust authentication method --- Supported pgsql message types MessageType = { Error = 0x45, BackendKeyData = 0x4b, AuthRequest=0x52, ParameterStatus = 0x53, ReadyForQuery = 0x5a, PasswordMessage = 0x70, } --- Supported authentication types AuthenticationType = { Success = 0x00, Plain = 0x03, MD5 = 0x05 } -- Version 2 of the protocol v2 = { --- Pad a string with zeroes -- -- @param str string containing the string to be padded -- @param len number containing the wanted length -- @return string containing the padded string value zeroPad = function(str, len) return str .. string.rep('\0', len - #str) end, messageDecoder = { --- Decodes an Auth Request packet -- -- @param data string containing raw data received from socket -- @param len number containing the length as retrieved from the header -- @param pos number containing the offset into the data buffer -- @return pos number containing the offset after decoding, -1 on error -- @return response table containing zero or more of the following salt and success -- error string containing error message if pos is -1 [MessageType.AuthRequest] = function( data, len, pos ) local _, authtype local response = {} authtype, pos = string.unpack(">I4", data, pos) if ( authtype == AuthenticationType.MD5 ) then if ( len - pos + 1 ) < 3 then return -1, "ERROR: Malformed AuthRequest received" end response.salt, pos = string.unpack("c4", data, pos) elseif ( authtype == AuthenticationType.Plain ) then --do nothing elseif ( authtype == 0 ) then response.success = true else stdnse.debug1("unknown auth type: %d", authtype) end response.authtype = authtype return pos, response end, --- Decodes an Error packet -- -- @param data string containing raw data received from socket -- @param len number containing the length as retrieved from the header -- @param pos number containing the offset into the data buffer -- @return pos number containing the offset after decoding -- @return response table containing zero or more of the following error.severity, -- error.code, error.message, error.file, -- error.line and error.routine [MessageType.Error] = function( data, len, pos ) local tmp = data:sub(pos, pos + len - 4) local response = {} local pos_end = pos + len response.error = {} response.error.message, pos = string.unpack("z", data, pos) return pos, response end, }, --- Process the server response -- -- @param data string containing the server response -- @param pos number containing the offset into the data buffer processResponse = function(data, pos) local ptype, len, status, response local pos = pos or 1 ptype, pos = string.unpack("B", data, pos) len = data:len() - 1 if v2.messageDecoder[ptype] then pos, response = v2.messageDecoder[ptype](data, len, pos) if pos ~= -1 then response.type = ptype return pos, response end else stdnse.debug1("Missing decoder for %d", ptype) return -1, ("Missing decoder for %d"):format(ptype) end return -1, "Decoding failed" end, --- Reads a packet and handles additional socket reads to retrieve remaining data -- -- @param socket socket already connected to the pgsql server -- @param data string containing any data already retrieved from the socket -- @param pos number containing the offset into the data buffer -- @return data string containing the initial and any additional data readPacket=function(socket, data, pos) local pos = pos or 1 local data = data or "" local status = true local tmp = "" local ptype, len local catch = function() socket:close() stdnse.debug1("processResponse(): failed") end local try = nmap.new_try(catch) if ( data == nil or data:len() == 0 ) then data = try(socket:receive()) end return data end, --- Sends a startup message to the server containing the username and database to connect to -- -- @param socket socket already connected to the pgsql server -- @param user string containing the name of the user -- @param database string containing the name of the database -- @return status true on success, false on failure -- @return table containing a processed response from processResponse -- string containing error message if status is false sendStartup=function(socket, user, database) local data, response, status, pos local proto_ver, ptype, _, tmp local tty, unused, args = "", "", "" proto_ver = 0x0020000 user = v2.zeroPad(user, 32) database = v2.zeroPad(database, 64) data = string.pack(">I4I4", 296, proto_ver) .. database .. user .. v2.zeroPad(args, 64) .. v2.zeroPad(unused, 64) .. v2.zeroPad(tty,64) socket:send( data ) -- attempt to verify version status, data = socket:receive_bytes( 1 ) if ( not(status) ) then return false, "sendStartup failed" end data = v2.readPacket(socket, data ) pos, response = v2.processResponse( data ) if ( pos < 0 or response.type == MessageType.Error) then return false, response.error.message or "unknown error" end return true, response end, --- Attempts to authenticate to the pgsql server -- Supports plain-text and MD5 authentication -- -- @param socket socket already connected to the pgsql server -- @param params table containing any additional parameters authtype, version -- @param username string containing the username to use for authentication -- @param password string containing the password to use for authentication -- @param salt string containing the cryptographic salt value -- @return status true on success, false on failure -- @return result table containing parameter status information, -- result string containing an error message if login fails loginRequest = function ( socket, params, username, password, salt ) local catch = function() socket:close() stdnse.debug1("loginRequest(): failed") end local try = nmap.new_try(catch) local response = {} local status, data, len, pos, tmp if ( params.authtype == AuthenticationType.MD5 ) then local hash = createMD5LoginHash(username,password,salt) data = string.pack( ">I4z", 40, hash) try( socket:send( data ) ) elseif ( params.authtype == AuthenticationType.Plain ) then local data data = string.pack(">I4z", password:len() + 4, password) try( socket:send( data ) ) elseif ( params.authtype == AuthenticationType.Success ) then return true, nil end data, response.params = "", {} data = v2.readPacket(socket, data, 1) pos, tmp = v2.processResponse(data, 1) -- this should contain the AuthRequest packet if tmp.type ~= MessageType.AuthRequest then return false, "Expected AuthRequest got something else" end if not tmp.success then return false, "Login failure" end return true, response end, } -- Version 3 of the protocol v3 = { messageDecoder = { --- Decodes an Auth Request packet -- -- @param data string containing raw data received from socket -- @param len number containing the length as retrieved from the header -- @param pos number containing the offset into the data buffer -- @return pos number containing the offset after decoding, -1 on error -- @return response table containing zero or more of the following salt and success -- error string containing error message if pos is -1 [MessageType.AuthRequest] = function( data, len, pos ) local _, authtype local response = {} authtype, pos = string.unpack(">I4", data, pos) if ( authtype == AuthenticationType.MD5 ) then if ( len - pos + 1 ) < 3 then return -1, "ERROR: Malformed AuthRequest received" end response.salt, pos = string.unpack("c4", data, pos) elseif ( authtype == AuthenticationType.Plain ) then --do nothing elseif ( authtype == 0 ) then response.success = true else stdnse.debug1("unknown auth type: %d", authtype ) end response.authtype = authtype return pos, response end, --- Decodes an ParameterStatus packet -- -- @param data string containing raw data received from socket -- @param len number containing the length as retrieved from the header -- @param pos number containing the offset into the data buffer -- @return pos number containing the offset after decoding -- @return response table containing zero or more of the following key and value [MessageType.ParameterStatus] = function( data, len, pos ) local response = {} local tmp = data:sub(pos, pos + len - 4) response.key, response.value = string.unpack("zz", tmp) return pos + len - 4, response end, --- Decodes an Error packet -- -- @param data string containing raw data received from socket -- @param len number containing the length as retrieved from the header -- @param pos number containing the offset into the data buffer -- @return pos number containing the offset after decoding -- @return response table containing zero or more of the following error.severity, -- error.code, error.message, error.file, -- error.line and error.routine [MessageType.Error] = function( data, len, pos ) local tmp = data:sub(pos, pos + len - 4) local _, value, prefix local response = {} local pos_end = pos + len response.error = {} while ( pos < pos_end - 5 ) do prefix, value, pos = string.unpack("c1z", data, pos) if prefix == 'S' then response.error.severity = value elseif prefix == 'C' then response.error.code = value elseif prefix == 'M' then response.error.message = value elseif prefix == 'F' then response.error.file = value elseif prefix == 'L' then response.error.line = value elseif prefix == 'R' then response.error.routine = value end end return pos, response end, --- Decodes the BackendKeyData packet -- -- @param data string containing raw data received from socket -- @param len number containing the length as retrieved from the header -- @param pos number containing the offset into the data buffer -- @return pos number containing the offset after decoding, -1 on error -- @return response table containing zero or more of the following pid and key -- error string containing error message if pos is -1 [MessageType.BackendKeyData] = function( data, len, pos ) local response = {} if len ~= 12 then return -1, "ERROR: Invalid BackendKeyData packet" end response.pid, response.key, pos = string.unpack(">I4I4", data, pos) return pos, response end, --- Decodes an ReadyForQuery packet -- -- @param data string containing raw data received from socket -- @param len number containing the length as retrieved from the header -- @param pos number containing the offset into the data buffer -- @return pos number containing the offset after decoding, -1 on error -- @return response table containing zero or more of the following status -- error string containing error message if pos is -1 [MessageType.ReadyForQuery] = function( data, len, pos ) local response = {} if len ~= 5 then return -1, "ERROR: Invalid ReadyForQuery packet" end response.status, pos = string.unpack("B", data, pos ) return pos, response end, }, --- Reads a packet and handles additional socket reads to retrieve remaining data -- -- @param socket socket already connected to the pgsql server -- @param data string containing any data already retrieved from the socket -- @param pos number containing the offset into the data buffer -- @return data string containing the initial and any additional data readPacket = function(socket, data, pos) pos = pos or 1 data = data or "" -- Make sure that we have enough data for the header local targetlen = pos - 1 + 5 -- header is 5 bytes (structure >BI4) while #data < targetlen do local status, extra = socket:receive_bytes(targetlen - #data) if not status then stdnse.debug1("Not enough data for PGSQL response header") return nil, extra end data = data .. extra end local _, header = v3.decodeHeader(data, pos) targetlen = targetlen + header.len - 4 -- header.len is 4 bytes while #data < targetlen do local status, extra = socket:receive_bytes(targetlen - #data) if not status then stdnse.debug1("Not enough data for declared PGSQL response size") return nil, extra end data = data .. extra end return data end, --- Decodes the postgres header -- -- @param data string containing the server response -- @param pos number containing the offset into the data buffer -- @return pos number containing the offset after decoding -- @return header table containing type and len decodeHeader = function(data, pos) local ptype, len ptype, len, pos = string.unpack(">BI4", data, pos) return pos, { ['type'] = ptype, ['len'] = len } end, --- Process the server response -- -- @param data string containing the server response -- @param pos number containing the offset into the data buffer -- @return pos number containing offset after decoding -- @return response string containing decoded data -- error message if pos is -1 processResponse = function(data, pos) pos = pos or 1 if not data then return -1, "Not enough response data" end local header pos, header = v3.decodeHeader( data, pos ) if not v3.messageDecoder[header.type] then stdnse.debug1("Missing decoder for %d", header.type ) return -1, ("Missing decoder for %d"):format(header.type) end local response pos, response = v3.messageDecoder[header.type](data, header.len, pos) if pos == -1 then return -1, "Decoding failed" end response.type = header.type return pos, response end, --- Attempts to authenticate to the pgsql server -- Supports plain-text and MD5 authentication -- -- @param socket socket already connected to the pgsql server -- @param params table containing any additional parameters authtype, version -- @param username string containing the username to use for authentication -- @param password string containing the password to use for authentication -- @param salt string containing the cryptographic salt value -- @return status true on success, false on failure -- @return result table containing parameter status information, -- result string containing an error message if login fails loginRequest = function ( socket, params, username, password, salt ) local creds if params.authtype == AuthenticationType.MD5 then creds = createMD5LoginHash(username, password, salt) elseif params.authtype == AuthenticationType.Plain then creds = password elseif params.authtype == AuthenticationType.Success then return true, nil else return false, "Unexpected authentication type 0x" .. stdnse.tohex(params.authtype) end local status, err = socket:send(string.pack(">BI4z", MessageType.PasswordMessage, #creds + 5, creds)) if not status then return false, "Unable to send AuthRequest: " .. err end local data = v3.readPacket(socket) local pos, tmp = v3.processResponse(data, 1) -- this should contain the AuthRequest packet if tmp.type ~= MessageType.AuthRequest then return false, "Expected AuthRequest; got something else" end if not tmp.success then return false, "Login failure" end local params = {} repeat data = v3.readPacket(socket, data, pos) pos, tmp = v3.processResponse(data, pos) if ( tmp.type == MessageType.ParameterStatus ) then table.insert(params, {name=tmp.key, value=tmp.value}) end until pos >= data:len() or pos == -1 return true, {params} end, --- Sends a startup message to the server containing the username and database to connect to -- -- @param socket socket already connected to the pgsql server -- @param user string containing the name of the user -- @param database string containing the name of the database -- @return status true on success, false on failure -- @return table containing a processed response from processResponse -- string containing error message if status is false sendStartup = function(socket, user, database ) local data, response, status, pos local proto_ver, ptype, _, tmp proto_ver = 0x0030000 data = string.pack(">I4zzzzB", proto_ver, "user", user, "database", database, 0) data = string.pack(">I4", data:len() + 4) .. data socket:send( data ) -- attempt to verify version status, data = socket:receive_bytes( 2 ) if ( not(status) ) then return false, "sendStartup failed" end if ( not(status) or data:match("^EF") ) then return false, "Incorrect version" end data = v3.readPacket(socket, data ) pos, response = v3.processResponse( data ) if ( pos < 0 or response.type == MessageType.Error) then return false, response.error.message or "unknown error" end return true, response end } --- Sends a packet requesting SSL communication to be activated -- -- @param socket socket already connected to the pgsql server -- @return boolean true if request was accepted, false if request was denied function requestSSL(socket) -- SSLRequest local ssl_req_code = 80877103 local data = string.pack( ">I4I4", 8, ssl_req_code) local status, response socket:send(data) status, response = socket:receive_bytes(1) if ( not(status) ) then return false end if ( response == 'S' ) then return true end return false end --- Creates a cryptographic hash to be used for login -- -- @param username username -- @param password password -- @param salt salt -- @return string suitable for login request function createMD5LoginHash(username, password, salt) local md5_1 = stdnse.tohex(openssl.md5(password..username)) return "md5" .. stdnse.tohex(openssl.md5(md5_1 .. salt)) end --- Prints the contents of the error table returned from the Error message decoder -- -- @param dberror table containing the error function printErrorMessage( dberror ) if not dberror then return end for k, v in pairs(dberror) do stdnse.debug1("%s=%s", k, v) end end --- Attempts to determine if the server supports v3 or v2 of the protocol -- -- @param host table -- @param port table -- @return class v2 or v3 function detectVersion(host, port) local status, response local socket = nmap.new_socket() socket:connect(host, port) status, response = v3.sendStartup(socket, "versionprobe", "versionprobe") socket:close() if ( not(status) and response == 'Incorrect version' ) then return v2 end return v3 end return _ENV;