Vulnerability with scanf ( "%s", string) - c

I have this code in C language
#include <stdio.h>
#include <stdlib.h>
int foo () {printf ("foo \n");return 1;}
int bar () {printf ("bar \n");return 1;}
typedef struct {
char buf[20];
int (*func)();
} Object ;
int main()
{
Object *o1 , *o2;
o1 = (Object*) malloc (sizeof(Object));
o2 = (Object*) malloc (sizeof(Object));
if(o1==NULL || o2==NULL)
return -1;
o1-> func =&foo;
o2-> func =&bar;
scanf ( "%s " , o1->buf);
scanf ( "%s " , o2->buf);
(* ( o1->func ))();
(* ( o2->func ))();
return 0;
}
When I run this code, it crashes
The problem is in this two line:
scanf ( "%s " , o1->buf);
scanf ( "%s " , o2->buf);
It has a vulnerability in this two line.
updated :
this is an example of result :
the program run without error but I think a Vulnerability exist in the scanf line

From man scanf:
int scanf(const char *format, ...);
The format string consists of a sequence of directives which describe how to process the sequence of input characters. <snip> A directive is one of the following:
A sequence of white-space characters (space, tab, newline, etc.; see isspace(3)). This directive matches any amount of white space, including none, in the input.
An ordinary character (i.e., one other than white space or '%'). This character must exactly match the next character of input.
A conversion specification, which commences with a '%' (percent) character. A sequence of characters from the input is converted according to this specification
Conversion specification: s: Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the terminating null byte (\0), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.
So when you write:
scanf("%s ",buf)
The function will scan for a string and absorb all following white-space characters (including your newlines). This implies that an input from stdin can only terminate by a non-white-space character followed by a new-line:
foo < string conversion specification + absorbed newline
< absorbed newline
< absorbed newline
b < end of white-space directive
So in short if you want to ensure that your scanf works as expected, you can do:
scanf("%s",buf)
You have to make sure that the entered string can fit the size of buf, including the NULL character.
If you hardcoded the size of buf, you can use something like:
scanf("%19s",buf)
if buf has a size of 20. This way it will read maximum 19 characters and store them in buf with an additional null.

Related

Why does sscanf read more than expected?

sscanf supports %n to count how many bytes are read.
Why does sscanf sometimes read additional bytes?
#include <stdio.h>
int main()
{
char *data = "X\n \n\x09\n \x10\n";
int len = 0;
sscanf(data, "X%n", &len);
printf("%i\n", len);
sscanf(data, "X\n%n", &len);
printf("%i\n", len);
return 0;
}
This program prints:
1
7
I would expect:
1
2
(1 for X and 2 for X\n.)
Why does it read more bytes than expected?
From cppreference:
The format string consists of
non-whitespace multibyte characters except %: each such character in the format string consumes exactly one identical character from the
input stream, or causes the function to fail if the next character on
the stream does not compare equal.
whitespace characters: any single whitespace character in the format string consumes all available consecutive whitespace characters from
the input (determined as if by calling isspace in a loop). Note that
there is no difference between "\n", " ", "\t\t", or other whitespace
in the format string.
Thus, your \n in the second format string will cause the function to consume all remaining whitespace characters – which is actually all 6 characters following the X and preceding the 0x10.

C Program won't terminate after compared to empty string

I am attempting to terminate my C program by checking for an empty string ("") but it seems not to work. I have tried to compare to "\0" as well but it was to no avail.
#include <stdio.h>
#include <string.h>
int main(void) {
char nameInput[128];
for(;;) {
printf("Enter nation name: ");
scanf("%s", nameInput);
if(!strcmp(nameInput, "")){
break;
}
printf("Got nation named \"%s\"\n", nameInput);
}
printf("All done getting nations!\n");
return 0;
}
The "%s" specifier in scanf("%s", nameInput); first consumes1 and discards leading white-space including all '\n' from the Enter before scanning and saving to nameInput.
That is why repeated entries of empty lines do not advance the scan. "%s" is waiting for some non-white-space input.
A better alternative to scanf() is to read all user input with fgets() and then parse the string.
fgets() reads a line and saves the result as a string - usually including the line's ending '\n'.
// scanf("%s", nameInput);
if (fgets(nameInput, sizeof nameInput, stdin)) {
// Success at reading input.
nameInput[strcspn(nameInput, "\n")] = '\0'; // lop off the potential trailing \n
if(!strcmp(nameInput, "")){ // or simply `if(nameInput[0] == '\0')
break;
}
...
have tried to compare to "\0" as well but it was to no avail.
if(!strcmp(nameInput, "")) and if(!strcmp(nameInput, "\0")) do the same thing. strcmp() is comparing strings.
"" is a string literal of 1 char: the null character.
"\0" is a string literal of 2 char: two null characters.
The string compare stops at the first null character.
"%s" by itself also lacks a width limit. Code has no safe guard against input like "BlahBlah...(120_some_more)Blah" and can lead to undefined behavior due a buffer overrun of char nameInput[128];. Code could use "%127s" to prevent that, yet that only handles one of the short-comings of scanf().
1
Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a [, c, or n specifier. C17dr § 7.21.6.2 8
It's not that it won't terminate, it is awaiting the input that wasn't (yet) typed in.
scanf is not using the right pattern string to scan in anything (including nothing) before the carriage return. You'll need to look into scanf patterns, and alter your pattern from "%s" to something that scanf will accept as input.
If you test out your program, you will see that after pressing "enter" you can type in a word and press enter again, and since you now have a word in the input, the scanf picks it up (discarding the whitespace, as it should with "%s").

What does %[^\n] mean in C?

What does %[^\n] mean in C?
I saw it in a program which uses scanf for taking multiple word input into a string variable. I don't understand though because I learned that scanf can't take multiple words.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
int main() {
char line[100];
scanf("%[^\n]",line);
printf("Hello,World\n");
printf("%s",line);
return 0;
}
[^\n] is a kind of regular expression.
[...]: it matches a nonempty sequence of characters from the scanset (a set of characters given by ...).
^ means that the scanset is "negated": it is given by its complement.
^\n: the scanset is all characters except \n.
Furthermore fscanf (and scanf) will read the longest sequence of input characters matching the format.
So scanf("%[^\n]", s); will read all characters until you reach \n (or EOF) and put them in s. It is a common idiom to read a whole line in C.
See also §7.21.6.2 The fscanf function.
scanf("%[^\n]",line); is a problematic way to read a line. It is worse than gets().
C defines line as:
A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. Whether the last line requires a terminating new-line character is implementation-defined.
The scanf("%[^\n]", line) has the specifier "%[^\n]". It scans for unlimited number of characters that match the scan-set ^\n. If none are read, the specifier fails and scanf() returns with line unaltered. If at least one character is read, all matching characters are read and saved and a null character is appended.
The scan-set ^\n implies all character that are not (due to the '^') '\n'.
'\n' is not read
scanf("%[^\n]",.... fails to read a new line character '\n'. It remains in stdin. The entire line is not read.
Buffer overflow
The below leads to undefined behavior (UB) should more than 99 characters get read.
char line[100];
scanf("%[^\n]",line); // buffer overflow possible
Does nothing on empty line
When the line consists of only "\n", scanf("%[^\n]",line); returns a 0 without setting line[] - no null character is appended. This can readily lead to undefined behavior should subsequent code use an uninitialized line[]. The '\n' remains in stdin.
Failure to check the return value
scanf("%[^\n]",line); assumes input succeeded. Better code would check the scanf() return value.
Recommendation
Do not use scanf() and instead use fgets() to read a line of input.
#define EXPECTED_INPUT_LENGTH_MAX 49
char line[EXPECTED_INPUT_LENGTH_MAX + 1 + 1 + 1];
// \n + \0 + extra to detect overly long lines
if (fgets(line, sizeof line, stdin)) {
size_t len = strlen(line);
// Lop off potential trailing \n if desired.
if (len > 0 && line[len-1] == '\n') {
line[--len] = '\0';
}
if (len > EXPECTED_INPUT_LENGTH_MAX) {
// Handle error
// Usually includes reading rest of line if \n not found.
}
The fgets() approach has it limitations too. e.g. (reading embedded null characters).
Handling user input, possible hostile, is challenging.
scanf("%[^\n]",line);
means: scan till \n or an enter key.
scanf("%[^\n]",line);
Will read user input until enter is pressed or a newline character is added (\n) and store it into a variable named line.
Question: what is %[^\n] mean in C?
Basically the \n command prints the output in the next line, but in
case of C gives the Null data followed by the above problem only.
Because of that to remove the unwanted data or null data, need to add
Complement/negotiated symbol[^\n]. It gives all characters until the next line
and keeps the data in the defined expression.
Means it is the Complemented data or rewritten data from the trash
EX:
char number[100]; //defined a character ex: StackOverflow
scanf("%[^\n]",number); //defining the number without this statement, the
character number gives the unwanted stuff `���`
printf("HI\n"); //normaly use of printf statement
printf("%s",number); //printing the output
return 0;

Using strstr to determine if a given string contains a string with spaces [C]

I'm working through an example of using the strstr() function.
If I input "Pamela Sue Smith", why does the program output ""Pamela" is a sub-string!" and not ""Pamela Sue Smith" is a sub-string!".
#include <stdio.h>
#include <string.h>
void main(void)
{
char str[72];
char target[] = "Pamela Sue Smith";
printf("Enter your string: ");
scanf("%s", str);
if (strstr( target, str) != NULL )
printf(" %s is a sub-string!\n", str);
}
main does not have return-type void but int.
scanf can fail. Check the return-value.
If successful, it returns the number of parameters assigned.
%s only reads non-whitespace, until the next whitespace (thus 1 word).
%s does not limit how many non-whitespace characters are read. A buffer-overflow can be deadly.
Use %71s (buffer-size: string-length + 1 for the terminator)
You swapped the arguments to strstr.
From the manual page for scanf:
“s” — Matches a sequence of non-white-space characters; the next
pointer must be a pointer to character array that is long enough
to hold the input sequence and the terminating null byte ('\0'),
which is added automatically. The input string stops at white
space or at the maximum field width, whichever occurs first.
So, the part “Sue Smith” never makes it to str. You could use fgets which allows you to read a whole line at a time:
if (fgets(str, sizeof str, stdin) == NULL) {
printf("End of file\n");
return;
}
Note that in this case, str contains the terminating end-of-line character. You could do
if (*str != '\0')
str[strlen(str) - 1] = '\0';
to remove it.
(Also, as some others already pointed out, the “haystack” argument to strstr goes first.)

using scanf to read a string and an int separated by /

The input consists a string and an integer, which are separated by a '/', like this:
hello/17
And I want to read the input into a string and an int, like this:
char str[20];
int num;
scanf("%s/%d", str, &num); // this how I tried to do it.
I can't seem to make it, any advice?
scanf awaits a whitespace terminated string when it tries to read %s.
Try to specify the forbidden character set directly:
scanf("%19[^/]/%d", str, &num);
You can read more about the formating codes here
You only need to run the following program:
#include <stdio.h>
int main (void) {
char str[20] = {'\0'};
int count, num = 42;
count = sscanf ("hello/17", "%s/%d", str, &num);
printf ("String was '%s'\n", str);
printf ("Number was %d\n", num);
printf ("Count was %d\n", count);
return 0;
}
to see why this is happening. The output is:
String was 'hello/17'
Number was 42
Count was 1
The reason has to do with the %s format specifier. From C99 7.19.6.2 The fscanf function (largely unchanged in C11, and the italics are mine):
s: matches a sequence of non-white-space characters.
Since / is not white space, it gets included in the string bit, as does the 17 for the same reason. That's also indicated by the fact that sscanf returns 1, meaning that only one item was scanned.
What you'll then be looking for is something that scans any characters other than / into the string (including white space). The same section of the standard helps out there as well:
[: matches a nonempty sequence of characters from a set of expected characters (the scanset). The conversion specifier includes all subsequent characters in the format string, up to and including the matching right bracket (]). The characters between the brackets (the scanlist) compose the scanset, unless the character after the left bracket is a circumflex (^), in which case the scanset contains all characters that do not appear in the scanlist between the circumflex and the right bracket.
In other words, something like:
#include <stdio.h>
int main (void) {
char str[20] = {'\0'};
int count, num = 42;
count = sscanf ("hello/17", "%[^/]/%d", str, &num);
printf ("String was '%s'\n", str);
printf ("Number was %d\n", num);
printf ("Count was %d\n", count);
return 0;
}
which gives you:
String was 'hello'
Number was 17
Count was 2
One other piece of advice: never ever use scanf with an unbounded %s or %[; you're asking for a buffer overflow attack. If you want a robust user input function, see this answer.
Once you have it in as a string, you can sscanf it to your heart's content without worrying about buffer overflow (since you've limited the size on input).
Could be like that:
char str[20];
int num;
scanf("%19[^/]%*c%d", str, &num);
%*c reads one character and discards it

Resources