2011-10-29 8 views
3

私は現在、ブルートフォースを介してsshを学んでいます。いくつかの試行錯誤の末、私は「pty-req」の後に「シェル」リクエストを送信して、ログインプリアンブルを取得し、コマンドを送信してstdoutを受け取ることができましたが、 SSHサービス標準エラーメッセージとステータスメッセージを受信したい。他のSSHの実装(paramiko、Net :: SSH)を読むことは、現時点ではあまり参考にされていません。クライアントとしてtwisted.conchを使用してsshで拡張データを受信

#!/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;[email protected]: ~\[email protected]:~$ 
       """ 
      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;[email protected]: ~\[email protected]:~$ 
      """ 

     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;[email protected]: ~\[email protected]:~$ 
      """ 

     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;[email protected]: ~\[email protected]:~$ 
      """ 

     #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() 

さらに私は、明示的な悪いコマンドに追加してみました:RFCのSSHのための1を見て、私はおそらく、記載された要求の一つは、私が探しています何のかもしれないと信じている、と述べた

リモートシェルに:

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;[email protected]: ~\[email protected]:~$ 
     """ 

そして、それは標準エラーのように見えるのOpenSSH、チャネルのソースコードを掘り標準エラー

答えて

0

に混入されていますセッションロジックは、 2227 function - > session_input_channel_reqでsession.cという行で処理されます.pty-reqが指定されている場合、 "shell"リクエストはdo_exec_ptyにつながり、最終的にはsession_set_fds(s、ptyfd、fdout、-1,1、 1)。第4引数は通常、stderrを処理するファイル記述子ですが、何も指定されていないので、stderrの拡張データはありません。

最終的に、openderを標準エラー出力FDに変更したとしても、問題はシェルにあります。この時点では完全な推測が可能ですが、xtermやputtyのような端末からsshサービスにログインするのと同様に、 "2> someFile"のようなものを経由して明示的にリダイレクトされない限り、stderrとstdoutは一緒に送信されますSSHサービスプロバイダ。

+1

PTYをリクエストしている場合は、stderrとstdoutが同じ場所に移動することが理にかなっています。疑似TTY自体を「画面」のように考えることができます。また、stdoutとstderrどちらもユーザーが見ることができる場所です。 'pty-req'リクエストではなく' exec'リクエストで何が起こるか見てきましたか? – Glyph

+0

execにはdo_exec_ptyとdo_exec_no_ptyが分割され、PTY stderrは取り込まれません。私のテストでは、それは多くの意味がある実行のためのリターンを提供していないのはシェルだけです。 – David

関連する問題