📜 ⬆️ ⬇️

Creating a banned information flow. 257 threads

This is the first article in a series of articles on creating prohibited information flows (PIs). The ideas of organizing these PIs are not invented here or by me. I was only able to implement utilities that demonstrate these vulnerabilities for educational purposes.

In this article I will talk about the organization of the information flow, based on the change by one of the applications of its state of the survey by another application of the state of the first. With this model of interaction, one listener may have several listeners. In future articles, both the interaction environments of the programs and the information exchange algorithms will be different.

So, to the code! First we need an array with the identifiers of an additional 256 threads. Until threads are created, identifiers are made equal to zero.
')
#define MAX_THREADS 256 pthread_t threads[MAX_THREADS]; 

Let's declare a couple of variables in which we will store information about the current number of threads and the necessary number of threads in the next step:

 int todayThreads = 0; int tomorrowThreads = 0; 

Now look at the function to which all newly created threads will be sent:

 void * funcParallel(void * p) { long threadIndex = (long)p; while (true) { usleep(200); if (threadIndex + 1 > tomorrowThreads) { pthread_exit(NULL); return NULL; } } } 

Now we will build a function that makes the number of running threads equal to the given one:

 void changeNumThreads(int num) { tomorrowThreads = num; if (tomorrowThreads > todayThreads) { // Creating new threads for (int i = todayThreads; i < tomorrowThreads; i++) pthread_create(threads+i, NULL, funcParallel, (void*)i); } else if (tomorrowThreads < todayThreads) { void * status; // Waiting for killing threads pthread_join(threads[tomorrowThreads], &status); } todayThreads = tomorrowThreads; } 

Now let's do the main function. Let, when launching a program without a parameter, the program will be the transferor and tell us its process identifier. And when started with a parameter, it reads the identifier of the process of the sending program from the parameter and tries to receive data from it. In other words, first run without parameters, see the PID. We launch the second copy of the program with the parameter equal to the PID seen.

 int main(int argc, char * argv[]) { int pid = getpid(); if (argc == 1) { runSender(pid); } else if (argc == 2) { sscanf(argv[1], "%d", &pid); runReceiver(pid); } else { printf("Usage: unichat [PID]\n"); printf("Examples: unichat\n"); printf(" unichat 2790\n"); } } 

Now we will look at the code of the transmitting function runSender () . It prints the PID, then reading one character from the standard input stream, changes the number of threads to the number equal to the code of the character read, waits 1000 microseconds, makes the number of threads equal to zero and waits again.

 void runSender(int pid) { printf("Sender PID = %d\n", pid); while (true) { char ch = getchar(); changeNumThreads(ch); usleep(1000); changeNumThreads(0); usleep(1000); } } 

It remains to see the code of the receiving party. It is somewhat more complicated. It monitors the number of threads in a particular application and remembers the maximum amount encountered. As soon as the number of threads becomes 0 (without taking into account the main thread) - it prints a symbol, the code of which is equal to the maximum seen value of threads. After printing, the memorized maximum number of threads is reset.

 void runReceiver(int pid) { printf("Receive from PID = %d\n", pid); char line[1024]; char filename[1024]; snprintf(filename, sizeof(filename), "/proc/%d/status", pid); int maxThreads = 0; int threads = 0; while (true) { usleep(200); FILE * f = fopen(filename, "rt"); if (f == NULL) { printf("Can't open file %s, error: %d!\n", filename, errno); return; } while (!feof(f)) { fscanf(f, "%s", line); if (feof(f)) break; if (memcmp(line, "Threads:", 8) == 0) { fscanf(f, "%d", &threads); threads--; if ((threads == 0) && (maxThreads != 0)) { printf("%c", maxThreads); fflush(stdout); maxThreads = 0; } else { if (threads > maxThreads) maxThreads = threads; } break; } } fclose(f); } } 

So here's what happens:
1. Run the program without parameters
2. See the number on the screen, for example 1234
3. Run the program with the parameter 1234
4. In the first program, press the 'a' button
5. The number of threads in the process of 1234 becomes 'a' + 1 = 98
6. The host program observes an increase in the number of threads (this is recorded in the file / proc / 1234 / status)
7. The host records a maximum value of 98
8. Transmission program kills all threads except the main one)
9. The receiving program fixes the number of threads = 1 (main)
10. The host program prints the symbol with the code 98-1 = 97 = 'a'.

Findings:
Thus, there is a transfer of data from a process with a higher level of secrecy to a process with a lower level of secrecy. The ability to exchange is based on the ability of one process to track the state of another process without any restrictions on the level of secrecy.

Source: https://habr.com/ru/post/126549/


All Articles