*preroll music*
Herald: And I'm gonna introduce Netanel Rubin.
He has been here last year with a talk that he got some bashing for.
This year he's gonna ensure, it's not the programmer's fault,
it's the language itself. No?
Well, here we go, well here we go.
Netanel, he is working for PerimeterX in Tel Aviv,
welcome on stage, your talk!
Netanel: Thank you very much, *applause*
thank you, thank you!
Last year I stood right on this very stage and I talked about several of Perl's
less thought out "features".
Now, I got some bashing from the Perl community, but mainly what happened was,
that the Perl community completely rejected my talk claiming that the language
is completely fine and great and all of this stuff are just improvements.
It was clear I had to give another talk.
This is why I'm very proud to present "Perl Jam 2 – The Camel strikes back"!
*applause*
Thank you
At the last talk, I showed that "lists" are expressions, used in… many confusing ways.
I also showed how CGI parameters can create lists, directly from user input.
But most importantly, I showed that when these two things combine, shit happens.
Great
But the really interesting part was the PerlMonks response.
The Perl community… *laughter*
The Perl community had a long discussion at the PerlMonks forum
that started with the words "Sad news from Germany".
A bit dramatic, but who am I to judge?
So, after a long, long discussion, they came to the unavoidable conclusion
that my talk was, in fact, a "polemic shit", and they should all just "piss on it". Wink.
They also realized I'm just a "script kiddie preaching to other script kiddies",
and not just any script kiddies, the CCC audience is a
"heterogeneous group of chaotic punks who love to see themselves in the hacker
image of Hollywood media". *applause and whistling from audience*
What hacker image? What are they talking about?
We have no hacker image.
Anyway, it got quite surreal, as in some point they even critized
the "crude use of propaganda in the camel images". WAT?
*laughing* *applause*
Propaganda in the camel images. Alright.
Anyway, they completely rejected the entire talk, even though the technical points were valid.
They rejected it because of some jokes and camel images.
But still, they got so offended they just threw lame excuses as to why their language sucks.
Two of these lame excuses were repeated over and over again.
The first was that I should read the fucking manual, which is funny
because I thought I was the only one who did…
*laughter*
…and the second is that
I'm using the old, ancient Perl, and not the new, modern Perl.
*more laughter*
Remember these two points carefully as I'll later break them in the presentation.
But, enough with the intro, let's start with the new madness.
So, Perl allows declaring variables without specifying their data type.
Of course, this functionality exists in many dynamic languages,
and is completely fine and very convenient.
But, as usual, Perl took it to a whole different level.
Perl went as far as removing data type declarations from function arguments.
You can see that in this example,
I'm just receiving two different arguments without knowing what type they are.
Let me be clear about that,
you don't get to choose whether you want to specify argument data types or not,
you can't specify what data types you're expecting to get.
So even if I built a function that only works with strings,
I have no way of forcing that at the function declaration.
Now that's annoying.
But, the real kicker is how this feature is used.
Apparently, it is very common to write two completely different blocks of code,
one that handles scalar types, like strings or ints,
and one that handles non-scalar types, like arrays or hashes.
Let me repeat that:
Writing multiple code, for multiple data-types, in one function, is a Perl standard.
And that's sad. You shouldn't write redundant code because the language lacks the capability
of letting you decide which cases you don't want to handle.
By the way, Python doesn't let you declare your function argument data types too,
but unlike Perl, writing redundant code to cover that up
is definitely not the standard.
Anyway, sad as this may be, this Perl convention is not dangerous.
The dangerous part begins
when hashes and arrays are considered as "secure" data types,
mainly because they can't be created by user input.
This results in this kind of code,
where if the function argument is a hash, for example,
it is used un-escaped in dangerous functions.
Hashes, specifically, are considered so secure, that even if you use "taint mode",
which is some kind of safe mode for Perl,
hash keys are not tainted, meaning that, even if you use safe mode,
they can be still used in dangerous functions
without any validation, as opposed to other data types.
Now this kind of code appears a lot in Perl applications,
and apart from the many bugs this method can cause,
it also makes your code exploitable.
So we know function arguments are of unknown data type.
And we know developers treat hashes and arrays as "secure" data types,
inserting their values into dangerous functions.
But this practices isn't something
that was created a long time ago, and found only on redundant code.
Because of how the language is built, its supposedly restriction-less type of developing,
even now it is the natural way to code when you're using Perl.
And that's the real problem: Perl is like a shotgun,
with one trigger you know about and a dozen that you don't.
So for now, we know that if we'll somehow manage
to create these "secure" data types, with our user input,
we could exploit the code.
So the only question remaining really is what are we gonna exploit?
And the answer, again, is Bugzilla.
*laughter*
Like every other Perl project, Bugzilla is heavily using functions
that treat scalar and non-scalar argument types very differently.
This is one of them: the load_from_DB function is responsible
for extracting object specific data out of the database.
Like I just said, it treats scalars, and in this case hashes, very differently.
If the function argument is a hash, it takes one of its values
and inserts it as is, un-escaped, into an SQL statement.
Again, because hashes are considered secure,
so there's no point of escaping them.
On the other hand, if the argument is a scalar,
it converts it into an integer and only then use it in an SQL statement.
Because scalar values are not secure.
hashes: secure
scalar: not secure
This means that if we could control the function argument entirely,
including its data type, we could control the SQL query,
effectively exploiting an SQL injection attack,
by inserting a hash containing that specific value.
But…
CGI input doesn't allow hashes, right?
The whole Perl security module is built on that assumption.
The problem is that, like us, developers are assuming
CGI input is the only input method available.
CGI.
But CGI isn't the only way to go.
Bugzilla developers missed the fact that their own system
is also featuring an XMLRPC and a JSONRPC,
both supporting input of non-scalar data types like arrays and hashes!
But I'm not blaming them.
Yes, they forgot that there are more ways for a user to input than CGI,
but still, they're just the product of how Perl programming is taught,
filled with false assumptions and inconsistencies.
Expecting anything but this kind of security problems is just naive.
But back to the vulnerability.
If we'll use one of these RPCs,
sending our input parameter with a malicious hash,
instead of just a regular numeric parameter,
we will be able to exploit the SQL injection!
So, if we'll send this regular request, using the JSONRPC interface,
the number 1 will be used as the ID of a bug to extract,
but if we'll send this request,
where instead of an integer we'll supply a hash,
then suddenly we will be able to inject any SQL we'd like
into that statement, effectively compromising the entire database.
Now when you look at this request, you realize
that this is not a sophisticated vulnerability.
All I did was just change the input data type from scalar in this case to a hash,
and that's it, the system is compromised.
It was so heavily built on the assumption
that hashes are secure, that it offered me
almost unlimited access security wise.
The funny thing about that is, that although it's so simple,
the attack has existed for over 5 years.
That's the year I was born in.
So, we now proved this "unknown-argument-type" feature
is a huge source for problems.
We also know writing different code to handle different data types
just causes a lot of false assumptions.
But most importantly, treating non-scalar data types such as hashes as "secure",
just because they supposedly can't be created by the user,
is very, very, BAD. Just ask Bugzilla.
But the shocking part really, is that, again, this is the Perl Standard!
You're not expected to use it, you have to
as you don't have any other choice.
This security mess is a fundamental part of the language.
The problem is that creating non-scalar data types is impossible in some cases.
We can't rely that some kind of RPC
will exist in the code and support different data types,
and we can't create data types using regular user input… Right?
Well, let's have a look at
how different CGI modules handle different kind of input.
First, we'll take the most trivial scenario.
A single valued parameter, something that looks like this request,
where the "foo" parameter is assigned the string "bar".
In this case, a scalar is created on all three CGI modules,
which doesn't really help us, but is pretty much what we've expected.
It is secure.
But what happens if instead of sending a single-valued parameter,
we'll send a multi-valued parameter, like in this request?
Now things are starting to get complicated.
On CGI.PM, as we already know, a list is created,
which is very useful for us, but not what we're after.
Let's have a look at what the "new" Perl modules are creating.
We'll see that both of them are returning arrays containing our values.
Arrays! WAT?
I thought you can't create these kind of data types with regular input,
after all, they're considered safe.
But let's continue.
What happens if instead of sending a regular value,
we'll try and upload a file in that parameter?
Now things are really getting out of hand,
because CGI.PM now returns a file descriptor, and Catalyst and Mojolicious returns a hash.
WAT?
We just exploited the most popular Perl project in the world
because they assumed hashes can't be created by the user,
and now we're finding out that not only we can create hashes,
it is a god-damned feature?!
That's insane!
The whole Perl security standard is built on that assumption
that users can't create non-scalar data-types
and now suddenly these are features?
But let's send a multi-file upload request as in several files in the same parameter.
Watch closely, because this is where it gets ridiculous.
Now, CGI.PM returns a list of File Descriptors,
Catalyst returns a list of Hashes
and Mojolicious returns an Array of Objects! WAT?!
*laughter and applause*
Almost any Perl project in the world
uses one of these modules for parsing CGI input.
Just think how many developers assumed the exact same thing Bugzilla assumed
and treated hashes and arrays as secure data types.
So if you're using CGI.PM,
instead of the expected scalar value you could be getting
a list, a file descriptor or a list of file descriptors
and if you're using Catalyst
you could receive a scalar, an array, a hash or a list,
which is basically any data type.
So expecting your function… yeah
*audience chuckling*
So expecting your function arguments to be of a specific data type is false.
Expecting hashes and arrays to be secure is also false.
Expecting scalar only user input
is a major false.
And to be honest, it seems that in Perl expecting is false!
*laughter and applause*
You just can't expect anything
even the most basic of things such as what data type your variable is made of.
You just don't know.
But I felt all of these points will go un-noticed
without an extreme example of Perl's absurdity.
So I found an extreme example.
One that will clearly show
the ridiculous nature of the language.
And this is it:
All this code does is print an uploaded file's content.
And to show you how basic and simple that code is, I'll explain each line.
The first line just creates a new CGI instance, so we could get the file from the user.
The second line checks if a file has been uploaded in the "file" parameter.
The third line gets the file descriptor from the CGI module,
while the fourth line loops through the file and the fifth prints it.
That's it. Again: all this code does is get a file and print it.
*clapping* That's it.
A user has uploaded a file to the server and the server is just returning its content.
It's not saving it anywhere, it's not moving it anywhere,
it just prints its content.
There should be absolutely nothing dangerous in this code,
it contains literally five lines.
Yet, it's demo time.
*laughter*
So trust me, you don't need to see the text,
all you need to see is that when I'm sending a regular request nothing happens.
When I send it now, nothing happens, I'm just getting the file content.
We're having fun, you don't see the burp…
Now, nice. Okay.
So… …L't me just…
…I have no idea where my mouse is, okay.
So…
I'm sending a regular request, nothing happens, just getting the content.
I know, you can't see the text… …and…
when I'm sending my malicious request,
something interesting will pop up.
Watch closely! It's gonna be quick.
Ready?
Oh, you haven't seen it, it's on the different screen.
Just a second… oh… duplicate…
(from audience): … magnify it!
Netanel: I'll magnify you!
*laughter*
Alright, so… watch closely.
Ohh, uuh? What was that?
Let's see it again.
*mocking* Uuuuuh?!
*laughter and applause*
Yupp, *clearing throat*
… just a second.
Nice.
So you're probably asking yourself right now
"What the fuck did I just see?" *laughter*
"Was that a terminal screen?"
And the answer is … "Yes" Yes, it was,
specifically the "ipconfig" command output.
Or in other words: What you just saw
was me exploiting that five lines with a remote code execution attack.
So now that you saw the magic happens, I think it's time for some explanations.
The first line, responsible for checking
if a file has been uploaded in the "file" parameter,
doesn't exactly do as it says.
Instead of checking if the "file" parameter is an uploaded file,
it checks if one of its values is a file descriptor.
Let me clarify that, instead of checking if the parameter is only a file,
it checks if the parameter is also a file.
*laughter*
Meaning that uploading a file
and assigning another scalar value to the same parameter
will still work and bypass the check!
WAT?
*more laughter and applause*
Creative fellows those guys are.
So now we can assign the "file" parameter both a regular file and a scalar value.
But what happens when we try to get the "file" parameter value?
In a regular request, it should return the uploaded file descriptor,
but now that we're adding another value to that parameter,
param() returns a list containing all the values we sent:
the file we've uploaded and our scalar value.
But the "file" variable can't contain two values, right?
So instead of converting the returned list into an array
Perl only uses the first element of that list.
So if we'll send our scalar value before we send our file,
the $file variable will be assigned our scalar value
instead of the uploaded file descriptor.
Which means, that $file is now a regular string!
*in high pitched voice:* WAT?
But what happens to this operator when we use a string
instead of a file descriptor?
Well, the brackets operator doesn't work with strings, right?
It works with file descriptors, why should it work with strings?
Well, that appears true
unless that string is "ARGV".
*laughter and applause*
That's not a crazy part.
*more laughter*
In that case the brackets operator, listen closely,
loops through the script arguments,
which in CGI comes directly from the query string instead the command line,
and it treats them as file paths, inserting each one into an open() call!
*again laughter*
WAT?
Yeah, that made sense in some point, I guess.
All of this basically means that now,
instead of displaying our own uploaded file content,
we can display the content of any file on the server.
But that's not the end, as we haven't executed code yet.
To execute code, we have to look at the open() function.
Again, this is the function being called with the ARGV values as file paths.
open() is responsible for opening a file descriptor to a given file.
Unless a "pipe" character is added
to the end of the string, *laughter*
and in that case instead of opening the file,
it executes it… *applause rising*
…acting as an exec() call! *more applause*
So … when we send our exploit,
containing our uploaded file, the "ARGV" malicious scalar value,
and the ipconfig command followed by a pipe
this is what we get. WAT?
*applause*
I know, I'm shocked too, but I'm not done yet. *laughter*
Truth be told, I didn't write that code.
Remember that PerlMonks told me that I should read their fucking manual?
*more laughter* Guess where that code came from:
the official CGI documentation! *big applause and audience whistling*
But, I'm not blaming CGI.PM developers.
Nor am I blaming developers who copied from CGI.PM examples.
After all, who could have known that this is what this code will do?
This is how it could be exploited?
There's no exec calls, the file is not saved anywhere,
and we're only using a "print".
The sole responsible for this mess, is the Perl language.
Perl is the one silently expanding lists,
Perl is the one mixing up your data types,
Perl is the one executing user input with no exec calls,
Perl is the problem,
not its developers. *applause rising*
And until this god-damned, bizarre, dangerous language is fixed,
you could only stop
using
Perl!
Thank you! *more applause*
Herald: So I guess we have some time for questions now.
*laughter* Netanel: Maybe
Herald: And I have the funny feeling, we will have some questions now.
Ok, so we have some microphones here. Please queue up.
Please do not shout in, because we need to record it on the stream.
Well, here we go.
And we also have some questions from the internet, don't we?
Signal Angel: Oh yes, we do! *laughter*
Signal: But before we come to the technical questions,
the IRC wants you to know, what you did to it:
it felt like there were explosions and camels everywhere.
Netanel *laughing*: That's the point
Signal: And incidently they want to know, if you have a list of those camel pics somewhere?
Netanel: I think Google has it? *more laughter*
Just there search camels.
Signal: So for the first question. Opello(?) wants to know,
if the take-away is, that Perl project authors so shouldn't trust input
and instead verify types with REF and always use prepared SQL statements?
Netanel: That's a good question. The take-away should be… *laughter*
well, how will I phrase it …
I think I have a slide … somewhere … *more laughter*
Oh wait, where's my slide?
Don't worry, have it right here.
But really, trusting user input is always a bad idea
and most developers know it.
The problem is, that…
well, at least from the code I saw written in Perl,
and that's a lot of code, trust me
…is that hashes and arrays are almost always considered secured
as they supposedly can't be created by user input, as I said.
But, when you're expecting your user input to be a scalar, a string or even a list
and instead you get a hash from unexpected directions, you get confused.
And you can't always live in the fear of not knowing
what data type you're trying to handle.
Well, not trusting scalar data types is a wise decision, because it's dangerous.
But not trusting your hashes, as well not trusting your arrays?
What's next? Not trusting your own code?
You just can't expect anything to really work as it should.
When you're writing Perl,
you are constantly attacked by all these different directions.
And even the data type direction is a problem now.
I hope that answered the question beside the slide.
Herald: Well, then we're gonna go over and start with number one.
Questioner: So thank you for opening our eyes.
Even I use Perl, I would say, for cooking and yes …
Netanel: I remember you Q: Sorry?
N: I remember you from the last talk! Q: No no
N: Oh, you're new? Oh… *smirking* Q: I'm new, I'm new…
Q: So… I can't say, I'm not guilty of that, but I still would say yes,
Perl is a bit like cooking with my mum.
Sometimes I put something into… the… with the boiling thing…
and sometimes she, sometimes I go away, sometimes she go away
and the only thing you can do is always taste.
And yes, you're maybe right, Perl is a language
where you never know what comes out, but it's real cool!
If you get the right response you can use it,
if you use it to write web applications I would agree.
Web applications, the professional ones at least, are not for cooking,
but for doing funny things and have some fun, I think it's a perfect language.
N: I think Perl is a lot of fun. *laughter*
I completely agree on that. *laughing*
Herald: Then we're gonna go over to two.
Question: Was your life ever threatened while interacting with the Perl community?
*laughter* N: Could you please repeat that? I …
Q: Was your life ever threatened while interacting with the Perl community?
N: Definitely. Definitely,
I'm getting hate mail every day, living in fear …
H: And over to the three, please.
Q: I think I speak for all of us, when I thank you for this wonderful talk,
N: Uh, thank you. Thank you really! Thank you. *applause*
Q: Brilliantly executed, but… ehm… you spoke about Perl 5 I think.
N: Yes, you are absolutely right. Q: As some of you might know, this christmas…
*laughter* Q: …so tomorrow Ingo Blechschmidt
is going to give a talk about how Perl 6 will make everything better
and how everyone should start using Perl 6 and…
N: It also craps rainbows Q: Yeah, of course…
Q: My personal comment is: wouldn't it have happened
with a statically typed language?
So I think some nice folks at Haskell in IRC are waiting for you Perl developers
to please come, join us … Thank you! N: *smirking*
*Herald and Netanel start speaking simultaneously*
H: …sorry, to answer first, where am I… sorry N: Ah, no..., I am not answering, just...
just a quick note about Perl 6. This talk is all about Perl 5, alright?
I … Perl 6 came out a couple of days ago and …
...from …at least from what I saw, Perl 6 is to Perl as…
C++ is to C. It's the same name, but it's a whole different language.
So yes, this is Perl 5. Maybe I'll come back next year about Perl 6?
*laughter* Who knows?
Herald: I'm looking forward to that already. *applause*
*Herald pointing to Signal Angel*
Signal: Yeah… Joerd(?) wants to know: of course you talked a lot about CGI.PM
which you know was removed from repository from Perl even before your talk last year.
So what about it's replacements from CPAN like CGI::Simple.
Netanel: I don't know, I haven't checked it. When I decided on which modules to check,
I took CGI.PM because even though it is old, it is the most popular in the world as of today
and I took Mojolicious and Catalyst because they were really popular, too.
So I didn't take the newest modules, I take the most popular modules.
And I think, that's the important aspect of … deciding.
Herald: And over to one, please.
Questioner: Hi… I'm… part of the Perl community, and… *laughter*
N: Hi! Q: But I just start with Perl – 5
N: Uhh… ehm… uhh… didn't you… nhaa… *laughter*
Q: We use Perl for almost every module that we have at work
and this worked really fine. N: …yeah…
Q: And I don't know why you're picking Perl as language to attack.
It's a really old language, it's also every language that we can pick, that has problems.
But it doesn't mean this has to die or stop using it. So I don't know why…
N: …you're right, you're right. First of all, you're completely right,
because a language shouldn't die, it should improve.
C got critized and it improved. PHP got critized and it improved.
Why can't Perl be critized, too?
Why is it like a code, when you say something bad about Perl, then,
I don't know, a horde of PerlMonks jumps on you?
Why don't improve the language? Don't use it in your work though,
it's dangerous. *laughter and applause*
H: Then we're gonna jump over to five, please.
Q: Hi. I'm not a Perl developer, but I use a lot of Ruby and Python.
Is this really limited to Perl or
does this apply to more or less any dynamic language?
N: As I said in one of the first few slides,
some of it also applies to Python. Specifically the thing
when you can't specify what data types your function arguments can get.
But, what's unique to Perl is that writing different code
for different data types in one function is very, very common.
You can do it in every language, of course!
But it is very common only in Perl! And that is unique about it,
of course besides the thing that hashes and arrays are secure.
That's of course Perls only fault.
H: Good, then we're gonna go over to six, please.
Q: Hey! Did you say WAT more while preparing this talk or while holding it?
N: Uhm. Both. *Laughing*. Did I rant? That was the … right?
Q: Did you say it more while preparing it or while holding it?
N: I'm missing your word, man, can you...
Ahh, wat… WAT! Ohh… Yeah, both! *laughter*
H: Ok, do we have another from the internet?
Signal: Does your exploit also work in tainted mode?
N: No, I believe not. No, it doesn't.
H: And another one...
S: Is there any Perl obfuscated code exploits like this for Catalyst or Mojolicious?
*someone chuckling in audience*
N: I've no idea, man, maybe. I didn't check it of course.
I didn't check every module for every exploit, I ever want to create, but
on CGI.PM, which is again the most popular CGI library, it did.
So, maybe the internet can find more exploits. I know it can.
H: Bring it on. That's it? N: That's it?
Thank you!
*applause*
Herald: Thank you very much! Netanel: Thank you!
*postroll music*
subtitles created by c3subtitles.de in the year 2016. Join, and help us!