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 using kmdb.

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.