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

Wrong assembly generated by shellcraft.execve('ecx', [], 0) #1193

Closed
david942j opened this issue Sep 6, 2018 · 1 comment
Closed

Wrong assembly generated by shellcraft.execve('ecx', [], 0) #1193

david942j opened this issue Sep 6, 2018 · 1 comment
Assignees

Comments

@david942j
Copy link
Contributor

Output:

    /* execve(path='ecx', argv=[], envp=0) */
    /* push argument array [] */
    /* push '\x00' */
    push 1
    dec byte ptr [esp]
    xor ecx, ecx
    push ecx /* null terminate */
    mov ecx, esp

    mov ebx, ecx // <- ecx has be polluted
    xor edx, edx
    /* call execve() */
    push SYS_execve /* 0xb */
    pop eax
    int 0x80

Because shellcraft.execve('ecx', [], 0) will call push_array first, which modifies ecx.

Same situation occurs when architecture is amd64, which pollutes rsi during pushing the empty array.

@zachriggle
Copy link
Member

This is a bug, but I don't think it's something that can be easily fixed (and right now we lack sufficient resources to handle e.g. #1189 in a timely manner).

For this to work the way you intend (i.e., have the register value of ecx as the first syscall argument), we'd have to encode a bunch of register dependency logic into the pushstr and pushstr_array. There's not a nice way to do it.

Generally, pwntools tries to just "be magic" and have things work the way you'd expect. This one unfortunately isn't one of those cases, and you have to do a little bit of work.

I'd recommend moving the value manually to ebx, and invoking with execve('ebx', ...).

>>> print shellcraft.mov('ebx', 'ecx') + shellcraft.execve('ebx', [], {})
    mov ebx, ecx
    /* execve(path='ebx', argv=[], envp={}) */
    /* push argument array [] */
    /* push '\x00' */
    push 1
    dec byte ptr [esp]
    xor edx, edx
    push edx /* null terminate */
    mov edx, esp
    /* push argument array [] */
    /* push '\x00' */
    push 1
    dec byte ptr [esp]
    xor ecx, ecx
    push ecx /* null terminate */
    mov ecx, esp
    /* setregs noop */
    /* call execve() */
    push SYS_execve /* 0xb */
    pop eax
    int 0x80

Alternately, you can specify argv and envp such that the offending code is not emitted.

>>> print shellcraft.execve('ecx', 0, 0)
    /* execve(path='ecx', argv=0, envp=0) */
    mov ebx, ecx
    xor ecx, ecx
    xor edx, edx
    /* call execve() */
    push SYS_execve /* 0xb */
    pop eax
    int 0x80

Finally, you can always invoke the syscall manually:

>>> print shellcraft.syscall(constants.SYS_execve, 'ecx', 0, 0)
    /* call execve('ecx', 0, 0) */
    push SYS_execve /* 0xb */
    pop eax
    mov ebx, ecx
    xor ecx, ecx
    cdq /* edx=0 */
    int 0x80

Ultimately this is a bug, but not one that we can fix without major rework, as i386 has a very limited number of registers, and ecx is a volatile register in most ABIs. Hopefully the workarounds I suggested will help.

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

2 participants