VBA Excel code using echo y with PLink - excel

WARNING: **PLEASE MAKE SURE THAT YOU DO NOT USE THIS APPROACH IF YOU ARE WORKING WITH UN-TRUSTED DEVICES/IP's. You do not want to automatically cache Ssh Host Keys which are unknown to you. Do take care.
The purpose of my code is to run ssh through plink.exe from VBA Excel for a given list of IP's. I am just checking for SSH connectivity and IP's list is dynamic.
I am trying to pass y when running ssh using plink.exe. The reason for y is because first time PLink (PuTTY) asks for caching the IP.
Echo y does that automatically from command prompt and runs fine as below.
C:\>echo y | Users\Admin\Desktop\plink.exe -ssh 10.0.0.1
The command passes y and the IP is cached which makes my code automated and the code cycles through the whole list.
I am unable to execute that same command in VBA excel (which is where the tool is) and would need guidance in how to implement it. Please suggest where I am going wrong.
Dim strShellCommand As String
Dim filename As String
Dim Run As String
Dim a As String
Dim b As String
filename = Sheet1.Cells(8, 2).Value
a = "echo y |"
b = "-ssh"
' Comments!!
' filename from cell = "C:\Users\Admin\Desktop\plink.exe"
' echo y | C:\Users\Admin\Desktop\plink.exe -ssh 10.0.0.1
' strCompaddress is any IP
Run = a & " " & filename & " " & b & " " & strCompAddress
Set osh = CreateObject("Wscript.Shell")
Set oEx = osh.Exec(Run)

Do not try to circumvent the verification of the SSH host key. It's there by purpose for your own security:
This is a feature of the SSH protocol. It is designed to protect you against a network attack known as spoofing: secretly redirecting your connection to a different computer, so that you send your password to the wrong machine. Using this technique, an attacker would be able to learn the password that guards your login account, and could then log in as if they were you and use the account for their own purposes.
Use the -hostkey switch instead to provide a fingerprint of the expected host key.
3.8.3.20 -hostkey: manually specify an expected host key
This option overrides PuTTY's normal SSH host key caching policy by telling it exactly what host key to expect, which can be useful if the normal automatic host key store in the Registry is unavailable. The argument to this option should be either a host key fingerprint, or an SSH-2 public key blob. See section 4.20.2 for more information.
You can specify this option more than once if you want to configure more than one key to be accepted.
Note that the -hostkey switch was introduced in PuTTY/PLink 0.64.
If you use your code to test connectivity, why do you need to accept the host key? The mere fact that the server was able to present the host key is proof of the connectivity.
If you really use your code to cache the host keys, so that "they don't have the click 'yes' for every IP to cache it, " do not. That's absolutely unacceptable. Not only that you break your own security. You deliberately break security of other users that will unknowingly trust any host key that you blindly accepted.
The only correct way to pre-cache the host keys is by importing the known host keys to the registry.
Export personally verified (I mean it) host keys from your registry to a file like SshHostKeys.reg:
[HKEY_CURRENT_USER\SOFTWARE\SimonTatham\PuTTY\SshHostKeys]
"rsa2#22:example.com"="0x23,0xab603b8511a67679bdb540db3bd2034b004ae936d06be3d760f08fcbaadb4eb4edc3b3c791c70aae9a74c95869e4774421c2abea92e554305f38b5fd414b3208e574c337e320936518462c7652c98b31e16e7da6523bd200742a6444d83fcd5e1732d03673c7b7811555487b55f0c4494f3829ece60f94255a95cb9af537d7fc8c7fe49ef318474ef2920992052265b0a06ea66d4a167fd9f3a48a1a4a307ec1eaaa5149a969a6ac5d56a5ef627e517d81fb644f5b745c4f478ecd082a9492f744aad326f76c8c4dc9100bc6ab79461d2657cb6f06dec92e6b64a6562ff0e32084ea06ce0ea9d35a583bfb00bad38c9d19703c549892e5aa78dc95e250514069"
and import them on the target machine.
For example using:
reg import SshHostKeys.reg
If you really do not care about a security, for example because you are connecting within a private network, use:
Run = Environ("COMSPEC") & " /c echo y | " & filename & " -ssh " & strCompAddress
(assuming a path to plink.exe in filename)
To make an input redirection working, you have to run the process via shell interpreter (cmd.exe). The environment variable COMSPEC points to it (typically C:\WINDOWS\system32\cmd.exe).
See also Redirecting input to an executable from Excel VBA.

Related

Return my email adress in linux by TCL/csh command

I write script in TCL and want to get my email address.
Is there any way to get my email address?
Unless you're lucky and it's in an environment variable, there's no way to get that unless you've provided for it in an explicit setting. In terms of standard ones, the USER or LOGNAME environment variables might be set (the latter is more email-address-like) but neither have the hostname for making a full mailbox address, as that's often different from the local machine name (and DNS might not have a helpful record). Plus a lot of people have email addresses that are just totally different from their local username and machine name. How is anyone to guess that someone is joe.schmoe.1234567890 at gmail without being told explicitly when their local username is just js or js2000?
Ask explicitly, either interactively (this gets annoying if used a lot) or through setting something. Could be in a configuration file, via a command line argument, or an environment variable.
# Interactive on the terminal
puts -nonewline "Email address: "
flush stdout
gets stdin email
# From a command line argument; use the argv global list
set email [lindex $argv 1]
# Probably need more complex parsing than that
# From an environment variable
set email $env(USER_EMAIL_ADDRESS)
# From a simple setting file, in this case holding a Tcl dictionary
set f [open "my.setting.file.txt"]
set data [read $f]
close $f
set email [dict get $data "email"]

Update IIS 6 IP Restrictions using command line

I found the command line below that is used to add IP addresses to restrict in IIS 7
appcmd set config /section:ipsecurity /+"[ipaddress='10.0.0.1',allowed='false']"
Is there an equivalent command for IIS 6?
Thanks!
No, there's no built-in Windows command to do it. You can find evidence of scripts that people have written to mitigate for this.
Ultimately, you want to modify a metabase entry called IPSecurity. Here's the thing: this IPSecurity entry can be set up at the top level (W3SVC service) all of the way down to individual files. So, you can define security for any of:
Service
Site
VDir
Folder
File
The example in your question is service-wide, so you'd want to target IIS://localhost/W3SVC. If you wanted to configure only the default website, you'd target IIS://localhost/W3SVC/1/Root.
Once you know what level you want to modify, you need to identify what the course of action is for a matching IP. You clearly want to block. That means you'll need to modify the IPDeny List.
Now you just need to write a script in the language of your choice that connected to the metabase via ADSI and modifies the IPDeny list to include the additional IP.
I've modified the one from the MSDN page to take an argument:
Dim SecObj
Dim MyIPSec
Dim IPList
Set SecObj = GetObject("IIS://LocalHost/W3SVC")
Set MyIPSec = SecObj.IPSecurity
If (FALSE = MyIPSec.GrantByDefault) Then
MyIPSec.GrantByDefault = TRUE
End If
if WScript.Arguments.Count = 0 then
WScript.Echo "Missing IP Address"
WScript.Quit(1)
end if
' WScript.Echo "Adding " & WScript.Arguments(0)
IPList = MyIPSec.IPDeny
Redim Preserve IPList (Ubound(IPList)+1)
IPList (Ubound(IPList)) = WScript.Arguments(0)
MyIPSec.IPDeny = IPList
SecObj.IPSecurity = MyIPSec
SecObj.Setinfo
If you save this as blockip.vbs, you can call it with:
wscript blockip.vbs 10.0.0.1
FYI, This works fine with IIS6, but works once, then fails after the list exists, on Win7 (IIS 7.5).

X3270 Connection and Programming

I'm looking at using a X3270 terminal emulator. I have http://x3270.bgp.nu/ looked over this source material and still don't see how to start using the tool or configure it.
I'm wonder how I can open a terminal and connect. Another question is how could I integrate this into a python program?
edit:
here is a snippet:
em = Emulator()
em.connect(ip)
em.send_string('*user name*')
em.exec_command('Tab')
em.send_string('*user password*')
em.send_enter()
em.send_enter()
em.wait_for_field()
em.save_screen("{0}screenshot".format(*path*))
looking at the save screen i see that the cursor hasn't moved? I can move the cursor using
em.move_to(7,53)
but after that i don't get any text sent through. Any Ideas?
Here's what I do; it works 100% of the time:
from py3270 import *
import sys, os
host = "%s" % sys.argv[1].upper()
try:
e = Emulator()
e.connect(host)
e.wait_for_field()
except WaitError:
print "py3270.connect(%s) failed" % (host)
sys.exit(1)
print "--- connection made to %s ---" % (host)`
If you haven't got a network connection to your host, that wait_for_field() call is going to wait for a full 120 seconds. No matter what I do, I don't seem to be able to affect the length of that timeout.
But your user doesn't have to wait that long, just have him kill your script with a KeyboardInterrupt. Hopefully, your user will grow accustomed to success equaling the display of that "--- connection made ..." message so he'll know he's in trouble when/if the host doesn't respond.
And that's a point I need to make: you don't connect to a terminal (as you described), rather you connect to a host. That host can be either a VTAM connection or some kind of LPAR, usually TSO or z/VM, sometimes CICS or IMS, that VTAM will take you to. Each kind of host has differing prompts & screen content you might need to test for, and sometimes those contents are different depending on whose system you're trying to connect to. Your script becomes the "terminal", depending on what you want to show your user.
What you need to do next depends on what kind of system you're trying to talk to. Through VTAM? (Need to select a VTAM application first?) To z/VM? TSO? Are you logging on or DIALing? What's the next keystroke/field you have to use when you're working with a graphic x3270/c3270 terminal? You need to know that in order to choose your next command.
Good luck!
Please read my comment above first - it would be helpful to have more detail as to what you need to do.
After considering that…have you looked at the py3270 package at https://pypi.python.org/pypi/py3270/0.1.5 ? The summary says it talks to x3270.

Passing key material to openssl commands

Is it safe to pass a key to the openssl command via the command line parameters in Linux? I know it nulls out the actual parameter, so it can't be viewed via /proc, but, even with that, is there some way to exploit that?
I have a python app that I want to use OpenSSL to do the encryption/description through stdin/stdout streaming in a subprocess, but I want to know my keys are safe.
Passing the credentials on the command line is not safe. It will result in your password being visible in the system's process listing - even if openssl erases it from the process listing as soon as it can, it'll be there for an instant.
openssl gives you a few ways to pass credentials in - the man page has a section called "PASS PHRASE ARGUMENTS", which documents all the ways you can pass credentials into openssl. I'll explain the relevant ones:
env:var
Lets you pass the credentials in an environment variable. This is better than using the process listing, because on Linux your process's environment isn't readable by other users by default - but this isn't necessarily true on other platforms.
The downside is that other processes running as the same user, or as root, will be able to easily view the password via /proc.
It's pretty easy to use with python's subprocess:
new_env=copy.deepcopy(os.environ)
new_env["MY_PASSWORD_VAR"] = "my key data"
p = subprocess.Popen(["openssl",..., "-passin", "env:MY_PASSWORD_VAR"], env=new_env)
fd:number
This lets you tell openssl to read the credentials from a file descriptor, which it will assume is already open for reading. By using this you can write the key data directly from your process to openssl, with something like this:
r, w = os.pipe()
p = subprocess.Popen(["openssl", ..., "-passin", "fd:%i" % r], preexec_fn=lambda:os.close(w))
os.write(w, "my key data\n")
os.close(w)
This will keep your password secure from other users on the same system, assuming that they are logged in with a different account.
With the code above, you may run into issues with the os.write call blocking. This can happen if openssl waits for something else to happen before reading the key in. This can be addressed with asynchronous i/o (e.g. a select loop) or an extra thread to do the write()&close().
One drawback of this is that it doesn't work if you pass closeFds=true to subprocess.Popen. Subprocess has no way to say "don't close one specific fd", so if you need to use closeFds=true, then I'd suggest using the file: syntax (below) with a named pipe.
file:pathname
Don't use this with an actual file to store passwords! That should be avoided for many reasons, e.g. your program may be killed before it can erase the file, and with most journalling file systems it's almost impossible to truly erase the data from a disk.
However, if used with a named pipe with restrictive permissions, this can be as good as using the fd option above. The code to do this will be similar to the previous snippet, except that you'll need to create a fifo instead of using os.pipe():
pathToFifo = my_function_that_securely_makes_a_fifo()
p = subprocess.Popen(["openssl", ..., "-passin", "file:%s" % pathToFifo])
fifo = open(pathToFifo, 'w')
print >> fifo, "my key data"
fifo.close()
The print here can have the same blocking i/o problems as the os.write call above, the resolutions are also the same.
No, it is not safe. No matter what openssl does with its command line after it has started running, there is still a window of time during which the information is visible in the process' command line: after the process has been launched and before it has had a chance to null it out.
Plus, there are many ways for an accident to happen: for example, the command line gets logged by sudo before it is executed, or it ends up in a shell history file.
Openssl supports plenty of methods of passing sensitive information so that you don't have to put it in the clear on the command line. From the manpage:
pass:password
the actual password is password. Since the password is visible to utilities (like 'ps' under Unix) this form should only be used where security is not important.
env:var
obtain the password from the environment variable var. Since the environment of other processes is visible on certain platforms (e.g. ps under certain Unix OSes) this option should be used with caution.
file:pathname
the first line of pathname is the password. If the same pathname argument is supplied to -passin and -passout arguments then the first line will be used for the input password and the next line for the output password. pathname need not refer to a regular file: it could for example refer to a device or named pipe.
fd:number
read the password from the file descriptor number. This can be used to send the data via a pipe for example.
stdin
read the password from standard input.
All but the first two options are good.

python Argparse: how to enter default values when using repeated options/arguments and yet some may be dropped in the command line

I'm trying to write a code that connects (via ssh) to multiple machines (servers), using a given ip address, username and password given by the user for each machine, and gets a list of files for each machine, in order to do something with them. The goal is for the user to be able to enter all those options and arguments via a command-line interface. I am using python's argparse module.
In the easier scenario the user calls the function via the following list of options and arguments: (assuming the code is save in myFile.py)
$ python3 mFile.py --machine ip1 --username u1 --pass p1 --file f11 f12 f13 --machine ip2 --username u2 --pass p2 --file f21 f22
The following python code could handle the above usage:
parser.add_argument('-m', '--machine', action='append', const=["m1"], nargs='?')
parser.add_argument('-p', '--pass', action='append', const =['default_pass'], nargs='?')
parser.add_argument('-u', '--username', action='append', const =['default_uname'], nargs='?')
parser.add_argument('-f', '--file', nargs='+', action='append')
and if I run print(parser.parse_args()) I will get:
Namespace(file=[['f11', 'f12', 'f13'], ['f21', 'f22']], machine=['ip1', 'ip2'], password=['p1', 'p2'], user=['u1', 'u2'])
which is all nice and dandy. But here is the problem that I am having:
The user may drop some of the options and arguments, in which case a default value should be used. The user may drop username AND/OR password for a particular machine (since some of the servers have the same username and/or password) or drop the machine ip, and so I want the following possible calls at the command line and yet get pretty much the same namespace as I had above (except w/ default values in place of missing options/args):
For instance user drops username and password for the 2nd machine:
$ python3 mFile.py --machine ip1 --username u1 --pass p1 --file f11 f12 f13 --machine ip2 --file f21 f22
and yet I get the following namespace for the above usage:
Namespace(file=[['f11', 'f12', 'f13'], ['f21', 'f22']], machine=['ip1', 'ip2'], password=['p1', 'default_pass'], user=['u1','default_uname'])
Or the user drops the machine ip which by default would mean the current machine he's sitting at:
$ python3 mFile.py --file f11 f12 f13 --machine p2 --username u2 --pass p2 --files f21 f22
and I still get the following namespace:
Namespace(file=[['f11', 'f12', 'f13'], ['f21', 'f22']], machine=['current_ip', 'ip2'], password=['default_pass', 'p2'], user=['default_uname', 'u2'])
Note that if the username or password is dropped, some default username and password string can be used, but if the machine ip is dropped, the ip of the current machine should be retrieved and used. Also I'm assuming the user never drops the list of files, since that's necessary.
I tried using the "default" option when using add_argument but that just appends the default values to the lists mindlessly at the beginning of the list and not where the missing option/arg occurred. Essentially all the lists (i.e. file, machine, pass and username) should have same length at the end.
I have no clue how to make this work! Thank you so much for your help.
With the ? and const, your users can provide a 'dummy' entry:
In [122]: parser=argparse.ArgumentParser()
In [123]: parser.add_argument('-m', '--machine', action='append', const=["m1"], nargs='?')
In [124]: parser.parse_args(''.split())
Out[124]: Namespace(machine=None)
In [125]: parser.parse_args('-m one -m -m three'.split())
Out[125]: Namespace(machine=['one', ['m1'], 'three'])
The machine action is invoked each time a -m flag is found, filling in the provided value or the const is none. But the default is entered only once - at the beginning of parsing.
Since each optional is processed independently there isn't a way of telling the parser to expect 3 of each, or to expect them in any particular order, or to fill in missing one. Obviously after parsing you can check the lengths of the attribute lists, but there's no way of know which one is missing.
I can't think of any simple way (or for that matter complex) adding such expectations to an argparse parser.

Resources