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.49k stars 622 forks source link

x64 .NET EXE not working #73

Closed physics-sec closed 1 year ago

physics-sec commented 3 years ago

Since this commit: https://github.com/TheWover/donut/commit/0c1d3d09e73a25c65ca7381385a9570c4994239c Donut is not working properly when it comes to a x64 .NET EXE.

previous to that commit, is working fine (except the python module, which got fixed in a later commit here: https://github.com/TheWover/donut/commit/e71cde14a7e0c715eea047eb8c8f58d87777a619)

Hope this helps fixing the issue, Regards.

TheWover commented 3 years ago

Thanks for the Issue. Do you have some code we could use to reproduce this error you are receiving?

physics-sec commented 3 years ago

Sure thing, I just created a dummy program to be injected:

using System;
using System.IO;
using System.Text;

namespace Program
{
    class Program
    {
        public static void Main(string[] args)
        {
            string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\newfile.txt";
            using (FileStream fs = File.Create(path))
            {
                byte[] info = new UTF8Encoding(true).GetBytes("some text");
                fs.Write(info, 0, info.Length);
            }
        }
    }
}

Compiled it with: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /nologo /warn:0 /platform:x64 /t:exe .\createfile.cs

Transformed the .NET assembly into shellcode with: ./donut -a2 createfile.exe (compiled donut with make, in a linux machine, if you use the python module, you will get the same result)

When I inject the resulting loader.bin into a remote process (sublime text) with the simple Alloc, Write, CreateRemoteThread technique (I can provide the injector if you like, is just a little long for a comment) the process dies instantly.

The same thing but with a: git checkout a260ea09f0149f4c9ca1a63744c3f977a2d0947f works perfectly and the file is created both the donut binary and the python module (the python module has a small issue with the "sys" import, easy to fix with: sed -i 's/, sys/\nimport sys/' setup.py)

Note that the following commit to that one, changed the LOADER_EXE_X64 shellcode, probably some instruction is bogus or something.

Let me know if you want the injector and if you need any more information. Thank you for this amazing tool! 😃

TheWover commented 3 years ago

Which branch of donut is this from? master or dev?

physics-sec commented 3 years ago

master

TheWover commented 3 years ago

Did you end up resolving this? If not, could you try it with what's in dev now?

physics-sec commented 3 years ago

Sure thing, I will try it when I find a little time and let you know

physics-sec commented 3 years ago

Still doesn't work, (I also re-tested the old commit referenced before to make sure I wasn't making a mistake while testing and it works fine) plus, if I try to install the python module, I get this error:

Building wheels for collected packages: donut-shellcode
  Building wheel for donut-shellcode (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-req-build-m2wearij/setup.py'"'"'; __file__='"'"'/tmp/pip-req-build-m2wearij/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-e3grnimx
       cwd: /tmp/pip-req-build-m2wearij/
  Complete output (59 lines):
  running bdist_wheel
  running build
  running build_ext
  building 'donut' extension
  creating build
  creating build/temp.linux-x86_64-3.9
  creating build/temp.linux-x86_64-3.9/loader
  gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -Iinclude -I/usr/include/python3.9 -c donut.c -o build/temp.linux-x86_64-3.9/donut.o
  donut.c: In function ‘read_file_info’:
  donut.c:573:19: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
    573 |           if (ofs != -1) {
        |                   ^~
  donut.c:578:22: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
    578 |               if(ofs != -1) {
        |                      ^~
  donut.c: In function ‘gen_random_string’:
  donut.c:674:15: warning: comparison of integer expressions of different signedness: ‘int’ and ‘uint64_t’ {aka ‘long unsigned int’} [-Wsign-compare]
    674 |     for(i=0; i<len; i++) {
        |               ^
  donut.c: In function ‘is_dll_export’:
  donut.c:1444:16: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
   1444 |         if(ofs != -1) {
        |                ^~
  In function ‘build_module’,
      inlined from ‘DonutCreate’ at donut.c:1553:17:
  donut.c:775:7: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
    775 |       strncpy(mod->method, c->method, DONUT_MAX_NAME-1);
        |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  donut.c:753:9: warning: ‘strncpy’ output may be truncated copying 8 bytes from a string of length 255 [-Wstringop-truncation]
    753 |         strncpy(mod->domain, c->domain, DONUT_DOMAIN_LEN);
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  donut.c:760:9: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
    760 |         strncpy(mod->cls, c->cls, DONUT_MAX_NAME-1);
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  donut.c:763:9: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
    763 |         strncpy(mod->method, c->method, DONUT_MAX_NAME-1);
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  donut.c:770:7: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
    770 |       strncpy(mod->runtime, c->runtime, DONUT_MAX_NAME-1);
        |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  donut.c:799:7: warning: ‘strncat’ output may be truncated copying 250 bytes from a string of length 255 [-Wstringop-truncation]
    799 |       strncat(mod->args, c->args, DONUT_MAX_NAME-6);
        |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -Iinclude -I/usr/include/python3.9 -c donutmodule.c -o build/temp.linux-x86_64-3.9/donutmodule.o
  donutmodule.c: In function ‘Donut_Create’:
  donutmodule.c:135:16: error: ‘DONUT_CONFIG’ {aka ‘struct _DONUT_CONFIG’} has no member named ‘param’
    135 |       strncpy(c.param, params, DONUT_MAX_NAME - 1);
        |                ^
  donutmodule.c:169:15: error: ‘DONUT_ERROR_SUCCESS’ undeclared (first use in this function); did you mean ‘DONUT_ERROR_FILE_ACCESS’?
    169 |     if(err != DONUT_ERROR_SUCCESS) {
        |               ^~~~~~~~~~~~~~~~~~~
        |               DONUT_ERROR_FILE_ACCESS
  donutmodule.c:169:15: note: each undeclared identifier is reported only once for each function it appears in
  donutmodule.c: At top level:
  donutmodule.c:186:9: warning: 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]
    186 |         Donut_Create, // C wrapper function
        |         ^~~~~~~~~~~~
  donutmodule.c:186:9: note: (near initialization for ‘Donut_FunctionsTable[0].ml_meth’)
  error: command '/usr/bin/gcc' failed with exit code 1
  ----------------------------------------
  ERROR: Failed building wheel for donut-shellcode
  Running setup.py clean for donut-shellcode
Failed to build donut-shellcode
Installing collected packages: donut-shellcode
  Attempting uninstall: donut-shellcode
    Found existing installation: donut-shellcode 0.9.3
    Uninstalling donut-shellcode-0.9.3:
      Successfully uninstalled donut-shellcode-0.9.3
    Running setup.py install for donut-shellcode ... error
    ERROR: Command errored out with exit status 1:
     command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-req-build-m2wearij/setup.py'"'"'; __file__='"'"'/tmp/pip-req-build-m2wearij/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-jobz3lsw/install-record.txt --single-version-externally-managed --user --prefix= --compile --install-headers /home/someuser/.local/include/python3.9/donut-shellcode
         cwd: /tmp/pip-req-build-m2wearij/
    Complete output (59 lines):
    running install
    running build
    running build_ext
    building 'donut' extension
    creating build
    creating build/temp.linux-x86_64-3.9
    creating build/temp.linux-x86_64-3.9/loader
    gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -Iinclude -I/usr/include/python3.9 -c donut.c -o build/temp.linux-x86_64-3.9/donut.o
    donut.c: In function ‘read_file_info’:
    donut.c:573:19: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
      573 |           if (ofs != -1) {
          |                   ^~
    donut.c:578:22: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
      578 |               if(ofs != -1) {
          |                      ^~
    donut.c: In function ‘gen_random_string’:
    donut.c:674:15: warning: comparison of integer expressions of different signedness: ‘int’ and ‘uint64_t’ {aka ‘long unsigned int’} [-Wsign-compare]
      674 |     for(i=0; i<len; i++) {
          |               ^
    donut.c: In function ‘is_dll_export’:
    donut.c:1444:16: warning: comparison of integer expressions of different signedness: ‘ULONG64’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
     1444 |         if(ofs != -1) {
          |                ^~
    In function ‘build_module’,
        inlined from ‘DonutCreate’ at donut.c:1553:17:
    donut.c:775:7: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
      775 |       strncpy(mod->method, c->method, DONUT_MAX_NAME-1);
          |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    donut.c:753:9: warning: ‘strncpy’ output may be truncated copying 8 bytes from a string of length 255 [-Wstringop-truncation]
      753 |         strncpy(mod->domain, c->domain, DONUT_DOMAIN_LEN);
          |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    donut.c:760:9: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
      760 |         strncpy(mod->cls, c->cls, DONUT_MAX_NAME-1);
          |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    donut.c:763:9: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
      763 |         strncpy(mod->method, c->method, DONUT_MAX_NAME-1);
          |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    donut.c:770:7: warning: ‘strncpy’ output may be truncated copying 255 bytes from a string of length 255 [-Wstringop-truncation]
      770 |       strncpy(mod->runtime, c->runtime, DONUT_MAX_NAME-1);
          |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    donut.c:799:7: warning: ‘strncat’ output may be truncated copying 250 bytes from a string of length 255 [-Wstringop-truncation]
      799 |       strncat(mod->args, c->args, DONUT_MAX_NAME-6);
          |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -Iinclude -I/usr/include/python3.9 -c donutmodule.c -o build/temp.linux-x86_64-3.9/donutmodule.o
    donutmodule.c: In function ‘Donut_Create’:
    donutmodule.c:135:16: error: ‘DONUT_CONFIG’ {aka ‘struct _DONUT_CONFIG’} has no member named ‘param’
      135 |       strncpy(c.param, params, DONUT_MAX_NAME - 1);
          |                ^
    donutmodule.c:169:15: error: ‘DONUT_ERROR_SUCCESS’ undeclared (first use in this function); did you mean ‘DONUT_ERROR_FILE_ACCESS’?
      169 |     if(err != DONUT_ERROR_SUCCESS) {
          |               ^~~~~~~~~~~~~~~~~~~
          |               DONUT_ERROR_FILE_ACCESS
    donutmodule.c:169:15: note: each undeclared identifier is reported only once for each function it appears in
    donutmodule.c: At top level:
    donutmodule.c:186:9: warning: 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]
      186 |         Donut_Create, // C wrapper function
          |         ^~~~~~~~~~~~
    donutmodule.c:186:9: note: (near initialization for ‘Donut_FunctionsTable[0].ml_meth’)
    error: command '/usr/bin/gcc' failed with exit code 1
    ----------------------------------------
  Rolling back uninstall of donut-shellcode
  Moving to /home/someuser/.local/lib/python3.9/site-packages/donut.cpython-39-x86_64-linux-gnu.so
   from /tmp/pip-uninstall-4a201t17/donut.cpython-39-x86_64-linux-gnu.so
  Moving to /home/someuser/.local/lib/python3.9/site-packages/donut_shellcode-0.9.3.dist-info/
   from /home/someuser/.local/lib/python3.9/site-packages/~onut_shellcode-0.9.3.dist-info
ERROR: Command errored out with exit status 1: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-req-build-m2wearij/setup.py'"'"'; __file__='"'"'/tmp/pip-req-build-m2wearij/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-jobz3lsw/install-record.txt --single-version-externally-managed --user --prefix= --compile --install-headers /home/someuser/.local/include/python3.9/donut-shellcode Check the logs for full command output.

I used the donut binary for generating the shellcode with:

$ ./donut -a2 --input:dummy.exe

I used dev as you requested.

TheWover commented 3 years ago

Thanks! I'll look into this for the next version.

physics-sec commented 3 years ago

Awesome, let me know how I can help.