Skip to content

MASTG-DEMO-0086: Uses of BSD Sockets Bypassing ATS

Download MASTG-DEMO-0086 IPA Open MASTG-DEMO-0086 Folder Build MASTG-DEMO-0086 IPA

Sample

The code sample code uses BSD sockets directly to establish a connection to httpbin.org on port 80. The demo doesn't send any data over the connection, but for the purposes of this demo, assume that it does.

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
import Foundation

struct MastgTest {
    // SUMMARY: This sample demonstrates the use of BSD sockets that bypass ATS.

    static func mastgTest(completion: @escaping (String) -> Void) {
        var result = "Testing BSD socket connection (bypasses ATS):\n\n"

        // FAIL: [MASTG-TEST-0322] Using BSD sockets bypasses ATS
        let host = "httpbin.org"
        let port: UInt16 = 80

        // Resolve hostname to IP address
        var hints = addrinfo()
        hints.ai_family = AF_INET
        hints.ai_socktype = SOCK_STREAM
        hints.ai_protocol = IPPROTO_TCP

        var serverInfo: UnsafeMutablePointer<addrinfo>?
        let status = getaddrinfo(host, String(port), &hints, &serverInfo)

        guard status == 0, let info = serverInfo else {
            result += "Failed to resolve hostname: \(String(cString: gai_strerror(status)))\n"
            completion(result)
            return
        }

        defer { freeaddrinfo(serverInfo) }

        // Create socket - this bypasses ATS entirely
        let sock = socket(info.pointee.ai_family, info.pointee.ai_socktype, info.pointee.ai_protocol)

        guard sock >= 0 else {
            result += "Failed to create socket\n"
            completion(result)
            return
        }

        defer { close(sock) }

        // Connect to server using raw socket
        let connectResult = connect(sock, info.pointee.ai_addr, info.pointee.ai_addrlen)

        guard connectResult == 0 else {
            result += "Failed to connect: \(String(cString: strerror(errno)))\n"
            completion(result)
            return
        }

        result += "Socket connection established to httpbin.org:80 (cleartext)\n"
        result += "This connection bypasses ATS because it uses BSD sockets directly\n"

        // Send HTTP request over cleartext socket
        let request = "GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n"
        let sendResult = request.withCString { ptr in
            send(sock, ptr, strlen(ptr), 0)
        }

        if sendResult > 0 {
            result += "HTTP request sent successfully over cleartext socket connection\n"

            // Receive response
            var buffer = [CChar](repeating: 0, count: 1024)
            let recvResult = recv(sock, &buffer, buffer.count - 1, 0)

            if recvResult > 0 {
                let response = String(cString: buffer)
                let firstLine = response.components(separatedBy: "\r\n").first ?? ""
                result += "Received response: \(firstLine)\n"
            }
        } else {
            result += "Failed to send request\n"
        }

        completion(result)
    }
}

Steps

  1. Unzip the app package and locate the main binary file ( Exploring the App Package), which in this case is ./Payload/MASTestApp.app/MASTestApp.
  2. Run radare2 (iOS) with the script to search for BSD socket APIs in the binary.
bsd_sockets.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
e asm.bytes=false
e scr.color=false
e asm.var=false

?e Uses of the BSD sockets functions:
ii~getaddrinfo,send,recv,connect,socket

?e

?e xrefs to getaddrinfo:
axt @ 0x1000069c4

?e

?e Use of getaddrinfo:

# Seek to the function where getaddrinfo is called
pd-- 20 @ 0x1000041d0

?e

?e Value passed to getaddrinfo:

? 0x50~uint32[1]
run.sh
1
r2 -q -i bsd_sockets.r2 -A MASTestApp > output.asm

Observation

The output contains references to BSD socket APIs found in the binary:

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
Uses of the BSD sockets functions:
139 0x100006940 NONE FUNC               connect
150 0x1000069c4 NONE FUNC               getaddrinfo
155 0x100006a00 NONE FUNC               recv
157 0x100006a18 NONE FUNC               send
158 0x100006a24 NONE FUNC               socket

xrefs to getaddrinfo:
sym.func.100004108 0x1000041d0 [CALL:--x] bl sym.imp.getaddrinfo

Use of getaddrinfo:
           0x100004180      str w8, [arg_f0hx5c]
           0x100004184      str xzr, [var_48h]
           0x100004188      mov w8, 0x50                              ; 'P'
           0x10000418c      strh w8, [var_30h]
           0x100004190      adrp x0, segment.__DATA_CONST             ; 0x100008000
           0x100004194      ldr x0, [x0, 0x3e8]                       ; [0x1000083e8:4]=124 ; "|"
           0x100004198      adrp x1, segment.__DATA_CONST             ; 0x100008000
           0x10000419c      ldr x1, [x1, 0x3f0]                       ; [0x1000083f0:4]=125 ; "}"
           0x1000041a0      add x20, sp, 0x30
           0x1000041a4      bl sym.imp.CustomStringConvertible.description__String_...vgTj_ ; CustomStringConvertible.description__String(...vgTj)
           0x1000041a8      mov x20, x1
           0x1000041ac      bl sym.imp.utf8CString.ContiguousArray.setter...Vys4Int8VGvg
           0x1000041b0      mov x24, x0
           0x1000041b4      mov x0, x20                               ; void *arg0
           0x1000041b8      bl sym.imp.swift_bridgeObjectRelease      ; void swift_bridgeObjectRelease(void *arg0)
           0x1000041bc      adrp x0, 0x100006000
           0x1000041c0      add x0, x0, 0xd50                         ; 0x100006d50 ; "httpbin.org"
           0x1000041c4      add x1, x24, 0x20
           0x1000041c8      add x2, sp, 0x50
           0x1000041cc      add x3, sp, 0x48
           0x1000041d0      bl sym.imp.getaddrinfo
           0x1000041d4      mov x21, x0
           0x1000041d8      mov x0, x24                               ; void *arg0
           0x1000041dc      bl sym.imp.swift_release                  ; void swift_release(void *arg0)
       ┌─< 0x1000041e0      cbz w21, 0x10000429c
          ; CODE XREF from sym.func.100004108 @ 0x1000042a0(x)
          0x1000041e4      mov x8, -0x2000000000000000
          0x1000041e8      stp xzr, x8, [var_30h]
          0x1000041ec      add x20, sp, 0x30
          0x1000041f0      mov w0, 0x1f
          0x1000041f4      bl sym.imp._StringGuts.grow_...SiF_       ; _StringGuts.grow(...SiF)
          0x1000041f8      ldr x0, [arg0]                            ; void *arg0
          0x1000041fc      bl sym.imp.swift_bridgeObjectRelease      ; void swift_bridgeObjectRelease(void *arg0)
          0x100004200      adrp x8, 0x100006000
          0x100004204      add x8, x8, 0xd60                         ; 0x100006d60 ; "Failed to resolve hostname: "
          0x100004208      sub x8, x8, 0x20
          0x10000420c      orr x8, x8, 0x8000000000000000
          0x100004210      add x9, x27, 9
          0x100004214      stp x9, x8, [var_30h]
          0x100004218      mov x0, x21
          0x10000421c      bl sym.imp.gai_strerror

Value passed to getaddrinfo:
80

Evaluation

The test fails because the app uses BSD sockets directly, including socket, connect, send, recv, and getaddrinfo. The binary imports these symbols and calls them to create a cleartext network connection that bypasses ATS. The getaddrinfo call resolves the hostname httpbin.org and is supplied with a service value representing port 80.

The signature of the getaddrinfo function from the Darwin libc man pages is as follows:

int getaddrinfo(
    const char *restrict nodename,
    const char *restrict servname,
    const struct addrinfo *restrict hints,
    struct addrinfo **restrict res
);

Where nodename is the host (e.g., "example.com") and servname is the service name or port string (e.g., "http" or "80").

At 0x100004188 the code loads w8 with 0x50 and stores it as a halfword at var_30h, then sets x20 to sp + 0x30 and later calls utf8CString.ContiguousArray.setter. This sequence uses the numeric value 0x50, which is 80 in decimal, and converts it into a NUL terminated UTF8 string buffer.

At 0x1000041c4 the code sets x1 to x24 + 0x20, which points to the previous string buffer, and passes it into getaddrinfo as the servname parameter. The nodename parameter in x0 points to the literal httpbin.org. The hints and res pointers are passed via stack addresses sp + 0x50 and sp + 0x48.