start	end	text
0	5660	All right, we're back live. Thank you, Liron, for your support and supporting DEF CON and the
5660	10860	Red Team Village. It's a pleasure to have you here. So without further ado, take it away.
11440	18920	All right, thank you. So thank you all for coming to this talk. It's my first time presenting at
18920	23900	DEF CON. I think last year I got caught up in the DEF CON Blue Team Village trying to win that CTF
23900	29660	from OpenSOC. It was pretty dope. But I think this time today, especially, we're going to talk about
30600	33980	something more Red Team-ish. Why? Because I'm on the Red Team, and it just seems
34360	39760	appropriate. So this is the talk, y'all trying to bypass Python 3.8 autohooks or nah?
43270	49090	So who am I? My name is Liron Gray. I go by DaddyCocoman in most areas. You can check out
49090	54910	my website, DaddyCocoman.dev. I haven't updated it in a while, but there's some cool stuff there,
54910	59610	I think. You can also check out my GitHub. I have a couple of projects I've been working on,
59610	65110	and hopefully I might have some more interesting projects up there very shortly. I was in the Navy
65110	71050	for about ten years. I used to do electronics. I used to work on radar and communication systems,
71050	76090	and I moved over into the cyber field. And I'm on the Microsoft Azure Red Team now.
76130	81310	So you may hear some Azure-related things if you follow me on Twitter or wherever you may
81310	85390	follow me. Hopefully it's only on Twitter and not anywhere else like real life.
85390	91070	I also love Python and Pythonic things. So we'll talk about some other Pythonic
91070	97070	things later in this talk that I really got into at a certain point in time.
98190	103990	Because it was... it's pretty cool. I like the way Python is written as a language,
103990	109450	more so than statically compiled languages. And I'm also a Nerdcore rapper. I go by OhMy.
109450	116990	It's an I, not a one. You can find my music at MCOhMy.com. I'm actually on the DEF CON 28 tape.
116990	122390	I got that hot track. I got that heat. Xs and Ram. If you want to listen to that,
123230	130370	please do. And hopefully you enjoy all the music. So let's talk about the agenda. So first let's
130370	135350	talk about why we're using Python 3. Then we'll talk about the two peps that have influenced
135350	141290	audit hooks for Python 3.8. We'll talk about how to implement them, how to bypass them for Windows,
141290	148710	how to bypass them for Linux, and how to bypass Python. And bypass Python is very... it's in
148710	153450	quotes for a reason. But we'll talk about some interesting concepts that came up over the last
153450	162090	year. So quick question. Why are you still using Python 2? I want you to look every single one of
162090	167710	these people in the face and convince them that they have the time to listen to you explain why
167710	181940	you're still using Python 2. I'll wait. All right. So you might have a legitimate reason, right?
181940	187520	Sometimes there are tools out there that haven't been updated in the last 10 years or whatever.
187600	193320	And if you are using something that is written in Python 2, I just suggest making a pull request,
193320	199760	helping them move up to Python 3, and try not to break that backward compatibility if you really
199760	206780	need it. So here's some Python 3 changes that have happened in the last couple of versions.
206780	213620	Python 3.5, we had the async, await keywords that came out. Python 3.6, we had format strings.
213620	218600	So you could say things like, hey, my name is Daddy Coco Man. And then you could print my name
218600	225080	is in Curly Brace's name, and you would get my name is Daddy Coco Man. We also had ordered
225080	231820	dictionaries. So now Python recognizes the insertion order for dictionaries, and you can
231820	236540	always keep track of it. Previously, dictionaries would just be a random result every time you would
236540	243040	try to print them or call them. But now they're ordered, which is great. In Python 3.7, async and
243040	247600	await actually became reserved keywords. We had the breakpoint functions, so we could enter
247600	252000	debuggers in the middle of our scripts. And we had the data class decorators, so we could make
252000	258500	these struct-like classes. So you can say, here are these fields that a class is supposed to have,
258500	263820	and here are some functions that it may want to implement, just like classes, but a little bit
263820	269280	better, in my opinion. They serve different purposes. So for Python 3.8, we have this
269280	274380	controversial thing called the walrus operator, where you can assign values while evaluating an
274380	281840	expression. So on something like this, we can say, it reads, if the length of a is greater than 10,
281840	289860	then we can assign it to n, and then be moving to the loop logic for print, list is too long,
289860	295520	blah, blah, blah. But if length of a was not equal to 10, then we wouldn't assign it to n,
295520	300480	and we would just move on. So it's nice, it saves you a line of code. A lot of Python purists
300480	305520	really don't like it, and that's okay. People generally don't like changes to languages that
305520	311980	they've been using for so long, but oh, well. So next, we have the async IO enabled REPL,
311980	318520	so you can do Python dash M, async IO, and you can start using those await and async keywords
318520	324700	inside of your REPL. And what we're here to talk about primarily are these audit hooks.
324700	330280	So audit hooks are runtime events, are hooking those runtime events to perform some action,
330280	335980	some arbitrary action, whatever you want to do. So the first pep we'll talk about explains how
337360	341720	the audit hooks are supposed to be implemented, or how they're supposed to work, why you should
341720	348260	have them. So the proposal was that sysadmins could use audit hooks in order to add some feature
348260	356320	to their Python executable. So some suggestions that came in were integrating AMSI, script block
356920	365120	for Windows, and then for Linux, you could send events to syslog or audit D. And
366840	371340	if you... I meant to link it in here. I guess I didn't. I must have removed it by accident.
371340	378840	There is a GitHub, GitHub.com slash ZOOBA, Z-O-O-B-A, from Steve Dower, who has a bunch of
379960	384860	examples of what these audit hooks can look like in different implementations. So I would
384860	390040	definitely check that out. Another thing you can do is restrict the importable modules. So you can
390040	396380	say, well, maybe I need my people who are using Python in my environment to use the request
396380	403300	module, but maybe for whatever reason, they don't need to use the mmap module. So you can say,
403300	408700	well, I want them to use specific modules that are only for my environment, and I don't want
408700	413620	them to use anything else. You can also do things like remove command line arguments. So before I
413620	420460	mentioned with async IO or just a lot of other command line Python modules like HTTP server,
420460	427020	you can actually remove that dash M or the dash C to prevent people from using those switches.
428420	433240	But one thing to keep in mind is that the PEP actually reads that you should
433820	438220	detect the code over preventing the code. And the reason for that is because
441080	447880	sometimes you may use the Python dependency tree graph is out of control sometimes, right? You may
447880	454380	try to use the NumPy module, and underneath it, it uses C types. So if you try to block C types
454380	458920	completely because you didn't want someone importing it, you may actually mess up using NumPy.
458920	464600	So it's important to just detect what's occurring rather than prevent what's occurring because you
464600	471180	might mess up something that you had no idea was going to go wrong. Another thing that the PEP
471180	475800	recommends you do is implement OS security level features to protect your modified Python.
475800	481640	So that means device guard for Windows, maybe do some exe signing, use catalog files, that kind of
481640	487720	stuff, and extended attributes for Unix. So maybe you want to add file attributes that have the
487720	496600	hashes of the permitted Python files that are allowed to be used. PEP 578 says here's how we're
496600	502320	going to implement or how you can implement these audit hooks. And they can be implemented inside
502320	510080	the executable or inside of the script. So if you want to do it in C, you can actually recompile a
510080	517060	new executable, a new Python.exe, and all the hooks that you apply to that code will be run by this
517060	525780	new exe. Or if you want to just do it in Python inside of your script, you can, without compiling
525780	531760	it, you can use the sys module, so import sys, and then sys.addAuditHook, and then the audit hook
531760	538400	will be a function where you can handle all of your audit hook logic the same way that you can do it
538400	546060	in C, except it will only apply to the script where it's implemented. So here's two examples.
546060	552460	On the left side, we have the Python implementation of network prompt hook, right? We have, this is
552460	562120	some bad Python, so ignore this, but we have an event that gets passed into the function,
562120	566340	and then we're looking at this event and we're saying, does it equal socket.getAddressInfo?
566340	570340	If so, I want to take some action. I want to warn the user that we're attempting to resolve
570340	576480	some URL. If it's socket.connect, we're saying we're attempting to connect to this URL. Do you
576480	581940	want to continue? On the right side, we have the C version, which you can use to compile
582560	588060	a new Python executable. Notice it's considerably longer, and personally, I never really learned C
588060	595200	and I don't really plan on writing C any time soon, so good luck to you if that's your thing.
595900	601020	So here's a list of audit events that are currently implemented in Python 3.8. You can
601020	608260	actually pull this from the documentation itself, but what I have highlighted are some of the
608260	613840	things that you may really be interested in if you see these events occurring. Now, keep in mind
613840	622200	that you have to modify this for your particular environment, but some things that you may find
622200	629420	interesting are when if a script uses the compile function or the exec function or maybe some of
629420	633780	these winreg functions on the right, things that you may not normally see in your environment,
633780	640800	you have to be able to adjust your audit hooks appropriately. Pty.spawn, I don't know a single
640800	647400	case of anyone using pty.spawn outside of trying to get a shell after they've already compromised
647400	653560	your system. So things like that may be of interest to you, and you may want to capture those events
654280	661080	as they occur. But again, the focus is on detection and not prevention. So we wanted to
661080	664780	make sure that we are aware of these things that are happening, but we don't want to actually
664780	671560	prevent them from happening unless you have a really good use case or use scenario to not
672120	678000	let a particular event happen. I mean, you can totally do. You can block all events if you want
678000	683820	to. It's not like a fake Python just sitting there. I don't know, honeypot Python or whatever.
684620	688920	So bypassing audit hooks for Windows. So first we'll talk about how auditing works.
689180	696620	So when an action is or when an audit hook is signaled, Python determines whether the action
696620	702880	matches an audit rule. And that's kind of maybe not the best explanation. What happens is it will
702880	709300	run through every single audit event or audit hook that you have, but you have to write your audit
709300	714800	hooks to only care about the events that you want. So for example, in this network prompt hook, we
714800	719580	only care about socket events. If it doesn't equal socket, then we're going to return because we don't
719580	727900	want the rest of the hook to go through all that logic. So when you write your audit hooks,
727900	732480	you have to make sure that you only process the events that you care about. Also, you probably
732480	739320	shouldn't do anything like a while loop, some unbreakable condition, which is not a good idea.
739820	745000	So the other thing to know is that all audit hooks are processed for all events. So even if I open a
745000	750660	file, which is an audit hook event open, the open function, it's still going to get passed to this
750660	754500	network prompt hook, but it won't do anything because I set a condition to only care about
754500	762960	socket. So what's in the source code to figure out how these things work? So on the left side,
762960	768940	we have the PySys add audit hook function. And what it does when it adds an audit hook,
768940	776020	it literally just adds the hook to the end of a linked list. When the hook is called in the middle
776020	783420	or the upper right, it will actually just go through each hook that has been defined and
783940	788040	just goes through all those functions and determines whether or not it needs to do anything.
788820	794900	Or based on... it will... you decide whether or not it needs to do anything, but it will just run all
794900	802020	those functions at once. In order, I should say. On the right side, we also see that
802020	807900	tracing is disallowed. Now, what I originally thought this meant was that I could not see into
807900	814160	what an audit hook looks like. But it turns out that that tracing set to false is only for the
814160	820120	Python debugger. So if you're using the Python debugger, for whatever reason, maybe in your IDE
820120	827080	or maybe on the command line, you actually can't trace into the audit hooks. But if you're using
827080	832460	an external debugger, so let's say maybe you grabbed a copy of a modified Python off of a
832460	837520	system and you took it back to your station and you opened up a debugger, you can actually see
837520	842420	what's going on. So here's an example, right? The same example where we had the socket.getaddress
842420	847680	info. I can actually look into this audit hook to determine what it's doing. I'm not the greatest at
847680	852880	assembly, and so I know what this says simply because I know what the code says. But you can
852880	858100	still get a general idea. You can see the comparison strings, the compare, spython.string
858100	863160	compare, and you can see that it's looking for certain audit hooks to occur before moving into
863160	870100	some other logic. And you can also see the strings there attempt to resolve the URL that it
870100	878900	tried to resolve. So here are some ideas for bypassing. By implementation, audit hooks can't
878900	884180	be cleared once they're set. So there is a function, I think, to clear audit hooks, I think,
884180	888740	but it doesn't actually work. You also can't overwrite. You can't, like, reload the sys module
888740	895840	in order to clear all the audit hooks either. So one thing we could do is maybe overwrite the head
895840	903020	of the linked list to point to null, but it might be possible. And attempting to locate the start of
903120	908180	a linked list in memory might be a little bit difficult. But I'm actually really new to exploit
908180	912180	development, so it might be possible. So if someone knows something I don't, please tell me,
912180	916840	and maybe we'll figure that out. But in the meantime, maybe we can just overwrite the bytes
916840	925140	in memory that calls the hook functions. And that smells like PowerShell, right? Everybody
925140	931000	knows PowerShell and AMSI bypassing, and we can actually apply the same concept here.
931040	937040	So there's plenty of resources to draw inspiration from. Adam Chester over at MDSec has plenty of
937040	943760	articles about bypassing AMSI, so does RastaMouse, and there's a lot of inspiration just to draw
943760	949920	just by reading their blogs. And it's also significantly easier to determine the address
949920	954940	that you need to overwrite with these new bytes that you want to overwrite with.
955400	964280	So let's go that way. So if I take the Python 3.8 DLL, and I open it up, or if I run Python,
964280	970160	rather, this modified Python, and I go check out the Python 3.8 DLL, I can go find the address
970160	976860	for PySysAudit, and we can see this is the 32-bit version. We can see the address and set
976960	984100	a breakpoint on it. If I scroll further down, I'll get to a point where a function is called,
984100	990860	and under call EAX, it has the name of the hook that is being called, right? So if I were to step
990860	999240	into that call, I would actually see the logic that I showed you earlier. So the offending,
999240	1007260	I say offending, but the interesting call that we want to overwrite is where EIP is currently
1007260	1013480	pointing, and that is call EAX for 32-bit, and call keyword pointer for 64-bit.
1017370	1022370	So, again, it's very similar to PowerShell. We need to load the Python 3.8 DLL, we need to find
1022370	1027550	the git address for PySysAudit, we need to change the memory permissions, and we need to just
1027550	1037330	overwrite those bytes. And I learned this week, a couple of days ago, that I really need to go
1037330	1042890	through all these versions of Python and grab their offsets. So I went through the 32 and 64-bit
1042890	1051810	versions of Python, from 3.8 to 3.85, and even just for poops and laughter, I went to 3.9.0
1052570	1058090	Beta 5, just to see if it still worked, and it does. So we just have to set these offsets,
1058090	1064170	and we can change the memory permissions and overwrite those offsets.
1065210	1073030	Let's talk about Linux real quick. So I have never done any sort of, like, major
1074570	1078990	Linux exploitation other than, like, a buffer overflow, right, that you find in, like, CTFs.
1078990	1085090	I've never done any patching, so I didn't fully understand at the time how the Python
1087290	1094410	packages worked on Linux. So there are no DLLs, right, because it's not Windows,
1094410	1101410	and there was no sysmodule.so library that I thought was going to be there, and it just wasn't.
1101410	1107330	So it's like the same, though, right? You can find the PySysAudit address by searching the Python
1107330	1112150	executable itself. So I guess it's statically compiled into the binary, and you can just pull
1112150	1121710	it out. So if you use the readelf, we can use readelf to figure out what the address of PySysAudit
1121710	1129630	is, and we're going to cheat a bit, because I actually don't know how to parse ELF files.
1129630	1137850	So in my code, I just subprocess readelf and grab the output, because I actually have no
1137850	1143090	idea how to do it otherwise. And then we have to locate this address in memory.
1143650	1149890	So once we grab that address here, it's 53007 foxtrot 0. We need to figure out where it's
1149890	1155710	located and what region of memory that it's mapped to. And in this picture, we can see that it's
1155710	1165530	under... it falls in the range of F230 and 6AE. So we need to modify those permissions.
1166070	1172390	And we can do that, of course, with libc and mprotect. So we need the write execute permissions
1172390	1178870	to change and to read write execute permissions. And sometimes, yes, sometimes it just be like
1178870	1184930	that. I am watching the Discord, by the way. So what this looks like is we load
1184930	1193210	the DLL for libc. Or we load libc, rather, I should say. And then we find the readelf. We find the
1193210	1200930	address of pysis audit. We find the location of the memory mapped files. And then we determine
1200930	1208050	in what region it falls under. And then we change the permissions. And then we just overwrite the
1209810	1218670	the bytes in memory with these nops. So one thing I didn't go too far in depth in Linux
1218670	1225950	is because every build for Python in Linux is different. So it's... I don't have the time
1226450	1231250	of the day. Like, I'm stuck at home. I only go out to the supermarket if I need to.
1231250	1236250	But I still don't have the time in the day to go through all these Linux versions and try to
1236250	1244190	find these offsets. So later today I'll probably put this code up on GitHub. And if you have a
1244190	1253090	version of Linux where you want to bypass audit hooks for 3.8, then you can send a pull request
1253090	1260040	and I will happily accept once it's been tested. Let's talk about detection.
1261120	1268300	While everything that I've mentioned works, everything up until how... up until you patch
1268300	1275040	the binaries... the bytes, rather, can be detected. So importing C types can be detected,
1275040	1280680	importing platform, importing sys. Every single function call that you make can be detected
1281960	1289920	until you hit that men move and you overwrite the bytes. The way to defeat this
1290040	1295600	is ironically better audit hook logging. Because if you can detect that these things are occurring
1295600	1300340	and you send them off and you ship them to your seam or however you... your Windows event logs
1300340	1304860	or however you collect your logs and you see that these are occurring, well, then you have a better
1304860	1310540	chance of understanding what's going on with your Python executable on a random given station.
1311100	1319960	So, yes, it's sort of like... it's like running AMSI bypass in PowerShell where you might get
1319960	1327320	caught. It's the same thing. So that is something you should probably implement better audit hooks.
1327400	1331840	And it might be harder for someone who doesn't program in C to implement, right? I don't think
1331840	1337600	most sys admins have a lot of C knowledge. I could be wrong. I could be naive. I've never
1337600	1343240	been a sys admin. I have no idea. But it's probably not something that you come across
1343720	1350140	in the general resume where you need 10 years of Adobe XD that came out last year.
1350660	1357820	So it would be great if somebody came up with a template of audit options and an action to
1357820	1363920	help sys admins compile those custom Python executables. But as I mentioned, I don't have
1363920	1370600	the time. So it's not me. So I want to give a demo real quick.
1377770	1384790	Hopefully we can all still see the screen that I have up. I think it should be working.
1386210	1389790	A Discord confirmation would be great if you can see this terminal.
1391590	1393210	We can see it.
1395760	1397700	Or a confirmation from Omar.
1398320	1400300	Yeah, we can see it.
1405180	1416080	Thank you. Great. So I have this executable here. This sPython that's been compiled.
1416680	1423360	And I'm just going to enter it directly. And I'm going to try to
1425120	1431020	run this code. And all I want to do is get the status code
1433680	1437260	of the Red Team Village.io. I just want to make sure it's up. Because Red Team Village
1437260	1440800	has been doing a great job. And I want to make sure that their website is up.
1440880	1446580	But if I do that, I get this warning saying you're attempting to resolve Red Team Village.io.
1446580	1449980	Do you want to continue? Yes, I do want to continue. Yes, I do want to handle the socket
1449980	1455380	event. Yes, I do want to connect to this. And finally, I can get the HTTP status code.
1455580	1461740	That's great. But I don't want to have to go through all that again. So maybe I'll just do
1461740	1467380	this audit bypass. And we'll see. We're getting the handle. I'm printing out all this arbitrary
1467380	1472080	stuff that doesn't really need to be printed out. We get the audit address. We find the
1472080	1476660	protections. Using the offsets, we find the region of memory that we need to overwrite.
1476660	1481180	And we successfully change the memory. And if you ever played NBA Jam, like the video game
1481180	1486280	of your life, this is the point in time where I say, boom, shakalaka! And we bypass the audit
1486280	1496080	hooks. So now when I run the same code, again, we just get status code 200. And yay! Right?
1496080	1501020	We win. We don't have to worry about the audit hooks and going through all that stuff again.
1501020	1516500	So that's great. Let's get back to these slides. Whoops. I lost my cursor. There we go.
1518920	1525300	So let's talk about bypassing Python. Because as I mentioned, everything up until that point
1525300	1533280	where you overwrite those bytes can be detected in memory. Or detected by an audit hook. And
1533280	1539240	that could be bad. Right? So let's take this a step further. So since the bypass is easy to detect,
1539240	1546880	are there any other methods that we can use to overwrite or to bypass Python in a sense? Right?
1546880	1551440	So there are other ways to load and patch DLLs. I've had conversations with smart people
1551440	1559060	about Python internals. And they've suggested other ways to do it. So, you know, just loading
1561400	1565280	DLLs to kernel 32 and virtual protect and all that stuff is one way to do it. But there are
1565280	1572300	other ways to do it. But everything that you do that way has to be run in the context of the
1572300	1576920	Python interpreter. So no matter what I do, or no matter what you do, rather,
1578300	1584480	you can be detected. No matter how you do it. So the question is, can we run Python
1584980	1588840	without running the Python executable? And obviously, the answer is yes. Because I
1588840	1592580	wouldn't be asking you this question and wasting your time with that bit of the slide.
1592600	1598060	So there's a package called Python net, which is a legitimate package. It's great.
1598060	1606020	And it uses it allows you to access the .NET CLR from normal Python. It's not Iron Python,
1606020	1612980	right? It's not Iron Python at all. Iron Python is an implementation of Python that is,
1612980	1620700	as far as I'm aware, not even MSIL compliant. But the syntax is still similar to Iron Python.
1621760	1627760	And as of May 2020, it supports Python 3.8. That's great.
1628500	1632460	And my camera just went out. So I'm going to turn my camera off, if that's okay.
1634900	1642980	So we have the here's some syntax, right? We can import CLR from collections that we import
1642980	1649060	counter. And then we can access the CLR the same way that we do it in sort of Iron Python-ish ways,
1649060	1653800	right? Where from system.diagnostics, we can import process, and we can actually list all
1653800	1661980	the processes in a CLR manner. CLR-like syntax. Or .NET-like syntax, I should say.
1663200	1669840	You can actually also embed Python in your .NET executable. So Python.NET comes with a .NET
1669840	1675600	assembly called Python.runtime.dll. And that can be used with .NET languages to run Python.
1676160	1682480	The Python types for the basic primitive types are actually written in C sharp and compiled as
1682480	1688140	part of this assembly. The only requirement is that your path variable must include a directory
1688140	1692440	with the Python runtime installed. So in this case, we're looking for Python 3.8.dll.
1693700	1697660	So here's an example of what it looks like in C sharp. We have this main function,
1697660	1704120	and then we are using the Python gill, and we can import modules that Python knows about,
1704120	1709160	such as numpy. And when we run this function, we get this output to the right,
1709600	1714940	and it's pretty cool that we can actually run Python inside of .NET.
1716980	1722220	And we can use C sharp to write this, but I like boolang. The reason I like boolang is because
1722220	1727340	of Bytebleeder and the Silent Trinity project, that C2 framework that he has. It's pretty dope.
1727340	1734520	I've contributed to it in the past, and I really like boolang because it's very Pythonic. If you
1734520	1738540	look at the code that's on the screen right now, it almost seems like it's Python, but it's not.
1738540	1744900	It's boolang. Also, support Bytebleeder in his open source sponsorship program that he has. It's
1745860	1751740	definitely worth investing in people who have helped you in your corporate endeavors for
1751740	1758240	pentesting and retting. So what we need to do, as I mentioned before, is point the path variable
1758240	1765080	to a Python installation folder, which gives me an idea, right? Because the path variable
1765700	1772480	can be set to any folder on the disk or on a network share. So you can already see where I'm
1772480	1779320	going with this, hopefully. Hmm. I tried to find a black one, but there are no black emojis.
1779840	1786480	So we can do Python across a network share by simply by pointing the Python path to it. So in
1786480	1793220	this case, I had a share called winwork slash Python 3.7. And this is 3.7 because at the time
1793220	1798060	I was testing it with 3.7 before 3.8 came out. It still works with 3.8, so ignore that.
1798600	1802040	And that's what it looks like, right? So the path variable can be set to
1802040	1808620	the server slash share, just as long as that share points to a Python 3x installation.
1809820	1813660	And this is what it looks like. So you can see all this SMB traffic,
1813660	1820260	and you can clearly see that I'm loading Python 3.7 across a share.
1820260	1826120	Now, this isn't like an abuse of anything, right? You can actually install Python when you install
1826120	1830520	Python normally on a network share and have everybody access it. It's slower because you
1830520	1836480	have to load those resources remotely, but you can actually do that. But if we can use SMB,
1836480	1844660	that probably means we can also use WebDAV. And sure enough, we can actually load Python
1844660	1856840	across WebDAV by setting the server and the port. So whack whack server at port,
1856840	1865960	and SSL is optional. There's a registry setting in Windows that determines how you can authenticate
1865960	1873960	with WebDAV, and you may have to set that in some environments. I was having issues with
1873960	1881040	this earlier today, or earlier, I guess it was today, or last night, whatever. And something
1881040	1885720	broke, and I couldn't figure out what it was. So I can't show you what it looks like going across
1886180	1891600	a WebDAV server. But it works. Here are the packets. It worked at some point in time.
1891600	1898300	I just need to figure out what happened. So we can also host our own repo to remotely
1898300	1902160	load packages, right? We don't need to pip install anything on the target box,
1902160	1907680	assuming that we need extra modules that aren't already there on the Python 3.8 installation.
1907820	1913180	Empire did this by implementing a custom import hook that allows you to modify the logic and how
1913180	1917100	modules are loaded. So it allows you to load things remotely, and if you want more information
1917100	1921960	on that, you can check out Chris Ross's blog on in-memory Python imports, or you can check out
1921960	1930700	the Sulink's GitHub repo for remote importer, and you can check that out. It's really interesting
1930700	1935100	stuff. There's also another module out there called HTTP importer, or something like that,
1935100	1941760	that allows you to import modules over HTTP directly. And of course, Empire was written
1942640	1947520	a long time ago. I know a new company is picking up Empire, I think it was BC Security, and I think
1947520	1951360	they recently released a tool called Starkiller that seems really interesting. You should probably
1951360	1958360	go check that out. If you want to load a package remotely, you need to just have it in the standard
1958360	1964080	format. So here's the request module, and I want to just import this. This is what it needs to look
1964080	1968480	like. It needs to be the zip file. Inside it, you need to have the name of the package, and then
1968480	1972660	inside a folder with the name of the package, and inside of that, you need to have all the files
1972660	1990120	that you need for the package. Let's do a demo of that real quick. All right. So in this folder,
1990120	1997100	I have some code called scarysnake.boo. Everybody who writes C Sharp tools likes
1997100	2002060	to call their tools Sharp something, but no, this is Boo. It's about ghosts, so we're doing
2002060	2010420	scarysnake. And so we can actually compile this. I wrote a little thing using the compiler that
2010420	2019240	comes with Boo, and we can just compile it real quick. We'll also use ILRepack to package the
2019680	2026620	the additional assemblies that we need. That includes the Boo.lang assembly and some other
2026620	2032660	additional things. Most importantly, the Python runtime.dll that you see here. That's the most
2032660	2040660	important one, right? And now we have this tool called scarysnake, and it tells me that the Python
2040660	2047160	path must be set. All right? So I'm going to set this Python path
2054620	2058640	by just passing it as an argument. Sorry, I had to go find it.
2060320	2064620	And then we can do something like maybe enter interactive mode.
2065960	2069500	And this is the output that we get. It's a little, well, it's basically, I tried to
2069500	2073740	imitate the same output that Python gives you when you start the interpreter.
2074220	2078940	It shows us where the path is. And again, if you were doing this remotely, it would be over
2079720	2088420	WebDAV. I think I had set up in the past, I had set up a server in Azure, and the load time was
2088420	2094480	about 30 seconds just to run Python. But if it takes that much time to further escalate or do
2094480	2098340	whatever you need to do with the Python and not get caught, I think it's worth the investment.
2098340	2108700	So we can do things like, here we go again. And we're going to print out the status code,
2108700	2118120	of course, from red team village.io. And without changing the normal Python executable,
2118120	2123860	we basically brought our own interpreter to the system, and we were able to run Python,
2123860	2128440	using the Python that's on the system, be able to run Python without worrying about all the hooks.
2128440	2135140	Once again, boom, shakalaka, I'm on fire. I'm not literally on fire.
2136480	2141820	Don't call the fire department. So some of the other options that are here
2141820	2148220	include the local file. So you can run a local file. You can also run a remote file. I forgot
2148220	2152100	to actually test this this morning, so I don't know if it works. So I'm not going to actually
2152100	2157900	do it. Because that would be embarrassing. But it has worked in the past of trying to
2157900	2166060	run a remote file. So you basically download the Python file, right, from GitHub or wherever
2166060	2173420	you have it hosted, and you just sort of run it in memory. So let's try something real quick,
2173420	2186330	right? I forget which system I'm on sometimes. There we go. Windows terminal. Split pane.
2186330	2194330	Great. So I'm going to try doing this, right? I want to import the request module.
2195110	2200410	But I don't have the request module. Just to show you that
2201950	2210390	it works, that I'm actually using the actual Python that's hosted on the system,
2210390	2217130	I'm going to Python-npip. Which, by the way, you should always do Python-npip because if you have
2217130	2221310	several different Python versions, you don't want to confuse yourself. So you should always
2221310	2226790	specify the version of Python that you're using when you run things like pip. It's just good
2226790	2237740	practice. And we're going to just pip install request. And if I've installed request, and now
2237740	2245880	I can import it with no problem. So, again, you can definitely use the Python libraries
2245880	2252140	that are on the system to do it. Or you can host your own remotely and sort of just pull from a
2252140	2256460	remote repo without actually touching anything on the disk. It's really up to you and what's
2256460	2263980	in the best case scenario for your immediate situation. But I think it's great that we can
2263980	2268960	actually just do this in the first place. So, so far, just to reiterate, we've talked about
2269600	2277640	how to bypass autohooks for Windows, how to bypass autohooks for Linux, and how to not run
2277640	2284700	Python on a system without running the Python executable whatsoever. For detections, it's a
2284700	2290200	little bit difficult because everything is in .NET. So you have the same problem of .NET detections
2290200	2296800	that you would find in any other .NET binary. If you're collecting ECW logs somehow and you
2296800	2301680	have the space for that, wow, that's impressive. But if you have that space, you'll actually see
2301680	2305060	that the Python runtime is loaded. So if you're in an environment where you don't normally see
2305060	2309780	that, that could be something you detect. In this case, because I wrote it in Boo,
2310260	2318360	it actually also detects the Boo.lang binary. But you could write this in C Sharp if you really
2318360	2323080	wanted to. If you wanted to be a conformist and write all your tools in C Sharp, you could do
2323080	2332700	that. And obviously, you wouldn't have the Boo.lang. So in conclusion, Python autohooks are
2332700	2339640	great when they're implemented correctly. You should protect your custom Python executables
2339640	2347320	with some OS-level protections. You need to do something, right? The same way you do any
2347320	2352880	protection of any other binaries on your device. You need to make sure that your Python executables
2352880	2356700	are protected. Because if I can pull your executable off of the system and you have
2356700	2365440	autohooks enabled, I can debug it and get a pretty good idea of what autohooks you've
2365440	2371720	implemented just by looking at the code or looking at the assembly. And of course, that ties in with
2371720	2377000	don't rely solely on Python to protect your Python. So even if you had all these autohooks,
2377000	2382800	I can pull your executable off and do some other nonsense with it and figure out how to bypass
2384280	2392840	your autohooks. So thanks to everyone who has had some influence on this talk. Steve Dower,
2392840	2397260	who works at Microsoft, he's a Python core dev, and he's actually the one who's done most of the
2397260	2404480	implementation work for autohooks. Bytebleeder, who got me into Boo.lang and embedded scripting,
2404480	2411600	Chris Ross over at SpecterOps, worked on Empire, and Adam Chester.
