Discovery Thru Pivot with the Metasploit Pentest Plugin

Recently I have been working more and more on my pentest plugin for Metasploit doing bug fixes and trying to improve some of the current areas of it. I added the Auto Exploit plugin to it for exploit automation and added some commands to aid in doing enumeration and discovery thru a pivot.

I was talking with the guys in Defensive Intuition and Black Hills Defensive Security and one of the areas they wanted to see me cover in the plugin was being able to quickly move enumerating and scanning other targets when they got a Meterpreter session on a clients network. So I added 2 commands to help with this to the plugin.

Lets start with a session that is connected to a host that is behind NAT:

msf > sessions

Active sessions
===============

  Id  Type                   Information                            Connection
  --  ----                   -----------                            ----------
  1   meterpreter x86/win32  VICTIMLAB\Administrator @ WIN2K3LAB01  192.168.1.100:4444 -> 192.168.1.138:49323 (10.10.10.2)

msf >

lets start by loading the plugin

msf >  load pentest

       ___         _          _     ___ _           _
      | _ \___ _ _| |_ ___ __| |_  | _ \ |_  _ __ _(_)_ _
      |  _/ -_) ' \  _/ -_|_-<  _| |  _/ | || / _` | | ' \
      |_| \___|_||_\__\___/__/\__| |_| |_|\_,_\__, |_|_||_|
                                              |___/
			
Version 1.2
Pentest plugin loaded.
by Carlos Perez (carlos_perez[at]darkoperator.com)
[*] Successfully loaded plugin: pentest
msf  exploit(handler) > back
msf >

As shown in my others post about the plugin it will add commands to the list of commands available in the console to look at the command available we can just enter the help command or ? :

msf > help
. . . .
Discovery Commands
==================

    Command                 Description
    -------                 -----------
    discover_db             Run discovery modules against current hosts in the database.
    network_discover        Performs a portscan and enumeration of services found for non pivot networks.
    pivot_network_discover  Performs enumeration of networks available to a specified Meterpreter session.
    show_session_networks   Enumerate the networks one could pivot thru Meterpreter in the active sessions.
. . . .

The commands we have available are:

* discover_db - this command will go thru the hosts that are present in the database and will run a set of modules to enumerate and gather information from the services that have been detected on those host. One can provide a range of host to limit the discovery and SMB settings for the SMB modules that will be ran against hosts.

* network_discovery - will run the nmap scanner against a given CIDR, it will determine the ports that are used by Metasploit auxiliary and exploit modules and use those if none are specified and after running the scan it will run additional discovery modules to further enumerate and gather information from those services.

* pivot_network_discover - This command will enumerate all interfaces and routes on a given Windows Meterpreter session, it will create routes to the found networks thru the session specified, it will determine which of the enumerated networks are directly connected to the host or are remote so as to determine the best way to run detection of hosts, it will do a ARP Sweep if the network is directly connected since this provides the most accuracy and if the network is a remote one it will execute a ping scan against it, it will execute if specified a TCP and UDP port scan against the hosts it discovered, if a port list is not provided it will auto generate one from the existing auxiliary and exploit modules currently available in addition to adding some additional common ones, if specified it will launch discovery modules to further enumerate the services found.

* show_session_networks - will list the networks available thru Windows Meterpreter Sessions.

Lets start by listing the networks available thru a session. Lets first look at the options available for the show_session_networks command:

msf > show_session_networks -h
This command will show the networks that can be routed thru a Meterpreter session.

OPTIONS:

    -h        Help Message.
    -s   Sessions to enumerate networks against. Example  or .


Now lets list the networks available:

msf > show_session_networks -s all
Network     Netmask        Session
-------     -------        -------
10.10.10.0  255.255.255.0  1

Now that we know the networks connected to the session we can check the options we have available to the command pivot_network_discover :

msf > pivot_network_discover -h

OPTIONS:

    -D   SMB Domain for discovery(optional).
    -P   SMB Password for discovery(optional).
    -U   SMB Username for discovery(optional).
    -d        Run Framework discovery modules against found hosts.
    -h        Help Message.
    -p   Port list. Provide a comma separated list of port and/or ranges to TCP scan.
    -s   Session to do discovery of networks and hosts.
    -t        Perform TCP port scan of hosts discovered.
    -u        Perform UDP scan of hosts discovered.
    -v        Be verbose and show pending actions.


Lets see what information we have in our current workspace for hosts, services and notes:

msf > hosts

Hosts
=====

address  mac  name  os_name  os_flavor  os_sp  purpose  info  comments
-------  ---  ----  -------  ---------  -----  -------  ----  --------

msf > services

Services
========

host  port  proto  name  state  info
----  ----  -----  ----  -----  ----

msf > notes
msf >

Lets run a discovery thru the current session we have:

 msf > pivot_network_discover -t -u -d -s 1
[*] Identifying networks to discover
[*] Routing new subnet 10.10.10.0/255.255.255.0 through session 1
[*] Running windows/gather/arp_scanner against 1

[*] Running module against WIN2K3LAB01
[*] ARP Scanning 10.10.10.0/24
[*] 	IP: 10.10.10.2 MAC 00:0c:29:5e:e3:bd
[*] 	IP: 10.10.10.1 MAC 00:0c:29:4d:e7:5a
[*] 	IP: 10.10.10.200 MAC 00:0c:29:45:73:cb
[*] 	IP: 10.10.10.201 MAC 00:0c:29:c9:15:98
[*] 	IP: 10.10.10.239 MAC 00:0c:29:1e:8d:30
[*] 	IP: 10.10.10.238 MAC 00:0c:29:10:5c:d7
[*] 	IP: 10.10.10.243 MAC 00:0c:29:2e:97:ff
[*] Generating list of ports used by Auxiliary Modules
[*] Generating list of ports used by Exploit Modules
[*] Discovering 10.10.10.0/24 Network
[+] Running TCP Portscan against 10.10.10.2
[+] Running TCP Portscan against 10.10.10.1
[+] Running TCP Portscan against 10.10.10.200
[+] Running TCP Portscan against 10.10.10.201
[+] Running TCP Portscan against 10.10.10.239
[+] Running TCP Portscan against 10.10.10.238
[+] Running TCP Portscan against 10.10.10.243
[+] Running UDP Portscan against 10.10.10.2
[+] Running UDP Portscan against 10.10.10.1
[+] Running UDP Portscan against 10.10.10.200
[*] 10.10.10.1:80 - TCP OPEN
[*] 10.10.10.243:514 - TCP OPEN
[*] 10.10.10.2:445 - TCP OPEN
[*] 10.10.10.243:111 - TCP OPEN
[*] 10.10.10.243:445 - TCP OPEN
[*] 10.10.10.239:23 - TCP OPEN
[*] 10.10.10.243:23 - TCP OPEN
[*] 10.10.10.243:21 - TCP OPEN
[*] 10.10.10.2:135 - TCP OPEN
[*] 10.10.10.243:1099 - TCP OPEN
[*] 10.10.10.243:80 - TCP OPEN
[*] 10.10.10.243:22 - TCP OPEN
[*] 10.10.10.243:513 - TCP OPEN
[*] 10.10.10.2:389 - TCP OPEN
[*] 10.10.10.239:135 - TCP OPEN
[*] 10.10.10.243:25 - TCP OPEN
[*] 10.10.10.201:135 - TCP OPEN
[*] 10.10.10.200:445 - TCP OPEN
[*] 10.10.10.200:135 - TCP OPEN
[*] 10.10.10.243:512 - TCP OPEN
[*] 10.10.10.239:445 - TCP OPEN
[*] 10.10.10.238:445 - TCP OPEN
[*] 10.10.10.238:135 - TCP OPEN
[*] Discovered NTP on 10.10.10.2:123 (1c0104fa00000000000a0da14c4f434cd3b1d5bebfd032b2c54f234b71b152f3d3b1e271bbb79f3ed3b1e271bbb79f3e)
[*] Discovered DNS on 10.10.10.1:53 (403e858000010001000000000756455253494f4e0442494e440000100003c00c0010000300000000000d0c646e736d6173712d322e3435)
[*] Discovered NetBIOS on 10.10.10.200:137 (WINXPLAB01::U :VICTIMLAB::G :WINXPLAB01::U :VICTIMLAB::G :00:0c:29:45:73:cb)

. . . .

[*] Scanned 1 of 1 hosts (100% complete)
[*] 10.10.10.243:23 TELNET _                  _       _ _        _     _      ____  \x0a _ __ ___   ___| |_ __ _ ___ _ __ | | ___ (_) |_ __ _| |__ | | ___|___ \ \x0a| '_ ` _ \ / _ \ __/ _` / __| '_ \| |/ _ \| | __/ _` | '_ \| |/ _ \ __) |\x0a| | | | | |  __/ || (_| \__ \ |_) | | (_) | | || (_| | |_) | |  __// __/ \x0a|_| |_| |_|\___|\__\__,_|___/ .__/|_|\___/|_|\__\__,_|_.__/|_|\___|_____|\x0a                            |_|                                          \x0a\x0a\x0aWarning: Never expose this VM to an untrusted network!\x0a\x0aContact: msfdev[at]metasploit.com\x0a\x0aLogin with msfadmin/msfadmin to get started\x0a\x0a\x0ametasploitable login:
[*] Scanned 1 of 1 hosts (100% complete)
[-] File doesn't seem to exist. The upload probably failed.
[*] Scanned 1 of 1 hosts (100% complete)
[*] 10.10.10.243 (Apache/2.2.8 (Ubuntu) DAV/2) WebDAV disabled.
[*] Scanned 1 of 1 hosts (100% complete)
[*] 10.10.10.243:80 Apache/2.2.8 (Ubuntu) DAV/2 ( Powered by PHP/5.2.4-2ubuntu5.10 )
[*] Scanned 1 of 1 hosts (100% complete)
[*] Scanned 1 of 1 hosts (100% complete)
[*] waiting for some modules to finish
msf >
[*] 10.10.10.243:3306 is running MySQL 5.0.51a-3ubuntu5 (protocol 10)
[*] 10.10.10.243:5900, VNC server protocol version : 3.3
[*] Scanned 1 of 1 hosts (100% complete)
[*] Scanned 1 of 1 hosts (100% complete)
[*] 10.10.10.243:5900, VNC server security types supported : VNC
[*] Scanned 1 of 1 hosts (100% complete)
[*] 10.10.10.243:5432 Postgres - Version 8.3.8 (Pre-Auth)
[*] Scanned 1 of 1 hosts (100% complete)
[*] 10.10.10.239:23 Does not support encryption: Welcome to Microsoft Telnet Service \x0a\x0a\x0dlogin:
[*] Scanned 1 of 1 hosts (100% complete)
[*] 10.10.10.243:23 Does not support encryption: _                  _       _ _        _     _      ____  \x0a _ __ ___   ___| |_ __ _ ___ _ __ | | ___ (_) |_ __ _| |__ | | ___|___ \ \x0a| '_ ` _ \ / _ \ __/ _` / __| '_ \| |/ _ \| | __/ _` | '_ \| |/ _ \ __) |\x0a| | | | | |  __/ || (_| \__ \ |_) | | (_) | | || (_| | |_) | |  __// __/ \x0a|_| |_| |_|\___|\__\__,_|___/ .__/|_|\___/|_|\__\__,_|_.__/|_|\___|_____|\x0a                            |_|                                          \x0a\x0a\x0aWarning: Never expose this VM to an untrusted network!\x0a\x0aContact: msfdev[at]metasploit.com\x0a\x0aLogin with msfadmin/msfadmin to get started\x0a\x0a\x0ametasploitable login:
[*] Scanned 1 of 1 hosts (100% complete)

msf >

One thing that we have to keep in mind scanning thru a pivot is slow, that is why I decided to go with the list of generated ports since these will provide me the best chances to leverage the tools and modules in the framework. For large networks I recommend also using the -v option to see how many scanner jobs are pending.

Now that we finished the discovery lets look at the hosts, services and notes we now have:

msf > hosts

Hosts
=====

address       mac                name             os_name            os_flavor        os_sp  purpose  info  comments
-------       ---                ----             -------            ---------        -----  -------  ----  --------
10.10.10.1    00:0c:29:4d:e7:5a  10.10.10.1       Unknown                                    device
10.10.10.2    00:0c:29:5e:e3:bd  win2k3lab01      Microsoft Windows  2003             SP2    server
10.10.10.200  00:0c:29:45:73:cb  winxplab01       Microsoft Windows  XP               SP2    client
10.10.10.201  00:0c:29:c9:15:98                   Unknown                                    device
10.10.10.238  00:0c:29:10:5c:d7  win-yr4v852v71y  Microsoft Windows  2008 Enterprise  SP1    server
10.10.10.239  00:0c:29:1e:8d:30  test-01bcdaf47c  Microsoft Windows  XP               SP2    client
10.10.10.243  00:0c:29:2e:97:ff  metasploitable   Linux              Debian                  server

msf > services

Services
========

host          port   proto  name      state  info
----          ----   -----  ----      -----  ----
10.10.10.1    53     udp    dns       open   403e858000010001000000000756455253494f4e0442494e440000100003c00c0010000300000000000d0c646e736d6173712d322e3435
10.10.10.1    80     tcp    http      open   lighttpd/1.4.23
10.10.10.2    135    tcp              open
10.10.10.2    123    udp    ntp       open   1c0104fa00000000000a0da14c4f434cd3b1d5bebfd032b2c54f234b71b152f3d3b1e271bbb79f3ed3b1e271bbb79f3e
10.10.10.2    53     udp    dns       open   Microsoft DNS
10.10.10.2    137    udp    netbios   open   WIN2K3LAB01::U :VICTIMLAB::G :VICTIMLAB::G :WIN2K3LAB01::U :VICTIMLAB::U :VICTIMLAB::G :VICTIMLAB::U :__MSBROWSE__::G :00:0c:29:5e:e3:bd
10.10.10.2    3389   tcp              open
10.10.10.2    445    tcp    smb       open   Windows 2003 Service Pack 2 (language: Unknown) (name:WIN2K3LAB01) (domain:VICTIMLAB)
10.10.10.2    389    tcp              open
10.10.10.200  123    udp    ntp       open   1c020efa00000000001000000a0a0a02d3b17b6e0454d46dc54f234b71b152f3d3b1e2508240cefdd3b1e2508240cefd
10.10.10.200  135    tcp              open
10.10.10.200  3389   tcp              open
10.10.10.200  445    tcp    smb       open   Windows XP Service Pack 2 (language: English) (name:WINXPLAB01) (domain:VICTIMLAB)
10.10.10.200  137    udp    netbios   open   WINXPLAB01::U :VICTIMLAB::G :WINXPLAB01::U :VICTIMLAB::G :00:0c:29:45:73:cb
10.10.10.201  135    tcp              open
10.10.10.238  137    udp    netbios   open   WIN-YR4V852V71Y::U :WORKGROUP::G :WIN-YR4V852V71Y::U :00:0c:29:10:5c:d7
10.10.10.238  135    tcp              open
10.10.10.238  445    tcp    smb       open   Windows 2008 Enterprise Service Pack 1 (language: Unknown) (name:WIN-YR4V852V71Y) (domain:WORKGROUP)
10.10.10.239  23     tcp    telnet    open   Welcome to Microsoft Telnet Service \x0a\x0a\x0dlogin:
10.10.10.239  123    udp    ntp       open   Microsoft NTP
10.10.10.239  135    tcp              open
10.10.10.239  137    udp    netbios   open   TEST-01BCDAF47C::U :WORKGROUP::G :TEST-01BCDAF47C::U :WORKGROUP::G :WORKGROUP::U :__MSBROWSE__::G :00:0c:29:1e:8d:30
10.10.10.239  445    tcp    smb       open   Windows XP Service Pack 2 (language: English) (name:TEST-01BCDAF47C) (domain:WORKGROUP)
10.10.10.243  80     tcp    http      open   Apache/2.2.8 (Ubuntu) DAV/2 ( Powered by PHP/5.2.4-2ubuntu5.10 )
10.10.10.243  22     tcp    ssh       open   SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu1
10.10.10.243  23     tcp    telnet    open   _                  _       _ _        _     _      ____  \x0a _ __ ___   ___| |_ __ _ ___ _ __ | | ___ (_) |_ __ _| |__ | | ___|___ \ \x0a| '_ ` _ \ / _ \ __/ _` / __| '_ \| |/ _ \| | __/ _` | '_ \| |/ _ \ __) |\x0a| | | | | |  __/ || (_| \__ \ |_) | | (_) | | || (_| | |_) | |  __// __/ \x0a|_| |_| |_|\___|\__\__,_|___/ .__/|_|\___/|_|\__\__,_|_.__/|_|\___|_____|\x0a                            |_|                                          \x0a\x0a\x0aWarning: Never expose this VM to an untrusted network!\x0a\x0aContact: msfdev[at]metasploit.com\x0a\x0aLogin with msfadmin/msfadmin to get started\x0a\x0a\x0ametasploitable login:
10.10.10.243  25     tcp    smtp      open   220 metasploitable.localdomain ESMTP Postfix (Ubuntu)

10.10.10.243  21     tcp    ftp       open   220 (vsFTPd 2.3.4)\x0d\x0a
10.10.10.243  111    udp    portmap   open   100000 v2 TCP(111), 100000 v2 UDP(111), 100024 v1 UDP(55600), 100024 v1 TCP(53257), 100003 v2 UDP(2049), 100003 v3 UDP(2049), 100003 v4 UDP(2049), 100021 v1 UDP(58825), 100021 v3 UDP(58825), 100021 v4 UDP(58825), 100003 v2 TCP(2049), 100003 v3 TCP(2049), 100003 v4 TCP(2049), 100021 v1 TCP(47361), 100021 v3 TCP(47361), 100021 v4 TCP(47361), 100005 v1 UDP(40587), 100005 v1 TCP(42089), 100005 v2 UDP(40587), 100005 v2 TCP(42089), 100005 v3 UDP(40587), 100005 v3 TCP(42089)
10.10.10.243  111    tcp    sunrpc    open   100000 v2
10.10.10.243  137    udp    netbios   open   METASPLOITABLE::U :METASPLOITABLE::U :METASPLOITABLE::U :WORKGROUP::G :WORKGROUP::G :00:00:00:00:00:00
10.10.10.243  139    tcp              open
10.10.10.243  445    tcp    smb       open   Unix Samba 3.0.20-Debian (language: Unknown) (domain:WORKGROUP)
10.10.10.243  512    tcp              open
10.10.10.243  513    tcp              open
10.10.10.243  514    tcp              open
10.10.10.243  1099   tcp              open
10.10.10.243  1524   tcp              open
10.10.10.243  2049   udp    sunrpc    open   100003 v4
10.10.10.243  2049   tcp    sunrpc    open   100003 v4
10.10.10.243  3306   tcp    mysql     open   5.0.51a-3ubuntu5
10.10.10.243  3632   tcp              open
10.10.10.243  5432   tcp    postgres  open   8.3.8
10.10.10.243  5900   tcp    vnc       open   VNC protocol version 3.3
10.10.10.243  6000   tcp              open
10.10.10.243  6667   tcp              open
10.10.10.243  6697   tcp              open
10.10.10.243  8180   tcp              open
10.10.10.243  8787   tcp              open
10.10.10.243  40587  udp    sunrpc    open   100005 v3
10.10.10.243  42089  tcp    sunrpc    open   100005 v3
10.10.10.243  47361  tcp    sunrpc    open   100021 v4
10.10.10.243  53257  tcp    sunrpc    open   100024 v1
10.10.10.243  55600  udp    sunrpc    open   100024 v1
10.10.10.243  58825  udp    sunrpc    open   100021 v4

msf > notes
[*] Time: 2012-07-19 01:35:46 UTC Note: host=10.10.10.2 type=host.virtual_machine data={:vendor=>"VMWare", :method=>"netbios"}
[*] Time: 2012-07-19 01:35:47 UTC Note: host=10.10.10.200 service=smb type=smb.fingerprint data={:os_flavor=>"Windows XP", :os_name=>"Microsoft Windows", :os_sp=>"Service Pack 2", :os_lang=>"English"}
[*] Time: 2012-07-19 01:36:03 UTC Note: host=10.10.10.239 service=smb type=smb.fingerprint data={:os_flavor=>"Windows XP", :os_name=>"Microsoft Windows", :os_sp=>"Service Pack 2", :os_lang=>"English"}
[*] Time: 2012-07-19 01:36:05 UTC Note: host=10.10.10.200 type=host.virtual_machine data={:vendor=>"VMWare", :method=>"netbios"}
[*] Time: 2012-07-19 01:36:16 UTC Note: host=10.10.10.238 service=smb type=smb.fingerprint data={:os_flavor=>"Windows 2008 Enterprise", :os_name=>"Microsoft Windows", :os_sp=>"Service Pack 1", :os_lang=>"Unknown"}
[*] Time: 2012-07-19 01:36:22 UTC Note: host=10.10.10.239 type=host.virtual_machine data={:vendor=>"VMWare", :method=>"netbios"}
[*] Time: 2012-07-19 01:36:28 UTC Note: host=10.10.10.243 service=smb type=smb.fingerprint data={:os_flavor=>"Unix", :os_name=>"Unknown", :os_sp=>"Samba 3.0.20-Debian"}
[*] Time: 2012-07-19 01:36:33 UTC Note: host=10.10.10.238 type=host.virtual_machine data={:vendor=>"VMWare", :method=>"netbios"}
[*] Time: 2012-07-19 01:36:34 UTC Note: host=10.10.10.243 service=139/tcp type=smb.domain.enumusers data={:sid_txt=>"5-21-1042354039-2475377354-766472396", :pass_min=>5, :pass_min_history=>0, :server_role=>3, :lockout_threshold=>0, :lockout_duration=>1480786430454, :lockout_window=>1480786430454, :users=>{1010=>"games", 501=>"nobody", 1210=>"bind", 1026=>"proxy", 1204=>"syslog", 3002=>"user", 1066=>"www-data", 1000=>"root", 1018=>"news", 1216=>"postgres", 1004=>"bin", 1016=>"mail", 1222=>"distccd", 1226=>"proftpd", 1202=>"dhcp", 1002=>"daemon", 1208=>"sshd", 1012=>"man", 1014=>"lp", 1218=>"mysql", 1082=>"gnats", 1200=>"libuuid", 1068=>"backup", 3000=>"msfadmin", 1224=>"telnetd", 1006=>"sys", 1206=>"klog", 1212=>"postfix", 3004=>"service", 1076=>"list", 1078=>"irc", 1214=>"ftp", 1220=>"tomcat55", 1008=>"sync", 1020=>"uucp"}, :name=>"METASPLOITABLE"}
[*] Time: 2012-07-19 01:36:36 UTC Note: host=10.10.10.243 service=139/tcp type=smb.shares data={:shares=>[["print$", "DISK", "Printer Drivers"], ["tmp", "DISK", "oh noes!"], ["opt", "DISK", ""], ["IPC$", "IPC", "IPC Service (metasploitable server (Samba 3.0.20-Debian))"], ["ADMIN$", "IPC", "IPC Service (metasploitable server (Samba 3.0.20-Debian))"]]}
[*] Time: 2012-07-19 01:35:33 UTC Note: host=10.10.10.2 service=smb type=smb.fingerprint data={:os_flavor=>"Windows 2003", :os_name=>"Microsoft Windows", :os_sp=>"Service Pack 2", :os_lang=>"Unknown"}

You can download the latest version from my GitHub repository at https://github.com/darkoperator/Metasploit-Plugins/blob/master/pentest.rb Also I got a request from a friend to turn the discovery command in to a post module so I started work on that also. I hope you find these new commands useful.

MDNSRecon

Recently I was chatting with my good friend Elliot Cutright also known in twitter as @nullthreat about the recent changes I have been doing to DNSRecon and several of the improvements. He commented that he would miss the MDNS enumeration feature I had on it originally. Do to my move of supporting Python 3.x and supporting Python 2.x and above for the tool I had to drop that feature in addition that library I used for it was abandoned by the author for quite some time. MDNS is a great way to find all sorts of information about hosts in your same subnet specially since the MDSN records act as regular DNS SRV records where we get Service name that most times include the protocol and name, Target for the service, Port and a text field with additional information. In addition to this one can resolve the hosts to their IPv4 and IPv6 addresses.

Based on the request I wrote a Ruby script that leveraged the tool avahi-browser and set as my goals for the script:

  • Detect most of the supported MDNS Records in the local subnet the attacker is connected on.
  • Do not resolve those services running on the attackers machine.
  • Make sure that the out put was useful and easy to parse and manipulate for a tester.

The resulting script I called MDNSRecon and can be downloaded from my GitHub account at https://github.com/darkoperator/MDNSRecon 

root@bt:~# ./mdnsrecon.rb -h
MDNSRecon Script by Carlos Perez (carlos_perez[at]darkoperator.com)
Version 0.1
Usage: mdnsrecon.rb [OPTION]
--help, -h:
show help
--csv <file>, -c <file>:
CSV File to save records found.
--grep, -g:
Output grepable Output with a delimiter of \
<service>\domain\host\IP\port\txt
If no option is given it will print records found to standard output.

If ran with no option we get output similar to this one if machines are available:

root@bt:~# ./mdnsrecon.rb 
[-] Records found:
[*] Host: bt.local
[*] IP: 192.168.192.128
[*] Port: 9
[*] Service:Workstation
[*] Text:''
[*]
[*] Host: ubuntu.local
[*] IP: 192.168.192.129
[*] Port: 9
[*] Service:Workstation
[*] Text:''
[*]
[*] Host: ubuntu.local
[*] IP: 192.168.192.129
[*] Port: 22
[*] Service:_udisks-ssh._tcp
[*] Text:''
[*]

If We want the output in a grepable format we use the -g options so the cut command and grep can be used to better find targets, in this example we will look for SSH services:

root@bt:~# ./mdnsrecon.rb -g | grep ssh |cut -d '\' -f4,5 --output-delimiter=" " -n
192.168.192.129 22

Now in the case we want to save the results in a format we can email someone or parse a larger set of results like those you can find on a conference floor ( or so I was told) you can select to save to a CSV file and later user a spreadsheet program or PowerShell on Windows to parse and slice:

root@bt:~# ./mdnsrecon.rb -c lab.csv
[-] Saving found records to lab.csv
[*] 3 Records saved
root@bt:~# cat lab.csv 
service,domain,host,ip,port,txt
Workstation,local,bt.local,192.168.192.128,9,''
_udisks-ssh._tcp,local,ubuntu.local,192.168.192.129,22,''
Workstation,local,ubuntu.local,192.168.192.129,9,''

So far I'm only supporting Debian, Ubuntu and Backtrack 5 as the platforms to run the script on, recommending Backtrack 5 as the preferred one. I will add other distributions of Linux depending on the amount of requests I get. I do hope you find the script useful and as with any of my projects feedback and feature request are always welcomed.

Parsing Nessus CSV Reports with PowerShell

Recently in the Pauldotcom Podcast Paul was mentioning how he uses Awk, cut and other bash tools to process a Nessus CSV report file and format the host output so he could use it in another tool. I saw his command and thought I would do it in PowerShell for kicks since PowerShell turns each row in to an object I can manipulate. Lets take a look at the Import-Csv cmandlet and what are the members of the object it returns:

Import-Csv C:\Users\carlos\Desktop\nessus.csv | Get-Member
   TypeName: System.Management.Automation.PSCustomObject
Name          MemberType   Definition                                                              
----          ----------   ----------                                                              
Equals        Method       bool Equals(System.Object obj)                                          
GetHashCode   Method       int GetHashCode()                                                       
GetType       Method       type GetType()                                                          
ToString      Method       string ToString()                                                       
CVE           NoteProperty System.String CVE=N/A                                                   
CVSS          NoteProperty System.String CVSS=                                                     
Description   NoteProperty System.String Description=One of several ports that were previously o...
Host          NoteProperty System.String Host=192.168.1.161                                        
Name          NoteProperty System.String Name=Open Port Re-check                                   
Plugin ID     NoteProperty System.String Plugin ID=10919                                           
Plugin Output NoteProperty System.String Plugin Output=Port 49156 was detected as being open but...
Port          NoteProperty System.String Port=0                                                    
Protocol      NoteProperty System.String Protocol=tcp                                              
Risk          NoteProperty System.String Risk=None                                                 
Solution      NoteProperty System.String Solution=- increase checks_read_timeout and/or reduce m...
Synopsis      NoteProperty System.String Synopsis=Previously open ports are now closed. 

We can see that the Nessus data for each plugin that ran is represented as NoteProperty for the object as a string. Lets see if I have some vulnerabilities found that it reports as being High for this we will use the Where-Object cmdlet to filter the objects:

 Import-Csv C:\Users\carlos\Desktop\nessus.csv | where {$_.risk -eq "high"}
Plugin ID     : 53382
CVE           : CVE-2010-3190
CVSS          : 9.3
Risk          : High
Host          : 192.168.1.161
Protocol      : tcp
Port          : 445
Name          : MS11-025: Vulnerability in Microsoft Foundation Class (MFC) Library Could Allow 
                Remote Code Execution (2500212)
Synopsis      : Arbitrary code can be executed on the remote host through the Microsoft Foundation 
                Class library.
Description   : The remote Windows host contains a version of the Microsoft Foundation Class (MFC) 
                library affected by an insecure library loading vulnerability.  The path used for 
                loading external libraries is not securely restricted. 
                
                An attacker can exploit this by tricking a user into opening an MFC application in 
                a directory that contains a malicious DLL, resulting in arbitrary code execution.
Solution      : Microsoft has released a set of patches for Visual Studio .NET 2003, 2005, and 
                2008, as well as Visual C++ 2005, 2008, and 2010 :
                
                http://technet.microsoft.com/en-us/security/bulletin/ms11-025
Plugin Output : 
                
                The following Visual C++ Redistributable Package has not
                been patched : 
                
                  Product           : Visual C++ 2008 SP1 Redistributable Package
                  Installed version : 9.0.30729.4148
                  Fixed version     : 9.0.30729.6161
                
.......
Great we found several. One advantage is that PowerShell is not case sensitive in it's operators unless you implicitly specify it, this allows me to specify the risk and “hight” and the value being capitalized . Now lets say I want to know what hosts have high vulnerabilities and only want their IP returned, for this we will use the Select-Object cmdlet and only get unique entries:
Import-Csv C:\Users\carlos\Desktop\nessus.csv | where {$_.risk -eq "high"} | select host -Unique
Host                                                                                               
----                                                                                               
192.168.1.161                                                                                      
192.168.1.160   

We could also filter by plugin if we are performing a pentest and what to come up with list of targets to feed to a tool piping it to the cmdlet out-file and giving it a file to save the IP’s to.

If we like doing all of the matching and sorting in a GUI interface we can take a look of the results in a grid view. Lets look at high, medium and low vulnerabilities and we will look at the Plugin ID, CVE, CVSS, Risk, Host, Protocol, Port and Name:

Import-Csv C:\Users\carlos\Desktop\nessus.csv | where {"high","medium","low" -contains $_.risk} | select "Plugin ID", CVE, CVSS, Risk, Host, Protocol, Port, Name | Out-GridView

This would bring up the following screen for us to filter and sort the information in a graphical manner:

image

Now what if we want to turn the data in to an HTML Report, well PowerShell also allows us to do it using the ConvertTo-Html cmdlet and turn it in to an HTML report in list format:

Import-Csv C:\Users\carlos\Desktop\nessus.csv | where {"high","medium","low" -contains $_.risk} | select "Plugin ID", CVE, CVSS, Risk, Host, Protocol, Port, Name | ConvertTo-Html -As List > report.html

This will create a report that looks like this:

image

One could go even fancier with pre-made CSS and HTML templates, but I will leave that for you to explore.

Introduction to Microsoft PowerShell – Variables

There are several types of variables this are:

  • User Created – These variables are the ones we create in the shell and in scripts. This variables are present only in the current process we are on and are lost when we close the session. We can create variables in scripts with global, script, or local scope.
  • Automatic – These variables keep the state of the PowerShell session and can not be modified directly. The values of this variables change as we execute and use the PowerShell session. This variables will save last run state of cmdlets, commands as well as other objects and information.
  • Preference – These variables store user preferences for PowerShell. These variables are created by PowerShell  when a session is started and are populated with default values. We can change the values of these variables. For example, MaximumHistoryCount that sets  the maximum number of entries in the session history.
  • Environment – These variables are the variables set by the system for Command and PowerShell environments.

Creating and Accessing Variables

In PowerShell variables behave a bit differently than from what we are used to in other shell environments. We will see that do to the the unique way that PowerShell treats everything as an object variables are treated like so. Variable in PowerShell in reality are units of memory where we store values. Variables start with the symbol $ and followed by a string of letters like:

$this_is_a_variable

The string of letters and characters must be continuous and I recommend as a best practice to use descriptive names for the variables, you can use a mix of camel case where each word is capitalized or separate each work with a underscore as the example above.

Variables in PowerShell are not case sensitive and they may contain any letter, number and special character. When special characters are used they need to be enclosed in {}:

PS C:\Users\Carlos\Desktop> ${this is an actual var of var's} = 10

PS C:\Users\Carlos\Desktop> ${this is an actual var of var's}

10

To assign a value to a variable we have 3 methods in PS. The first one is by just setting a name and using the = sign and providing any value we want to set:

$var1 = 1

We can also use the the New-Variable cmdlet:

New-Variable -Name var3 -Value "hello" -Description "Sample string variable"

As we can see the cmdlet provide us with the largest amount of options. Lets look at the the help for it:

PS C:\Users\Carlos\Desktop> help New-Variable

NAME
    New-Variable

SYNOPSIS
    Creates a new variable.


SYNTAX
    New-Variable [-Name] <string> [[-Value] <Object>] [-Description <string>] [-Force] [-Option {None | ReadOnly | Constant | Private | AllScope}] [-PassThru] [-Scope <string>] [-Visibility {Public |
     Private}] [-Confirm] [-WhatIf] [<CommonParameters>]


DESCRIPTION
    The New-Variable cmdlet creates a new variable in Windows PowerShell. You can assign a value to the variable while creating it or assign or change the value after it is created.

    You can use the parameters of New-Variable to set the properties of the variable (such as those that create read-only or constant variables), set the scope of a variable, and determine whether va
    riables are public or private.

    Typically, you create a new variable by typing the variable name and its value, such as "$var = 3", but you can use the New-Variable cmdlet to use its parameters.


RELATED LINKS
    Online version: http://go.microsoft.com/fwlink/?LinkID=113361
    Get-Variable
    Set-Variable
    Remove-Variable
    Clear-Variable

REMARKS
    To see the examples, type: "get-help New-Variable -examples".
    For more information, type: "get-help New-Variable -detailed".
    For technical information, type: "get-help New-Variable -full".

As we can see it provides a lot of flexibility when creating the variable. The Se-Variable cmdlet can also be used and has a similar list of options as the New-Varibale cmdlet with some slight differences, like the ability to pass the variable content with the –PassThru parameter to the pipe to be consumed by another cmdlet.

When we want to get the value of a variable we can just type the variable name in the shell and hit enter. We can also use the Get-Variable cmdlet:

PS C:\Users\Carlos\Desktop> $var1 = 1
PS C:\Users\Carlos\Desktop> $var1
1
PS C:\Users\Carlos\Desktop> Get-Variable -Name var1
Name                           Value
----                           -----
var1                           1

One thing to keep in mind is that as we covered in previous blog post variables are also available as a PSDrive so we can treat them also as a file system. If we want to get a listing of all variable we would use the Get-Variable cmdlet with no parameters we can also do a Dir of the PSDrive:

PS C:\Users\Carlos\Desktop> dir variable:

Name                           Value
----                           -----
$                              variables:
?                              False
^                              dir
_
args                           {}
ConfirmPreference              High
ConsoleFileName
DebugPreference                SilentlyContinue
Error                          {Cannot find drive. A drive with the name 'variables' does not exist., Cannot find drive. A drive with the name 'variables' does not exist., Cannot find drive. A dri...
ErrorActionPreference          Continue
ErrorView                      NormalView
ExecutionContext               System.Management.Automation.EngineIntrinsics
false                          False
FormatEnumerationLimit         4
HOME                           C:\Users\Carlos
Host                           System.Management.Automation.Internal.Host.InternalHost
input                          System.Collections.ArrayList+ArrayListEnumeratorSimple
LASTEXITCODE                   0
MaximumAliasCount              4096
MaximumDriveCount              4096
MaximumErrorCount              256
MaximumFunctionCount           4096
MaximumHistoryCount            64
MaximumVariableCount           4096
MyInvocation                   System.Management.Automation.InvocationInfo
NestedPromptLevel              0
null
OutputEncoding                 System.Text.ASCIIEncoding
PID                            6648
PROFILE                        C:\Users\Carlos\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
ProgressPreference             Continue
PSBoundParameters              {}
PSCulture                      en-US
PSEmailServer
PSHOME                         C:\Windows\System32\WindowsPowerShell\v1.0
PSSessionApplicationName       wsman
PSSessionConfigurationName     http://schemas.microsoft.com/powershell/Microsoft.PowerShell
PSSessionOption                System.Management.Automation.Remoting.PSSessionOption
PSUICulture                    en-US
PSVersionTable                 {PSVersion, PSCompatibleVersions, BuildVersion, PSRemotingProtocolVersion...}
PWD                            C:\Users\Carlos\Desktop
ReportErrorShowExceptionClass  0
ReportErrorShowInnerException  0
ReportErrorShowSource          1
ReportErrorShowStackTrace      0
ShellId                        Microsoft.PowerShell
srvs                           {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController...}
StackTrace                        at System.Management.Automation.PropertyReferenceNode.SetValue(PSObject obj, Object property, Object value, ExecutionContext context)
test                           hello
true                           True
var1                           1.3
var2                           20
VerbosePreference              SilentlyContinue
WarningPreference              Continue
WhatIfPreference               False

To get the contents of a variable when using the PSDrive Method we would use the Get-Content cmdlet just like we would with a file:

PS C:\Users\Carlos\Desktop> Get-Content Variable:\PSHOME

C:\Windows\System32\WindowsPowerShell\v1.0

When we want to know what type of value we have in a variable we can use the the .GetType() method and we can get the property of .Name to see the name of the type or use .FullName to get the .Net type.

PS C:\Users\Carlos\Desktop> $var1.GetType().Name

Int32

As we can see in several of the examples we treat variables as object. We can even get the members of the object with the Get-Members cmdlet:

PS C:\Users\Carlos\Desktop> get-variable -name var2 | Get-Member

TypeName: System.Management.Automation.PSVariable Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() IsValidValue Method bool IsValidValue(System.Object value) ToString Method string ToString() Attributes Property System.Collections.ObjectModel.Collection`1[[System.Attribute, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] Attributes {get;} Description Property System.String Description {get;set;} Module Property System.Management.Automation.PSModuleInfo Module {get;} ModuleName Property System.String ModuleName {get;} Name Property System.String Name {get;} Options Property System.Management.Automation.ScopedItemOptions Options {get;set;} Value Property System.Object Value {get;set;} Visibility Property System.Management.Automation.SessionStateEntryVisibility Visibility {get;set;}

Just like objects if the property allows us to set it’s value we can change it:

PS C:\Users\Carlos\Desktop> $srvs = Get-Service

PS C:\Users\Carlos\Desktop> (get-variable srvs).Description = "This variable contains the services objects"

PS C:\Users\Carlos\Desktop> get-variable srvs | select name,description | ftAutoSize

Name Description ---- ----------- srvs This variable contains the services objects

 

Dynamic and Static Typing of Variables

PowerShell uses the .Net Framework variable types. The most common types of values we can have in a variable are shown in the table bellow:

Variable type Description
[array] An array
[bool] Yes-no value
[byte] Unsigned 8-bit integer, 0...255
[char] Individual unicode character
[datetime] Date and time indications
[decimal] Decimal number
[wmi] WMI Object
[double] Double-precision floating point decimal
[guid] Globally unambiguous 32-byte identification number
[hashtable] Hash table
[int16] 16-bit integer with characters
[int32], [int] 32-bit integers with characters
[int64], [long] 64-bit integers with characters
[nullable] Widens another data type to include the ability to contain null values.
[psobject] PowerShell object
[regex] Regular expression
[sbyte] 8-bit integers with characters
[scriptblock] PowerShell scriptblock
[single], [float] Single-precision floating point number
[string] String
[switch] PowerShell switch parameter
[timespan] Time interval
[type] Type
[uint16] Unsigned 16-bit integer
[uint32] Unsigned 32-bit integer
[uint64] Unsigned 64-bit integer

 

In PowerShell variables are dynamic. This means that we do not have to declare them and specify a type ahead of use and it can take any value type we want to give it.

PS C:\Users\Carlos\Desktop> $var1 = 1

PS C:\Users\Carlos\Desktop> $var1.GetType().Name

Int32

PS C:\Users\Carlos\Desktop> $var1 = "string"

PS C:\Users\Carlos\Desktop> $var1.GetType().Name

String

PS C:\Users\Carlos\Desktop> $var1 = 1.30

PS C:\Users\Carlos\Desktop> $var1.GetType().Name

Double

Now as mentioned before PowerShell variables can be dynamically typed, but we can also strong type variable by casting them using the variable type:

PS C:\Users\Carlos\Desktop> [int32]$var2 = 10

PS C:\Users\Carlos\Desktop> $var2.GetType().Name

Int32

PS C:\Users\Carlos\Desktop> $var2 = "hello"

Cannot convert value "hello" to type "System.Int32". Error: "Input string was not in a correct format." At line:1 char:6 + $var2 <<<< = "hello" + CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException + FullyQualifiedErrorId : RuntimeException

As we can see we got an error when we tried to save a string to the variable. The type is set in the Attribute property of the variable and if we remove the attribute it will become a dynamic variable again.

Variable Options and Attributes

We can also mark variables as read only using the SetVariable cmdlet on existing variables or when creating them with the New-Variable cmdlet:

PS C:\Users\Carlos\Desktop> Set-Variable -Name var2 -Option ReadOnly

PS C:\Users\Carlos\Desktop> $var2 = 20

Cannot overwrite variable var2 because it is read-only or constant. At line:1 char:6 + $var2 <<<< = 20 + CategoryInfo : WriteError: (var2:String) [], SessionStateUnauthorizedAccessException + FullyQualifiedErrorId : VariableNotWritable

As we can see we could not change the value on a ReadOnly variable by using assignment. But we can change it using the Set-Variable cmdlet and giving it the parameter of –Force:

PS C:\Users\Carlos\Desktop> Set-Variable -Name var2 -Value 20 -Force

PS C:\Users\Carlos\Desktop> $var2

20

If we want an immutable variable we have to create the variable as a Constant. By declaring it as one it can not be deleted, changed nor cleared during the duration of a session.

For clearing a variable we can use the Clear-Variable cmdlet or assign to it the $null value ($null is an Automatic variable created by PowerShell at startup of a session)

PS C:\Users\Carlos\Desktop> $testvar = "hello"

PS C:\Users\Carlos\Desktop> $testvar

hello

PS C:\Users\Carlos\Desktop> Clear-Variable testvar

PS C:\Users\Carlos\Desktop> $testvar

PS C:\Users\Carlos\Desktop>

We can also treat it as file (Child-Item) in a file system in the variables PSDrive:

PS C:\Users\Carlos\Desktop> $testvar = "hello"

PS C:\Users\Carlos\Desktop> Get-Content Variable:\testvar

hello

PS C:\Users\Carlos\Desktop> Set-Content -Value $null -Path Variable:\testvar

PS C:\Users\Carlos\Desktop> Get-Content Variable:\testvar

PS C:\Users\Carlos\Desktop>


We can also use assignment to clear the variable this is done by assigning $null to it:

PS C:\Users\Carlos\Desktop> $var4 = "PS Rocks!"

PS C:\Users\Carlos\Desktop> $var4 = $null

PS C:\Users\Carlos\Desktop> $var4

PS C:\Users\Carlos\Desktop>

To delete a variable we use the Remove-Variable cmdlet and it will be deleted from the current session:

PS C:\Users\Carlos\Desktop> Remove-Variable var4
PS C:\Users\Carlos\Desktop> dir variable:\var*

Name                           Value
----                           -----
var1                           1.3
var2                           20

If a Variable has an option of ReadOnly we can remove it by passing the parameter of –Force to the Remove-Variable cmdlet.

Variables in PowerShell can have several attributes that will control not only the variable type it will accept but other restrictions we might want to impose upon them. Attributes are saved as an Array in the property which allows us to have several attributes assigned to the variable object. Lets look at the attributes of $var2:

# We get the variable object first in to another variable to make it easier to manipulate

PS C:\Users\Carlos\Desktop> $avar = Get-Variable var2

# Lets get members of the variable

PS C:\Users\Carlos\Desktop> $avar | Get-Member

TypeName: System.Management.Automation.PSVariable Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() IsValidValue Method bool IsValidValue(System.Object value) ToString Method string ToString() Attributes Property System.Collections.ObjectModel.Collection`1[[System.Attribute, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] Attributes {get;} Description Property System.String Description {get;set;} Module Property System.Management.Automation.PSModuleInfo Module {get;} ModuleName Property System.String ModuleName {get;} Name Property System.String Name {get;} Options Property System.Management.Automation.ScopedItemOptions Options {get;set;} Value Property System.Object Value {get;set;} Visibility Property System.Management.Automation.SessionStateEntryVisibility Visibility {get;set;} #Lets get the attribute property

PS C:\Users\Carlos\Desktop> $avar.Attributes

TypeId ------ System.Management.Automation.ArgumentTypeConverterAttribute

The attributes we can set are:

  • System.Management.Automation. ValidateSetAttribute – The value may have only a given set of values.
  • System.Management.Automation. ValidateRangeAttribute – The value must match a particular number range.
  • System.Management.Automation. ValidatePatternAttribute – The value must match a Regular Expression.
  • System.Management.Automation.ValidateNotNullOrEmptyAttribute –The value may not be zero or empty ($null).
  • System.Management.Automation. ValidateNotNullAttribute – The value may not be zero.
  • System.Management.Automation. ValidateLengthAttribute – The value must be in a specified range given a minimum and maximum length.

The attributes must be objects and they are set using the method of Attribute.Add() and we pass as an argument a new object created with the New-Object cmdlet. Lets start by clearing the  attribute for Int types.

PS C:\Users\Carlos\Desktop> $avar.Attributes.Clear()

PS C:\Users\Carlos\Desktop> $avar.Attributes

PS C:\Users\Carlos\Desktop>

Let make a variable only take a Range:

PS C:\Users\Carlos\Desktop>$var2 = 5 PS C:\Users\Carlos\Desktop> $avar.Attributes.Add($(New-Object System.Management.Automation.ValidateRangeAttribute -argumentList 1,20))

PS C:\Users\Carlos\Desktop> $var2 = 1

PS C:\Users\Carlos\Desktop> $var2 = 22

The variable cannot be validated because the value 22 is not a valid value for the var2 variable.

At line:1 char:6

+ $var2 <<<< = 22

+ CategoryInfo : MetadataError: (:) [], ValidationMetadataException

+ FullyQualifiedErrorId : ValidateSetFailure

Lets look now at setting a set of approved values:

PS C:\Users\Carlos\Desktop> $var2 = "yes"

PS C:\Users\Carlos\Desktop> $avar = Get-Variable var2

PS C:\Users\Carlos\Desktop> $avar.Attributes.Add($(New-Object System.Management.Automation.ValidateSetAttribute -argumentList "yes", "no", "y", "n"))

PS C:\Users\Carlos\Desktop> $var2 = "no"

PS C:\Users\Carlos\Desktop> $var2 = "y"

PS C:\Users\Carlos\Desktop> $var2 = "n"

PS C:\Users\Carlos\Desktop> $var2 = "si"

The variable cannot be validated because the value si is not a valid value for the var2 variable. At line:1 char:6 + $var2 <<<< = "si" + CategoryInfo : MetadataError: (:) [], ValidationMetadataException + FullyQualifiedErrorId : ValidateSetFailure

Lets set a pattern to match a string starting with a specific string. The pattern should be a regular expression:

PS C:\Users\Carlos\Desktop> $pattern_var = "PS Rocks uhmmm"

PS C:\Users\Carlos\Desktop> $pvar = Get-Variable pattern_var

PS C:\Users\Carlos\Desktop> $pattern = "PS Rocks*"

PS C:\Users\Carlos\Desktop> $pvar.Attributes.Add($(New-Object System.Management.Automation.ValidatePatternAttribute -ArgumentList $pattern))

PS C:\Users\Carlos\Desktop> $pattern_var = "PS Sucks!"

The variable cannot be validated because the value PS Sucks! is not a valid value for the pattern_var variable.

At line:1 char:13

+ $pattern_var <<<< = "PS Sucks!"

+ CategoryInfo : MetadataError: (:) [], ValidationMetadataException

+ FullyQualifiedErrorId : ValidateSetFailure

Lets look at validating a length from 1 to 8:

PS C:\Users\Carlos\Desktop> $length_var = "1234"

PS C:\Users\Carlos\Desktop> $lvar = Get-Variable length_var

PS C:\Users\Carlos\Desktop> $lvar.Attributes.Add($(New-Object System.Management.Automation.ValidateLengthAttribute -ArgumentList 1,8))

PS C:\Users\Carlos\Desktop> $length_var = "Hello I'm longer than 8 chars"

The variable cannot be validated because the value Hello I'm longer than 8 chars is not a valid value for the length_var variable.

At line:1 char:12

+ $length_var <<<< = "Hello I'm longer than 8 chars"

+ CategoryInfo : MetadataError: (:) [], ValidationMetadataException

+ FullyQualifiedErrorId : ValidateSetFailure

For the other attributes of Null and Empty checking we just create the object with no arguments and pass it as an attribute.

Variable Scopes

Just like with any other shell that supports scripting and most modern scripting languages variables will have a scope. Scope is in what parts of a session or script the variable is available to us for use. In PowerShell the scopes are:

  • $global – Variables are accessible to scripts, function and to any cmdlet in the current session.
  • $script – Variables are only accessible inside the running context of the script and are discarded after the script finishes executing.
  • $private – Variables are valid only in the current scope, either a script or a function. They cannot be passed to other scopes.
  • $local – Variables are valid only in the current scope of the script or session. All scopes called with them can read, but not change, the contents of the variable and it is the default when creating a variable.

to declare a variable in an scope other than local scope we do it by appending to the beginning of the variable declaration the scope:

# Declaring the variable

PS C:\Users\Carlos\Desktop> $global:gvar = "This is a global variable"

PS C:\Users\Carlos\Desktop> $gvar

This is a global variable

# Using the New-Variable cmdlet

PS C:\Users\Carlos\Desktop> $global:gvar = "This is a global variable"

PS C:\Users\Carlos\Desktop> $gvar

This is a global variable

Automatic Variables

Automatic variables are created and populated when the session is launched. These variables will contain user information, system information, default variables, run time variables and settings for PowerShell. To get a look at the variables and what they do we can either do Get-Help about_Automatic_Variables or list the variables and select only the name and description as shown bellow:

PS C:\Users\Carlos> Get-Variable | select name,description | ft -AutoSize -Wrap

Name Description ---- ----------- $ ? Execution status of last command. ^ _ args ConfirmPreference Dictates when confirmation should be requested. Confirmation is requested when the Confir mImpact of the operation is equal to or greater than $ConfirmPreference. If $ConfirmPrefe rence is None, actions will only be confirmed when Confirm is specified. ConsoleFileName Name of the current console file. DebugPreference Dictates action taken when an Debug message is delivered. Error ErrorActionPreference Dictates action taken when an Error message is delivered. ErrorView Dictates the view mode to use when displaying errors. ExecutionContext The execution objects available to cmdlets. false Boolean False FormatEnumerationLimit Dictates the limit of enumeration on formatting IEnumerable objects. HOME Folder containing the current user's profile. Host This is a reference to the host of this Runspace. input MaximumAliasCount The maximum number of aliases allowed in a session. MaximumDriveCount The maximum number of drives allowed in a session. MaximumErrorCount The maximum number of errors to retain in a session. MaximumFunctionCount The maximum number of functions allowed in a session. MaximumHistoryCount The maximum number of history objects to retain in a session. MaximumVariableCount The maximum number of variables allowed in a session. MyInvocation NestedPromptLevel Dictates what type of prompt should be displayed for the current nesting level. null References to the null variable always return the null value. Assignments have no effect. OutputEncoding The text encoding used when piping text to a native executable. PID Current process ID. PROFILE ProgressPreference Dictates action taken when Progress Records are delivered. PSBoundParameters PSCulture Culture of the current Windows PowerShell Session. PSEmailServer Variable to hold the Email Server. This can be used instead of HostName parameter in Send -MailMessage cmdlet. PSHOME Parent folder of the host application of this Runspace. PSSessionApplicationName AppName where the remote connection will be established PSSessionConfigurationName Name of the session configuration which will be loaded on the remote computer PSSessionOption Default session options for new remote sessions. PSUICulture UI Culture of the current Windows PowerShell Session. PSVersionTable Version information for current PowerShell session. PWD ReportErrorShowExceptionClass Causes errors to be displayed with a description of the error class. ReportErrorShowInnerException Causes errors to be displayed with the inner exceptions. ReportErrorShowSource Causes errors to be displayed with the source of the error. ReportErrorShowStackTrace Causes errors to be displayed with a stack trace. ShellId The ShellID identifies the current shell. This is used by #Requires. StackTrace true Boolean True VerbosePreference Dictates the action taken when a Verbose message is delivered. WarningPreference Dictates the action taken when a Warning message is delivered. WhatIfPreference If true, WhatIf is considered to be enabled for all commands.

One of the variables you might find your self using is to check if the last cmdlet you invoked ran successfully or not, the exit state is saved in $? with a value of False if it failed and True if it was successful.

PS C:\Users\Carlos\Desktop> Get-nonexistingcmdlet

The term 'Get-nonexistingcmdlet' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path

is correct and try again.

At line:1 char:22

+ Get-nonexistingcmdlet <<<<

+ CategoryInfo : ObjectNotFound: (Get-nonexistingcmdlet:String) [], CommandNotFoundException

+ FullyQualifiedErrorId : CommandNotFoundException

PS C:\Users\Carlos\Desktop> $?

False

PS C:\Users\Carlos\Desktop> Get-Variable pshome

Name Value

---- -----

PSHOME C:\Windows\System32\WindowsPowerShell\v1.0

PS C:\Users\Carlos\Desktop> $?

True

If we are executing system executable the variable with the last exit code would be $lastexitcode returning the exit code for the error found when executing or 0 if it executed successfully.

PS C:\Users\Carlos\Desktop> wmic systemdrive

systemdrive - Alias not found.

PS C:\Users\Carlos\Desktop> $LASTEXITCODE

44135

PS C:\Users\Carlos\Desktop> hostname

infidel01

PS C:\Users\Carlos\Desktop> $LASTEXITCODE

0

Some of the automatic variables can be changed so as to customize the session, others are read only and others are modified by the session it self as it executes. Many of these variable will prove useful as you work with PowerShell so I invite you to read the help on automatic variables.

Conclusion

I only covered some of the main points of variables and how to work with them. I do invite you to read more about them in the internal documentation that Microsoft PowerShell provides using the Get-Help cmdlet:

  • about_Variables
  • about_Automatic_Variables
  • about_Environment_Variables
  • about_Preference_Variables
  • about_Scopes

As always I hope you find this blog post useful and informative.

Creating Test Accounts on a Windows 2008 R2 DC with PowerShell

Recently I had to rebuild my lab do to that I had cloned a bunch of VM’s and forgot to run sysprep on them. This caused problems do to link SID’s when I installed Exchange 2010 in my home lab so I decided to rebuild the whole AD and services in it. So I decided to share how I created 100 test accounts on an isolated part of my lab network.

After installing the Active Directory Service and making the changes to DNS so it would forward to the proper DNS and made sure I had a Reverse Lookup Zone I wanted to create 100 test domain accounts. I normally use cmd.exe with dsadd.exe command, but this time I wanted to do it using PowerShell and this is with what I came up with as a command:

  1: import-module activedirectory
  2: (1..100) | foreach {New-ADUser -SamAccountName "User$($_.tostring())" -Name "User$($_.tostring())" -DisplayName "User$($_.tostring())" -AccountPassword (ConvertTo-SecureString -AsPlainText "P@ssword$($_.tostring())" -Force) -Enabled $true -EmailAddress "user$($_.tostring())@acmelabs.com" }

The commands are broken as so:

  • On line 1 I import the Active Directory PowerShell Module on the DC. If you want to see the cmdlets available on this module you can run  Get-Command -Module activedirectory this will list all of the cmdlets available to us to manage Active Directory.
  • On line 1 I generated a range from 1 to 100 and piped it to the cmdlet ForEach-Object and gave I a code block to run the cmdlet New-ADUser. To get more info on this cmdlet I invite you to run Get-Help New-ADUser –Full this will give you the full help plus examples of the cmdlet. Since the default variable of each object processed by the pipe is $_ and in the case of a range what I’m getting are Int32 objects I need to use the method of .ToString() to convert them to string and I use $() inside a double quoted string to expand the variable. What I do for each user I created was:
    • Set a Name
    • Set a Display Name
    • Set SAM Account Name
    • Set the Password. Now the cmdlet requires a secure string as value for the parameter, for this I used the ConvertTo-SecureString cmdlet to generate one from a plaintext quoted string.
    • Enable the account and set an email address since I will be installing Exchange later in this environment.

I do hope you find this useful and informative as always.