{"id":153,"date":"2020-11-08T20:54:02","date_gmt":"2020-11-08T20:54:02","guid":{"rendered":"http:\/\/lockedbyte.com\/blog\/?p=153"},"modified":"2021-05-17T12:41:08","modified_gmt":"2021-05-17T12:41:08","slug":"kipodafterfree-ctf-2020-shadow-stuck","status":"publish","type":"post","link":"https:\/\/lockedbyte.com\/blog\/index.php\/2020\/11\/08\/kipodafterfree-ctf-2020-shadow-stuck\/","title":{"rendered":"KipodAfterFree CTF 2020 &#8211; Shadow stuck"},"content":{"rendered":"\nThis challenge was from KipodAfterFree CTF 2020\n\n The challenge was interesting as it is an implementation of a Shadow stack to save a backup of saved RIP addresses and compare them before function returns, that is a good way to detect Buffer overflow exploitation attemps and block them.<br><br>\n\n<!--more Continue reading-->\n\nThe main vulnerability in the challenge was a Use-After-Free (UAF), that leads to write-what-where due to an insuficient check of the pointers in the linked list. I managed to solve the challenges in multiple ways.<br>\n\n<pre class=\"prettyprint\">void ss_init(void) {\n  int flg;\n  void *addr;\n  uint *__g;\n  \n  addr = mmap((void *)0,12288,0,34,0,0);\n  if (addr == (void *)0) {\n    fwrite(&#34;Could not create shadowstack pages\\n&#34;,1,35,stderr);\n    _exit(1);\n  }\n  flg = mprotect((void *)((long)addr + 4096),4096,3);\n  if (flg != 0) {\n    __g = (uint *)__errno_location();\n    fprintf(stderr,&#34;Could not set RW protections on shadowstack page, errno: %d\\n&#34;,(ulong)*__g);\n    _exit(1);\n  }\n  printf(&#34;Shadow stack set up at %p\\n&#34;,(long)addr + 4096);\n  SHADOW_STACK = (long)addr + 4096;\n  return;\n}\n<\/pre><br>\n\nThis function performs the shadow stack initialization. First it creates an mmap memory page, and then mprotect&#8217;s it with Read-Write permissions. Finally saves in a BSS entry it&#8217;s address.<br>\n\n<pre class=\"prettyprint\">int main(void) {\n  int c;\n  int x;\n  long local_res0;\n  char buf &#91;9&#93;;\n  \n  __cyg_profile_func_enter(main,local_res0);\n  setvbuf(stdout,(char *)0,2,0);\n  puts(&#34;Welcome to KEMS, the Kipod Employee Management System!\\nWhat would you like to do today?&#34;);\n  buf&#91;0&#93; = &#39;\\0&#39;;\n  do {\n    printf(\n          &#34;&#91;A&#93;dd a new a employee\\n&#91;F&#93;ire an employee\\n&#91;C&#93;hange employee name\\n&#91;R&#93;ead employeename\\n&#91;Q&#93;uit\\n&gt; &#34;\n          );\n    c = getc(stdin);\n    buf&#91;0&#93; = (char)c;\n    do {\n      x = getc(stdin);\n    } while (x != 10);\n    switch(buf&#91;0&#93;) {\n    case &#39;A&#39;:\n      kems_add();\n      break;\n    default:\n      puts(&#34;\\nInvalid action! Please try again.&#34;);\n      break;\n    case &#39;C&#39;:\n      kems_change();\n      break;\n    case &#39;F&#39;:\n      kems_fire();\n      break;\n    case &#39;Q&#39;:\n      puts(&#34;Sure you want to leave? Here, have a BOF on me.&#34;);\n      fgets(buf,64,stdin);\n      remove_newlines((long)buf,64);\n      __cyg_profile_func_exit(main,local_res0);\n      return 0;\n    case &#39;R&#39;:\n      kems_read();\n    }\n  } while( true );\n}\n<\/pre><br>\nApart from the typical menu we usually find on CTFs, the most interesting lines are the ones that call the shadow-stack-implementation functions <code>__cyg_profile_func_enter(main,local_res0)<\/code>, which can remind us to the <code>__chk_stack_fail<\/code> of the canary implementation.<br>\n\n<pre class=\"prettyprint\">void __cyg_profile_func_enter(undefined8 addr,undefined8 value) {\n  *SHADOW_STACK = value;\n  SHADOW_STACK = SHADOW_STACK + 1;\n  return;\n}\n<\/pre><br>\nThe first function basically adds the saved RIP value to the shadow stack.<br>\n\n<pre class=\"prettyprint\">void __cyg_profile_func_exit(undefined8 addr,long value) {\n  SHADOW_STACK = (long *)((long)SHADOW_STACK + -8);\n  if (value != *SHADOW_STACK) {\n    puts(&#34;Return address tampering detected, exiting.&#34;);\n    _exit(1);\n  }\n  return;\n}\n<\/pre><br>\nFinally at the end of the function it checks whether the value coincides with the shadow stack one or it changed, if so, it will exit printing the message &#8220;Return address tampering detected, exiting.&#8221;.<br>\n\nNow, let&#8217;s analyze the available operations in the program.<br>\n\n<pre class=\"prettyprint\">undefined8 manager_add(char *buf) {\n  void *ptr;\n  size_t x;\n  undefined8 n;\n  long local_res0;\n  void *p;\n  \n  __cyg_profile_func_enter(manager_add,local_res0);\n  ptr = calloc(1,24);\n  x = strlen(buf);\n  if (((buf == (char *)0) || (15 &lt; (uint)x)) || (ptr == (void *)0)) {\n    n = 1;\n  }\n  else {\n    memcpy(ptr,buf,x &amp; 4294967295);\n    if (g_list_root == (void *)0) {\n      n = 0;\n      g_list_root = ptr;\n    }\n    else {\n      p = g_list_root;\n      while (*(long *)((long)p + 16) != 0) {\n        p = *(void **)((long)p + 16);\n      }\n      *(void **)((long)p + 16) = ptr;\n      n = 0;\n    }\n  }\n  __cyg_profile_func_exit(manager_add,local_res0);\n  return n;\n}\n\n<\/pre><br>\nThis function adds chunks with custom values up to 15 bytes length, something important about this function is that it allocates memory using calloc instead of malloc, which can make more difficult the exploitation as it zeroes-out the chunk content before returning it to the user, also when using calloc, tcache chunks are not returned.<br>\n\nAnother important thing is that the chunks are identified using IDs among a linked-list, which uses chunk_ptr+0x10 to store the chunk to the next element of the list.<br>\n\n<pre class=\"prettyprint\">long manager_read(int id) {\n  long local_res0;\n  long x;\n  long i;\n  \n  __cyg_profile_func_enter(manager_read,local_res0);\n  if (g_list_root == 0) {\n    x = 0;\n  }\n  else {\n    i = 0;\n    x = g_list_root;\n    while (x != 0) {\n      if (i == (long)id) goto LAB_00101ca8;\n      x = *(long *)(x + 16);\n      i = i + 1;\n    }\n    x = 0;\n  }\nLAB_00101ca8:\n  __cyg_profile_func_exit(manager_read,local_res0);\n  return x;\n}\n<\/pre><br>\nThis is just a way to read the chunk content given the ID, so the chunk to read is identified in the linked-list by the position, which is the ID.<br>\n\n<pre class=\"prettyprint\">undefined8 manager_rename(int id,char *buf)\n\n{\n  size_t x;\n  undefined8 n;\n  long local_res0;\n  void *e;\n  long i;\n  \n  __cyg_profile_func_enter(manager_rename,local_res0);\n  if (buf == (char *)0) {\n    n = 1;\n  }\n  else {\n    x = strlen(buf);\n    if ((g_list_root == (void *)0) || (15 &lt; (uint)x)) {\n      n = 1;\n    }\n    else {\n      i = 0;\n      e = g_list_root;\n      while (e != (void *)0) {\n        if (i == (long)id) {\n          memcpy(e,buf,x &amp; 4294967295);\n          n = 0;\n          goto LAB_00101d7f;\n        }\n        e = *(void **)((long)e + 16);\n        i = i + 1;\n      }\n      n = 1;\n    }\n  }\nLAB_00101d7f:\n  __cyg_profile_func_exit(manager_rename,local_res0);\n  return n;\n}\n<\/pre><br>\nThis function, like the manager_read, identifies the chunk to use by ID, but instead of reading it, we can edit it&#8217;s content with a value up to 15-bytes length.<br>\n\n<pre class=\"prettyprint\">void kems_fire(void)\n\n{\n  int x;\n  char *ptr;\n  long local_res0;\n  char buf &#91;16&#93;;\n  \n  __cyg_profile_func_enter(kems_fire,local_res0);\n  printf(&#34;Which employee would you like to fire?\\n&gt; &#34;);\n  fgets(buf,16,stdin);\n  remove_newlines((long)buf,16);\n  _x = manager_remove(buf);\n  if ((int)_x == 0) {\n    printf(&#34;Employee %s removed. Please enter a short note as to why they were fired.\\n&gt; &#34;,buf);\n    ptr = (char *)malloc(24);\n    fgets(ptr,24,stdin);\n    remove_newlines((long)ptr,24);\n    manager_log(0,(long)buf,(long)ptr);\n    free(ptr);\n  }\n  else {\n    printf(&#34;Could not fire employee with name %s. Maybe no such employee exists?\\n&#34;,buf);\n  }\n  __cyg_profile_func_exit(kems_fire,local_res0);\n  return;\n}\n<\/pre><br>\nkems_fire is a wrapper to call manager_remove, the important fact to analyze here is that it asks you a reason after deleting a manager entry, it allocates a 24-sized chunk, same as calloc so we can reutilize the calloc chunk using malloc, and now it allows us to edit up to 24 bytes, which includes the position of the linked list pointer.<br>\n\n<pre class=\"prettyprint\">undefined8 manager_remove(char *buf)\n\n{\n  int n;\n  undefined8 f;\n  long local_res0;\n  char *i;\n  char *w;\n  \n  __cyg_profile_func_enter(manager_remove,local_res0);\n  if (g_list_root == (char *)0) {\n    f = 1;\n  }\n  else {\n    i = g_list_root;\n    while (i != (char *)0) {\n      n = strcmp(buf,i);\n      w = g_list_root;\n      if (n == 0) goto LAB_00101af3;\n      i = *(char **)(i + 16);\n    }\n    f = 1;\n  }\nLAB_00101b2b:\n  __cyg_profile_func_exit(manager_remove,local_res0);\n  return f;\nLAB_00101af3:\n  while (*(long *)(w + 16) != 0) {\n    if (i == *(char **)(w + 16)) {\n      *(undefined8 *)(w + 16) = *(undefined8 *)(i + 16);\n    }\n  }\n  free(i);\n  f = 0;\n  goto LAB_00101b2b;\n}\n<\/pre><br>\nFinally as we can see the chunks to free are very limited.<br>\n\nThe main vulnerability here is, that the first chunk is not unlinked from the linked list, so it can be deleted and then read or edited, so we can trigger a Use-After-Free vulnerability. As the chunk is not unlinked, the pointer to next chunk is not restored, thus allowing us to point next value in the linked list to anywhere as in the malloc(24) we are able to modify it.<br>\n\nNow we figure out that the edit and read functions use ID to identify chunks, which is the position of the chunk in the linked list, this means we can edit the modified next chunk, which leads to write-what-where.<br>\n\nAs we can read a chunk content, we can also trigger arbitrary read, which can help us to leak addresses. The easier address to leak is located in the shadow stack, and it is a known address, the first value in the shadow stack is the saved RIP of main, which is a pointer to the ret instruction of <code>__libc_start_main<\/code>, so now we have a libc leak.<br>\n\nThere is a buffer overflow vulnerability in the program when calling Quit (located in main()). It reads from stdin 64 bytes into a buffer of just 9 bytes.<br>\n\nI could get shell locally using two main methods. In the first one, as we have libc leak, we can overwrite <code>__free_hook<\/code> to the address of system, then store &#8220;\/bin\/sh\\x00&#8221; string in a chunk and then delete it. The result will be <code>system(\"\/bin\/sh\\x00\")<\/code> which will give us a shell.<br>\n\nThe other method I used is triggering the Buffer overflow after overwriting the first value in the shadow stack by the first gadget in our ROP, this will be enough to bypass the shadow stack protection giving us the possibility to ROP.<br>\n\nI ROPed with <code>system(\"\/bin\/sh\\x00\")<\/code> and <code>execve(\"\/bin\/sh\\x00\", NULL, NULL)<\/code>.<br>\n\nThe full exploits and challenge binary can be found <a href=\"https:\/\/github.com\/lockedbyte\/exploit-challenges\/blob\/main\/KipodAfterFree_2020\/execve_rop_exploit.py\">here<\/a>.<br>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This challenge was from KipodAfterFree CTF 2020 The challenge was interesting as it is an implementation of a Shadow stack to save a backup of saved RIP addresses and compare them before function returns, that is a good way to detect Buffer overflow exploitation attemps and block them.<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[6],"tags":[],"_links":{"self":[{"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/153"}],"collection":[{"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=153"}],"version-history":[{"count":10,"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/153\/revisions"}],"predecessor-version":[{"id":244,"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/153\/revisions\/244"}],"wp:attachment":[{"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=153"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=153"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lockedbyte.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=153"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}