I'm recently writing a piece of code to access an external server using SSH and then communicating with interactive shell-like application to which I'm directly connecting through it.
I'm using embedded Linux with only basic libraries available, with no possibility to use any additional software nor library. Also, I have to do it via C/C++ code inside the application. So I've decided to use pipes and read(), write() system calls, and I would rather stick to that.
I've written some code to better understand and test the concept. But it doesn't work as expected. I've used a snippet from here. It seems to work fine, but then the loop in main behaves not as expected
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
int p1[2], p2[2];
if (!pid || !infd || !outfd)
return false;
if (pipe(p1) == -1)
goto err_pipe1;
if (pipe(p2) == -1)
goto err_pipe2;
if ((*pid = fork()) == -1)
goto err_fork;
if (*pid) {
/* Parent process. */
*infd = p1[1];
*outfd = p2[0];
close(p1[0]);
close(p2[1]);
return true;
} else {
/* Child process. */
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execvp(*command, command);
/* Error occured. */
fprintf(stderr, "error running %s: %s", *command, strerror(errno));
abort();
}
err_fork:
close(p2[1]);
close(p2[0]);
err_pipe2:
close(p1[1]);
close(p1[0]);
err_pipe1:
return false;
}
int main() {
char *cmd[4];
cmd[0] = "/usr/bin/ssh";
cmd[1] = "-tt";
cmd[2] = "user#localhost";
cmd[3] = NULL;
char buf[65535];
char msg[65535];
int pid, infd, outfd;
start_subprocess(cmd, &pid, &infd, &outfd);
printf ("Started app %s as %d\n\n", *cmd, pid);
while(1) {
read(outfd, buf, 65535);
printf(">>> %s\n", buf);
printf("<<< ");
scanf("%s", msg);
if(strcmp(msg, "exit") == 0) break;
write(infd, msg, strlen(msg));
}
return 0;
}
I've experimeted with various SSH -t settings and it seems to somehow work with -tt option enabled (As I understand it forces pseudoterminal), without it I'm getting
Pseudo-terminal will not be allocated because stdin is not a terminal.
So I assume -tt is correct here. But the beheviour is strange. I wanted to connect through SSH, then issue ls command and see the output, which should be similar to normal SSH :
user#xubuntuLTS ~/dev/cpp/pipes$ ssh localhost
>>>> WELCOME TO SSH SERVER <<<<
Last login: Thu Jan 3 22:34:35 2019 from 127.0.0.1
user#xubuntuLTS ~$ ls
Desktop dev Documents Downloads Music Pictures Public Templates TEST_FILE Videos
user#xubuntuLTS ~$
But insted, my application works like this:
user#xubuntuLTS ~/dev/cpp/pipes$ ./a.out
Started app /usr/bin/ssh as 18393
>>>> WELCOME TO SSH SERVER <<<<
>>> Last login: Thu Jan 3 22:35:28 2019 from 127.0.0.1
<<< ls
>>> user#xubuntuLTS ~$
<<<
ls
>>> ls0;user#xubuntuLTS: ~user#xubuntuLTS ~$
<<< ls
>>> ls0;user#xubuntuLTS: ~user#xubuntuLTS ~$
Can you hint me what is wrong in my code? I want to read exactly the same output as I see during "normal" SSH session from terminal, possibly having "appending" output during each read() call, so I can easily perform some automated task with this type of interactive communication. Please note that using standard terminal here is just an example, in the real world solution I'm connecting to some kind of command line interface program directly by logging through SSH, without actual access to shell.
I'm pretty sure that there is something wrong with correct usage of write() and read() here, but I'm not an expert in that matter.
Related
I have a Setuid binary that has a printf format string vulnerability that is supposed to be exploited with "%n" to overwrite the value of the authenticated global variable. The execution of /bin/bash works with root Setuid permissions when authenticated = 1, but not when authenticated = 0 and the exploit is used.
I have tried with ls and it works, so the exec is happening. I have also tried making authenticated = 1 in the source so it automatically runs bash with no exploit. This works in spawning a root shell. When the exploit is used, the program calls the access granted function as expected, but ends at the exec and perror is never reached. The parent process dies, though, meaning the exec of bash must have happened. Bash must be being executed, but it is crashing/exiting on startup.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
int authenticated = 0;
void read_flag() {
if (!authenticated) {
printf("Sorry, you are not *authenticated*!\n");
}
else {
printf("Access Granted.\n");
int cpid = fork();
if(cpid == 0){
printf("child!\n");
execlp("/bin/bash", "bash", NULL);
perror("error");
}
else{
wait(NULL);
}
}
}
int main(int argc, char **argv) {
setvbuf(stdout, NULL, _IONBF, 0);
char buf[64];
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
setreuid(geteuid(), getuid());
printf("Would you like a shell? (yes/no)\n");
fgets(buf, sizeof(buf), stdin);
if (strstr(buf, "no") != NULL) {
printf("Okay, Exiting...\n");
exit(1);
}
else if (strstr(buf, "yes") == NULL) {
puts("Received Unknown Input:\n");
printf(buf);
}
read_flag();
}
With authenticated = 0, I use gdb to find the address of authenticated is somewhere like 0x0804a050. I run the program with AAAA %x %x %x... to find that buf begins at the 4th stack position. My exploit then is: python -c "print('\x50\xa0\x04\x08%x%x%x%n')" which successfully overwrites the global var as "Access Granted!" is printed. The perror is never reached, and Bash must spawn, but the parent process dies, so the Bash process must have died also.
This does not happen when authenticated = 1. In that scenario, the Setuid binary behaves as expected and pops a root shell.
My question is: why is Bash dying on startup but only when the Detuid binary is exploited?
Bash must be dying because ps -aux does not list a new Bash process, and running exit exits the calling bash instance.
When you run one of:
python -c "print('\x50\xa0\x04\x08%x%x%x%n')" | ./vuln
./vuln < myPayload
The only input is your exploit. You don't input any commands, so bash has nothing to do and exits. This is the same thing that happens if you run true | bash or bash < /dev/null.
If you want to be able to type in some commands manually afterwards, the easiest way to do that is:
{ python -c "print('\x50\xa0\x04\x08%x%x%x%n')"; cat; } | ./vuln
I need to load a *.ml file into the Ocaml toplevel (the interactive interpreter, when you type 'ocaml' in a shell) and then send an instruction from a Matlab process, get back the result of the instruction, send back another instruction, ...
I've made this C program. The parent process gets the Matlab's instruction from a named pipe, sends it to the child process (with ocaml running) and gets the response back so it can send it to Matlab.
But there is some kind of bug: when I send an instruction, I get back some weird characters, I send another instruction and then I receive the response of the first instruction...
(I didn't copy the perror() test to have less text)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void) {
// Parent -> Child
int pipe_in[2];
// Child -> parent
int pipe_out[2];
/*
pipe[0] = output
pipe[1] = input
*/
pipe(pipe_in);
pipe(pipe_out);
pid_t pid;
if ((pid = fork()) == 0) {
// CHILD SIDE
close(pipe_in[1]);
close(pipe_out[0]);
dup2(pipe_in[0], STDIN_FILENO);
dup2(pipe_out[1], STDOUT_FILENO);
dup2(pipe_out[1], STDERR_FILENO);
close(pipe_in[0]);
close(pipe_out[1]);
char *args[] = {"ocaml", NULL};
execvp("ocaml", args);
printf("FAIL\n");
exit(EXIT_FAILURE);
} else {
// PARENT SIDE
printf("[*] PID : %d\n", (int) pid);
close(pipe_in[0]);
close(pipe_out[1]);
char cmd[1024];
char feedback[1024];
ssize_t cmd_read;
ssize_t feedback_read = sizeof(feedback);
while (1) {
// Get the instruction from Matlab.
printf("[>] ");
int fifo_in = open("/tmp/pipe_in", O_RDONLY);
cmd_read = read(fifo_in, cmd, sizeof(cmd));
close(fifo_in);
printf("%s\n", cmd);
// Send the instruction to the ocaml interpreter.
write(pipe_in[1], cmd, cmd_read);
// Read the response of the ocaml interpreter.
while (feedback_read == sizeof(feedback)) {
feedback_read = read(pipe_out[0], feedback, sizeof(feedback));
printf("[-] %d\n", (int) feedback_read);
}
printf("[<] %s\n", feedback);
// Send to Matlab the response.
int fifo_out = open("/tmp/pipe_out", O_WRONLY);
write(fifo_out, feedback, feedback_read);
close(fifo_out);
cmd_read = 0;
feedback_read = sizeof(feedback);
}
close(pipe_in[1]);
close(pipe_out[0]);
}
}
I compile the code with gcc -Wall -std=c99 -o tphr tphr.c
I run the programm in one shell and in another :
> printf 'let x = 10;;\n' > /tmp/pipe_in
> cat /tmp/pipe_out
OCaml version 4.03.0
# %
> printf 'let y = 5;;\n' > /tmp/pipe_in
> cat /tmp/pipe_out
val x : int = 10
# %
How can I fix the result ?
If, by "weird characters", you mean
OCaml version 4.03.0
that is simply because this is what the OCaml toplevel prints out on startup. So you need to read this line when your own program starts up.
If you mean the # symbol, also known as the prompt, you can turn it off by running ocaml -nopromt.
You don't want to run the interactive ocaml toplevel here for multiple reasons:
the toplevel parses the configfiles of the user and then loads different modules. This can change the available values, change behaviour, and make your matlab process get different results per user.
the output of the toplevel may change between versions making it difficult to parse and return the right reply to matlab
Did you know that you can call the toplevel from ocaml bytecode to interprete strings? I suggest dumping the C code and writing ocaml byte code to read from the pipe, interpret the command and reply with the result.
I'm using libssh to execute commands on a remote server.
Here is my code (return codes are not checked here for simplification, but all of them are OK):
#include <stdio.h>
#include <stdlib.h>
#include <libssh/libssh.h>
int main() {
/* opening session and channel */
ssh_session session = ssh_new();
ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
ssh_options_set(session, SSH_OPTIONS_PORT_STR, "22");
ssh_options_set(session, SSH_OPTIONS_USER, "julien");
ssh_connect(session);
ssh_userauth_autopubkey(session, NULL);
ssh_channel channel = ssh_channel_new(session);
ssh_channel_open_session(channel);
/* command execution */
ssh_channel_request_exec(channel, "echo 'foo' && whoam");
char *buffer_stdin = calloc(1024, sizeof(char));
ssh_channel_read(channel, buffer_stdin, 1024, 0);
printf("stdout: %s\n", buffer_stdin);
free(buffer_stdin);
char *buffer_stderr = calloc(1024, sizeof(char));
ssh_channel_read(channel, buffer_stderr, 1024, 1);
printf("stderr: %s", buffer_stderr);
free(buffer_stderr);
ssh_channel_free(channel);
ssh_free(session);
return EXIT_SUCCESS;
}
The output is as axpected:
stdout: foo
stderr: command not found: whoam
Now if I add a call to ssh_channel_request_pty just after ssh_channel_open_session:
...
ssh_channel channel = ssh_channel_new(session);
ssh_channel_open_session(channel);
ssh_channel_request_pty(channel);
ssh_channel_request_exec(channel, "echo 'foo' && whoam");
...
There is no stderr output any more:
stdout: foo
stderr:
And if I change the command by:
ssh_channel_request_exec(channel, "whoam");
Now the error output is read on stdout!
stdout: command not found: whoam
stderr:
I am missing something with ssh_channel_request_pty?
For information, I'm using it because on some servers I get the following error when running a command with sudo:
sudo: sorry, you must have a tty to run sudo
Conceptually, a pty represents a terminal (or, at an even lower level, a serial port) -- there's only one channel in each direction. The standard output and standard error of a process running in a pty both go to the same place by default.
(Consider what happens when you run a command in a terminal, for instance -- if you haven't redirected anything, both stdout and stderr will appear on screen, and there's no way to tell them apart.)
Generally speaking, if you need to automate running commands remotely as root, you should either SSH in as root, or grant the user sudo access with the NOPASSWD option. Don't automate entering the password.
The following simplified piece of code is executed by a thread in the background. The thread runs until he is told to exit (by user input).
In the code below I have removed some error checking for better readability. Even with error checking the code works well and both the master and the slave are created and/or opened.
...
int master, slave;
char *slavename;
char *cc;
master = posix_openpt(O_RDWR);
grantpt(master);
unlockpt(master);
slavename = ptsname(master);
slave = open(slavename, O_RDWR);
printf("master: %d\n",master);
printf("slavename: %s\n",slavename);
On my machine the output is the following:
master: 3
slavename: /dev/pts/4
So I thought that opening an xterm with the command xterm -S4/3 (4 = pt-slave, 3 = pt-master) while my program is running should open a new xterm window for the created pseudoterminal. But xterm just starts running without giving an error or any further informations but does not open a window at all. Any suggestions on that?
EDIT:
Now with Wumpus Q. Wumbley's help xterm starts normally, but I can't redirect any output to it. I tried:
dup2(slave, 1);
dup2(slave, 2);
printf("Some test message\n");
and opening the slave with fopen and then using fprinf. Both didn't work.
The xterm process needs to get access to the file descriptor somehow. The intended usage of this feature is probably to launch xterm as a child process of the one that created the pty. There are other ways, though. You could use SCM_RIGHTS file descriptor passing (pretty complicated) or, if you have a Linux-style /proc filesystem try this:
xterm -S4/3 3<>/proc/$PID_OF_YOUR_OTHER_PROGRAM/fd/3
'
You've probably seen shell redirection operators before: < for stdin, > for stdout, 2> for stderr (file descriptor 2). Maybe you've also seen other file descriptors being opend for input or output with things like 3<inputfile 4>outputfile. Well the 3<> operator here is another one. It opens file descriptor 3 in read/write mode. And /proc/PID/fd/NUM is a convenient way to access files opened by another process.
I don't know about the rest of the question. I haven't tried to use this mode of xterm before.
OK, the trick with /proc was a bad idea. It's equivalent to a fresh open of /dev/ptmx, creating a new unrelated pty.
You're going to have to make the xterm a child of your pty-creating program.
Here's the test program I used to explore the feature. It's sloppy but it revealed some interesting things. One interesting thing is that xterm writes its window ID to the pty master after successful initialization. This is something you'll need to deal with. It appears as a line of input on the tty before the actual user input begins.
Another interesting thing is that xterm (the version in Debian at least) crashes if you use -S/dev/pts/2/3 in spite of that being specifically mentioned in the man page as an allowed format.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int master;
char *slavename, window[64], buf[64];
FILE *slave;
master = posix_openpt(O_RDWR);
grantpt(master);
unlockpt(master);
slavename = ptsname(master);
printf("master: %d\n", master);
printf("slavename: %s\n", slavename);
snprintf(buf, sizeof buf, "-S%s/%d", strrchr(slavename,'/')+1, master);
if(!fork()) {
execlp("xterm", "xterm", buf, (char *)0);
_exit(1);
}
slave = fopen(slavename, "r+");
fgets(window, sizeof window, slave);
printf("window: %s\n", window);
fputs("say something: ", slave);
fgets(buf, sizeof buf, slave);
fprintf(slave, "you said %s\nexiting in 3 seconds...\n", buf);
sleep(3);
return 0;
}
I realize this question is asked frequently, mainly by people who want to intercept the password-asking phase of SSH. This is not what I want. I'm after the post-login text.
I want to write a wrapper for ssh, that acts as an intermediary between SSH and the terminal. I want this configuration:
(typing on keyboard / stdin) ----> (wrapper) ----> (ssh client)
and the same for output coming from ssh:
(ssh client) -----> (wrapper) -----> stdout
I seem to be able to attain the effect I want for stdout by doing a standard trick I found online (simplified code):
pipe(fd)
if (!fork()) {
close(fd[READ_SIDE]);
close(STDOUT_FILENO); // close stdout ( fd #1 )
dup(fd[WRITE_SIDE]); // duplicate the writing side of my pipe ( to lowest # free pipe, 1 )
close(STDERR_FILENO);
dup(fd[WRITE_SIDE]);
execv(argv[1], argv + 1); // run ssh
} else {
close(fd[WRITE_SIDE]);
output = fdopen(fd[READ_SIDE], "r");
while ( (c = fgetc(output)) != EOF) {
printf("%c", c);
fflush(stdout);
}
}
Like I said, I think this works. However, I can't seem to do the opposite. I can't close(STDIN_FILENO) and dup the readside of a pipe. It seems that SSH detects this and prevents it. I've read I can use the "-t -t" option to force SSH to ignore the non-stdin nature of its input; but when I try this it still doesn't work.
Any hints?
Thanks very much!
Use popen (instead of execv) to execute the ssh cmd and be able to read and write to the session.
A pipe will not work if you want to allow any interactive use of ssh with the interceptor in place. In this case, you need to create a pseudo-tty. Look up the posix_openpt, ptsname, and grantpt functions. There's also a nonstandard but much-more-intuitive function called openpty, and a wrapper for it called forkpty, which make what you're trying to do extremely easy.
Python's Paramiko does all of this with SSH but it is in Python source code. However, for a C programmer, reading Python is a lot like reading pseudocode so go to the source and learn exactly what works.
Here's a working example that writes to ssh:
#include <unistd.h>
int main(int argc, char **argv)
{
int pid;
int fds[2];
if (pipe(fds))
return -1;
pid = fork();
if (!pid)
{
close(fds[1]);
close(STDERR_FILENO);
dup2(fds[0], STDIN_FILENO);
execvp(argv[1], argv + 1);
}
else
{
char buf[256];
int rc;
close(fds[0]);
while ((rc = read(STDIN_FILENO, buf, 256)) > 0)
{
write(fds[1], buf, rc);
}
}
wait(NULL);
return 0;
}
This line is probably wrong:
execv(argv[1], argv + 1); // run ssh
The array must be terminated by a NULL pointer, if you are using argv[] the parameter from main() I don't think there is any guarantee that this is the case. Edit: just checked the C99 standard and argv is NULL terminated.
execv() does not search the path for the file to execute, so if you are passing ssh as the parameter, it is equivalent to ./ssh which is probably not what you want. You could use execvp() but that is a security risk if a malicious program called ssh appears in $PATH before /bin/ssh. Better to use execv() and force the correct path.