Exploit against Perl Kit 1.0
Security issues
Q: Are there any security issues with Perl 1?
A: The Perl 1 interpreter is vulnerable to an attack that has to do with temporary files. Race conditions exist when a program is passed with âeâ command line option. Hereâs an example traditional Hello World program.
$ ./perl -e 'print "hello, world\n";'
This creates a file like /tmp/perl-eO3gdPn that can be overwritten by an external process with another program in Perl language. It can for example print âowned by stagyriteâ instead of âhello, worldâ. There is a proof-of-example exploit named Perl Harbor. Letâs observe how to use it against a Perl 1 interpreter in a sample session.
$ ./perl -e 'print "hello, world\n";'
hello, world
$ ./perl-harbor.c &
[1] 2146125
$ ./perl -e 'print "hello, world\n";'
owned by stagyrite
[1]+ Done ./perl-harbor.c
As one can see, Perl 1 isnât secure when it comes to handling temporary files with its programs. Our exploit managed to overwrite the user input. It might require several retries before the exploit succeeds in infecting the temporary file. Itâs because Perl 1 might overwrite the exploitâs file too in one of the lines in perly.c.
switch_end:
if (e_fp) {
fclose(e_fp);
argc++,argv--;
argv[0] = e_tmpname;
}
The exploit has to write the file after the âfclose()â function is called. The code below comes from the perly.c file, and shows how Perl 1 starts writing a temporary file.
case 'e':
if (!e_fp) {
e_tmpname = strcpy(safemalloc(sizeof(TMPPATH)),TMPPATH);
mktemp(e_tmpname);
e_fp = fopen(e_tmpname,"w");
}
if (argv[1])
fputs(argv[1],e_fp);
putc('\n', e_fp);
argc--,argv++;
break;
One might want to replace a call to function âmktemp()â with âmkstemp()â but that wouldnât fix this particular security issue at all.
Perl Harbor
Q: Can you explain your Perl Harbor exploit against Perl 1?
A: 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:
- Create a temporary file prefixed with âperl-eâ by calling a âmktemp()â function.
- Write Perl 1 code passed as a program argument to the newly created file.
- Close the temporary file flushing the buffers.
- Proceed as if the temporary file would be passed to the Perl 1 interpreter.
Suppose you want to print a âhello, worldâ text with Perl 1.
$ ./perl -e 'print "hello, world\n";'
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:
$ ./perl /tmp/perl-emMuFDJ
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.
case 'e': if (!e_fp) { /* This pattern is '/tmp/perl-eXXXXXX'. */ e_tmpname = strcpy(safemalloc(sizeof(TMPPATH)),TMPPATH); /* Create a temporary file prefixed with "perl-". */ mktemp(e_tmpname); /* It would now open the temporary file for writing. */ e_fp = fopen(e_tmpname,"w"); } if (argv[1]) fputs(argv[1],e_fp); /* Write a Perl code passed in a command-line argument. */ putc('\n', e_fp); /* Add a new line to the Perl code. */ argc--,argv++; break;
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().
switch_end: if (e_fp) { /* Close the temporary file. */ fclose(e_fp); /* Set a new program path. */ argc++,argv--; argv[0] = e_tmpname; }
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:
- he uses the same user, as the âperlâ command executor,
- heâs ârootâ and executes âchmod -t /tmpâ first on modern operating systems,
- the /tmp directory otherwise allows this file operation.
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:
$ ./perl-harbor & [1] 5406 $ ./perl -e 'print "hello, world\n";' owned by stagyrite
$ ./perl-harbor & [1] 5426 $ ./perl -e 'print "hello, world\n";' $ ./perl -e 'print "hello, world\n";' hello, world [1]+ Fertig ./perl-harbor $ ./perl-harbor & [1] 5432 $ ./perl -e 'print "hello, world\n";' $ ./perl -e 'print "hello, world\n";' hello, world [1]+ Fertig ./perl-harbor $ ./perl-harbor & [1] 5435 $ ./perl -e 'print "hello, world\n";' owned by stagyrite
Hereâs a plan implemented:
- Change the working directory to /tmp where Perl 1 temporary files reside.
- Loop until any function confirms that the system has been just exploited.
- Scan the /tmp directory for files starting with âperl-eâ.
- Wait until the Perl program is written by the âperlâ process. Itâs done by repetitively getting a file status. A single call to the âstat()â function should be enough.
- Open the Perl program for writing, truncate it and write another Perl code. The other Perl code prints âowned by stagyriteâ, although it could be anything that compiles.
- Perl Harbor now exists leaving.
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 three possible endings:
- The exploit doesnât detect any temporary file.
- The file will be truncated, and therefore wonât compile.
- The file will be original, and therefore exploitation is unsuccessful.
- This file will be swapped, and therefore exploitation is successful.
You can get the complete exploit in C programming language from GitHub.
âđ âđ đ âđ đ đ âđ đ đ đ