15 Replies
      Latest reply on May 25, 2020 3:28 AM by eskimo
      weichao119 Level 1 Level 1 (0 points)

        Hi,

         

        I am studying of Network Extension to filter network traffic on OSX 10.15 beta version.

        I can run the demo on

        https://developer.apple.com/documentation/networkextension/filtering_network_traffic

         

        But when I try to filter the outbound stream, I can't get the info of which process setup the new flow in handleNewFlow function.

        I need the process info to decide whether to do the following filter.

         

        Is there any method to get the process info?

         

        I noticed that there is a sourceAppIdentifier property for NEFilterFlow in the document, but it seems no such property in real.

        And there is  a sourceAppAuditToken property, how can I get the process info from this property?

         

        Thank you very much!

        • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
          eskimo Apple Staff Apple Staff (13,895 points)

          What are you planning to do with the PID?  In general, using PIDs is a bad idea because PIDs can be reused (search the ’net for “pid reuse attack”).

          As such, sourceAppAuditToken is your friend.  If you need to map that to properties of the code, you can feed it into the code signing APIs (specifically, SecCodeCopyGuestWithAttributes with the kSecGuestAttributeAudit attribute).

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
              weichao119 Level 1 Level 1 (0 points)

              Hi  Eskimo,

               

              Thank you for your answer.

               

              I want to get the PID's binary's path with PID (I can use proc_selfpid() API in kernel to get it with socket filter, and then get it's binary's path).

              If the path match the setting, I can decide to filter it or let it go.

               

              And I also need to log the binary's path if I block the new flow to show to customers.

               

              Is there a methord to get the binary path with Network Extension?

               

              And more questions about sourceAppAuditToken:

              1.If a command line tool is accessing the network (such as curl), can I get this sourceAppAuditToken?

              2.If an unsigned command line tool is accessing the network, can I also get this sourceAppAuditToken?

               

              Thank you very much!

                • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                  eskimo Apple Staff Apple Staff (13,895 points)

                  I want to get the PID's binary's path with PID (I can use proc_selfpid API in kernel to get it with socket filter, and then get it's binary's path).

                  If the path match the setting, I can decide to filter it or let it go.

                  OK, so, to be clear, this is an example of what you’re currently doing, and you’re looking for info on how to do the equivalent in a NetworkExtension world, right?

                  If so, sourceAppAuditToken is definitely your friend.  You can use SecCodeCopyGuestWithAttributes with the kSecGuestAttributeAudit attribute to map it to a code object (SecCode) and then use various code signing routines to get properties from that code object.

                  IMPORTANT One of those routines is SecCodeCopyPath, although I strongly advise you to not track code identity by path.  The problem with doing that is that the user can move code around on the disk, and that will confuse your tracking.  It is much better to track code via its code signature, and you can get information about its code signature using SecCodeCopySigningInformation and, most critically, SecCodeCopyDesignatedRequirement.

                  If a command line tool is accessing the network (such as curl), can I get this sourceAppAuditToken?

                  Yes.  All code on our system is signed, and signed code always has a designated requirement (DR).  For example, the DR for curl is:

                  $ codesign -d --requirements - `which curl`
                  Executable=/usr/bin/curl
                  designated => identifier "com.apple.curl" and anchor apple

                  If an unsigned command line tool is accessing the network, can I also get this sourceAppAuditToken?

                  Yes, but you won’t be able to map this to a code signature as explained above because the code is not signed.  My recommendation is that you simply block all unsigned code.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"

                    • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                      weichao119 Level 1 Level 1 (0 points)

                      Hi  Eskimo,

                       

                      Thank you very much for your great help!

                      I will try to do this.

                       

                      And I have another related question:

                      There is a NEFilterBrowserFlow class on IOS, but not on Mac.

                       

                      Can I filter the browser's flow with NEFilterSocketFlow?

                      I did some test with the demo, but it seems can't get the flow from Safari.

                      Is there any setting?

                       

                      Thank you very much!

                      • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                        weichao119 Level 1 Level 1 (0 points)

                        Hi Eskimo,

                         

                        I tried your suggestion, and it worked!

                        Thank you very much!

                         

                        But one more question:

                         

                        If an unsigned command line tool is accessing the network, can I also get this sourceAppAuditToken?

                        Yes, but you won’t be able to map this to a code signature as explained above because the code is not signed.  My recommendation is that you simply block all unsigned code.

                         

                        If I block the flow, how do I show customer which process is blocked?If customer think it should be allowed, he maybe add it to exception list. How can I do this with unsigned code?

                         

                        And I  still can't get the flow from Safari.Could you please help?

                          • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                            eskimo Apple Staff Apple Staff (13,895 points)

                            There is a NEFilterBrowserFlow class on iOS, but not on Mac.

                            Correct.

                            Can I filter the browser's flow with NEFilterSocketFlow?

                            I would expect so, yes.  The absence of NEFilterBrowserFlow simply means that you don’t get the nice features associated with that class; the browser still generates traffic on the wire and this should be seen by the socket flow.

                            I did some test with the demo, but it seems can't get the flow from Safari.

                            Weird.  I don’t have an easy explanation for that.

                            If you create a small web view test app (using WKWebView), can you see its traffic?


                            If I block the flow, how do I show customer which process is blocked? If customer think it should be allowed, he maybe add it to exception list.  How can I do this with unsigned code?

                            Have you tried calling SecCodeCopySigningInformation on that audit token?  It’s possible it’ll return some info even though the process isn’t signed.

                            If not, you have to drop down to much lower-level code.  Specifically, you can get various properties from an audit token using the API in <bsm/libbsm.h>, for example, audit_token_to_pid.

                            WARNING To reiterate my comment above, identifying code by PID is a terrible idea security-wise.  Only do this when dealing with unsigned code.  Even then, if I were doing this I’d simply block all unsigned code.  There are two source of unsigned code:

                            • Code that the user has created themselves (A)

                            • Code generated by an attacker (B)

                            Any user who is smart enough to do A is smart enough to sign that code (even if it’s only ad hoc signing).  And by not holding the line here, you’re opening yourself up to B.

                            Share and Enjoy

                            Quinn “The Eskimo!”
                            Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                            let myEmail = "eskimo" + "1" + "@apple.com"

                              • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                                weichao119 Level 1 Level 1 (0 points)

                                Hi Eskimo,

                                 

                                Thank you very much!

                                I will try your suggestion.

                                • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                                  weichao119 Level 1 Level 1 (0 points)

                                  Hi Eskimo,

                                   

                                  Sorry for bothering you again.

                                   

                                  I tried a small tool by using WKWebView, and I can't filter it's flow.

                                  With the same network extension demo, I can filter others, such as  /usr/bin/curl, /usr/sbin/httpd.

                                   

                                  Do you have any suggestion?

                                   

                                  And a new question is:

                                  There is a sock_inject_data_in API in kernel for injecting special content to socket.

                                  I use this API to inject the block reason to socket to show customer why the connection is blocked.

                                   

                                  I didn't find such API in current network extension document.

                                  Is there a same API in network extension?

                                   

                                  Thank you very much!

                                  • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                                    sadasdasrewrew Level 1 Level 1 (0 points)

                                    Hello,

                                    Thank you for these answers, it helped me a lot in my reasearch.

                                     

                                    For my usecase I need to find out the PID of the process that makes the request. I understand that I can use "audit_token_to_pid". But this function accepts a "audit_token_t" parameter.

                                    How can I convert the "flow.sourceAppAuditToken" (which is a Data type) to the required "audit_token_t" type?

                                    Any hints?

                                     

                                    Thank you again

                                      • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                                        vishal1kumar Level 1 Level 1 (0 points)

                                        I have struggled enough to drive (collect data from multiple sources) pid, gid, uid, process name out from flow metadata 'sourceAppAuditToken'. I will be really glad if below code would save time for others. I have computed said parameters for NEAppProxyTCPFlow but same cvan be done for 'NEFilerFlow::sourceAppAuditToken':

                                         

                                        extension NEAppProxyTCPFlow

                                        {

                                            private var sourceAppAuditTokenQ: audit_token_t? {

                                                guard

                                                    let tokenData = self.metaData.sourceAppAuditToken,

                                                    tokenData.count == MemoryLayout<audit_token_t>.size

                                                else { return nil }

                                                return tokenData.withUnsafeBytes { buf in

                                                    buf.baseAddress!.assumingMemoryBound(to: audit_token_t.self).pointee

                                                }

                                            }

                                            var pid: pid_t {

                                                return audit_token_to_pid(sourceAppAuditTokenQ!)

                                            }

                                          

                                            var uid: uid_t {

                                                return audit_token_to_ruid(sourceAppAuditTokenQ!)

                                            }

                                            var gid: gid_t {

                                                return audit_token_to_rgid(sourceAppAuditTokenQ!)

                                            }

                                          

                                            var processPath: String? {

                                                var codeQ: SecCode? = nil

                                                var err = SecCodeCopyGuestWithAttributes(nil, [kSecGuestAttributeAudit: self.metaData.sourceAppAuditToken as Any] as NSDictionary, [], &codeQ)

                                                guard err == errSecSuccess else {

                                                    return nil

                                                }

                                                let code = codeQ!

                                                var staticCodeQ: SecStaticCode? = nil

                                                err = SecCodeCopyStaticCode(code, [], &staticCodeQ) // Convert that to a static code.

                                                guard err == errSecSuccess else {

                                                    return nil

                                                }

                                                let staticCode = staticCodeQ!

                                                var pathCodeQ: CFURL?

                                                err = SecCodeCopyPath(staticCode, SecCSFlags(rawValue: 0), &pathCodeQ);

                                                guard err == errSecSuccess else {

                                                    return nil

                                                }

                                              

                                                let posixPath = CFURLCopyFileSystemPath(pathCodeQ, CFURLPathStyle.cfurlposixPathStyle);

                                                let posixPathStr: String = (posixPath! as NSString) as String

                                                //strdup(CFStringGetCStringPtr(posixPath, CFStringBuiltInEncodings.UTF8.rawValue));

                                                return posixPathStr

                                            }

                                        }

                                         

                                        enjoy!!!

                                          • Re: how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?
                                            eskimo Apple Staff Apple Staff (13,895 points)

                                            Thanks for sharing.

                                            Three things:

                                            • You don’t need CFURLCopyFileSystemPath here.  CFURL is toll-free bridged to NSURL which can be cast to URL.  To wit:

                                              let posixPathStr = (pathCodeQ! as URL).path

                                              .

                                            • The code within processPath is likely to be slow, so you should cache the results.  Key that cache of the audit token, not the PID.

                                            • Because the process path can change, it’s best not to rely on it for security decisions.

                                            Share and Enjoy

                                            Quinn “The Eskimo!”
                                            Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                            let myEmail = "eskimo" + "1" + "@apple.com"