demo
ios
MASTG-TEST-0344
MASTG-DEMO-0111: Network.framework TLS Minimum Version Lowered via sec_protocol_options
Download MASTG-DEMO-0111 IPA
Open MASTG-DEMO-0111 Folder
Build MASTG-DEMO-0111 IPA
Sample
The code sample uses NWProtocolTLS.Options with sec_protocol_options_set_min_tls_protocol_version to set the minimum TLS version to TLS 1.0 for a Network.framework connection. It also sets the maximum TLS version to TLS 1.0, constraining the connection to TLS 1.0 only. Because ATS doesn't apply to Network.framework, this configuration is not mitigated by any ATS policy and the connection will succeed against a TLS 1.0 server.
MastgTest.swift 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 import Foundation
import Network
struct MastgTest {
// SUMMARY : This sample demonstrates NWProtocolTLS . Options configured with an insecure minimum TLS version , bypassing ATS entirely .
static func mastgTest ( completion : @escaping ( String ) -> Void ) {
var result = "Testing Network.framework TLS configuration: \n\n "
let host = NWEndpoint . Host ( "tls-v1-0.badssl.com" )
let port = NWEndpoint . Port ( rawValue : 1010 ) !
// FAIL : Minimum TLS version set to TLS 1.0 for a Network . framework connection .
let tlsOptions = NWProtocolTLS . Options ()
sec_protocol_options_set_min_tls_protocol_version (
tlsOptions . securityProtocolOptions ,
. TLSv10
)
// Optional , but useful for the demo because this endpoint only supports TLS 1.0 .
sec_protocol_options_set_max_tls_protocol_version (
tlsOptions . securityProtocolOptions ,
. TLSv10
)
// Make the TLS server name explicit .
sec_protocol_options_set_tls_server_name (
tlsOptions . securityProtocolOptions ,
"tls-v1-0.badssl.com"
)
let parameters = NWParameters ( tls : tlsOptions )
let connection = NWConnection ( host : host , port : port , using : parameters )
var didComplete = false
func finish ( _ text : String ) {
guard ! didComplete else { return }
didComplete = true
connection . cancel ()
DispatchQueue . main . async {
completion ( text )
}
}
connection . stateUpdateHandler = { state in
switch state {
case . ready :
result += "TLS connection established. \n "
result += "Host: tls-v1-0.badssl.com \n "
result += "Port: 1010 \n "
result += "Minimum TLS version: TLS 1.0 \n\n "
let request =
"""
GET / HTTP/1.1\r
Host: tls-v1-0.badssl.com\r
Connection: close\r
\r
"""
connection . send (
content : request . data ( using : . utf8 ),
completion : . contentProcessed { sendError in
if let sendError = sendError {
finish ( result + "HTTP request send failed: \(sendError) \n " )
return
}
receiveResponse (
connection : connection ,
result : result ,
finish : finish
)
}
)
case . failed ( let error ):
finish ( result + "Connection failed: \(error) \n " )
case . cancelled :
break
default :
break
}
}
connection . start ( queue : . main )
}
private static func receiveResponse (
connection : NWConnection ,
result : String ,
finish : @escaping ( String ) -> Void
) {
var mutableResult = result
connection . receive ( minimumIncompleteLength : 1 , maximumLength : 4096 ) { data , _ , isComplete , error in
if let data = data , ! data . isEmpty {
let text = String ( data : data , encoding : . utf8 ) ?? "<non UTF8 response data>"
mutableResult += "Received response: \n "
mutableResult += text
mutableResult += " \n "
}
if let error = error {
finish ( mutableResult + "Receive failed: \(error) \n " )
return
}
if isComplete {
finish ( mutableResult + " \n Connection closed by server. \n " )
return
}
receiveResponse (
connection : connection ,
result : mutableResult ,
finish : finish
)
}
}
}
Steps
Unzip the app package and locate the main binary file ( Exploring the App Package ), which in this case is ./Payload/MASTestApp.app/MASTestApp.
Run radare2 (iOS) with the script to search for calls to sec_protocol_options_set_min_tls_protocol_version in the binary.
nw_tls.r2 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 e asm . bytes = false
e scr . color = false
e asm . var = false
? e References to sec_protocol_options_set_min_tls_protocol_version :
f ~ sec_protocol_options_set_min_tls_protocol_version
? e
? e xrefs to sec_protocol_options_set_min_tls_protocol_version :
axt @ 0x10000c240
? e
? e Import stub for sec_protocol_options_set_min_tls_protocol_version :
pd -- 10 @ 0x1000099e8
? e
? e Search for ARM64 instructions that load TLS constants into w1 :
? e 0x0301 , TLS 1.0
? e 0x0302 , TLS 1.1
? e 0x0303 , TLS 1.2
? e 0x0304 , TLS 1.3
/ x 21608052
/ x 41608052
/ x 61608052
/ x 81608052
? e
? e Print the surrounding instructions for the mov w1 , 0x301 instruction that sets TLS 1.0 :
pd -- 10 @ 0x1000041d4
The r2 script works in three stages:
First, it looks up the symbol and cross-references for sec_protocol_options_set_min_tls_protocol_version to confirm the function is imported and to find where it is called.
Second, it disassembles the import stub to show the indirect call structure.
Third, it searches the binary for ARM64 mov w1 instructions that load each of the known TLS protocol constants immediately before the function call. On ARM64, w1 holds the second argument of a C function call (the first being the sec_protocol_options_t handle in x0). The TLS constants are 0x0301 (TLS 1.0), 0x0302 (TLS 1.1), 0x0303 (TLS 1.2), and 0x0304 (TLS 1.3). Each produces a fixed 4-byte encoding, so the script searches for those byte sequences directly, then disassembles the surrounding instructions.
run.sh #!/bin/bash
r2 - q - i nw_tls . r2 - A MASTestApp > output . asm
Observation
The output shows the imported symbol, its cross-references, the import stub, and the byte-pattern search results:
output.asm 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 References to sec_protocol_options_set_min_tls_protocol_version :
0x1000099e4 12 sym . imp . sec_protocol_options_set_min_tls_protocol_version
0x10000c240 8 reloc . sec_protocol_options_set_min_tls_protocol_version
xrefs to sec_protocol_options_set_min_tls_protocol_version :
sym . imp . sec_protocol_options_set_min_tls_protocol_version 0x1000099e8 [ DATA : r -- ] ldr x16 , reloc . sec_protocol_options_set_min_tls_protocol_version
Import stub for sec_protocol_options_set_min_tls_protocol_version :
; CALL XREF from sym.func. 100005 b50 @ 0x100005d78 ( x )
┌ 12 : void sym . imp . objc_retainAutoreleasedReturnValue ( void * instance );
│ 0x1000099c0 adrp x16 , segment . __DATA_CONST ; 0x10000c000
│ 0x1000099c4 ldr x16 , [ x16 , 0xb0 ] ; [ 0x10000c0b0 : 4 ] = 20
│ ; reloc . objc_retainAutoreleasedReturnValue
└ 0x1000099c8 br x16
; CALL XREF from sym.func. 100009068 @ 0x1000091f0 ( x )
┌ 12 : void sym . imp . rewind ( FILE * stream );
│ 0x1000099cc adrp x16 , segment . __DATA_CONST ; 0x10000c000
│ 0x1000099d0 ldr x16 , [ x16 , 0x138 ] ; [ 0x10000c138 : 4 ] = 37 ; "%"
└ 0x1000099d4 br x16
; CALL XREF from sym.func. 100004000 @ 0x1000041f4 ( x )
┌ 12 : sym . imp . sec_protocol_options_set_max_tls_protocol_version ();
│ 0x1000099d8 adrp x16 , segment . __DATA_CONST ; 0x10000c000
│ 0x1000099dc ldr x16 , [ x16 , 0x238 ] ; [ 0x10000c238 : 4 ] = 69 ; "E"
└ 0x1000099e0 br x16
; CALL XREF from sym.func. 100004000 @ 0x1000041d8 ( x )
┌ 12 : sym . imp . sec_protocol_options_set_min_tls_protocol_version ();
│ 0x1000099e4 adrp x16 , segment . __DATA_CONST ; 0x10000c000
│ 0x1000099e8 ldr x16 , [ x16 , 0x240 ] ; [ 0x10000c240 : 4 ] = 70 ; "F"
└ 0x1000099ec br x16
; CALL XREF from sym.func. 100004000 @ 0x100004214 ( x )
┌ 12 : sym . imp . sec_protocol_options_set_tls_server_name ();
│ 0x1000099f0 adrp x16 , segment . __DATA_CONST ; 0x10000c000
│ 0x1000099f4 ldr x16 , [ x16 , 0x248 ] ; [ 0x10000c248 : 4 ] = 71 ; "G"
└ 0x1000099f8 br x16
; CALL XREF from sym.func. 100009068 @ 0x100009314 ( x )
┌ 12 : int sym . imp . sscanf ( const char * s , const char * format , ... );
│ 0x1000099fc adrp x16 , segment . __DATA_CONST ; 0x10000c000
│ 0x100009a00 ldr x16 , [ x16 , 0x140 ] ; [ 0x10000c140 : 4 ] = 38 ; "&"
└ 0x100009a04 br x16
; XREFS : CALL 0x100004120 CALL 0x1000041b8 CALL 0x10000423c CALL 0x1000042c8 CALL 0x1000042e4
; XREFS : CALL 0x100004564 CALL 0x100004a30 CALL 0x100004d10 CALL 0x100004d30 CALL 0x100004d4c
; XREFS : CALL 0x100005444 CALL 0x100005460 CALL 0x1000061c8 CALL 0x100006240 CALL 0x100006548
; XREFS : CALL 0x10000673c
┌ 12 : sym . imp . swift_allocObject ();
│ 0x100009a08 adrp x16 , segment . __DATA_CONST ; 0x10000c000
│ 0x100009a0c ldr x16 , [ x16 , 0x608 ] ; [ 0x10000c608 : 4 ] = 191
│ ; reloc . swift_allocObject
Search for ARM64 instructions that load TLS constants into w1 :
0x0301 , TLS 1.0
0x0302 , TLS 1.1
0x0303 , TLS 1.2
0x0304 , TLS 1.3
0x1000041d4 hit4_0 21608052
0x1000041f0 hit4_1 21608052
Print the surrounding instructions for the mov w1 , 0x301 instruction that sets TLS 1.0 :
│ 0x1000041ac bl sym . imp . Network . NWProtocolTLS . Options . allocator .. metadata . accessor_ ... a_ ; Network . NWProtocolTLS . Options . allocator .. metadata . accessor ( ... a )
│ 0x1000041b0 ldr w1 , [ x0 , 0x30 ]
│ 0x1000041b4 ldrh w2 , [ x0 , 0x34 ]
│ 0x1000041b8 bl sym . imp . swift_allocObject
│ 0x1000041bc mov x20 , x0
│ 0x1000041c0 bl sym . imp . Network . NWProtocolTLS . Options . allocator . bool : _allocator__Options . bool : _allocator__C . _ ... cfc_ ; Network . NWProtocolTLS . Options . allocator . bool : allocator , Options . bool : allocator , C . ( ... cfc )
│ 0x1000041c4 mov x26 , x0
│ 0x1000041c8 mov x20 , x0
│ 0x1000041cc bl sym . imp . Network . NWProtocolTLS . Options . allocator . securityProtocol_ ... vgTj_ ; Network . NWProtocolTLS . Options . allocator . securityProtocol ( ... vgTj )
│ 0x1000041d0 mov x20 , x0
│ ; -- _0 :
│ 0x1000041d4 mov w1 , 0x301
│ 0x1000041d8 bl sym . imp . sec_protocol_options_set_min_tls_protocol_version
│ 0x1000041dc mov x0 , x20
│ 0x1000041e0 bl sym . imp . swift_unknownObjectRelease
│ 0x1000041e4 mov x20 , x26
│ 0x1000041e8 bl sym . imp . Network . NWProtocolTLS . Options . allocator . securityProtocol_ ... vgTj_ ; Network . NWProtocolTLS . Options . allocator . securityProtocol ( ... vgTj )
│ 0x1000041ec mov x20 , x0
│ ; -- _1 :
│ 0x1000041f0 mov w1 , 0x301
│ 0x1000041f4 bl sym . imp . sec_protocol_options_set_max_tls_protocol_version
│ 0x1000041f8 mov x0 , x20
Evaluation
The test case fails because the binary calls both sec_protocol_options_set_min_tls_protocol_version and sec_protocol_options_set_max_tls_protocol_version with 0x0301 loaded in w1, which corresponds to TLS 1.0.
The relevant sequence in the output is:
0 x1000041cc bl sym.imp.Network.NWProtocolTLS.Options.allocator.securityProtocol_...vgTj_
0 x1000041d0 mov x20 , x0 ; sec_protocol_options_t handle
0 x1000041d4 mov w1 , 0x301 ; second argument: TLS 1.0
0 x1000041d8 bl sym.imp.sec_protocol_options_set_min_tls_protocol_version
0 x1000041e8 bl sym.imp.Network.NWProtocolTLS.Options.allocator.securityProtocol_...vgTj_
0 x1000041ec mov x20 , x0 ; sec_protocol_options_t handle
0 x1000041f0 mov w1 , 0x301 ; second argument: TLS 1.0
0 x1000041f4 bl sym.imp.sec_protocol_options_set_max_tls_protocol_version
On ARM64, C function arguments are passed in x0, x1, x2, ... (or their 32-bit aliases w0, w1, w2, ...). Here x0 carries the sec_protocol_options_t handle retrieved from NWProtocolTLS.Options.securityProtocolOptions, and w1 carries the TLS version constant. The value 0x0301 corresponds to tls_protocol_version_TLSv10 (TLS 1.0).
The two calls are equivalent to:
sec_protocol_options_set_min_tls_protocol_version ( tlsOptions . securityProtocolOptions , . TLSv10 )
sec_protocol_options_set_max_tls_protocol_version ( tlsOptions . securityProtocolOptions , . TLSv10 )
This pins the connection to TLS 1.0 only. Because Network.framework operates entirely outside of ATS, this configuration is not subject to any ATS enforcement and the connection will succeed against a TLS 1.0 server.