[tor-bugs] #24037 [Core Tor/Torsocks]: Use syscall blacklist rather than whitelist for torsocks

Tor Bug Tracker & Wiki blackhole at torproject.org
Sat Oct 28 09:49:48 UTC 2017


#24037: Use syscall blacklist rather than whitelist for torsocks
-----------------------------------+---------------------
     Reporter:  cypherpunks        |      Owner:  dgoulet
         Type:  enhancement        |     Status:  new
     Priority:  Medium             |  Milestone:
    Component:  Core Tor/Torsocks  |    Version:
     Severity:  Normal             |   Keywords:
Actual Points:                     |  Parent ID:
       Points:                     |   Reviewer:
      Sponsor:                     |
-----------------------------------+---------------------
 Torsocks hooks network-related syscalls using LD_PRELOAD, by modifying
 their arguments and forcing them through the `syscall()` libc function.
 Currently, this works by disallowing syscalls by default and explicitly
 whitelisting "safe" syscalls. Unfortunately, this whitelisting must be
 done explicitly, with matching function signatures, and each time a new
 syscall is used, torsocks breaks. I can think of two solutions.

 **Solution one**
 On 64-bit Linux, all syscalls use exactly 6 arguments, each of type
 `unsigned long`, so there is no actual need to copy the function
 signature. For syscalls which do not take 6 arguments, the extra ones are
 ignored. As such, `syscall(SYS_fork, a0, a1, a2, a3, a4, a5)` is valid
 even though `fork()` does not take any arguments. This can be exploited by
 torsocks by only examining the syscall number, and passing all arguments
 to `syscall()`. Syscalls which are network-related can be redirected to
 their torsocks counterparts.

 An example `tsocks_syscall()` replacement that is called whenever a
 syscall is invoked through its libc wrapper:
 {{{
 static unsigned long handle_syscall(int nr, va_list args)
 {
     unsigned long a0, a1, a2, a3, a4, a5;

     /* get the arguments passed to this syscall (must always be 6)
     a0 = va_args(args, unsigned long);
     a1 = va_args(args, unsigned long);
     a2 = va_args(args, unsigned long);
     a3 = va_args(args, unsigned long);
     a4 = va_args(args, unsigned long);
     a5 = va_args(args, unsigned long);

     switch (nr) {
     /* hook networking syscalls */
     case SYS_socket:
         return tsocks_socket(a0, a1, a2);
     case SYS_listen:
         return tsocks_listen(a0, a1);
     case SYS_connect:
         return tsocks_connect(a0, a1, a2);
     case SYS_accept:
         return tsocks_accept(a0, a1, a2);
     /* allow all non-networking syscalls through */
     default:
         return syscall(nr, a0, a1, a2, a3, a4, a5);
     }
 }
 }}}

 For additional safety, a check could be placed at low-level libc syscall
 functions where all calls converge. This would have the advantage of not
 needing to track new libc functions which may end up using the network,
 for example `res_query()` which was added to glibc and took a while to be
 supported. Tracking only the syscall numbers makes this much easier.

 **Solution two**
 An alternative, and potentially easier and more secure technique would be
 to use libseccomp. This library allows creating filters for mode 2
 SECCOMP, a Linux kernel feature which allows whitelisting or blacklisting
 individual syscalls based on number or argument contents. Even if the
 application intentional or unintentionally calls a syscall directly (e.g.
 by using a currently unhooked libc function or by making the syscall in
 assembly), illegal syscalls still get blocked. The only time they would
 not be blocked is if they contain safe arguments, or go through torsocks'
 internal networking functions (which would use `AF_UNIX` as the domain
 type, allowing only network access through UNIX domain sockets). All other
 network-related syscalls would then be safe to call (to the best of my
 knowledge), because the only valid socket file descriptors would be ones
 pointing to a UNIX domain socket.

 A simple example to set up a syscall filter to deny all calls to
 `socket()` which do not use `AF_UNIX`:
 {{{
 static int enable_seccomp(void)
 {
     int rc;
     scmp_filter_ctx ctx;

     /* allow all syscalls by default */
     ctx = seccomp_init(SCMP_ACT_ALLOW);
     if (ctx == NULL)
         return -1;

     /* blacklist socket() if the first argument is not AF_UNIX */
     rc = seccomp_rule_add(ctx, SCMP_ACT_TRAP, SCMP_SYS(socket), 1,
         SCMP_A0(SCMP_CMP_NE, AF_UNIX));
     if (rc < 0)
         goto out;

     /* load the filter and enforce it */
     rc = seccomp_load(ctx);
     if (rc < 0)
         goto out;

 out:
     seccomp_release(ctx);
     return -rc;
 }
 }}}

 With `SCMP_ACT_TRAP`, a violation will raise `SIGSYS`. Torsocks can trap
 the signal and output a diagnostic message, aiding the debugging of
 applications which may be unsupported.

--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/24037>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online


More information about the tor-bugs mailing list