Certificate is not standards compliant on MacOS/iOS, error -9802, strict TLS Trust evaluation failed

This is a little rant about Apple and how they implemented their security checks around certificates. TL;DR: You can’t have a certificate that is valid for more than 825 days.

Like many others, I have a private trusted CA certificate installed on my MacOS. It’s in the keychain an marked as trusted. So far everything worked well, but one day I got error messages when connecting to a Jabber server (XMPP with Starttls). I got it for all software that used the MacOS TLS stack, but not for software like Firefox. So it had to be something Apple specific. The CA certificate was shown as trusted, the intermediate certificate was accepted, but the leaf certificate (certificate of the server) had an error tagged on to it:

certificate is not standards compliant

Wow, what a helpful error message! Now I definitely know what to do. So I started investigating what the problem could be. The first few hits you get when searching the web are totally red herrings, but I didn’t know at that time: crypto/x509: “certificate is not standards compliant” on MacOS and SSL certificate is not Standards compliant“. Of course after reading that I thought I knew what the problem was: The new certificate transparency rules.

Red herring: Certificate transparency

My CA doesn’t have a certificate transparency log, that would make sense I thought at first… but then read about how you have to apply at Apple to get on the list, which obviously isn’t what other people do as there were only official CAs on the list. Searching a little further you find some posts that say internal/private CAs are excluded from certificate transparency in Chrome. That makes sense, but what about Apple? Couldn’t find any information there. So just to debug this, let’s just try to get rid of Certificate Transparency on my local MacOS and see if that helps. How do you do that? Well, only via device management profiles (aka .mobileconfig file). I knew those from my MDM analysis days, so I fired up Apple Configurator to create a new profile. In the profile I was really able to set domains (wildcard-style) that should be excluded from certificate transparency, great! So I exported the profile to a .mobileconfig file and tried to install it. However, when trying to install it I got the following error:

Profile installation failed. A Certificate Transparency Settings payload can only be included in a device profile.

Try searching for that error message on the web. There wasn’t a single hit at the time of writing… So what’s a device profile? I vaguely remembered, but I had to look it up first. The documentation of Apple on how to install profiles didn’t help either. Then I thought maybe I need to sign the .mobileconfig to make it install as a device profile. So I took a random cert/private key from a trusted CA I had (yes, you can take any) and signed the .mobileconfig. It worked and it’s weird, but when you sign with any certificate, the profile is actually shown as “verified”. However, that didn’t help either, couldn’t install as device profile and I couldn’t remember how I used to do it. I searched for a while but couldn’t find a solution.

Unhelpful error messages

Ok, I was stuck there, it would take even more time to figure out how to install that device profile. Let’s go back to square one. So I checked the console for Safari (which only says “can’t established secure connection”) specific errors regarding the certificate “not being standards compliant”. I checked Wireshark (which was pointless, I know, I should only see an “encrypted alert” packet, but I was desperate). I copy pasted some swift code I could run in the “swift repl” interactive console to get an error message:

import Foundation

let url = URL(string: "https://foo.example.org/")!
let (data, _) = try await URLSession.shared.data(from: url)
print(data)

But the only unhelpful messages I got was:

2024-01-30 18:02:02.103330+0100 repl_swift[29733:501906] Connection 1: strict TLS Trust evaluation failed(-9802)
2024-01-30 18:02:02.103370+0100 repl_swift[29733:501906] Connection 1: TLS Trust encountered error 3:-9802
2024-01-30 18:02:02.103376+0100 repl_swift[29733:501906] Connection 1: encountered error(3:-9802)
2024-01-30 18:02:02.206271+0100 repl_swift[29733:501906] Connection 2: strict TLS Trust evaluation failed(-9802)
2024-01-30 18:02:02.206310+0100 repl_swift[29733:501906] Connection 2: TLS Trust encountered error 3:-9802
2024-01-30 18:02:02.206319+0100 repl_swift[29733:501906] Connection 2: encountered error(3:-9802)
2024-01-30 18:02:02.259575+0100 repl_swift[29733:501906] [boringssl] boringssl_context_handle_fatal_alert(1991) [C3.1.1.1:2][0x100307470] read alert, level: fatal, description: protocol version
2024-01-30 18:02:02.263724+0100 repl_swift[29733:501906] [boringssl] boringssl_session_handshake_incomplete(88) [C3.1.1.1:2][0x100307470] SSL library error
2024-01-30 18:02:02.263782+0100 repl_swift[29733:501906] [boringssl] boringssl_session_handshake_error_print(43) [C3.1.1.1:2][0x100307470] Error: 4328543672:error:1000042e:SSL routines:OPENSSL_internal:TLSV1_ALERT_PROTOCOL_VERSION:/AppleInternal/Library/BuildRoots/9ba9e62a-acc5-11ee-9f46-d64f9dd5e0b3/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/tls_record.cc:594:SSL alert number 70
2024-01-30 18:02:02.263797+0100 repl_swift[29733:501906] [boringssl] nw_protocol_boringssl_handshake_negotiate_proceed(774) [C3.1.1.1:2][0x100307470] handshake failed at state 12288: not completed
2024-01-30 18:02:02.264613+0100 repl_swift[29733:501906] Connection 3: received failure notification
2024-01-30 18:02:02.264780+0100 repl_swift[29733:501906] Connection 3: failed to connect 3:-9836, reason -1
2024-01-30 18:02:02.264808+0100 repl_swift[29733:501906] Connection 3: encountered error(3:-9836)
2024-01-30 18:02:02.265218+0100 repl_swift[29733:501906] Task <9E99888A-E626-467B-A532-FDC6E7BC7F90>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9836])
2024-01-30 18:02:02.269813+0100 repl_swift[29733:501905] Task <9E99888A-E626-467B-A532-FDC6E7BC7F90>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey=https://foo.example.org/, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <9E99888A-E626-467B-A532-FDC6E7BC7F90>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <9E99888A-E626-467B-A532-FDC6E7BC7F90>.<1>"
), NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://foo.example.org/, NSUnderlyingError=0x600000c18600 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9836, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9836, _NSURLErrorNWPathKey=satisfied (Path is satisfied), viable, interface: utun3, dns}}, _kCFStreamErrorCodeKey=-9836}

Please be aware that only the first three lines are relevant for our problem (TLS1.3 connection failing because “strict TLS Trust evaluation failed”) and that the error message is not helpful at all. A different error message indeed, but searching for that on the web didn’t help either. Everything after the first 3 lines is just boringssl having issues connecting with TLS1.0 etc. So this means there is no error that would help me figure it out, again. Thanks Apple.

Solution

So I asked on Mastodon and someone pointed out the following link: Requirements for trusted certificates in iOS 13 and macOS 10.15. Which had the solution: TLS server certificates must have a validity period of 825 days or fewer (as expressed in the NotBefore and NotAfter fields of the certificate). As soon as we changed to shorter certificate validity, the errors were gone.

All in all I would say this is a pretty bad example for Apple. Even though they say on their Technical Note TN2232 – HTTPS Server Trust Evaluation that you should never disable TLS verification. The openssl examples in there also don’t help in this case. I can see why developers would give up if they don’t get proper error messages with reasons included. Or error codes that can be looked up. Or documentation that easily tells you howto debug it and get details. Or just one single proper documentation what Apple’s view on “standards compliant certificate” could be.

Bad practice, dark pattern. There, I wrote it.

Shellshock fix – bash compiling for OSX

By now probably all of you heard of the shellshock vulnerability. Just as a small heads-up, I wasn’t able to compile the bash version 4.3 on Mac OSX as the last few patches simply don’t work for me. But here’s how you can compile, test and install version 4.2 on your OSX:

#adopted from an original post (that was deleted) from http://www.linus-neumann.de/2014/09/26/clean-your-mac-from-shellshock-by-updating-bash/

PATCH_COMMAND=patch
#No better results with gnu-patch from mac ports -> /opt/local/bin/gpatch


#VERSION_TO_COMPILE=4.1
#VERSION_TO_COMPILE_NO_DOT=41
#VERSION_NUMBER_OF_PATCHES=17

VERSION_TO_COMPILE=4.2
VERSION_TO_COMPILE_NO_DOT=42
VERSION_NUMBER_OF_PATCHES=53

#patches starting from 029 don't work for me in version 4.3
#VERSION_TO_COMPILE=4.3
#VERSION_TO_COMPILE_NO_DOT=43
#VERSION_NUMBER_OF_PATCHES=30


echo "* Downloading bash source code"
wget --quiet http://ftpmirror.gnu.org/bash/bash-$VERSION_TO_COMPILE.tar.gz
tar xzf bash-$VERSION_TO_COMPILE.tar.gz 
cd bash-$VERSION_TO_COMPILE

echo "* Downloading and applying all patches"
for i in $(seq -f "%03g" 1 $VERSION_NUMBER_OF_PATCHES); do
   echo "Downloading and applying patch number $i for bash-$VERSION_TO_COMPILE"
   wget --quiet http://ftp.gnu.org/pub/gnu/bash/bash-$VERSION_TO_COMPILE-patches/bash$VERSION_TO_COMPILE_NO_DOT-$i
   $PATCH_COMMAND -p0 < bash$VERSION_TO_COMPILE_NO_DOT-$i
   #sleep 0.5
done

echo "* configuring and building bash binary"
sleep 1
./configure
make

echo "* writing bash test script"
#The following script will only work when your cwd has the bash binary,
#so you can execute ./bash
#mostly taken from shellshocker.net:
cat << EOF > /tmp/tmp-bash-test-file.sh
    #CVE-2014-6271
    echo "* If the following lines contain the word 'vulnerable' your bash is not fixed:"
    env x='() { :;}; echo vulnerable' ./bash -c "echo no worries so far"
    #CVE-2014-7169
    echo "* If the following lines print the actual date rather than the string 'date' you are vulnerable:"
    env X='() { ()=>\' ./bash -c "echo date"; cat echo;
    #unknown
    echo "* If the following lines contain the word 'vulnerable' your bash is not fixed:"
    env X=' () { }; echo vulnerable' ./bash -c 'echo no worries so far'
    #CVE-2014-7186
    echo "* If the following lines contain the word 'vulnerable' your bash is not fixed:"
    ./bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' || echo "vulnerable CVE-2014-7186 , redir_stack"
    #CVE-2014-7187
    echo "* If the following lines contain the word 'vulnerable' your bash is not fixed:"
    (for x in {1..200} ; do echo "for x$x in ; do :"; done; for x in {1..200} ; do echo done ; done) | ./bash || echo "vulnerable CVE-2014-7187 , word_lineno"
    #CVE-2014-6278
    echo "* If the following lines contain the word 'vulnerable' your bash is not fixed:"
    shellshocker='() { echo vulnerable; }' ./bash -c shellshocker
    #CVE-2014-6277
    echo "* If the following lines contain the word 'vulnerable' your bash is not fixed:"
    ./bash -c "f() { x() { _;}; x() { _;} <<a; }" 2>/dev/null || echo vulnerable
    #more tests, probably often testing the same as above, but better safe than sorry
    echo "* If the following lines contain the word 'vulnerable' your bash is not fixed:"
    env X='() { _; } >_[$($())] { echo vulnerable; }' ./bash -c : 
    echo "* If the following lines contain the word 'vulnerable' your bash is not fixed:"
    foo='() { echo vulnerable; }' ./bash -c foo
EOF

echo ""
echo "* Starting a new bash process to check for vulnerabilities"
echo ""
sleep 1
./bash /tmp/tmp-bash-test-file.sh

echo ""
echo "* If the compiled bash binary is not vulnerable, you want to install that binary in your system:"
echo "cd bash-$VERSION_TO_COMPILE"
echo "sudo make install"
echo "sudo mv /bin/bash /bin/old_vulnerable_bash && sudo ln /usr/local/bin/bash /bin/bash"

cheers,
floyd

Android Pentest Tools

During my research for the Android platform and in some pentests I tried several things and used different techniques. This is kind of a summary post and I packed some of my tools together into one zip file. The contents are:

  • Importing Burp CA into the Android phone, which I already wrote a blog post about
  • Some Ubuntu bash scripts that can be used to compile statically linked ARM binaries for Android, which I already wrote a blog post about
  • Decompiling/Disassembling bash scripts that I used to disassemble/decompile 3’500 apps from the market, including the Apple Script for Mac to automate the JD-GUI decompilation
  • A simple Python script that can be used to install a list of apps on your Android mobile
  • A list of Google Market App IDs, one list with free apps, one list with apps that cost money
  • A bash script that creates the Metasploit ARM reverse TCP shell payload
  • GingerBreak2 and RageAgainstTheCage exploit but including Ubuntu bash ARM compilation scripts, that let you compile the binary on your own instead of using the shipped ARM binary (I only tested the RageAgainstTheCage exploit)
  • A list of interesting files on the Android filesystem, that serves as a starting point if you don’t know where to start. Having a rooted phone to access the entire filesystem and using a text editor (.xml and .conf files) and a sqlite db viewer (files ending on .db) you’ll find pretty interesting stuff.
  • A file with the Hidden Secret Codes I found on my HTC Desire and in some apps. Actually only two of the 3’500 apps I decompiled had secret codes: The Twicca Twitter client (dial *#*#459338#*#*) and Baidu, the chinese search engine app (*#*#22438#*#*)

You can download the zip file here. I didn’t want to make up my own Android tool project svn or anything like that, but if you have your own toolset (e.g. you’re the developer of one of the tools below), I’d be happy to give my scripts to your project. If you have any feedback, just let me know, I’m happy to discuss it.

Addtionally, I thought I’ll write down some project/tools I used or I want to look into in the future:

  • Androguard
  • Apkinspector (GUI combining apktool, dex2jar, a Java decompiler, byte code, etc.)
  • DED
  • androidAuditTools
  • Smartphonesdumbapps
  • Taintdroid (Privacy issues)
  • Android Forensic Toolkit
  • viaExtract (There’s a VMWare with viaExtract installed. Does standard Forensic for Android: calls, sms, etc. Needs USB debug on)

I might update this post once in a while

Automating JD-GUI decompilation on a Mac with AppleScript

I know the guys over at Java Decompiler don’t want to release a command line tool, because they fear that companies will use their code in commercial product. See the discussion here. I found a solution to my problem, so that I can still automate the decompilation process. During my Android research I really need to decompile a lot of jar files, therefore I wrote a simple AppleScript that saves me the sources to my /opt folder.

tell application "JD-GUI"
	activate
end tell

tell application "System Events"
	keystroke "s" using {command down, option down}
end tell
tell application "System Events"
	keystroke tab
	keystroke tab
	keystroke tab
	key code 125 #Down
	key code 125 #Down
	key code 125 #Down
	key code 125 #Down
	key code 125 #Down
	key code 36 #Enter
	delay 1
	key code 36 #Enter
	delay 2
end tell

repeat while appIsRunning("JD-GUI")
	tell application "System Events"
		keystroke "q" using {command down} #Close JD-GUI
	end tell
	delay 2
end repeat

on appIsRunning(appName)
	tell application "System Events" to (name of processes) contains appName
end appIsRunning

After saving the script as decompile_jar.applescript with the AppleScript Editor, you can invoke it from your bash script like this:

/Applications/JD-GUI.app/Contents/MacOS/jd-gui example.jar &
sleep 1
osascript decompile_jar.applescript
mv /opt/example.src.zip /your/destination