Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect prototype of the clone syscall in pwnlib.shellcraft #2283

Open
elay108 opened this issue Sep 29, 2023 · 2 comments
Open

Incorrect prototype of the clone syscall in pwnlib.shellcraft #2283

elay108 opened this issue Sep 29, 2023 · 2 comments

Comments

@elay108
Copy link

elay108 commented Sep 29, 2023

Hey,
I noticed that the pwnlib.shellcraft.clone() implementation has the prototype of the glibc wrapper function, and invokes the syscall by this prototype, but should be calling the syscall by its raw prototype.

The clone() system call glibc wrapper function and the actual raw system call have different function prototypes, as mentioned in the man page:

/* Prototype for the glibc wrapper function */

       #define _GNU_SOURCE
       #include <sched.h>

       int clone(int (*fn)(void *_Nullable), void *stack, int flags,
                 void *_Nullable arg, ...  /* pid_t *_Nullable parent_tid,
                                              void *_Nullable tls,
                                              pid_t *_Nullable child_tid */ );
...
C library/kernel differences
       The raw clone() system call corresponds more closely to fork(2)
       in that execution in the child continues from the point of the
       call.  As such, the fn and arg arguments of the clone() wrapper
       function are omitted.
...
The raw system call interface on x86-64 and some other
       architectures (including sh, tile, and alpha) is:

           long clone(unsigned long flags, void *stack,
                      int *parent_tid, int *child_tid,
                      unsigned long tls);

       On x86-32, and several other common architectures (including
       score, ARM, ARM 64, PA-RISC, arc, Power PC, xtensa, and MIPS),
       the order of the last two arguments is reversed:

           long clone(unsigned long flags, void *stack,
                     int *parent_tid, unsigned long tls,
                     int *child_tid);
...

From the documentation of pwnlib.shellcraft.clone():

Signature:
pwnlib.shellcraft.clone(
    fn=0,
    child_stack=0,
    flags=0,
    arg=0,
    vararg_0=None,
    vararg_1=None,
    vararg_2=None,
    vararg_3=None,
    vararg_4=None,
)

Simple code snippet and output:

In [15]: print(pwnlib.shellcraft.clone(1,2,3,4))
    /* clone(fn=1, child_stack=2, flags=3, arg=4) */
    mov  x0, #1
    mov  x1, #2
    mov  x2, #3
    mov  x3, #4
    /* call clone() */
    mov  x8, #SYS_clone
    svc 0

As you can see, in the above case (aarch64), the first argument passed is the fn pointer, when the syscall expects the flags argument. In the same way, the flags argument is passed as the parent_tid, and the fn argument is not even expected by the syscall.
Possible solutions would be to either implement the glibc wrapper function logic, or to edit the function prototype to match the raw syscall, per architecture.

@peace-maker
Copy link
Member

This is tricky to fix because the syscalls templates are generated once and shared between all architectures using a symlink. We could get rid of the symlinks and look into the templates/common/linux directory in the pwnlib.shellcraft module when looking up syscalls. Then we could only add special versions of syscalls to the archticture specific paths and give them precedence over the common ones.

@Arusekk
Copy link
Member

Arusekk commented Jan 2, 2024

This is exactly the problem, we generate the prototypes right from the man pages; if you have a solution, or even a prototype of a solution, feel free to submit a PR with it, or please share your ideas; this would really help shellcoding environments where you need clone().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants