Im trying to write a simple program that hooks into another binary (setuid binary, child process run it) using ptrace(), opens a 'flag' file and prints its content. The hook works, and i can control the binary using ptrace commands. I've executed syscall open(), and got a legitimate file descriptor(4).
My problem is that syscall read() fails, and returns a negative value of bytes, -21. errno is set to 0 (success). The machine is remote (32-bits), and i cannot install libexplain library there. I've searched the web ALOT, yet found no answers regarding the meaning of negative return values of read() syscall (according to the manual page, its only error value is -1..).
parent's code:
int run_syscall(pid_t pid, int syscall_number, int first_paramter, int second_parameter, int third_paramter) {
struct user_regs_struct regs = { 0 };
ptrace(PTRACE_GETREGS, pid, 0, ®s);
regs.eip = INT80;
regs.eax = syscall_number;
regs.ebx = first_paramter;
regs.ecx = second_parameter;
regs.edx = third_paramter;
ptrace(PTRACE_SETREGS, pid, 0, ®s);
ptrace(PTRACE_SINGLESTEP, pid, 0, 0);}
// eax = 5 (open), ebx = 'flag' path, ecx = 0, edx = 0
run_syscall(pid, 5, 0x8048200, 0 , 0);
wait(NULL);
ptrace(PTRACE_GETREGS, pid, 0, ®s);
int flag_fd = regs.eax; // success, returns fd = 4
printf("Open syscall worked, flag fd: %d\n", flag_fd);
printf("[*]Syscall was made..\nRegisters values:\nEAX:%08x\nORIG_EAX:%08x\nESP:%08x\nEBP:%08x\nEIP:%08x, should be 0xf7742b59\n\n\n\n\n\n", regs.eax, regs.orig_eax, regs.esp, regs.ebp, regs.eip);
long flag_addr = ptrace(PTRACE_PEEKTEXT, pid, regs.esp + 0x1000 - 8, 0);
long flag = ptrace(PTRACE_PEEKTEXT, pid, flag_addr, 0);
printf("Before read() text: %p\n", flag);
// eax = 3 (read), ebx = opened filedescriptor, ecx = address i want to store the flag content, edx = 1 byte to read
run_syscall(pid, 3, flag_fd, flag_addr, 1); // Read
wait(NULL);
ptrace(PTRACE_GETREGS, pid, 0, ®s);
printf("read %d bytes\n", regs.eax); // regs.eax = -21
printf("errno: %d\n", errno); // errno = 0
printf("Description: %s\n", strerror(errno));
flag = ptrace(PTRACE_PEEKTEXT, pid, flag_addr, 0);
printf("After read() text: %p\n", flag); // remains the same
i made sure that the destination address (flag_addr) is located in writeable memory area (the stack).
I've also tried different sizes to read (edx), in order to check if something was wrong with the alignment. Unfortunately the bug remains, and read() always returns with -21 (even if edx set to 0).
I will be very glad if someone knows what status the return value of -21 means, and how can i fix my code..
thank you.
EDIT: solved. The problem was that the file was named 'flag.txt', but it was a directory! what a troll
While read(2)
can't return -21
, the read system call can. In other words, you are overlooking a layer between the kernel and your program; libc. Have a look at these snippets from musl libc:
#define syscall_cp(...) __syscall_ret(__syscall_cp(__VA_ARGS__))
long __syscall_ret(unsigned long r)
{
if (r > -4096UL) {
errno = -r;
return -1;
}
return r;
}
ssize_t read(int fd, void *buf, size_t count)
{
return syscall_cp(SYS_read, fd, buf, count);
}
Most likely this means that the syscall returns the error code 21
, likely meaning EISDIR 21 Is a directory