TheWover / donut

Generates x86, x64, or AMD64+x86 position-independent shellcode that loads .NET Assemblies, PE files, and other Windows payloads from memory and runs them with parameters
BSD 3-Clause "New" or "Revised" License
3.61k stars 638 forks source link

Cast `Donut_Create` to `PyCFunction` #158

Closed jsf9k closed 4 weeks ago

jsf9k commented 2 months ago

When rebuilding an AMI using the latest Kali base AMI, I found that the donut Python extension failed to build with the following output:

❯ pip install .
Processing /home/jeremy_frasier/TheWover/donut
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
Building wheels for collected packages: donut-shellcode
  Building wheel for donut-shellcode (pyproject.toml) ... error
  error: subprocess-exited-with-error

  × Building wheel for donut-shellcode (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> [46 lines of output]
      running bdist_wheel
      running build
      running build_ext
      building 'donut' extension
      gcc -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -fPIC -Iinclude -I/home/jeremy_frasier/.pyenv/versions/3.12.6/envs/donut/include -I/home/jeremy_frasier/.pyenv/versions/3.12.6/include/python3.12 -c donut.c -o build/temp.linux-x86_64-cpython-312/donut.o
      donut.c: In function ‘read_file_info’:
      donut.c:574:19: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
        574 |           if (ofs != -1) {
            |                   ^~
      donut.c:579:22: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
        579 |               if(ofs != -1) {
            |                      ^~
      donut.c: In function ‘gen_random_string’:
      donut.c:667:15: warning: comparison of integer expressions of different signedness: ‘int’ and ‘uint64_t’ {aka ‘long unsigned int’} [-Wsign-compare]
        667 |     for(i=0; i<len; i++) {
            |               ^
      donut.c: In function ‘is_dll_export’:
      donut.c:1487:16: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
       1487 |         if(ofs != -1) {
            |                ^~
      In function ‘build_module’,
          inlined from ‘DonutCreate’ at donut.c:1596:17:
      donut.c:768:7: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
        768 |       strncpy(mod->method, c->method, DONUT_MAX_NAME-1);
            |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      donut.c:746:9: warning: ‘strncpy’ output may be truncated copying 8 bytes from a string of length 255 [-Wstringop-truncation]
        746 |         strncpy(mod->domain, c->domain, DONUT_DOMAIN_LEN);
            |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      donut.c:753:9: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
        753 |         strncpy(mod->cls, c->cls, DONUT_MAX_NAME-1);
            |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      donut.c:756:9: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
        756 |         strncpy(mod->method, c->method, DONUT_MAX_NAME-1);
            |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      donut.c:763:7: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
        763 |       strncpy(mod->runtime, c->runtime, DONUT_MAX_NAME-1);
            |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      donut.c:792:7: warning: ‘strncat’ output may be truncated copying 250 bytes from a string of length 255 [-Wstringop-truncation]
        792 |       strncat(mod->args, c->args, DONUT_MAX_NAME-6);
            |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      gcc -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -fPIC -Iinclude -I/home/jeremy_frasier/.pyenv/versions/3.12.6/envs/donut/include -I/home/jeremy_frasier/.pyenv/versions/3.12.6/include/python3.12 -c donutmodule.c -o build/temp.linux-x86_64-cpython-312/donutmodule.o
      donutmodule.c:199:9: error: initialization of ‘PyObject * (*)(PyObject *, PyObject *)’ {aka ‘struct _object * (*)(struct _object *, struct _object *)’} from incompatible pointer type ‘PyObject * (*)(PyObject *, PyObject *, PyObject *)’ {aka ‘struct _object * (*)(struct _object *, struct _object *, struct _object *)’} [-Wincompatible-pointer-types]
        199 |         Donut_Create, // C wrapper function
            |         ^~~~~~~~~~~~
      donutmodule.c:199:9: note: (near initialization for ‘Donut_FunctionsTable[0].ml_meth’)
      error: command '/usr/bin/gcc' failed with exit code 1
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for donut-shellcode
Failed to build donut-shellcode
ERROR: ERROR: Failed to build installable wheels for some pyproject.toml based projects (donut-shellcode)

Digging into the code, I found the reason to be that the ml_meth member of the PyMethodDef struct must be of type PyCFunction, even if it is secretly of type PyCFunctionWithKeywords, PyCMethod, etc. This is why the cast I added is necessary; gcc errors out without it - at least with the 14.2.0 version of gcc that is currently offered on Kali.

I confirmed that adding the cast made the Python extension build again.

jsf9k commented 2 months ago

Note that the change in this PR also fixes, e.g., RedSiege/EXCELntDonut which has donut-shellcode as a dependency. This tool cannot currently be installed on Kali Linux as seen here.

jsf9k commented 1 month ago

Any hope of getting some movement on this one-line change without which the donut Python extension does not build? What can I do to help?

D3vil0p3r commented 1 month ago

Getting the same error on PowerShell Empire that uses donut as dependency. @TheWover can you please give a look to this PR?

It affects Python 3.12.

D3vil0p3r commented 4 weeks ago

@TheWover thank you for merging it. Could you please create a new release of it on GitHub and PyPI (https://pypi.org/project/donut-shellcode/)?