Why does system call fails? - linux

I try to compile simple utility (kernel 3.4.67), which
all it does is trying to use system call very simply as following:
int main(void)
{
int rc;
printf("hello 1\n");
rc = system("echo hello again");
printf("system returned %d\n",rc);
rc = system("ls -l");
printf("system returned %d\n",rc);
return 0;
}
Yet, system call fails as you can see in the following log:
root#w812a_kk:/ # /sdcard/test
hello 1
system returned 32512
system returned 32512
I compile it as following:
arm-linux-gnueabihf-gcc -s -static -Wall -Wstrict-prototypes test.c -o test
That's really wierd becuase I used system in past in different linux and never had any issue with it.
I even tried another cross cpompiler but I get the same failure.
Version of kernel & cross compiler:
# busybox uname -a
Linux localhost 3.4.67 #1 SMP PREEMPT Wed Sep 28 18:18:33 CST 2016 armv7l GNU/Linux
arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (crosstool-NG linaro-1.13.1-4.7-2013.03-20130313 - Linaro GCC 2013.03) 4.7.3 20130226 (prerelease)
EDIT:
root#w812a_kk:/ # echo hello again && echo $? && echo $0
hello again
0
tmp-mksh
root#w812a_kk:/ #
But I did find something interesting:
On calling test_expander() withing the main, it works OK. so I suspect that maybe system call try to find a binary which is not founded ?
int test_expander(void)
{
pid_t pid;
char *const parmList[] = {"/system/bin/busybox", "echo", "hello", NULL};
if ((pid = fork()) == -1)
perror("fork error");
else if (pid == 0) {
execv("/system/bin/busybox", parmList);
printf("Return not expected. Must be an execv error.n");
}
return 0;
}
Thank you for any idea.
Ran

The return value of system(), 32512 decimal, is 7F00 in hex.
This value is strangely similar to 0x7F, which is the result of system() if /bin/sh can not be executed. It seems there is some problem with byte ordering (big/little endian). Very strange.
Update: while writing the answer, you edited the question and pulled in something about /system/bin/busybox.
Probably you simply don't have /bin/sh.

I think I understand what happens
From system man page:
The system() library function uses fork(2) to create a child process
that executes the shell command specified in command using execl(3)
as follows:
execl("/bin/sh", "sh", "-c", command, (char *) 0);
But in my filesystem sh is founded only /system/bin , not in /bin
So I better just use execv instead. (I can't do static link becuase it's read-only filesystem)
Thanks,
Ran

Related

Why does my RIP value change after overwriting via an overflow?

I've been working on a buffer overflow on a 64 bit Linux machine for the past few days. The code I'm attacking takes in a file. This original homework ran on a 32-bit system, so a lot is differing. I thought I'd run with it and try to learn something new along the way. I set sudo sysctl -w kernel.randomize_va_space=0 and compiled the program below gcc -o stack -z execstack -fno-stack-protector stack.c && chmod 4755 stack
/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(char *str)
{
char buffer[12];
/* The following statement has a buffer overflow problem */
strcpy(buffer, str);
return 1;
}
int main(int argc, char **argv)
{
char str[517];
FILE *badfile;
badfile = fopen("badfile", "r");
fread(str, sizeof(char), 517, badfile);
bof(str);
printf("Returned Properly\n");
return 1;
}
I could get it to crash by making a file with 20 "A"s. I made a small script to help.
#!/usr/bin/bash
python -c 'print("A"*20)' | tr -d "\n" > badfile
So now, if I add 6 "B"s to the mix after hitting the SIGSEGV in gbd I get.
RIP: 0x424242424242 ('BBBBBB')
0x0000424242424242 in ?? ()
Perfect! Now we can play with the RIP and put our address in for it to jump to! This is where I'm getting a little confused.
I added some C's to the script to try to find a good address to jump to
python -c 'print("A"*20 + "B"*6 + C*32)' | tr -d "\n" > badfile
after creating the file and getting the SEGFAULT my RIP address is no longer our B's
RIP: 0x55555555518a (<bof+37>: ret)
Stopped reason: SIGSEGV
0x000055555555518a in bof (
str=0x7fffffffe900 'C' <repeats 14 times>)
at stack.c:16
I thought this might be due to using B's, so I went ahead and tried to find an address. I then ran x/100 $rsp in gbd, but it looks completely different than before without the Cs
# Before Cs
0x7fffffffe8f0: 0x00007fffffffec08 0x0000000100000000
0x7fffffffe900: 0x4141414141414141 0x4141414141414141
0x7fffffffe910: 0x4242424241414141 0x0000000000004242
# After Cs
0x7fffffffe8e8: "BBBBBB", 'C' <repeats 32 times>
0x7fffffffe90f: "AAAAABBBBBB", 'C' <repeats 32 times>
0x7fffffffe93b: ""
I've been trying to understand why this is happening. I know after this I can supply an address plus code to get a shell like so
python -c 'print("noOPs"*20 + "address" + "shellcode")' | tr -d "\n" > badfile
The only thing that has come to mind is the buffer size? I'm not too sure, though. Any help would be great. Doing this alone without help has made me learn a lot. I'm just dying to create a working exploit!

Difference in behavior when hooking a library function via LD_PRELOAD on Ubuntu and CentOS

There is a hook function socketHook.c that intercepts socket() calls:
#include <stdio.h>
int socket(int domain, int type, int protocol)
{
printf("socket() has been intercepted!\n");
return 0;
}
gcc -c -fPIC socketHook.c
gcc -shared -o socketHook.so socketHook.o
And a simple program getpwuid.c (1) that just invokes the getpwuid() function:
#include <pwd.h>
int main()
{
getpwuid(0);
return 0;
}
gcc getpwuid.c -o getpwuid
getpwuid() internally makes a socket() call.
On CentOS:
$ strace -e trace=socket ./getpwuid
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
socket(AF_UNIX, SOCK_STREAM, 0) = 4
On Ubuntu:
$ strace -e trace=socket ./getpwuid
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 5
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 5
When running (1), socket() is intercepted on CentOS, but not on Ubuntu.
CentOS. printf() from socketHook.c is present:
$ uname -a
Linux centos-stream 4.18.0-301.1.el8.x86_64 #1 SMP Tue Apr 13 16:24:22 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ LD_PRELOAD=$(pwd)/socketHook.so ./getpwuid
socket() has been intercepted!
Ubuntu(Xubuntu 20.04). printf() from socketHook.c is NOT present:
$ uname -a
Linux ibse-VirtualBox 5.8.0-50-generic #56~20.04.1-Ubuntu SMP Mon Apr 12 21:46:35 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ LD_PRELOAD=$(pwd)/socketHook.so ./getpwuid
$
So my question is:
What does it depend on? I think this is affected by the fact that socket() is not called directly from the executable, but from getpwuid(), which in turn is called, if I understand correctly, from libc.so
How to achieve the same behavior in CentOS as in Ubuntu? I don't want intercept indirect calls from libc
What does it depend on?
There are two questions to ask:
Which function actually calls the socket system call?
How is that function getting called.
You can see how the socket system call is invoked by running your program under GDB, and using catch syscall socket command. On Ubuntu:
(gdb) catch syscall socket
Catchpoint 1 (syscall 'socket' [41])
(gdb) run
Starting program: /tmp/a.out
Catchpoint 1 (call to syscall socket), 0x00007ffff7ed3477 in socket () at ../sysdeps/unix/syscall-template.S:120
120 ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0 0x00007ffff7ed3477 in socket () at ../sysdeps/unix/syscall-template.S:120
#1 0x00007ffff7f08010 in open_socket (type=type#entry=GETFDPW, key=key#entry=0x7ffff7f612ca "passwd", keylen=keylen#entry=7) at nscd_helper.c:171
#2 0x00007ffff7f084fa in __nscd_get_mapping (type=type#entry=GETFDPW, key=key#entry=0x7ffff7f612ca "passwd", mappedp=mappedp#entry=0x7ffff7f980c8 <map_handle+8>) at nscd_helper.c:269
#3 0x00007ffff7f0894f in __nscd_get_map_ref (type=type#entry=GETFDPW, name=name#entry=0x7ffff7f612ca "passwd", mapptr=mapptr#entry=0x7ffff7f980c0 <map_handle>,
gc_cyclep=gc_cyclep#entry=0x7fffffffda0c) at nscd_helper.c:419
#4 0x00007ffff7f04fb7 in nscd_getpw_r (key=0x7fffffffdaa6 "0", keylen=2, type=type#entry=GETPWBYUID, resultbuf=resultbuf#entry=0x7ffff7f96520 <resbuf>,
buffer=buffer#entry=0x5555555592a0 "", buflen=buflen#entry=1024, result=0x7fffffffdb60) at nscd_getpw_r.c:93
#5 0x00007ffff7f05412 in __nscd_getpwuid_r (uid=uid#entry=0, resultbuf=resultbuf#entry=0x7ffff7f96520 <resbuf>, buffer=buffer#entry=0x5555555592a0 "", buflen=buflen#entry=1024,
result=result#entry=0x7fffffffdb60) at nscd_getpw_r.c:62
#6 0x00007ffff7e9e95d in __getpwuid_r (uid=uid#entry=0, resbuf=resbuf#entry=0x7ffff7f96520 <resbuf>, buffer=0x5555555592a0 "", buflen=buflen#entry=1024,
result=result#entry=0x7fffffffdb60) at ../nss/getXXbyYY_r.c:255
#7 0x00007ffff7e9dfd3 in getpwuid (uid=0) at ../nss/getXXbyYY.c:134
#8 0x0000555555555143 in main () at t.c:5
(gdb) info sym $pc
socket + 7 in section .text of /lib/x86_64-linux-gnu/libc.so.6
(gdb) up
#1 0x00007ffff7f08010 in open_socket (type=type#entry=GETFDPW, key=key#entry=0x7ffff7f612ca "passwd", keylen=keylen#entry=7) at nscd_helper.c:171
171 nscd_helper.c: No such file or directory.
(gdb) x/i $pc-5
0x7ffff7f0800b <open_socket+59>: callq 0x7ffff7ed3470 <socket>
From this we can see that
The function socket is called. Using nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep ' socket' we can confirm that that function is exported from libc.so.6, and thus should be interposable.
The caller does not call socket#plt (i.e. does not use the procedure linkage table), and so LD_PRELOAD will have no effect.
The call from open_socket() to socket() has been non-interposable since 2004, so it's likely that this call isn't intercepted on CentOS either, but some other call is. Probably the 3rd one in your strace output.
Using above method you should be able to tell where that call comes from.
I don't want intercept indirect calls from libc
In that case, LD_PRELOAD may be the wrong mechanism to use.
If you want to only intercept socket() calls from your own code, it's trivial to redirect them to e.g. mysocket() without any need for LD_PRELOAD.
You can do that at source level by adding e.g.
#define socket mysocket
to all your files, or using -Dsocket=mysocket argument at compile time.
Alternatively, using the linker --wrap=socket will do the redirection without recompiling.

override return address of main in c

i'm trying to execute a buffer overflow attack on a program written in c, i'm using GNU/Linux (Ubuntu 16.04 LTS).
this is the source code:
#include<stdio.h>
void CALLME(){
puts("successful!");
}
int main(void){
char s[16];
scanf("%s",s);
}
what i want to do is override the return address of main so that after main function, the function CALLME will be executed.
i compile the program with
gcc -m32 -fno-stack-protector -o prog prog.c
use command:
nm prog | grep CALLME
i got the address of CALLME: 0804845b
disassemble main in gdb i found that: during main function, the return address is located at 8(%ebp) and the address of string s is at -0x18(%ebp). So the difference is 0x8 + 0x18 = 32
i try to exploit:
perl -e 'print "a" x 32 . "\x5b\x84\x04\x08"' | ./main
it didn't work.
Segmentation fault (core dumped)
Why ? Is main function more special ? Because in other functions (i made) that have a similar vulnerability it works ?
NOTE: i don't think about ASLR, some guys said that happens only when i compile gcc -pie ... and other stuffs.

Suppressing the segfault signal

I am analyzing a set of buggy programs that under some test they may terminate with segfault. The segfault event is logged in /var/log/syslog.
For example the following snippet returns Segmentation fault and it is logged.
#!/bin/bash
./test
My question is how to suppress the segfault such that it does NOT appear in the system log. I tried trap to capture the signal in the following script:
#!/bin/bash
set -bm
trap "echo 'something happened'" {1..64}
./test
It returns:
Segmentation fault
something happened
So, it does traps the segfault but the segfault is still logged.
kernel: [81615.373989] test[319]: segfault at 0 ip 00007f6b9436d614
sp 00007ffe33fb77f8 error 6 in libc-2.19.so[7f6b942e1000+1bb000]
You can try to change ./test to the following line:
. ./test
This will execute ./test in the same shell.
We can suppress the log message system-wide with e. g.
echo 0 >/proc/sys/debug/exception-trace
- see also
Making the Linux kernel shut up about segfaulting user programs
Is there a way to temporarily disable segfault messages in dmesg?
We can suppress the log message for a single process if we run it under ptrace() control, as in a debugger. This program does that:
exe.c
#include <sys/wait.h>
#include <sys/ptrace.h>
main(int argc, char *args[])
{
pid_t pid;
if (*++args)
if (pid = fork())
{
int status;
while (wait(&status) > 0)
{
if (!WIFSTOPPED(status))
return WIFSIGNALED(status) ? 128+WTERMSIG(status)
: WEXITSTATUS(status);
int signal = WSTOPSIG(status);
if (signal == SIGTRAP) signal = 0;
ptrace(PTRACE_CONT, pid, 0, signal);
}
perror("wait");
}
else
{
ptrace(PTRACE_TRACEME, 0, 0, 0);
execvp(*args, args);
perror(*args);
}
return 1;
}
It is called with the buggy program as its argument, in your case
exe ./test
- then the exit status of exe normally is the exit status of test, but if test was terminated by signal n (11 for Segmentation fault), it is 128+n.
After I wrote this, I realized that we can also use strace for the purpose, e. g.
strace -enone ./test

gdb stops in a command file if there is an error. How to continue despite the error?

I my real gdb script while analyzing a core file I try to dereference a pointer and get "Error in sourced command file: Cannot access memory at address " and then my gdb script stops. What I want is just to go on executing my gdb script without stopping. Is it possible?
This is a test program and a test gdb script that demonstrates my problem. In this situation the pointer has NULL value but in a real situation the pointer will like have not null invalid value.
This is test C program:
#include <stdio.h>
struct my_struct {
int v1;
int v2;
};
int main()
{
my_struct *p;
printf("%d %d\n", p->v1, p->v2);
return 0;
}
This is a test gdb script:
>cat analyze.gdb
p p->v1
q
And this is demonstration of the problem (what I want from gdb here is to get this error message and then go process quit command):
>gdb -silent a.out ./core.22384 -x ./analyze.gdb
Reading symbols from /a.out...done.
[New Thread 22384]
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x0000000000400598 in main () at main.cpp:11
11 printf("%d %d\n", p->v1, p->v2);
./analyze.gdb:1: Error in sourced command file:
Cannot access memory at address 0x0
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6.x86_64
Update
Thanks to Tom. This is a gdb script that handles this problem:
>cat ./analyze.v2.gdb
python
def my_ignore_errors(arg):
try:
gdb.execute("print \"" + "Executing command: " + arg + "\"")
gdb.execute (arg)
except:
gdb.execute("print \"" + "ERROR: " + arg + "\"")
pass
my_ignore_errors("p p")
my_ignore_errors("p p->v1")
gdb.execute("quit")
This is how it works:
>gdb -silent ./a.out -x ./analyze.v2.gdb -c ./core.15045
Reading symbols from /import/home/a.out...done.
[New Thread 15045]
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x0000000000400598 in main () at main.cpp:11
11 printf("%d %d\n", p->v1, p->v2);
$1 = "Executing command: p p"
$2 = (my_struct *) 0x0
$3 = "Executing command: p p->v1"
$4 = "ERROR: p p->v1"
$5 = "Executing command: quit"
gdb's command language doesn't have a way to ignore an error when processing a command.
This is easily done, though, if your gdb was built with the Python extension. Search for the "ignore-errors" script. With that, you can:
(gdb) ignore-errors print *foo
... and any errors from print will be shown but not abort the rest of your script.
You can also do this:
gdb a.out < analyze.v2.gdb
This will execute the commands in analyze.v2.gdb line by line, even if an error occurs.
If you just want to exit if any error occurs, you can use the -batch gdb option:
Run in batch mode. Exit with status 0 after processing all the command
files specified with ā€˜-xā€™ (and all commands from initialization files,
if not inhibited with ā€˜-nā€™). Exit with nonzero status if an error
occurs in executing the GDB commands in the command files. [...]

Resources