Can't run printf() on a null-terminated string - c

printf() throws a segmentation fault when I pass a null-terminated string for some reason.
Here's a demonstration of my problem in GDB
λ sudo gdb -q notesearch
Reading symbols from notesearch...done.
(gdb) break 53
Breakpoint 1 at 0x400b32: file notesearch.c, line 53.
(gdb) run
Starting program: /home/frosty/hack/chapter_2/code/notesearch
[DEBUG] UserID: 0
[DEBUG] File Descriptor: 3
Breakpoint 1, print_notes (fd=3, uid=0, searchstring=0x7fff3daf7fc0 "")
at notesearch.c:53
53 printf("%s\n", note_buffer);
(gdb) x/8xb note_buffer
0x7feb5a997168: 0x68 0x65 0x6c 0x6c 0x6f 0x0a 0x00 0x00
(gdb) x/s note_buffer
0x7feb5a997168: "hello\n"
(gdb) next
Program received signal SIGSEGV, Segmentation fault.
_dl_fixup (l=, reloc_arg=)
at ../elf/dl-runtime.c:148
148 ../elf/dl-runtime.c: No such file or directory.
(gdb)
Here's the source code around the problem
int print_notes(int fd, int uid, char *searchstring){
int note_length = find_user_note(fd, uid);
if (note_length == -1)
return 0; // End of file
char* note_buffer;
read(fd, note_buffer, note_length);
note_buffer[note_length] = 0; // null terminator byte
if(search_note(note_buffer, searchstring)) {
printf("%s\n", note_buffer);
}
return 1;
}

remember that in C, arrays are indexed from 0 through (length of array-1)
This line:
char* note_buffer;
declares an initialized pointer to character. I.E. its value depends on what ever trash is currently in memory at that location. Such an action as accessing where that trash 'points' would be undefined behavior and can lead to a seg fault event.
Strongly suggest, after this line:
char* note_buffer;
inserting the following: (including the checking for a malloc() failure:
note_buffer = malloc( note_length+1 );
if( !note_buffer )
{
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
Note: without the +1 on the call to malloc() the following line:
note_buffer[note_length] = 0;
would be setting a byte that is one past the end of the allocated area. That would be undefined behavior and can lead to a seg fault event
Also, a 0 has the bit pattern 0x000000000000000000000000 and a '\0' has the bit pattern 0x00000000. the implicit conversion feature will save you in this instance, but do not rely on that feature, instead properly code literals. so the line should be:
note_buffer[note_length] = '\0';
Note: perror() outputs the enclosed text string AND the reason the OS thinks the error occurred to stderr, which is where all error messages should be output.

Null terminator is indicated by \0 not 0
note_buffer[note_length] = 0;
should be
note_buffer[note_length] = '\0';

Related

strtol resulting in a segmentation fault on the raspberry pi 3 b+

I have this problem that strtol doesn't work and results in my C program crashing. I am using a Raspberry Pi 3 b+ but that probably doesn't matter.
My program (which is a command line tool to control shift registers) uses strtol for parsing through the command line arguments the program gets.
Here's the output I get: fish: Job 2, “./a.out -p 16 -w 0xff” terminated by signal SIGSEGV (Addroundary error)
And here's the output on gdp:
Program received signal SIGSEGV, Segmentation fault.
__GI_____strtol_l_internal (
nptr=0x76f72968 <_nl_C_LC_CTYPE_toupper+512> "",
nptr#entry=0x7efff6e9 "16", endptr=0x7efff6e9,
base=<optimized out>, group=group#entry=0,
loc=0x76fa1c70 <_nl_global_locale>) at strtol_l.c:484
484 strtol_l.c: No such file or directory.
The following is the code:
else if(!strcmp(argv[arg], "-p") || !strcmp(argv[arg], "-pins")) {
if(argc <= arg+1)
return 1;
pins = strtol(argv[++arg], endptr, 0);
}
The command line argument parsing happens like so:
uint8_t arg, pins;
char **endptr;
uint64_t writeValue;
bool valueGiven; // The other bools that I define are irrelevant
for(arg = 1; arg < argc; arg++) {
if(!strcmp(argv[arg], "-w") || !strcmp(argv[arg], "-write")) {
if(argc <= arg+1)
return 1;
writeValue = strtol(argv[++arg], endptr, 0); // error happens here too
valueGiven = true;
}
else if(!strcmp(argv[arg], "-p") || !strcmp(argv[arg], "-pins")) {
if(argc <= arg+1)
return 1;
pins = strtol(argv[++arg], endptr, 0);
}
// There are more arguments but those are irrelevant
}
And I run the program like this: ./a.out -p 16 -w 0xFF
This error is very odd for the exact same thing worked before, could this be a case of data corruption?
Your char **endptr is uninitialized. It needs to point to a char * where the address of the first unconverted character will be stored. Instead, yours points nowhere, so strtol is going to try to write to whatever bogus memory location it points to, and very likely crash.
GCC and clang should both issue warnings about the uninitialized variable if you enable -Wall. Example. Always use compiler warnings, and don't ignore them!
Normally you would declare a char * variable and pass its address:
char *end;
strtol(argv[++arg], &end, 0);

Buffer overflow attack without changing the return address

I need to call the login function in the below sample code. We can achieve this by changing the return address to the login function directly using buffer overflow attack. But I need to keep the return address as the same. Is there any other way to print logged in message without changing the return address?
char getPass()
{
int flag = 'F';
char pass[10];
gets(pass);
return (char) flag;
}
void login()
{
printf("Logged in");
exit(0);
}
void main()
{
printf("Enter Passwd");
if(getPass() == 'T')
{
login();
}else{
print("Failed");
exit(1);
}
}
Getting this to work depends on how the compiler decided to arrange the variables. If flag appears in memory after pass, then entering in more characters than pass will hold results in flag getting overwritten.
When I ran this program in a debugger and printed the addresses of these variables, I got the following:
(gdb) start
Temporary breakpoint 1 at 0x40060e: file x1.c, line 19.
Starting program: /home/dbush/./x1
Temporary breakpoint 1, main () at x1.c:19
19 printf("Enter Passwd");
Missing separate debuginfos, use: debuginfo-install glibc-2.17-292.el7.x86_64
(gdb) step
20 if(getPass() == 'T')
(gdb)
getPass () at x1.c:6
6 int flag = 'F';
(gdb)
8 gets(pass);
(gdb) p pass
$1 = "\000\000\000\000\000\000\000\000", <incomplete sequence \341>
(gdb) p &pass
$2 = (char (*)[10]) 0x7fffffffdec0
(gdb) p &flag
$3 = (int *) 0x7fffffffdecc
We can see that in this particular instance that flag is 12 bytes past the start of pass. My machine is also little-endian, meaning that the first of the 4 bytes of flag contain the value to be overwritten.
So we can exploit the buffer overflow vulnerability by entering in 13 characters, the last of which is T. This results in 10 characters being written to pass, two more to the padding bytes between pass and flag, the character T in the first byte of flag, and a 0 for the terminated null byte in the second byte of flag. Now the variable flag contains 'T' which is what gets returned from the function.
Note also that doing so doesn't write past flag into the function's return value. This is possible because flag is an int and little endian byte ordering is used.
Sample input/output:
[dbush#db-centos7 ~]$ ./x1
Enter Passwd1234567890TTT
Logged in[dbush#db-centos7 ~]$

BoF return address overwrite problem with \x0A and \x00

I have code in C:
#include <stdio.h>
int main() {
int cookie;
char buf[16];
printf("&buf: %p, &cookie: %p\n", buf, &cookie);
gets(buf);
if (cookie == 0x000D0A00)
printf("You win!\n");
}
file: ELF-32bit-i386 BoF cannot be exploited by overwriting the cookie variable (due to the 0x000D0A00 that contains the \x0A character). So I did overwrite the return address ($eip) but it went wrong. Here is the ASM code I compiled using GDB and the python code I use to exploit.
import struct
cookie = 'A'*4
buf = 'B'*16
ebp = 'C'*4
eip = struct.pack("I", 0x00001209)
print cookie+buf+ebp+eip
How to rewrite the EIP to 0x00001209 (i.e. ignore the conditional branch and rewrite the return address that is the address of the printf function)?
The test is unlikely to succeed on windows systems because it requires for the user to enter a null byte before the CRLF sequence. You should instead write:
if (cookie == 0x0A0D)
If the compiler allocates cookie after buf in the stack, which can be verified with the printf call, entering a 16 byte string and hit enter, the 16 bytes will go into buf and the next 2 bytes will go into the 2 low bytes of cookie. These bytes are 0x0D and 0x0A, the codes for CR and LF, the line terminator used by legacy systems.
Note however that cookie should be initialized to 0 so the high byte of cookie is 0 and the program must be compile with optimisations disabled too.

Stack smashing while casting and copying from one buffer to another

I have a problem writing 8 alphanumeric symbols into my dest_buffer buffer.
I want to convert a char from buff to hexadecimal, then store the value in
different cases of my dest_buffer buffer.
For example:
buff[0] = 58 should give me dest_buffer[0] = '3' and dest_buffer[1] = 'a'
and so on to reach 8 chars.
src is a char[8*sizeof(int)+1] and buff is the same.
Unfortunatly, I only have this output:
2dbb771
*** stack smashing detected ***: ./a.out terminated
[1] 9843 abort (core dumped) ./a.out
This is the loop I use to do the job
for (i = 0; i < 4*sizeof(int); ++i)
{
snprintf (
&dest_buff[i*sizeof(int)*2],
sizeof(int)*2,
"%x",
*(int*)&buff[i * sizeof(int)]
) ;
}
I know I'm missing something but I don't know what, neither where, could
any of you help me?
Thanks
EDIT 1:
Here is another part of my code to be more precise:
int i, rndf ;
char buff[4*sizeof(int)+1];
rndf = open("/dev/urandom", O_RDONLY) ;
if( read (rndf, buff, 4*sizeof(int)) < 0)
{
fprintf (stderr, "%s\n", "An error occured while reading urandom") ;
exit (EXIT_FAILURE) ;
}
close(rndf) ;
after this come the loop
You have stack corruption because char buff[4*sizeof(int)+1]; is your variable.
Let's assume sizeof(int) is 4 bytes. Then variable size is 17 bytes.
By doing this: &dest_buff[i*sizeof(int)*2] you are at some time accessing (when i = 3) address offset 3 * 4 * 2 = 24 which is out of range of your variable.
Conclusion: you have undefined behaviour. Increase variable length or correct you pointer usage.

New segmentation fault with previously working C code

this code is meant to take a list of names in a text file, and convert to email form
so Kate Jones becomes kate.jones#yahoo.com
this code worked fine on linux mint 12, but now the exact same code is giving a segfault on arch linux.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *fp;
fp = fopen("original.txt", "r+");
if (fp == NULL )
{
printf("error opening file 1");
return (1);
}
char line[100];
char mod[30] = "#yahoo,com\n";
while (fgets(line, 100, fp) != NULL )
{
int i;
for (i = 0; i < 100; ++i)
{
if (line[i] == ' ')
{
line[i] = '.';
}
if (line[i] == '\n')
{
line[i] = '\0';
}
}
strcat(line, mod);
FILE *fp2;
fp2 = fopen("final.txt", "a");
if (fp == NULL )
{
printf("error opening file 2");
return (1);
}
if (fp2 != NULL )
{
fputs(line, fp2);
fclose(fp2);
}
}
fclose(fp);
return 0;
}
Arch Linux is a fairly fresh install, could it be that there is something else I didn't install that C will need?
I think the problem would be when your original string plus mod exceeds 100 characters.
When you call strcat, it simply copies the string from the second appended to the first, assuming there is enough room in the first string which clearly doesn't seem to be the case here.
Just increase the size of line i.e. it could be
char line[130]; // 130 might be more than what is required since mod is shorter
Also it is much better to use strncat
where you can limit maximum number of elements copied to dst, otherwise, strcat can still go beyond size without complaining if given large enough strings.
Though a word of caution with strncat is that it does not terminate strings with null i.e. \0 on its own, specially when they are shorter than the given n. So its documentation should be thoroughly read before actual use.
Update: Platform specific note
Thought of adding, it is by sheer coincidence that it didn't seg fault on mint and crashed on arch. In practice it is invoking undefined behavior and should crash sooner or latter. There is nothing platform specific here.
Firstly your code isn't producing segmentation fault. Instead it will bring up "Stack Smashing" and throws below libc_message in the output console.
*** stack smashing detected ***: _executable-name-with-path_ terminated.
Stack buffer overflow bugs are caused when a program writes more data to a buffer located on the stack than there was actually allocated for that buffer.
Stack Smashing Protector (SSP) is a GCC extension for protecting applications from such stack-smashing attacks.
And, as said in other answers, your problem gets resolved with incrementing (strcat() function's first argument). from
char line[100]
to
char line[130]; // size of line must be atleast `strlen(line) + strlen(mod) + 1`. Though 130 is not perfect, it is safer
Lets see where the issue exactly hits in your code:
For that I am bringing up disassembly code of your main.
(gdb) disas main
Dump of assembler code for function main:
0x0804857c <+0>: push %ebp
0x0804857d <+1>: mov %esp,%ebp
0x0804857f <+3>: and $0xfffffff0,%esp
0x08048582 <+6>: sub $0xb0,%esp
0x08048588 <+12>: mov %gs:0x14,%eax
0x0804858e <+18>: mov %eax,0xac(%esp)
..... //Leaving out Code after 0x0804858e till 0x08048671
0x08048671 <+245>: call 0x8048430 <strcat#plt>
0x08048676 <+250>: movl $0x80487d5,0x4(%esp)
.... //Leaving out Code after 0x08048676 till 0x08048704
0x08048704 <+392>: mov 0xac(%esp),%edx
0x0804870b <+399>: xor %gs:0x14,%edx
0x08048712 <+406>: je 0x8048719 <main+413>
0x08048714 <+408>: call 0x8048420 <__stack_chk_fail#plt>
0x08048719 <+413>: leave
0x0804871a <+414>: ret
Following the usual assembly language prologue,
Instruction at 0x08048582 : stack grows by b0(176 in decimal) bytes for allowing storage stack contents for the main function.
%gs:0x14 provides the random canary value used for stack protection.
Instruction at 0x08048588 : Stores above mentioned value into the eax register.
Instruction at 0x0804858e : eax content(canary value) is pushed to stack at $esp with offset 172
Keep a breakpoint(1) at 0x0804858e.
(gdb) break *0x0804858e
Breakpoint 1 at 0x804858e: file program_name.c, line 6.
Run the program:
(gdb) run
Starting program: /path-to-executable/executable-name
Breakpoint 1, 0x0804858e in main () at program_name.c:6
6 {
Once program pauses at the breakpoint(1), Retreive the random canary value by printing the contents of register 'eax'
(gdb) i r eax
eax 0xa3d24300 -1546501376
Keep a breakpoint(2) at 0x08048671 : Exactly before call strcat().
(gdb) break *0x08048671
Breakpoint 2 at 0x8048671: file program_name.c, line 33.
Continue the program execution to reach the breakpoint (2)
(gdb) continue
Continuing.
Breakpoint 2, 0x08048671 in main () at program_name.c:33
print out the second top stack content where we stored the random canary value by executing following command in gdb, to ensure it is the same before strcat() is called.
(gdb) p *(int*)($esp + 172)
$1 = -1546501376
Keep a breakpoint (3) at 0x08048676 : Immediately after returning from call strcat()
(gdb) break *0x08048676
Breakpoint 3 at 0x8048676: file program_name.c, line 36.
Continue the program execution to reach the breakpoint (3)
(gdb) continue
Continuing.
Breakpoint 3, main () at program_name.c:36
print out the second top stack content where we stored the random canary value by executing following command in gdb, to ensure it is not corrupted by calling strcat()
(gdb) p *(int*)($esp + 172)
$2 = 1869111673
But it is corrupted by calling strcat(). You can see $1 and $2 are not same.
Lets see what happens because of corrupting the random canary value.
Instruction at 0x08048704 : Pulls the corrupted random canary value and stores in 'edx` register
Instruction at 0x0804870b : xor the actual random canary value and the contents of 'edx' register
Instruction at 0x08048712 : If they are same, jumps directly to end of main and returns safely. In our case random canary value is corrupted and 'edx' register contents is not the same as the actual random canary value. Hence Jump condition fails and __stack_chk_fail is called which throws libc_message mentioned in the top of the answer and aborts the application.
Useful Links:
IBM SSP Page
Interesting Read on SSP - caution pdf.
Since you didn't tell us where it faults I'll just point out some suspect lines:
for(i=0; i<100; ++i)
What if a line is less than 100 chars? This will read uninitialized memory - its not a good idea to do this.
strcat(line, mod);
What if a line is 90 in length and then you're adding 30 more chars? Thats 20 out of bounds..
You need to calculate the length and dynamically allocate your strings with malloc, and ensure you don't read or write out of bounds, and that your strings are always NULL terminated. Or you could use C++/std::string to make things easier if it doesn't have to be C.
Instead of checking for \n only, for the end of line, add the check for \r character also.
if(line[i] == '\n' || line[i] == '\r')
Also, before using strcat ensure that line has has enough room for mod. You can do this by checking if (i < /* Some value far less than 100 */), if i == 100 then that means it never encountered a \n character hence \0 was not added to line, hence Invalid memory Access occurs inside strcat() and therefore Seg Fault.
Fixed it. I simply increased the size of my line string.

Resources