 Hey, I'm disconnected and as you could hear I will be talking about CPyton Bucks So few words about me. I work as a senior security engineer at TrailBits TrailBits is company where we perform security audits for our customers and also do a lot of security research in blocks of blockchain Cryptography and like application security Space I also play capture the flag competitions. So those are Security competitions where you play as a team and like solve some security challenges And I do a lot of low-level stuff and a little bit of reverse engineering when I have time and I also contribute to some open-source projects I also have a blog on the internet, but it's not very active. Yep So a quick question first who has ever been on this website? All right raise your hand Okay, lots of you. That's good. So this is the Python's bug tracker And it looks like this or maybe looked like this because currently it says that you know it migrated to GitHub And it's currently read only But yeah, here are some some recent stats from it. Well, obviously it's closed already So the stats are ending when it closed, but as you can see there are like maybe around 60 to 80 bugs that are being opened open on each month and Sometimes more or more of those are closed sometimes less. I mean in terms of numbers are closed But there are still plenty of bugs that are opened for like a lot of years So let's go to those bugs the first one I want to show you is about the read line module and The problem is that read read line module is loaded in interactive mode in Python and this is an issue that was reported in 2011 and it's it also like exists in Python 2 and 3 and Let's maybe see how it works. So the problem with with read line is that read line is a library that is being used for well reading lines and Those you basically read lines whenever you type something in the interpreter, right? So Libre Libre read line is a library written in C That is being used by C Python whenever you use your interpreter and the problem with it is that if you have something some file like this written in C Where you well include some stuff then you create a function And you well have some content here like that print some hacked and then that's some some fancy stuff and You add this attribute constructor here And now if you compile it properly with like into a search library that you will call leap read line So now if you open Python, I can maybe also open another console with a netcat Wait minus L for listen Yep, so if you now open Python after we have compiled this library in C So now you open Python interpreter. Okay, it did not work. Wait. Why? Should have been work. Oh Sorry, it shouldn't be Libre read line. So it should be read line. So, right? So now if you open Python, it basically will execute whatever you had in this constructor function In this shared library. So When can this actually be a problem? This can be a problem whenever you are an admin you log in into your server and then you go to some uploads directory And you want to for example process some files In Python interpreter and someone uploaded you this read line as oh Library then something like this can well trigger and execute and it's likely unwanted Someone could say it's a feature. By the way It's maybe not really well seen here, but in this constructor I have done this and this is called a reverse shell So I'm invoking a bash process here so that it will basically spawn bash and forward it as the dean and the CD out To this TCP socket. By the way, this depth tcp is a magic path supported by by bash So on the other console, I actually have this shell being spawned well The the Python interpreter connected here and now I can execute some commands So, you know, this could be an attacker server on the bottom and on the top. It could be the admin server, right? Yeah, so this is this is really weird. This shouldn't really work like this Yeah, and there is another catch so we can also investigate how this works more or less If only I will close it. Okay. I close this Okay, let me do so you can investigate it using strace on linux strace is a tool to basically trace system calls on linux so there is this open add system call whenever you know a file is being opened and You can do something like this. I need to forward std error into std out because strace is Printing things on std error. So if I launch strace like this It will basically show me all the files that the Python interpreter has opened With this open add syscall. There are also other syscalls like open, but we don't really care about them But I also want to grab these results for For looking into Dot so so I only want to see whenever it loads like shared objects. So dynamically loaded libraries in C So if we do this, we can see that this Readline so is being loaded here, right and some other libraries that are standard libraries used by C programs as C Python is a C program If I type something in this interpreter and press enter there are much more libraries that are being loaded Which means that you know the interpreter first loads some libraries and Then it will load even more of them whenever you will actually type something in and press enter So as you can see here are some weird some some a lot of libraries are there, right? And for example, there is this busy to as a DMA hash leap There should also be like SSL library loaded the Jason library So it turns out that those libraries can also be loaded from the current working directory Which means that if we come back to our readline example I can remove this one because this one was really not not really used and Rename it to for example Jason that so it could also be this longer name that we have seen in s trace And now type Python 3, you know nothing happened, right? The payload did not get executed I mean the malicious code, but if I type something in the interpreter will load the code and now it will also invoke this malicious library Yeah, and So there are a few libraries that works like that like that as I said for example the busy to as of the May Jason SSL Yeah, so can we actually mitigate this? Well, the answer is yes And we can look over the CPyton source code for the function that actually imports the read line module It's called pi mine import read line and we can see here this pie import import module So this actually also this I think this will also try to load read line dot pi I'm not sure someone should probably try it But the thing is that you know if there is an dot SO library That looks for Python as a valid shared object, then it will load it From kind of working directory And there are some like if conditions here that you know, we can skip this loading of of read line module And one of this is this config isolated and that's called an isolate mode in Python and If you look over the Python documentation or you know Python dash dash help You will see this minus i Where it says that it will run Python in isolated mode what it means it means that it will also imply minus e and minus s Because there are two separate flags for the two different things, but basically this isolated mode Will never add the current working directory into your sis dot path So it might be a good good idea to actually alias your Python interpreter like, you know alias Python Python minus i at least for your root user because you never want to compromise the original user well others as well, of course and now if you do, you know Okay, Python 3 won't really okay, so Python 3 do I have an alias for Python 3 now? Or did it just work because Python 3 links to Python 3? Let me maybe Close this console and try again Okay, so Python 3. Oh wait. It's executing this payload here So if I do Python 3 minus i and then type something in nothing happened It didn't really load this library right now And by the way this library should of course be also loaded with Python 2, but it doesn't work But if we would change it to Redline so and the Python now it's loaded in Python 2 so this Underscore json underscore busy to only works in Python 3 But redline so works in both Um, yeah, so that's one way to mitigate this But actually there was a pull requests on on May About fixing this and it's already merged, but where the hell it's merged I mean in which Python versions does it is it fixed? So in github if you go to like the pull request You then have this like merge commit on the bottom you click on this and then you can see the actual versions It's merged in so it's it's merged into 3.11 But some like beta versions because 3.11 is not released yet So I suppose this will be fixed in 3.11 But until then it might be good to use this isolate mode for this Okay, so let's maybe look at another issue that is there This another issue is called well, there's a github. Sorry the the backtracker Issue for this and this is called deprecate and remove code execution in pth files which aka stands more or less for Code execution whenever you install a package and then launch a python interpreter and not even import this package And it was reported in 2018 And thanks to artichapiel for for this one because he not noticed me about this one But what are the pth files files? It turns out there is of course a documentation page for it in the site library or module so the the pth files are basically path configuration files and You know they can exist in some directories that are mentioned in the documentation and The contents of this file is our additional items to be added to sys.path But that's not everything if you go further It it says that you know blank lines and lines beginning with Comments are skipped. Okay, but lines starting with import followed by space or tap are executed Are executed lines, you know starting with import So first it says that the pth files are path configuration files where you basically add something to sys.path And then it says that you can also execute lines starting with import And there's a note that an executable line in pth file is run at every python startup Regardless, whether a particular module is actually going to be used So you know you you start python you don't import a module, but the pth files are The lines that are executable are executed There is an explanation why that it writes there the primary intended purpose of executable lines is to make the corresponding modules Importable because you might you may have some kind of like import hooks or adjust the path environment variable Or I mean it may be required for you for your module So that's that's the reason why this feature is there, but this feature is a bit problematic So if we would like to test this feature Or a bug or maybe just not a great design We can install this library pip install delivery method It's written by Arthur by the way and you can see it on github But I would not really recommend you doing this, but you know we can do it here So yeah, now I am on directory apper So there is no this so there is not so the the breed line issue is not there anymore So if I do pip install delivery method I'm installing this artist package and now if I type Python 3, you know, there is some payload delivered print That is going on here Okay, so how does this work? We can actually see the packages contents if you do pip show files and the package name and By the way, this also executes the payload And you know that the package is installed in this in this directory and here are all the files that are in this package So there is this a a a a underscore deliver method dot pth file there. So let's look at its contents Yep, and if we go to this File it basically does import sys and import deliver method Okay, so what does import delivery method do because that's the package, right? So the limit deliver method pie is also there and it just prints this payload Delivered so this is how it works But it's a bit more scary because You know, they said that the Python lines here are executed If they begin with import, so can I do this? print something and You know if I save it, it should just now work and as you can see I'm I Printed as dealer in this pth file. So it's not really you dot py file But a pth file and some lines of its are executed Actually, not all of the lines because if I would not start a line with import But if I would start it with just print it would not be executed Because you know, that's also what the documentation said that the line has to start with import so if we go to the To source code how it works. There is this app package function that is being responsible for processing this pth files within the site packages directory and You know it opens the the file and then enumerates over its lines And if the line starts with with command, it's just continues But if it starts with import or import tab or import space, it will execute line with exec so this is how it works and So, you know, they said that some some modules may be using it, right? And they should use it. They should use the pth files basically to add to this path or maybe to import something but there is this unintended Unintended feature that you can execute code as well So let's use let's see at some packages that actually leverage this feature or maybe a bug So for example the pie test cough package. It does something like this in its pth file and you know, it's it's It's a module for pie test to to get code coverage from your tests And so it first imports OS and sys and then execute some code if some like environment variable is there Then it will import in it and initialize something It seems this is something related to like sub process coverage. No idea. What's what's that for there? But yeah Another example is the hunter module. So hunter is some module that can be used for tracing your Python code And it also executes some code even simpler, right? Just execute some some underscore private method from from the from its own module So can we actually prevent this behavior because it's a bit creepy or scary So there was this pipe six four eight that was proposed sometime ago and it's actually It says that you know extensible customizations of the interpreter at startup. They wanted to improve improve this this thing Maybe not necessarily disabled completely, but definitely like Make it better but its status is rejected and It got rejected because it has a limited number of use cases and further complicate the startup sequence Okay There is another way we can do it. We can use the isolate mode again here. So if I go back here, I Have this Wait, where is this module? Did I remove it? Oh, I I need an import here, right? Okay, so this is being executed But if we do Python free minus ease minus ISO isolated mode, this is not happening happening anymore So this is how we can mitigate it But yeah, note that this bug is about Dpk and remove code execution in pth files. So they don't really want to execute all the code They want the syspath Appended feature being there, but they also want the import feature being there So likely the pth files are going to stay there, but they don't want this like any code being executed in there There was also a proposal to skip lines that have, you know This not colon. I don't remember them. Yeah Character in it, but it was not really implemented So we will see how it goes. You know, it's already four years since it was reported But it's still there Um, the next issue is about socket iNet add-on function And if it has some parsing issues on some leapsie versions And I reported it in 2019. It's still there But yeah, let's see at the documentation for this function So socket iNet add-on is actually like function that is taken from from leapsie from the C library and what it does it's converts and IP string IPv4 Address from, you know, it's string representation to the 32-bit packed binary format So how does this work if I do Python free right on here? import socket socket iNet add-on and do this You will get the binary representation for this IP address There are so also more features into it. It also acts at strings with less than three dots like you can have an IP address like this and You know, it will map in a weird way. You can even have this you can even have some fancy number Like this This is treated as an IP address. By the way at some point where browsers also accepted this. I'm not sure if they still do But yeah, it could sometimes allow to like bypass some, you know IP validation where IP was validated by just a string Yeah, and There are some other weird things into it and by the way, yeah, if you pass in an incorrect IP like this or maybe like this Whoops, why did it work? This is not a valid IP address here but it actually worked and You know, this is something unwanted especially if you kind of you may expect that if you use this function that it will properly Like validate your IP address if it's a valid IP address But apparently it does not And now imagine such code on production Where you are taking the IP string from some requests and then you do an if condition or maybe a try accept Because well, it it throws an error here an exception and you do something like this or a system ping blah, blah, blah So where could such a code appear? Well, maybe in routers and this was actually the case But it was not really in Python. It was in C like there was a C code that used this function And you know if you have something like this or is that system ping plus the IP string Then you can bypass the command here and you can execute arbitrary code whenever you pass and malicious IP address And this was fun boy Blasty for whom I also check this issue in in CPython So I looked into CPython how it uses socket inet at on and by the way It's it's very important that this function is broken only in the glibc implementation At least I believe I'm not sure why exactly we are getting here this on Mac. So maybe it's also in the In the LLVM's implementation as well, but for example, if you use muscle or microleap see it won't be there I mean this won't really be supported on those weird on those other versions Yeah, so there are the differences in libc implementations Obviously So how it's used in in Python. So there was this issue within the SSL dot match hostname function in CPython Where if you did something like this So you want you wanted to match a hostname for a given certificate that should you know match a given IP address It was it was matching it So this could actually be a you know a security issue because you may want to do some IP filtering or match the Well, the certificates hostname But we didn't found any in a case where it was actually a exploit able Where someone could actually leverage this and do something malicious But it is already fixed and in this SSL library The the pull request is merged But I also found another case in the request library So I guess everyone knows requests. It's library for like sending HTTP requests But the thing is it also has some like utility functions. So for example, you can find out if a given IP address belongs to a given network or And or if it's an IP for before address and so on and you know, it's broken here As you can see I reported it not some time ago, but Nobody cared so it's there. So maybe don't really use those functions and Another thing I wanted to show you is this script dot crypt function on macOS So this crypt dot script function is basically broken on macOS and we can see it here if we import crypt Maybe let's do it in In ipyton Yeah, so first of all, let's see the documentation string for it. So the crypt dot crypt is supposed to return a string representation Representing the one-way hash of a password with a salt prepended. So Maybe let's see how it works on Linux first No, my payload is still executing here So if I do something like this And just to remember the first argument is the word so a password and then you pass in a salt which actually It uses some some method that you specify there Yeah, so if we execute this script dot script like this whoops wrong Yeah, so if we execute it like this we are getting The the password hashed we have a given hashed my hashing method And there is a default one and the salt is prepended here somewhere here. So the salt is This is actually the salt whatever is between the two dollars And you know the salt is randomized and then you have the hash of that password with the salt with a given method And by the way, you can find out this method with like man five crypt And you can look for like dollar six for example and dollar six Corresponds to Shafi five hundred twelve crypt. So shot to with 500 bit output And yeah, there's a format as well for us, but if we executed in macOS What happens? It gives us some weird output And you know, we don't really see any an assault being prepended here even no method So what is going on in here? The thing is that the script module Wait, I need to do two asterisks here Yeah It has this dot methods member which tells you what are the crypt methods that you are supporting and on macOS there is only one method here on On Linux you will have much more of them So as you can see it's basically using a different default methods and you can't even use any other Method that is there. So on Linux you can do something like ABC and then probably crypt method Crypt I believe and you will get the same output as on macOS, right? So this is a different crypt method I don't really remember which one is that but it's probably not very secure if it you know uses only this amount of bytes For for hashing, right? It's probably brutal Like you can brute force it But on macOS you can't really do You know other methods because you don't have them supported. So if you try to some do something like It was what was the name? like this it Wait, it actually worked So this is even more interesting because it worked only partially because as you can see the output is still truncated here So it's even more broken because you are getting now something that it's supposed to be hashed but you know You're only seeing part of it of its out and the Saudis It's for some reason constant for a given input or maybe it's wait, maybe it's still using the old old method I'm not sure but it's basically broken. So you should really not use it if you want your code to be, you know cross-platform And if you go to the crit function crit libraries Documentation it says that you know, it's deprecated since 3.11 so There is this pep 594 that details some alternatives But you should definitely use an alternative which is using hash leap module instead Because why would they use the crypt method which is broken basically? And that's all that I wanted to show you so for a quick summary Be careful whenever you are using, you know, Python invoking Python interpreter in random directories where there may be some files That you are not aware of what are those because there is this lib read line so and some other so's or shared objects that may be loaded I haven't really checked whether this is also about like Mac OS or Windows, but I suppose suppose it's probably yes But it's going to be fixed in Python 3.11 and you can also use isolate mode. So it may be good to do this alias here Then there's also this pth files so puff configuration files that may execute code on each Python Startup so isolate mode helps as well here, but other than that then there is no solution yet Then there's this socket inet atom that you should not really use So if you want to parse an IP address, you should probably use some IP address module instead of just a pure libc function Because even though you know you may want to use something more low-level and probably faster It doesn't really work properly as you can see or It's supposed to work properly because like the documentation states that Those functions are like just dispatching the calls into libc and whatever libc does is you know supposedly the correct solution But I wouldn't say so And you know the request module has this still this back when when they use this socket inet atom And there is this script script being broken on macOS And if you want to like see the slides from the presentation There is the link at the bottom that you can use and it has also that the code for demos So if you have any questions, I'm happy to answer them. Thank you Okay, please line up if you have questions Is that anyone online with a question? Thank you for a talk. I definitely learned a few things I wanted to ask about the first back Is it because it's on the Python path or is it because in the current working directory? Yeah, so it's a great question We can actually go to the pull request that fixes it because that's probably the easiest Right. I didn't link it here so we can probably do something like I have it a link to it here Yeah, so if we go to how it's how it's being fixed Yeah, I need to make it larger of course They basically moved to this imports for the For they basically moved this call to py main import read line to an earlier place in the code and you know, this is because here I believe here or maybe some somewhere between those two Differences they are adding the current working directory to the sys path. So yes indeed. This is exactly it That's why the module is imported because that current working directory is on the sys path whenever it's being imported and this You know, they made this unintended mistake that allowed for this behavior That probably that probably means that I could simplify like the code by just like putting some Python there I don't need to compile C extension Probably I'm not sure we can actually check this out very fast. I Think it didn't work back then but Wait, did I name it so? Sorry guys Yeah, so this is how it looks and Yeah, it actually it actually executed but now it's Now it gives you this attribute error here that you broke the read the read line module Yeah, so my question is if they just move the Importing the read line only does it fix the Under score Jason and other such libraries as well That's a good question. And you know if I would have pie and here here. I would install 3.11 and test it out but I don't Probably maybe do something like Let's see what's the network here if I have network I have network but it is probably too slow Oh wait, I need to stand This would take a bit, but I can do it. Maybe Like this I Don't have okay different machine. Oh, maybe there is no image for Python 3.11 yet Maybe we can see it in Docker hub if they have it here Because that would be the fastest way to check it out There is some Python 3.11 You should always be right Yeah, I missed Python, right? Thank you. I Probably need to launch SH or maybe just Python because maybe this I really see if this image has bash inside They also don't have VM there. So I would need to do something like This in order to test it Wait, I know well, I would actually need to compile something. Let's see if we can do it very fast Wait, GCC is installed there So now we need this maybe install VM again If there is another question I can maybe answer one fast one, but in the meantime Yeah, I was wondering if you use minus I Is it Python still very usable? Can you use virtual environments still or? It's a good question. We can see I Should have an environment here. Yeah, it is there in this environment I can import PON whatever that is by the way. So if I do Python minus I and import PON It still works The thing is that you want to be able to import whatever you have here So if I have a you know module like a Here in my you know current working directory. I can't really import it and normally I would be able to So that's the issue here. That is happening Yeah, so coming back to this Sorry, I Mean docker This this this shows me that I am in the docker container Sorry, it's not supposed to be lip see it's supposed to be Jason. So so maybe it's fixed Maybe because I don't know Wait, yeah, we have to write something for Jason. So it seems it's probably fixed Yeah, you know, there are only one Python here. So it seems it's fixed But you never know because you know this behavior is Hard to detect basically Maybe first trace. Well, that's maybe not as trace it here Yep, any more questions any questions online Anyone in the room still have anything they'd like to ask Or any comments Well, if not, let's thank disconnected for this fantastic talk