簡體   English   中英

使用twisted.conch作為客戶端使用ssh接收擴展數據

[英]Receiving extended data with ssh using twisted.conch as client

我目前正在通過蠻力學習ssh /只是繼續黑客攻擊直到我理解它的方法。 經過一些試驗和錯誤,我已經能夠成功發送“pty-req”后跟“shell”請求,我可以獲得登錄前導碼,發送命令和接收標准輸出但是我不確定如何告訴SSH服務我想收到stderr和狀態消息。 通過其他SSH實現(paramiko,Net :: SSH)閱讀目前還不是很多指南。

也就是說,看一下用於SSH的RFC之一,我相信也許所列出的請求之一可能就是我想要的: http//tools.ietf.org/html/rfc4250#section-4.9.3

#!/usr/bin/env python


from twisted.conch.ssh import transport
from twisted.conch.ssh import userauth
from twisted.conch.ssh import connection
from twisted.conch.ssh import common
from twisted.conch.ssh.common import NS
from twisted.conch.ssh import keys
from twisted.conch.ssh import channel
from twisted.conch.ssh import session
from twisted.internet import defer

from twisted.internet import defer, protocol, reactor
from twisted.python import log
import struct, sys, getpass, os
log.startLogging(sys.stdout)


USER = 'dward'  
HOST = '192.168.0.19' # pristine.local
PASSWD = "password"
PRIVATE_KEY = "~/id_rsa"

class SimpleTransport(transport.SSHClientTransport):
    def verifyHostKey(self, hostKey, fingerprint):
        print 'host key fingerprint: %s' % fingerprint
        return defer.succeed(1) 

    def connectionSecure(self):
        self.requestService(
            SimpleUserAuth(USER,
                SimpleConnection()))

class SimpleUserAuth(userauth.SSHUserAuthClient):
    def getPassword(self):
        return defer.succeed(PASSWD)

    def getGenericAnswers(self, name, instruction, questions):
        print name
        print instruction
        answers = []
        for prompt, echo in questions:
            if echo:
                answer = raw_input(prompt)
            else:
                answer = getpass.getpass(prompt)
            answers.append(answer)
        return defer.succeed(answers)

    def getPublicKey(self):
        path = os.path.expanduser(PRIVATE_KEY) 
        # this works with rsa too
        # just change the name here and in getPrivateKey
        if not os.path.exists(path) or self.lastPublicKey:
            # the file doesn't exist, or we've tried a public key
            return
        return keys.Key.fromFile(filename=path+'.pub').blob()

    def getPrivateKey(self):
        path = os.path.expanduser(PRIVATE_KEY)
        return defer.succeed(keys.Key.fromFile(path).keyObject)



class SimpleConnection(connection.SSHConnection):
    def serviceStarted(self):
        self.openChannel(SmartChannel(2**16, 2**15, self))        




class SmartChannel(channel.SSHChannel):
    name = "session"


    def getResponse(self, timeout = 10):
        self.onData = defer.Deferred()
        self.timeout = reactor.callLater( timeout, self.onData.errback, Exception("Timeout") )
        return self.onData

    def openFailed(self, reason):
        print "Failed", reason

    @defer.inlineCallbacks    
    def channelOpen(self, ignoredData):
        self.data = ''
        self.oldData = ''
        self.onData = None
        self.timeout = None
        term = os.environ.get('TERM', 'xterm')
        #winsz = fcntl.ioctl(fd, tty.TIOCGWINSZ, '12345678')
        winSize = (25,80,0,0) #struct.unpack('4H', winsz)
        ptyReqData = session.packRequest_pty_req(term, winSize, '')

        try:
            result = yield self.conn.sendRequest(self, 'pty-req', ptyReqData, wantReply = 1 )
        except Exception as e:
            print "Failed with ", e

        try:
            result = yield self.conn.sendRequest(self, "shell", '', wantReply = 1)
        except Exception as e:
            print "Failed shell with ", e


        #fetch preample    
        data = yield self.getResponse()
        """
        Welcome to Ubuntu 11.04 (GNU/Linux 2.6.38-8-server x86_64)

            * Documentation:  http://www.ubuntu.com/server/doc

             System information as of Sat Oct 29 13:09:50 MDT 2011

             System load:  0.0               Processes:           111
             Usage of /:   48.0% of 6.62GB   Users logged in:     1
             Memory usage: 39%               IP address for eth1: 192.168.0.19
             Swap usage:   3%

             Graph this data and manage this system at https://landscape.canonical.com/
           New release 'oneiric' available.
           Run 'do-release-upgrade' to upgrade to it.

           Last login: Sat Oct 29 01:23:16 2011 from 192.168.0.17
        """
        print data
        while data != "" and data.strip().endswith("~$") == False:
            try:
                data = yield self.getResponse()
                print repr(data)
                """
                \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
                """
            except Exception as e:
                print e
                break

        self.write("false\n")
        #fetch response
        try:
            data = yield self.getResponse()
        except Exception as e:
            print "Failed to catch response?", e
        else:
            print data
            """
                false
                \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
            """

        self.write("true\n")
        #fetch response
        try:
            data = yield self.getResponse()
        except Exception as e:
            print "Failed to catch response?", e
        else:
            print data
            """
            true
            \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
            """

        self.write("echo Hello World\n\x00")
        try:
            data = yield self.getResponse()
        except Exception as e:
            print "Failed to catch response?", e
        else:            
            print data
            """
            echo Hello World
            Hello World
            \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
            """

        #Close up shop
        self.loseConnection()
        dbgp = 1


    def request_exit_status(self, data):
        status = struct.unpack('>L', data)[0]
        print 'status was: %s' % status    

    def dataReceived(self, data):
        self.data += data
        if self.onData is not None:
            if self.timeout and self.timeout.active():
                self.timeout.cancel()
            if self.onData.called == False:                
                self.onData.callback(data)

    def extReceived(self, dataType, data):
        dbgp = 1
        print "Extended Data recieved! dataType = %s , data = %s " % ( dataType, data, )
        self.extendData = data

    def closed(self):
        print 'got data : %s' % self.data.replace("\\r\\n","\r\n")
        self.loseConnection()
        reactor.stop()



protocol.ClientCreator(reactor, SimpleTransport).connectTCP(HOST, 22)
reactor.run()

另外,我嘗試向遠程shell添加一個明確的錯誤命令:

    self.write("ls -alF badPathHere\n\x00")
    try:
        data = yield self.getResponse()
    except Exception as e:
        print "Failed to catch response?", e
    else:            
        print data
        """
        ls -alF badPathHere
        ls: cannot access badPathHere: No such file or directory
        \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
        """

而且看起來stderr正在混入stderr

通過OpenSSH的源代碼,通道會話邏輯在session.c中的第2227行處理函數 - > session_input_channel_req,如果給出一個pty-req,那么“shell”請求將導致do_exec_pty,最終導致對session_set_fds的調用(s) ,ptyfd,fdout,-1,1,1)。 第四個參數通常是負責處理stderr的文件描述符,但由於沒有提供,因此stderr不會有任何擴展數據。

最終,即使我修改openssh以提供stderr FD,問題仍然存在於shell中。 此時完成猜測工作,但我認為類似於通過xterm或putty等終端登錄ssh服務,stderr和stdout一起發送,除非通過類似“2> someFile”的內容明確重定向,這超出了SSH服務提供商。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM