Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: Support for managing certificate authorities #44

Open
baltpeter opened this issue Mar 9, 2023 · 40 comments
Open

iOS: Support for managing certificate authorities #44

baltpeter opened this issue Mar 9, 2023 · 40 comments
Assignees
Labels
feature New feature or request research

Comments

@baltpeter
Copy link
Member

We already have that on Android (tweaselORG/meta#18 (comment)), now we also need to be able to add and remove root CAs on iOS.

@baltpeter baltpeter added feature New feature or request research labels Mar 9, 2023
@baltpeter
Copy link
Member Author

I've documented the manual install process here: tweaselORG/meta#20 (comment)

@baltpeter
Copy link
Member Author

baltpeter commented Mar 9, 2023

So, let's see how we can automate that. I would prefer to find a method that doesn't require Frida, but we'll see.

My first approach: How does macOS store the CAs? Maybe it's similar for iOS.

According to https://stackoverflow.com/a/24694579, macOS doesn't have a CA bundle like Linux.

https://support.apple.com/en-us/HT208127 mentions a file /System/Library/Security/Certificates.bundle/Contents/Resources/TrustStore.html. The folder for Certificates.bundle also exists on the iPhone:

root# ls -lh /System/Library/Security/Certificates.bundle
total 6.6M
-rw-r--r-- 1 root wheel  764 Sep  3  2022 AnalyticsSamplingRates.plist
-rw-r--r-- 1 root wheel  499 Sep  3  2022 AppleCertificateAuthorities.plist
-rw-r--r-- 1 root wheel  103 Sep  3  2022 AssetVersion.plist
-rw-r--r-- 1 root wheel  267 Sep  3  2022 Blocked.plist
-rw-r--r-- 1 root wheel  18K Sep  3  2022 CertificatePinning.plist
-rw-r--r-- 1 root wheel 5.5K Sep  3  2022 EVRoots.plist
-rw-r--r-- 1 root wheel  796 Sep  3  2022 Info.plist
-rw-r--r-- 1 root wheel  66K Sep  3  2022 TrustStore.html
-rw-r--r-- 1 root wheel 3.8K Sep  3  2022 TrustedCTLogs.plist
-rw-r--r-- 1 root wheel 3.8K Sep  3  2022 TrustedCTLogs_nonTLS.plist
-rw-r--r-- 1 root wheel   86 Sep  3  2022 ValidUpdate.plist
drwxr-xr-x 6 root wheel  192 Sep  3  2022 _CodeSignature/
-rw-r--r-- 1 root wheel 3.9K Sep  3  2022 certsIndex.data
-rw-r--r-- 1 root wheel 184K Sep  3  2022 certsTable.data
-rw-r--r-- 1 root wheel  362 Sep  3  2022 manifest.data
-rw-r--r-- 1 root wheel 6.3M Sep  3  2022 valid.sqlite3

@baltpeter
Copy link
Member Author

I also checked how you would add a CA on macOS using the command line.

There is a security command for that (https://derflounder.wordpress.com/2011/03/13/adding-new-trusted-root-certificates-to-system-keychain/, https://apple.stackexchange.com/questions/80623/import-certificates-into-the-system-keychain-via-the-command-line). That unfortunately doesn't exist on iOS.

According to https://derflounder.wordpress.com/2011/03/13/adding-new-trusted-root-certificates-to-system-keychain/, the root CAs are stored in /Library/Keychains/System.keychain.

While there is no System.keychain on iOS, there is some stuff there:

root# ls -lh /Library/Keychains 
root wheel 24 Sep  3  2022 /Library/Keychains -> ../private/var/Keychains/
Vanessas-iPhone-2:~ root# ls -lh /private/var/Keychains/ 
total 6.9M
drwxrwxrwx 23 root       wheel  736 Jan 24 14:18 Analytics/
-rw-------  1 _securityd wheel    2 Jan 24 14:04 SOSAccountSettings.pb
-rw-r--r--  1 root       wheel 140K Jan 24 14:04 com.apple.security.keychain-defaultContext.TrustedPeersHelper.db
-rw-r--r--  1 root       wheel  32K Mar  9 13:56 com.apple.security.keychain-defaultContext.TrustedPeersHelper.db-shm
-rw-r--r--  1 root       wheel 149K Mar  9 14:00 com.apple.security.keychain-defaultContext.TrustedPeersHelper.db-wal
-rw-------  1 _securityd wheel 2.4M Mar  9 15:08 keychain-2.db
-rw-------  1 _securityd wheel  32K Mar  9 13:56 keychain-2.db-shm
-rw-------  1 _securityd wheel 2.3M Mar  9 15:34 keychain-2.db-wal
-r--------  1 root       wheel 346K Mar  9 14:27 keychain-ota-backup.plist

@baltpeter
Copy link
Member Author

support.apple.com/en-us/HT208127 mentions a file /System/Library/Security/Certificates.bundle/Contents/Resources/TrustStore.html. The folder for Certificates.bundle also exists on the iPhone

Let's see whether there's anything that helps us there:

  • CertificatePinning.plist is certainly interesting, but we don't care about certificate pinning here:

    image

  • certsTable.data looks like it may well contain the CAs, but I have no idea how to parse (and much less edit) that file:

    image

    But it notably doesn't contain the string mitmproxy. These will be the CAs that come bundled with the system and the user-added ones will be somewhere else.

  • TrustedCTLogs.plist is (unsurprisingly, given the name) about certificate transparency logs:

    image

  • TrustStore.html is a rendered table of the system CAs (again, no mitmproxy):

    image

  • valid.sqlite3 has a bunch of stuff that I don't really understand yet. These are the tables:

    image

    admin and dates are boring:

    image

    image

    Some rows in groups have a blob in the data column:

    image

    That is a zlib-ed plist, but I can't really make sense of its content (CyberChef link).

    And I don't know what the blobs in hashes, issuers, or serials are:

    image

    image

    image

@baltpeter
Copy link
Member Author

Next try: I've used an improved version of my SQLite grep script to check if any database on the system contains a string that might be our certificate:

# Adapted after: https://stackoverflow.com/a/53875499 and https://stackoverflow.com/a/29548123
NEEDLE="mitmproxy|begin certificate|MIIDoTCCAomg|IVCc6egf0T"

find / -name '*.db' -o -name '*.sqlite*' -print0 2>/dev/null | while IFS= read -r -d '' file; do
    for X in $(sqlite3 -readonly "$file" .tables 2>/dev/null);
        do sqlite3 -readonly "$file" "SELECT * FROM \"$X\";" 2>/dev/null | grep -iE >/dev/null $NEEDLE && echo "Found in file '$file', table '$X'";
    done
done

Result:

Found in file '/private/var/mobile/Media/PhotoData/Caches/search/psi.sqlite', table 'groups'
Found in file '/private/var/protected/trustd/private/TrustStore.sqlite3', table 'tsettings'

/private/var/mobile/Media/PhotoData/Caches/search/psi.sqlite is clearly a false positive. Seems like it put the name of the profile I downloaded into the photo search cache (for some reason…):

image

/private/var/protected/trustd/private/TrustStore.sqlite3 (download) is much more relevant:

image

Values of each column:

sha256:

00000000  e4 87 d9 63 6b 76 9d 45 82 f1 d5 f4 4c bf 23 e9  |ä.Ùckv.E.ñÕôL¿#é|
00000010  85 b6 98 cf 60 b1 41 7d 89 16 47 ff 74 ad 09 6d  |.¶.Ï`±A}..Gÿt..m|

subj:

00000000  31 12 30 10 06 03 55 04 03 0c 09 6d 69 74 6d 70  |1.0...U....mitmp|
00000010  72 6f 78 79 31 12 30 10 06 03 55 04 0a 0c 09 6d  |roxy1.0...U....m|
00000020  69 74 6d 70 72 6f 78 79                          |itmproxy|

tset:

<?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">
<array/>
</plist>

data:

00000000  30 82 03 a1 30 82 02 89 a0 03 02 01 02 02 06 0e  |0..¡0... .......|
00000010  9a 89 8f 5f b9 30 0d 06 09 2a 86 48 86 f7 0d 01  |..._¹0...*.H.÷..|
00000020  01 0b 05 00 30 28 31 12 30 10 06 03 55 04 03 0c  |....0(1.0...U...|
00000030  09 6d 69 74 6d 70 72 6f 78 79 31 12 30 10 06 03  |.mitmproxy1.0...|
00000040  55 04 0a 0c 09 6d 69 74 6d 70 72 6f 78 79 30 1e  |U....mitmproxy0.|
00000050  17 0d 32 30 31 31 31 36 30 38 35 32 34 32 5a 17  |..201116085242Z.|
00000060  0d 32 33 31 31 31 38 30 38 35 32 34 32 5a 30 28  |.231118085242Z0(|
00000070  31 12 30 10 06 03 55 04 03 0c 09 6d 69 74 6d 70  |1.0...U....mitmp|
00000080  72 6f 78 79 31 12 30 10 06 03 55 04 0a 0c 09 6d  |roxy1.0...U....m|
00000090  69 74 6d 70 72 6f 78 79 30 82 01 22 30 0d 06 09  |itmproxy0.."0...|
000000a0  2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00  |*.H.÷...........|
000000b0  30 82 01 0a 02 82 01 01 00 9d 78 98 b9 35 e7 d5  |0.........x.¹5çÕ|
000000c0  79 b3 ba 03 83 e0 de 64 2f 8c ef e7 64 77 76 c0  |y³º..àÞd/.ïçdwvÀ|
000000d0  1b 6b 99 9b e9 d4 16 f9 35 13 64 77 1b 94 c0 8f  |.k..éÔ.ù5.dw..À.|
000000e0  67 0f 47 d2 d8 d8 9e e8 1d 0a e9 84 5f a3 02 28  |g.GÒØØ.è..é._£.(|
000000f0  d5 34 f7 1d 51 60 dc 7c ac 7a 22 e7 f7 7b 57 5f  |Õ4÷.Q`Ü|¬z"ç÷{W_|
00000100  b7 8a c7 bc 5b be fe f9 8d ab 09 5c 5d f4 7a 67  |·.Ǽ[¾þù.«.\]ôzg|
00000110  f4 55 97 45 00 ec 21 a3 fa 27 43 33 76 01 80 42  |ôU.E.ì!£ú'C3v..B|
00000120  2d 3f 62 37 69 be 9c b3 87 53 c1 47 d9 81 fd 42  |-?b7i¾.³.SÁGÙ.ýB|
00000130  db b3 a3 97 85 07 cc 04 72 a8 8b 4d c4 9a eb 7a  |Û³£...Ì.r¨.MÄ.ëz|
00000140  79 cd 40 1e 06 c6 4b b7 c5 77 8e a3 b3 f1 6c dc  |yÍ@..ÆK·Åw.£³ñlÜ|
00000150  f6 ad 70 37 c4 87 96 34 ff 3b d8 a4 23 81 fb 82  |ö.p7Ä..4ÿ;ؤ#.û.|
00000160  9a a7 d5 f9 48 50 4c 55 1e b2 e4 ea 82 58 ea 2c  |.§ÕùHPLU.²äê.Xê,|
00000170  b1 44 df 6b f8 dc 91 c9 4f e9 68 db f5 a8 53 74  |±DßkøÜ.ÉOéhÛõ¨St|
00000180  2c 88 11 b0 77 06 64 f9 1f 93 df b3 27 a9 cc 87  |,..°w.dù..ß³'©Ì.|
00000190  98 50 29 b0 95 8b b2 fb 4e 7d ea e7 96 e4 52 64  |.P)°..²ûN}êç.äRd|
000001a0  a1 37 a7 cc d9 c2 90 fa 56 43 86 21 e3 34 0a a5  |¡7§ÌÙÂ.úVC.!ã4.¥|
000001b0  03 41 8d d5 e6 47 a7 99 63 02 03 01 00 01 a3 81  |.A.ÕæG§.c.....£.|
000001c0  d0 30 81 cd 30 0f 06 03 55 1d 13 01 01 ff 04 05  |Ð0.Í0...U....ÿ..|
000001d0  30 03 01 01 ff 30 11 06 09 60 86 48 01 86 f8 42  |0...ÿ0...`.H..øB|
000001e0  01 01 04 04 03 02 02 04 30 78 06 03 55 1d 25 04  |........0x..U.%.|
000001f0  71 30 6f 06 08 2b 06 01 05 05 07 03 01 06 08 2b  |q0o..+.........+|
00000200  06 01 05 05 07 03 02 06 08 2b 06 01 05 05 07 03  |.........+......|
00000210  04 06 08 2b 06 01 05 05 07 03 08 06 0a 2b 06 01  |...+.........+..|
00000220  04 01 82 37 02 01 15 06 0a 2b 06 01 04 01 82 37  |...7.....+.....7|
00000230  02 01 16 06 0a 2b 06 01 04 01 82 37 0a 03 01 06  |.....+.....7....|
00000240  0a 2b 06 01 04 01 82 37 0a 03 03 06 0a 2b 06 01  |.+.....7.....+..|
00000250  04 01 82 37 0a 03 04 06 09 60 86 48 01 86 f8 42  |...7.....`.H..øB|
00000260  04 01 30 0e 06 03 55 1d 0f 01 01 ff 04 04 03 02  |..0...U....ÿ....|
00000270  01 06 30 1d 06 03 55 1d 0e 04 16 04 14 ad 34 c5  |..0...U.......4Å|
00000280  90 69 f7 55 52 0f d8 b8 5f 28 fa 21 43 ef 05 09  |.i÷UR.ظ_(ú!Cï..|
00000290  0f 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00  |.0...*.H.÷......|
000002a0  03 82 01 01 00 58 41 ca a0 3a 91 26 1e 43 02 90  |.....XAÊ :.&.C..|
000002b0  71 84 4d a3 c9 71 d6 e0 e6 67 e1 07 f9 5b 1e 83  |q.M£ÉqÖàægá.ù[..|
000002c0  41 06 e0 ac 68 e5 65 9d f3 e3 9a ec cf ab 24 08  |A.à¬håe.óã.ìÏ«$.|
000002d0  8e 43 61 02 19 6f 60 03 1e a9 3d e6 3c 15 6c 4e  |.Ca..o`..©=æ<.lN|
000002e0  01 b0 c1 fc fb 9e b7 4c 27 23 e4 34 7b 7f a6 03  |.°Áüû.·L'#ä4{.¦.|
000002f0  0d 89 9d c6 a1 b8 ad 7e f4 02 7b 52 23 b9 2c db  |...Æ¡¸.~ô.{R#¹,Û|
00000300  1d f8 6e 44 fc 87 45 f4 29 34 10 82 6d eb 13 9e  |.ønDü.Eô)4..më..|
00000310  35 71 cb 2b 99 c0 ad 18 f7 dc 15 5a 72 6a 12 88  |5qË+.À..÷Ü.Zrj..|
00000320  da 72 48 59 03 a5 d0 94 e9 b4 d0 04 b5 a2 2a f1  |ÚrHY.¥Ð.é´Ð.µ¢*ñ|
00000330  09 36 3e 08 0b 84 e2 52 94 f4 49 54 1b c5 c3 4f  |.6>...âR.ôIT.ÅÃO|
00000340  b5 23 73 1f 68 39 43 a3 0f a2 b2 3b 32 14 c0 df  |µ#s.h9C£.¢²;2.Àß|
00000350  89 0c b8 32 09 36 b7 6a 9f 3a 16 5c 98 2f b1 9a  |..¸2.6·j.:.\./±.|
00000360  24 0a 8b 55 68 a2 41 dc 85 26 c0 61 fa ad d6 4b  |$..Uh¢AÜ.&Àaú.ÖK|
00000370  ca ab c7 8f 69 7d f5 68 6b 4b dd ab f4 e6 b8 a1  |Ê«Ç.i}õhkKÝ«ô渡|
00000380  fd 6f 7c 6e 59 bf fb 35 bf 8a f3 34 f0 54 d7 bb  |ýo|nY¿û5¿.ó4ðT×»|
00000390  1c 22 3f bc e4 37 29 08 95 d0 a0 bc d3 f2 15 09  |."?¼ä7)..Р¼Óò..|
000003a0  ce 9e 81 fd 13                                   |Î..ý.|

@baltpeter
Copy link
Member Author

CyberChef detected the data value as application/pkix-cert (cer,cat,p7b,p7c,p7m,p7s,swz,rsa,crl,crt,der)!

image

@baltpeter
Copy link
Member Author

baltpeter commented Mar 9, 2023

We can get that really easily using openssl:

openssl x509 -outform der -in ~/.mitmproxy/mitmproxy-ca-cert.pem -out mitmproxy-ca-cert.crt

@baltpeter
Copy link
Member Author

sha256 is just the sha256 hash of the .crt:

❯ sha256sum mitmproxy-ca-cert.crt
e487d9636b769d4582f1d5f44cbf23e985b698cf60b1417d891647ff74ad096d  mitmproxy-ca-cert.crt

@baltpeter
Copy link
Member Author

baltpeter commented Mar 9, 2023

subj really shouldn't be hard either. Clearly, it's some version of the subject name of the certificate. And I'm pretty sure the exact bytes are already contained in the certificate. Now, I just need a way to extract them for an arbitrary cert.

@baltpeter
Copy link
Member Author

Before I sink too much time into this, let's see whether I'm even on the right track (though I am pretty confident :D).

On the other iPhone, I have not yet installed the CA. Settings -> General -> About -> Certificate Trust Settings shows no user certificates. And indeed, the tsettings table of /private/var/protected/trustd/private/TrustStore.sqlite3 is empty there.

If I copy the database with the mitmproxy cert from the other phone, now the mitmproxy cert does show up but is not trusted:

image

So, I am definitely on the right track. \o/

@baltpeter
Copy link
Member Author

Looks like somebody else already figured this out: https://github.com/ADVTOOLS/ADVTrustStore :( :D

@baltpeter
Copy link
Member Author

Here's how they get the subj value:

https://github.com/ADVTOOLS/ADVTrustStore/blob/6a90c4f44a2fc02821a6340eb004fc56bf6205d2/iosCertTrustManager.py#L383-L424

Looks like there is no command in openssl that can do that natively (they do call openssl in other places).

@baltpeter
Copy link
Member Author

I've found PKI.js.

As a reminder, we want:

00000000  31 12 30 10 06 03 55 04 03 0c 09 6d 69 74 6d 70  |1.0...U....mitmp|
00000010  72 6f 78 79 31 12 30 10 06 03 55 04 0a 0c 09 6d  |roxy1.0...U....m|
00000020  69 74 6d 70 72 6f 78 79                          |itmproxy|

With this, I'm almost there:

import { readFile } from 'fs/promises';
import * as pkijs from 'pkijs';

(async () => {
    const certDer = await readFile('mitmproxy-ca-cert.crt');

    const c = pkijs.Certificate.fromBER(certDer);
    console.log(c.subject.valueBeforeDecode);
})();

This outputs:

ArrayBuffer {
  [Uint8Contents]: <30 28 31 12 30 10 06 03 55 04 03 0c 09 6d 69 74 6d 70 72 6f 78 79 31 12 30 10 06 03 55 04 0a 0c 09 6d 69 74 6d 70 72 6f 78 79>,
  byteLength: 42
}

There's just two extra bytes at the beginning.

@baltpeter
Copy link
Member Author

ChatGPT says:

The output you are seeing is the representation of an ArrayBuffer object, which contains the binary contents of the certificate in the form of a byte array. The two extra bytes at the beginning of the output (<30 28>) correspond to the hexadecimal representation of the DER-encoded tag and length of the top-level ASN.1 sequence that contains the certificate.

In ASN.1 encoding, a sequence is represented as a tag value of 0x30 followed by the length of the sequence. In this case, the length is 0x28 bytes, which means that the actual contents of the sequence (i.e., the certificate) start at the third byte of the ArrayBuffer. The reason why you are seeing the tag and length bytes in the output is because you are logging the entire ArrayBuffer object to the console.

This seems really plausible! 0x28 is 40. That's exactly the length we're looking for.

And indeed, it is correct. Great, that means we can just strip the first two bytes away.

@baltpeter
Copy link
Member Author

OK, actually tag and length can be longer than one byte:

The tag is usually one byte. There is a means to encode arbitrarily large tag numbers using multiple bytes (the “high tag number” form), but this is not typically necessary.

The encoding of length can take two forms: short or long. The short form is a single byte, between 0 and 127.
The long form is at least two bytes long, and has bit 8 of the first byte set to 1. Bits 7-1 of the first byte indicate how many more bytes are in the length field itself. Then the remaining bytes specify the length itself, as a multi-byte integer.

For the tag, that doesn't matter for us. We always expect a sequence (0x30). But the length might matter.

@baltpeter
Copy link
Member Author

Turns out I was making this harder than it has to be.

import { readFile } from 'fs/promises';
import * as pkijs from 'pkijs';

(async () => {
    const certDer = await readFile('mitmproxy-ca-cert.crt');

    const c = pkijs.Certificate.fromBER(certDer);;
    const subjectSchema = c.subject.toSchema();
    console.log(subjectSchema.valueBlock.toBER());
})();

Gives the desired result:

ArrayBuffer {
  [Uint8Contents]: <31 12 30 10 06 03 55 04 03 0c 09 6d 69 74 6d 70 72 6f 78 79 31 12 30 10 06 03 55 04 0a 0c 09 6d 69 74 6d 70 72 6f 78 79>,
  byteLength: 40
}

Side note: I had actually discovered toSchema() and the fact that it has a valueBlock of length 40 earlier, but was confused because the values were logged as decimal rather than hex (which I didn't notice, and which caused me to think it was something else):

  valueBeforeDecodeView: Uint8Array(40) [
     49,  18,  48,  16,   6,   3,  85,   4,   3,
     12,   9, 109, 105, 116, 109, 112, 114, 111,
    120, 121,  49,  18,  48,  16,   6,   3,  85,
      4,  10,  12,   9, 109, 105, 116, 109, 112,
    114, 111, 120, 121
  ],

@baltpeter
Copy link
Member Author

That only leaves trusting the certificate. I was pretty sure that this isn't handled by TrustStore.sqlite3. But seems like I'm wrong. Here's a little experiment I did:

  • I "untrusted" the certificate and copied the database.
  • I retrusted the certificate.
  • I overrode TrustStore.sqlite3 with the untrusted copy.
  • I checked whether the certificate was still trusted. It wasn't.

This also works the other way around.

@baltpeter
Copy link
Member Author

baltpeter commented Mar 13, 2023

Turns out, this is pretty easy as well. The column tset differs depending on whether the cert is trusted.

Here's the trusted version:

<?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">
<array/>
</plist>

And here's the untrusted version:

<?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">
<array>
	<dict>
		<key>kSecTrustSettingsPolicy</key>
		<string>1.2.840.113635.100.1.3</string>
		<key>kSecTrustSettingsPolicyName</key>
		<string>sslServer</string>
		<key>kSecTrustSettingsResult</key>
		<integer>4</integer>
	</dict>
	<dict>
		<key>kSecTrustSettingsResult</key>
		<integer>1</integer>
	</dict>
</array>
</plist>

EDIT: I had them the wrong way around. Curiously, the array is empty for the trusted one.

@baltpeter
Copy link
Member Author

I was able to successfully manually insert and trust the CA into an empty TrustStore.sqlite3. So, now I only need to implement the function.

@baltpeter
Copy link
Member Author

Hex literals in SQLite look like this: x'e487d9636b769d4582f1d5f44cbf23e985b698cf60b1417d891647ff74ad096d'

@baltpeter
Copy link
Member Author

I was getting errors from execa about the certificate data (The argument 'args[6]' must be a string without null bytes.) and tset (Error: unrecognized token).

The solution was simple: We're now encoding everything as hex in the command. .D

@baltpeter
Copy link
Member Author

Oh no, I just tried the new functions on the other iPhone with iOS 15, where I had never manually installed the CA, and while the CA now shows up under "Certificate Trust Settings", it is not trusted. :/

@baltpeter
Copy link
Member Author

After I manually trusted the CA once, it now works automatically. Seems like at least some part of the trust status is stored elsewhere.

This might explain why some people report that ADVTrustStore automatically trusts the CA, while other say the opposite (ADVTOOLS/ADVTrustStore#12).

@baltpeter
Copy link
Member Author

Interestingly, for a new CA, it works immediately. So, there seems to be some global "has the user ever trusted a root CA?" state.

@baltpeter
Copy link
Member Author

Interestingly, for a new CA, it works immediately. So, there seems to be some global "has the user ever trusted a root CA?" state.

As this only requires a one-time action by the user, I'll not look into this right now but include a note. We can come back at some later point.

baltpeter added a commit that referenced this issue Mar 13, 2023
Currently, this requires a one-time setup step for the added CAs to
actually be trusted, we'll have to look into that later, see:
#44 (comment)
@baltpeter baltpeter self-assigned this Mar 13, 2023
baltpeter added a commit that referenced this issue Mar 20, 2023
Currently, this requires a one-time setup step for the added CAs to
actually be trusted, we'll have to look into that later, see:
#44 (comment)
zner0L pushed a commit that referenced this issue Mar 21, 2023
Currently, this requires a one-time setup step for the added CAs to
actually be trusted, we'll have to look into that later, see:
#44 (comment)
zner0L pushed a commit that referenced this issue Mar 28, 2023
Currently, this requires a one-time setup step for the added CAs to
actually be trusted, we'll have to look into that later, see:
#44 (comment)
@zner0L
Copy link
Contributor

zner0L commented May 11, 2023

I looked at what the Settings app does when trusting the first certificate. Apparently it calls [PSGCertTrustSettings setFulltrustEnabled]. The same class also has a isFullTrustEnabled method:

[iOS Device::Settings ]-> ObjC.classes.PSGCertTrustSettings.$methods.filter(function(method) {return method.includes('FullTrust');})
[
    "- setFullTrustEnabled:forSpecifier:",
    "- isFullTrustEnabled:"
]

The question remains what the specifier is. I am not sure how to call these functions in frida, though.

@zner0L
Copy link
Contributor

zner0L commented May 11, 2023

Bing Chat says:

PSGCertTrustSettings is a configuration profile setting that allows you to trust a certificate payload in iOS and iPadOS. When you manually install a profile that contains a certificate payload in iOS and iPadOS, this certificate does not automatically trust SSL. You can learn how to manually trust an installed certificate profile on this Apple support page.

@zner0L
Copy link
Contributor

zner0L commented May 12, 2023

Ok, I did some thinking and looking around and I am not sure if I can find out what setting we need to set so easily. Instead, I have considered looking into configuration profiles. Apple says (https://support.apple.com/en-us/HT204477):

Apple recommends deploying certificates via Apple Configurator or Mobile Device Management (MDM). Certificate payloads are automatically trusted for SSL when installed with Configurator, MDM, or as part of an MDM enrollment profile.

So if we manage to deploy our certificates via a configuration profile this should be solved, shouldn't it? (Even though it bothers me that I would like to know what setting needs to be set)

Now, as for installing configuration profiles I am not sure how easy it is to automate them. There is a PR in libimobiledevice(libimobiledevice/libimobiledevice#1029) that aims at doing that but it hasn’t been merged, yet. I can’t see why they didn’t merge it, though. Even so, I think we could also use that work to figure out how to install configuration profiles ourselves (or help get that merged).

@zner0L
Copy link
Contributor

zner0L commented May 15, 2023

Before I sink even more time into this, i would like to decide whether we want to try configuration profiles. I feel like right now looking at frida trace is a shot in the dark and I could spend the time more productively in another way. What do you think, @baltpeter? What I like about configuration profiles is that they enable us to do all sorts of things, including setting the proxy or disabeling other settings.

@baltpeter
Copy link
Member Author

I assume that configuration profiles would work even without jailbreak? That would of course be a lot nicer.

That being said, do you think that they would be a realistic path forward at this time, because I have some serious doubts about that. Looking at the limd PR you linked this doesn't seem like something we would realistically want to implement ourselves. But it also doesn't look like the PR is going to be merged anytime soon, does it? And even if it is merged, how long will it take until that feature arrives in the distribution packages? This seems like more of a potential for a better implementation in the long term and less like something we can do right now, to be honest.

And have you checked what format these configuration profiles are? Can we feasible generate those?

@zner0L
Copy link
Contributor

zner0L commented May 15, 2023

I don't actually think it is that hard to do. All we need to do is send some XML to the socket of the com.apple.mobile.MCInstall service. At least that is what it looks like on the surface.

I also found this nice python library (https://github.com/doronz88/pymobiledevice3/) which implements everything we need from libimobiledevice except for iproxy (but it does implement the interesting sounding com.apple.pcapd to "[s]niff device's network traffic").

The configuration profiles are just .plist files which are annoying to work with, but there is a node package for it of which we even already import the types of, and there is this handy reference document to tell which keys to set.

@zner0L
Copy link
Contributor

zner0L commented May 22, 2023

Well, I tried around a little bit, first with Apple Configurator. I generated a configuration profile of the mitmproxy CA ceritificate:

<?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>PayloadContent</key>
	<array>
		<dict>
			<key>PayloadCertificateFileName</key>
			<string>mitmproxy-ca-cert.cer</string>
			<key>PayloadContent</key>
			<data>
			[base64 content]
			</data>
			<key>PayloadDescription</key>
			<string>Adds a CA root certificate</string>
			<key>PayloadDisplayName</key>
			<string>mitmproxy</string>
			<key>PayloadIdentifier</key>
			<string>com.apple.security.root.EFF0CCD5-2798-497D-8813-5A7A1C526CB0</string>
			<key>PayloadType</key>
			<string>com.apple.security.root</string>
			<key>PayloadUUID</key>
			<string>EFF0CCD5-2798-497D-8813-5A7A1C526CB0</string>
			<key>PayloadVersion</key>
			<integer>1</integer>
		</dict>
	</array>
	<key>PayloadDisplayName</key>
	<string>Untitled</string>
	<key>PayloadIdentifier</key>
	<string>Lorenzs-MacBook-Pro.6B40D2D8-E1E5-40E0-8F7F-2734C5A68E65</string>
	<key>PayloadRemovalDisallowed</key>
	<false/>
	<key>PayloadType</key>
	<string>Configuration</string>
	<key>PayloadUUID</key>
	<string>1247ED2B-E6E5-4F4D-8291-01351B3F37EB</string>
	<key>PayloadVersion</key>
	<integer>1</integer>
</dict>
</plist>

Apparently, there still is some manual interaction required to install configuration profiles. The process is the same as it is for downloading a configuration profile from the internet and the certificate is not automatically trusted after the profile is installed. According to Apple Configurator, profiles are only automatically trusted if they are installed to a "supervised" device by the supervising computer.

@zner0L
Copy link
Contributor

zner0L commented May 22, 2023

pymobiledevice3 install yields the same result. I guess if we want automatic installation of profiles, we need to supervise the devices. That seems similarly complicated to jailbreaking, though, since that also requires wiping the device (See iMazing, Apple).

@zner0L
Copy link
Contributor

zner0L commented May 22, 2023

Enabling "supervised mode" is apparently pretty easy, you just have to switch a flag in /var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/CloudConfigurationDetails.plist. IsSupervised needs to be 1.

You can do this using plutil from Sileo (and restart the launch daemon after that for the change to take effect):

plutil -key IsSupervised -value 1  /var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/CloudConfigurationDetails.plist
ldrestart

This helps to enable more settings than were previously available, but the configuration profiles are still not automatically trusted. I think there needs to be some kind of signature thing that signs the configurations or something like this. I will supervise my other phone and try it out.

@zner0L
Copy link
Contributor

zner0L commented May 22, 2023

As a sidenote, i think this way it should also be easy to enable/disable supervised mode from a (unencrypted) backup, by just editing this plist file, which would make setting this setting also possible for stock devices by backing them up and restoring a modified backup.

@zner0L
Copy link
Contributor

zner0L commented May 22, 2023

After lots of struggle with the jailbreak on iOS 15.7.5, read out the /var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/CloudConfigurationDetails.plist, and as you can see, there is some crypto going on:

{
    AllowPairing = 1;
    CloudConfigurationUIComplete = 1;
    ConfigurationSource = 2;
    ConfigurationWasApplied = 1;
    IsMDMUnremovable = 0;
    IsMandatory = 0;
    IsMultiUser = 0;
    IsSupervised = 1;
    OrganizationMagic = "F886A478-79CB-41F8-84E5-1D73BDFC97C4";
    OrganizationName = Testorga;
    PostSetupProfileWasInstalled = 1;
    SkipSetup =     (
        Location,
        Restore,
        SIMSetup,
        Android,
        AppleID,
        TOS,
        Siri,
        ScreenTime,
        Diagnostics,
        Passcode,
        Biometric,
        Payment,
        Zoom,
        DisplayTone,
        MessagingActivationUsingPhoneNumber,
        HomeButtonSensitivity,
        CloudStorage,
        ScreenSaver,
        TapToSetup,
        Keyboard,
        PreferredLanguage,
        SpokenLanguage,
        WatchMigration,
        OnBoarding,
        TVProviderSignIn,
        TVHomeScreenSync,
        Privacy,
        TVRoom,
        iMessageAndFaceTime,
        AppStore,
        Safety,
        TermsOfAddress,
        Welcome,
        Appearance,
        RestoreCompleted,
        UpdateCompleted
    );
    SupervisorHostCertificates =     (
        {length = 936, bytes = 0x308203a4 3082028c a0030201 02020500 ... 5f40dd10 31fd7228 }
    );
}

@zner0L
Copy link
Contributor

zner0L commented May 22, 2023

The SupervisorHostCertificate is detected by Cyberchef as application/pkix-cert and this is exactly the same certificate that I have in my macOS Keychain on the macbook on which I ran Apple Configurator to supervise the iPhone.
OpenSSL says:

❯ openssl x509 -in Apple\ Configurator:\ Testorga\ \(F886A478-79CB-41F8-84E5-1D73BDFC97C4\).cer -noout -text -inform der
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 3993730054 (0xee0b7c06)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = Apple Configurator: Testorga (F886A478-79CB-41F8-84E5-1D73BDFC97C4), O = Testorga, C = DE
        Validity
            Not Before: May 22 15:36:32 2023 GMT
            Not After : May 17 15:36:32 2043 GMT
        Subject: CN = Apple Configurator: Testorga (F886A478-79CB-41F8-84E5-1D73BDFC97C4), O = Testorga, C = DE
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:ad:3e:d5:3f:00:36:13:d8:cf:78:5b:b8:7f:61:
                    53:4d:92:91:ab:44:b8:f5:7a:e0:4d:cf:03:89:37:
                    0d:c1:b3:77:b5:be:0f:1e:3c:75:b6:55:a2:95:b9:
                    24:26:3d:fb:b2:6d:e3:f2:ff:b2:43:d7:30:9e:e6:
                    f2:01:ec:7c:eb:bf:e5:01:82:28:a2:82:cc:1a:99:
                    e5:92:2e:f6:5e:ae:01:b8:11:6e:ad:bc:24:c2:13:
                    4a:0d:d5:01:a0:c9:5d:84:45:94:fd:c0:74:cf:fc:
                    f9:01:5f:c1:ed:39:01:30:89:7b:3a:58:d0:76:cf:
                    fd:2a:21:65:82:eb:ae:29:65:66:21:61:8e:dc:ce:
                    43:76:3a:5f:a5:b7:3f:86:d2:c8:ae:00:85:8b:b4:
                    c6:d0:bb:86:ff:56:6b:4a:e7:fa:4a:f5:8a:dd:3d:
                    f0:ba:bb:af:ca:ad:63:29:66:1f:0d:89:ea:61:f3:
                    aa:9f:40:65:89:33:41:1d:cb:44:57:84:f2:f7:ee:
                    ae:19:ae:4a:22:6c:ab:81:8c:a3:ac:4f:45:ad:c7:
                    7c:68:8d:f6:eb:85:9f:23:3a:6a:e9:92:da:92:c9:
                    95:fb:ee:9b:8e:2c:32:10:a9:79:b7:76:62:87:dd:
                    43:2a:1d:75:bf:11:eb:b2:1d:62:31:26:28:95:66:
                    ad:f9
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: critical
                Code Signing
            X509v3 Subject Key Identifier: 
                9B:D3:AF:6E:4B:C4:CA:78:BD:F5:61:08:92:D4:E7:CE:96:05:FB:EB
    Signature Algorithm: sha256WithRSAEncryption
         4e:39:54:a9:a0:76:63:c4:c2:b6:0d:ef:f7:a4:bf:ae:23:83:
         3f:be:8b:b1:71:a3:0a:0c:ec:3f:ed:95:cf:4a:ed:3e:48:21:
         cf:79:4c:66:c7:f9:6e:36:b4:8d:9c:8b:ce:ed:01:76:62:7f:
         c2:c6:37:15:1a:dc:14:1c:b3:f0:c4:15:36:54:ed:ab:19:60:
         26:32:82:7c:5a:82:08:80:e5:c7:03:95:51:ab:02:4d:88:d4:
         11:d5:2b:24:22:ca:d8:e6:be:67:33:1c:82:01:11:6e:9a:c1:
         f9:12:5a:83:82:14:14:74:90:21:35:6b:a1:0f:c4:c3:d5:00:
         5c:53:d4:52:6c:fb:d2:2c:54:0e:26:e0:e7:a4:3e:d5:4e:1a:
         7a:9b:14:3b:21:55:10:58:90:bf:9d:1f:a6:57:ef:63:c6:a0:
         3d:57:c3:ca:73:ce:cf:6f:72:73:cc:c5:af:03:d3:86:d8:91:
         d7:58:3a:92:8c:f8:8a:1c:bd:d7:98:b3:c7:fc:99:a3:a8:86:
         db:86:cf:85:7a:cb:98:06:bf:78:48:ad:15:e0:4e:e8:9d:5a:
         2e:c9:3a:38:06:74:f6:35:47:4e:6c:d0:4d:1c:ee:bf:c3:b9:
         03:5e:fe:f7:5a:8e:d8:f5:4c:79:05:b8:99:26:5f:40:dd:10:
         31:fd:72:28

@zner0L
Copy link
Contributor

zner0L commented May 23, 2023

I tried signing a mobileconfig with the certificate in the hopes that this would install the certificate without interaction, but without luck. I tried: openssl smime -sign -signer cert.pem -inkey private_key.pem -certfile cert.pem -nodetach -outform der -in mitmproxy.mobileconfig -out mitmproxy-signed.mobileconfig after converting the key and certificate to the PEM format, and the mobileconfig correctly shows up as signed by the certificate in the installation process, but that changes nothing about the way it is installed.

I noticed that for installed profiles, iOS creates .stub files in /var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/, which essentially are plists. For a configuration profile I installed via downloading, it contains these InstallOptions:

InstallOptions =     {
    filterFlag = 17;
    installedBy = "com.apple.Preferences";
    isInstalledInteractively = 1;
    signatureVersion = 2;
}

While for a configuration profile installed via Apple Configurator, it says:

InstallOptions =     {
    filterFlag = 17;
    installationType = 1;
    installedBy = "com.apple.dmd";
    isInstalledInteractively = 0;
    signatureVersion = 2;
};

So clearly, there is another way to install configuration profiles that I didn’t know about. com.apple.dmd doesn’t really yield anything on Kagi and it seems nobody really knows what this is about. There is a service running on my iPhone:

root# ps ax | grep dmd
  108   ??  Ss     0:00.70 /usr/libexec/dmd

I tried to look at the network communication of configurator via usbmux by sniffing the loopnack traffic with wireshark like this (thanks to a Reddit tutorial):

ssh root@<iPhone IP> /usr/bin/tcpdump -i lo0 -w - | wireshark -k -i -

But it seems like all the interesting traffic is of course TLS encrypted. Though I can confirm that Apple Configurator triggers some loopback traffic that originates from a port reserved for lockdownd.

@zner0L
Copy link
Contributor

zner0L commented May 24, 2023

Just when I was about to give up, i reached a breakthrough. I had a look at the iPhone syslog while installing a profile via configurator, and it was actually informative:

May 23 14:12:02 iPhone lockdownd[76] <Notice>: handle_get_value: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Received message on connection 0x135d0fda0: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Attempting to perform Unsupervised request: HelloHostIdentifier
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending response on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending reply status: Acknowledged
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Executing command from lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Receiving on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Received message on connection 0x135d0fda0: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Attempting to perform Unsupervised request: Escalate
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending response on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending reply status: Acknowledged
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Executing command from lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Receiving on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Received message on connection 0x135d0fda0: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Attempting to perform Unsupervised request: EscalateResponse
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Escalation accepted.
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending response on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending reply status: Acknowledged
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Executing command from lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Receiving on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Received message on connection 0x135d0fda0: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel(MDM)[269] <Notice>: Attempting to perform Supervised request: ProceedWithKeybagMigration
May 23 14:12:02 iPhone backboardd(AccessibilityUtilities)[424] <Error>: Client (PID:269) does not have required entitlement for message: <AXIPCMessage: 0x1047f9050>. Client port: 0. Key: 2061. Payload: {} (32).
Needs one of:{(     "com.apple.private.kernel.jetsam",     "com.apple.GAX.SPI" )}
May 23 14:12:02 iPhone backboardd(AccessibilityUtilities)[424] <Notice>: Not privileged to communicate
May 23 14:12:02 iPhone runningboardd(RunningBoard)[33] <Notice>: [daemon<com.apple.mobile.MCInstall>:269] handle lookup could not find a matching process
May 23 14:12:02 iPhone mc_mobile_tunnel(FindMyDevice)[269] <Notice>: -[FMDFMIPManager isManagedLostModeActive]
May 23 14:12:02 iPhone mc_mobile_tunnel(MDM)[269] <Notice>: Sending rolld notification.
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending response on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending reply status: Acknowledged
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Executing command from lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Receiving on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Received message on connection 0x135d0fda0: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending response on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending reply status: Error
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Executing command from lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Receiving on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone lockdownd[76] <Notice>: handle_get_value: <private>
May 23 14:12:02 iPhone trustd[140] <Notice>: cert[0]: MissingIntermediate =(leaf)[force]> 0
May 23 14:12:02 iPhone trustd[140] <Notice>: cert[0]: NonEmptySubject =(path)[]> 0
May 23 14:12:02 iPhone lockdownd[76] <Notice>: handle_get_value: <private>
May 23 14:12:02 iPhone lockdownd[76] <Notice>: handle_start_service_with_socket: <private>
May 23 14:12:02 iPhone lockdownd[76] <Notice>: spawn_xpc_service: <private>
May 23 14:12:02 iPhone lockdownd[76] <Notice>: spawn_xpc_service_block_invoke: <private>
May 23 14:12:02 iPhone lockdownd[76] <Notice>: handle_get_value: <private>
May 23 14:12:02 iPhone trustd[140] <Notice>: cert[0]: AnchorTrusted =(leaf)[force]> 0
May 23 14:12:02 iPhone trustd[140] <Notice>: cert[0]: NonEmptySubject =(path)[]> 0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Connecting lockdown connection: 0x135d13040
May 23 14:12:02 iPhone mc_mobile_tunnel(IOKit)[269] <Error>: Assertion retain not supported in async mode
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Executing command from lockdown connection: 0x135d13040
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Receiving on lockdown connection: 0x135d13040
May 23 14:12:02 iPhone lockdownd[76] <Notice>: spawn_xpc_service_block_invoke: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Received message on connection 0x135d13040: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Attempting to perform Unsupervised request: Flush
May 23 14:12:02 iPhone profiled(ManagedConfiguration)[113] <Notice>: Creating new IOPMAssertion.
May 23 14:12:02 iPhone profiled(IOKit)[113] <Notice>: Setting gAssertionsOffloader timeout to 1
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending response on lockdown connection: 0x135d13040
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending reply status: Acknowledged
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Executing command from lockdown connection: 0x135d13040
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Receiving on lockdown connection: 0x135d13040
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Received message on connection 0x135d0fda0: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel(MDM)[269] <Notice>: Attempting to perform Supervised request: ProfileList
May 23 14:12:02 iPhone backboardd(AccessibilityUtilities)[424] <Error>: Client (PID:269) does not have required entitlement for message: <AXIPCMessage: 0x1048b97a0>. Client port: 0. Key: 2061. Payload: {} (33).
Needs one of:{(     "com.apple.private.kernel.jetsam",     "com.apple.GAX.SPI" )}
May 23 14:12:02 iPhone backboardd(AccessibilityUtilities)[424] <Notice>: Not privileged to communicate
May 23 14:12:02 iPhone runningboardd(RunningBoard)[33] <Notice>: [daemon<com.apple.mobile.MCInstall>:269] handle lookup could not find a matching process
May 23 14:12:02 iPhone mc_mobile_tunnel(FindMyDevice)[269] <Notice>: -[FMDFMIPManager isManagedLostModeActive]
May 23 14:12:02 iPhone mc_mobile_tunnel(MDM)[269] <Notice>: Handling request type: ProfileList
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending response on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Sending reply status: Acknowledged
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Executing command from lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Receiving on lockdown connection: 0x135d0fda0
May 23 14:12:02 iPhone mc_mobile_tunnel[269] <Notice>: Received message on connection 0x135d0fda0: <private>
May 23 14:12:02 iPhone mc_mobile_tunnel(MDM)[269] <Notice>: Attempting to perform Supervised request: InstallProfileSilent

Apparently, there is a request to mc_mobile_tunnel ([which is just the binary name of com.apple.mobile.MCInstall) which is called InstallProfileSilent. That sounds very promising. I tried to install a profile using this command in the lockdown shell of pymobiledevice3 ( pymobiledevice3 lockdown service com.apple.mobile.MCInstall ):

In [11]: client.send_plist({'RequestType': 'InstallProfileSilent', 'Payload': f.
    ...: read()})

In [12]: print(client.recv_plist())
{'Status': 'CommandFormatError'}

That didn’t work, there needs to be some form of authentication. Searching for InstallProfileSilent, however, let me to https://github.com/danielpaulus/go-ios/blob/main/ios/mcinstall/mcinstall.go#L327, which implemented this functionality in go already. Apparently, you need to Escalate your lockdown session, which we can also find in the syslog above. Testing out go-ios enabled me to install a profile without interaction and the certificate is automatically trusted!

@zner0L
Copy link
Contributor

zner0L commented Jun 20, 2023

I implemented silent profiles installs in pymobiledevice3: doronz88/pymobiledevice3#472
We are not in a hurry, so let‘s wait for them to merge and release the changes at least.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request research
Projects
None yet
Development

No branches or pull requests

3 participants
@baltpeter @zner0L and others