how to get the pid of NEFilterFlow in handleNewFlow of Network Extension?

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!

Accepted Reply

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"
  • How would it be possible to track the code identity with SecCodeCopyDesignatedRequirement? SecRequirement is only defined as a class implementing Hashable, so it cannot be stored and loaded again between app launches.

Add a Comment

Replies

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"

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!

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"
  • How would it be possible to track the code identity with SecCodeCopyDesignatedRequirement? SecRequirement is only defined as a class implementing Hashable, so it cannot be stored and loaded again between app launches.

Add a Comment

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!

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?

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"
  • Isn't there a third possibility: that the code is generated by someone who is not an attacker?

Add a Comment

Hi Eskimo,


Thank you very much!

I will try your suggestion.

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!

I tried a small tool by using

WKWebView
, and I can't filter it's flow.

Weird. I’m not sure what’s going on there. Please file a bug about this, and then post your bug number.

Is there a same API in network extension?

No, because a content filter can only filter, it can’t modify the traffic. You have two options here:

  • The content filter can notify the user via other means. For example, the sample code using IPC to route such prompts to its container app.

  • You can build a transparent proxy, which lets you modify the traffic.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Eskimo,



I filed a bug : FB7315246


I will try your suggestion for modifing the traffic.


Thank you very much!

I filed a bug : FB7315246

Thanks for that. I ran this past the NetworkExtension team and they confirmed that it shouldn’t be necessary to do anything special to filter WebKit traffic on macOS. Your bug will be the impetus for them to investigate why that’s not working.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Eskimo,


I am sorry, but I found whtat is the problom for not filtering WebKit traffic.

For Webkit traffic, socketFlow.localEndpoint is nil, so the sample code just ignore it.


I have closed the bug : FB7315246.


Sorry for troubling you so much, and thank you very much for your greate help!

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

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!!!

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"