Reset lost Solaris root password using kmdb
I have seen many articles written on recovering the Solaris root password. Where numerous articles in SunSolve or simply just dotted about the web. I've even written the procedures for various company runbooks. But all either require local media (cd/dvd) or a jumpstart server in order to achieve the end results.
<>In this article we show we can achieve the same results simply by booting the server into single-user usingkmdb
.
Note: In this example we are using a Solaris 10 system with the root on a UFS file system.
Firstly, we need to boot the server into single-user mode using kmdb:
ok> boot kmdb -s Resetting ... Sun Serverblade1 (UltraSPARC-IIe 650MHz), No Keyboard Copyright 1998-2003 Sun Microsystems, Inc. All rights reserved. OpenBoot 4.11.3, 4096 MB memory installed, Serial #16683964. Ethernet address 8:0:20:4d:3:74, Host ID: 804d0374. Rebooting with command: boot kmdb -s Boot device: /pci@1f,0/ide@d/disk@0,0:a File and args: kmdb -s Loading kmdb... SunOS Release 5.10 Version Generic_141444-09 64-bit Copyright 1983-2009 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. WARNING: todblade: kernel debugger detected: hardware watchdog disabled Booting to milestone "milestone/single-user:default". Hostname: solarisbox
As soon as the hostname is displayed on the console, Sent a break and dropped into kmdb:
cli>break -y s3 se: Break sent. cli>console -f s3 [Connected with input enabled on fru S3] Escape Sequence is '#.'
At this point we add a break point at exec_common
to trace the exec of sulogin process:
[0]> exec_common+0x16c:b [0]> :c kmdb: stop at exec_common+0x16c kmdb: target stopped at: exec_common+0x16c: orcc %g0, %o0, %i3 [0]> $C 000002a100728ff1 exec_common+0x16c(40e0c, 0, cdf18, 1813070, 0, 0) 000002a100729231 exece+0x10(40e0c, fd87bdf0, cdf18, b9fa8, 1010101, 80808080) 000002a1007292e1 syscall_trap32+0xcc(40e0c, fd87bdf0, cdf18, b9fa8, 1010101, 80808080) [0]> 000002a100729231+0x7c7::print struct pathname { pn_buf = 0x30002b20080 "/sbin/sh" pn_path = 0x30002b20080 "/sbin/sh" pn_pathlen = 0x8 pn_bufsize = 0x400 } [0]>
In the above output, this isn't the process we are looking for. We use the break point at exec_common+0x16c
because, the pathname passed in from userland is pulled into kernel land by pn_get
at exec_common+0x16c
If we step through exec_common+0x16c
until we see the "Requesting System Maintenance Mode" message to be displayed on the console (this is displayed then the sulogin
process is created):
[0]> :c Requesting System Maikmdb: stop at exec_common+0x16c kmdb: target stopped at: exec_common+0x16c: orcc %g0, %o0, %i3
Part of "Requesting System Maintenance Mode" message is displayed, this should be the process of our interest.
[0]> $C 000002a100758ff1 exec_common+0x16c(38f3c, 0, ffbfff18, 1813070, 0, 0) 000002a100759231 exece+0x10(38f3c, fd07be58, ffbfff18, 7cf28, 0, ff2303a8) 000002a1007592e1 syscall_trap32+0xcc(38f3c, fd07be58, ffbfff18, 7cf28, 0, ff2303a8) [0]> 000002a100759231+0x7c7::print struct pathname { pn_buf = 0x30002b1e480 "/sbin/sulogin" pn_path = 0x30002b1e480 "/sbin/sulogin" pn_pathlen = 0xd pn_bufsize = 0x400 }
Yes! This is the process we are loooking for. sulogin
will later read in /etc/shadow
for the root entry.
At this point, it is easier to track all UFS file system reads function from here-on-end and to look out for /etc/shadow
file:
[0]> ufs_read+8:b [0]> :c ntenkmdb: stop at ufs`ufs_read+8 kmdb: target stopped at: ufs`ufs_read+8: clr %l0
At this point, we step through uninteresting files until sulogin
opens the /etc/shadow file:
[0]> $c ufs`ufs_read+8(30003c2ca00, 2a100759a10, 0, 300004d1d98, 0, ff3f6ae8) fop_read+0x20(30003c2ca00, 2a100759a10, 0, 300004d1d98, 0, 135b98c) read+0x274(101, 0, 30001d3da48, 400, 2001, 0) syscall_trap32+0xcc(101, 261a0, 400, 0, ff3f4910, 821) [0]> 30003c2ca00::print vnode_t v_path v_path = 0x30001864908 "/etc/shadow" [0]> ::pgrep sulogin S PID PPID PGID SID UID FLAGS ADDR NAME R 139 7 139 139 0 0x4a004000 00000300002df858 sulogin [0]> 00000300002df858::pfiles FD TYPE VNODE INFO 0 CHR 000003000280edc0 /devices/pseudo/cn@0:console 1 CHR 000003000280edc0 /devices/pseudo/cn@0:console 2 CHR 000003000280edc0 /devices/pseudo/cn@0:console 3 CHR 0000030003c2d200 /devices/pseudo/sysmsg@0:sysmsg 256 REG 0000030003c2ca00 /etc/shadow 257 REG 0000030003c2ca00 /etc/shadow
I then put a break point at uiomove+8. Once the data is read into memory, UFS uses uiomove to copy read in data from kernel land to user land.
[0]> uiomove+8:b [0]> :c kmdb: stop at uiomove+8 kmdb: target stopped at: uiomove+8: be,pn %xcc, +0x13c <uiomove+0x144> [0]> $c uiomove+8(fffffa003a6e0000, 15f, 0, 2a100759a10, fffffa003a6e0000, 15f) vpm_data_copy+0x100(30003c2ca00, 0, 15f, 2a100759a10, 109a358, 15f) ufs`rdip+0x468(0, 1fff, ffffffffffffe000, 186a060, 30003c6b720, 18ffd58) ufs`ufs_read+0x208(30003c6b800, 2a100759a10, 0, 300004d1d98, 30003c6b800, 300000a1a80) fop_read+0x20(30003c2ca00, 2a100759a10, 0, 300004d1d98, 0, 135b98c) read+0x274(101, 0, 30001d3da48, 400, 2001, 0) syscall_trap32+0xcc(101, 261a0, 400, 0, ff3f4910, 821) [0]> fffffa003a6e0000 \s 0xfffffa003a6e0000: root:wqXY4dQaZCTfs:6445:::::: daemon:NP:6445:::::: bin:NP:6445:::::: sys:NP:6445:::::: adm:NP:6445:::::: lp:NP:6445:::::: uucp:NP:6445:::::: nuucp:NP:6445:::::: smmsp:NP:6445:::::: listen:*LK*::::::: gdm:*LK*::::::: webservd:*LK*::::::: postgres:NP::::::: svctag:*LK*:6445:::::: nobody:*LK*:6445:::::: noaccess:*LK*:6445:::::: nobody4:*LK*:6445:::::: [0]>
Here, the process is to modify read in data in memory and have the encrypted password for root removed:
[0]> fffffa003a6e0005 \v 3a 0xfffffa003a6e0005: 0x77 = 0x3a [0]> fffffa003a6e0006 \v 36 0xfffffa003a6e0006: 0x71 = 0x36 [0]> fffffa003a6e0007 \v 34 0xfffffa003a6e0007: 0x58 = 0x34 [0]> fffffa003a6e0008 \v 34 0xfffffa003a6e0008: 0x59 = 0x34 [0]> fffffa003a6e0009 \v 35 0xfffffa003a6e0009: 0x34 = 0x35 [0]> fffffa003a6e000a \v 3a 0xfffffa003a6e000a: 0x64 = 0x3a [0]> fffffa003a6e000b \v 3a 0xfffffa003a6e000b: 0x51 = 0x3a [0]> fffffa003a6e000c \v 3a 0xfffffa003a6e000c: 0x61 = 0x3a [0]> fffffa003a6e000d \v 3a 0xfffffa003a6e000d: 0x5a = 0x3a [0]> fffffa003a6e000e \v 3a 0xfffffa003a6e000e: 0x43 = 0x3a [0]> fffffa003a6e000f \v 3a 0xfffffa003a6e000f: 0x54 = 0x3a [0]> fffffa003a6e0010 \v d 0xfffffa003a6e0010: 0x66 = 0xd [0]> fffffa003a6e0000 \s s:14761:::::: root::6445:::::: daemon:NP:6445:::::: bin:NP:6445:::::: sys:NP:6445:::::: adm:NP:6445:::::: lp:NP:6445:::::: uucp:NP:6445:::::: nuucp:NP:6445:::::: smmsp:NP:6445:::::: listen:*LK*::::::: gdm:*LK*::::::: webservd:*LK*::::::: postgres:NP::::::: svctag:*LK*:6445:::::: nobody:*LK*:6445:::::: noaccess:*LK*:6445:::::: nobody4:*LK*:6445:::::: [0]>
Once completed, remove all break points and continue the boot process. Now the root entry without password is copied from kernel land to user land:
[0]> :z [0]> :c Root password for system maintenance (control-d to bypass): single-user privilege assigned to /dev/console. Entering System Maintenance Mode ? <root>#
After logging into the system, opening /etc/shadow
in vi shows:
<snip> root::6445::::::^Ms:14761:::::: daemon:NP:6445:::::: bin:NP:6445:::::: <snip>
Edit the first line and remove ^Ms:14761::::::
Rebooting the server now will not prompt for root password until a new password is set.