1 00:00:00,000 --> 00:00:02,100 All right, we're back live. 2 00:00:02,100 --> 00:00:06,640 Thank you, Liron, for your support and supporting DEF CON and the Red Team Village. 3 00:00:06,980 --> 00:00:08,620 It's a pleasure to have you here. 4 00:00:08,620 --> 00:00:10,860 So without further ado, take it away. 5 00:00:11,440 --> 00:00:12,980 All right, thank you. 6 00:00:13,220 --> 00:00:15,960 So thank you all for coming to this talk. 7 00:00:16,960 --> 00:00:19,840 It's my first time presenting at DEF CON. 8 00:00:20,000 --> 00:00:24,860 I think last year I got caught up in the DEF CON Blue Team Village trying to win that CTF from OpenSOC. 9 00:00:24,860 --> 00:00:25,940 It was pretty dope. 10 00:00:25,940 --> 00:00:32,180 But I think this time today, especially, we're going to talk about something more Red Team-ish. 11 00:00:32,180 --> 00:00:32,480 Why? 12 00:00:32,480 --> 00:00:35,080 Because I'm on the Red Team, and it just seems appropriate. 13 00:00:35,360 --> 00:00:39,760 So this is the talk, y'all trying to bypass Python 3.8 autohooks or nah? 14 00:00:43,270 --> 00:00:44,110 So who am I? 15 00:00:44,110 --> 00:00:45,290 My name is Liron Gray. 16 00:00:45,470 --> 00:00:48,270 I go by DaddyCocoman in most areas. 17 00:00:48,470 --> 00:00:51,650 You can check out my website, DaddyCocoman.dev. 18 00:00:51,970 --> 00:00:55,710 I haven't updated it in a while, but there's some cool stuff there, I think. 19 00:00:56,070 --> 00:00:57,470 You can also check out my GitHub. 20 00:00:57,470 --> 00:01:04,190 I have a couple of projects I've been working on, and hopefully I might have some more interesting projects up there very shortly. 21 00:01:04,470 --> 00:01:06,990 I was in the Navy for about ten years. 22 00:01:07,210 --> 00:01:08,870 I used to do electronics. 23 00:01:08,870 --> 00:01:13,010 I used to work on radar and communication systems, and I moved over into the cyber field. 24 00:01:13,630 --> 00:01:16,090 And I'm on the Microsoft Azure Red Team now. 25 00:01:16,130 --> 00:01:22,330 So you may hear some Azure-related things if you follow me on Twitter or wherever you may follow me. 26 00:01:22,330 --> 00:01:25,390 Hopefully it's only on Twitter and not anywhere else like real life. 27 00:01:25,390 --> 00:01:29,390 I also love Python and Pythonic things. 28 00:01:29,510 --> 00:01:37,070 So we'll talk about some other Pythonic things later in this talk that I really got into at a certain point in time. 29 00:01:38,190 --> 00:01:41,010 Because it was... it's pretty cool. 30 00:01:41,010 --> 00:01:46,530 I like the way Python is written as a language, more so than statically compiled languages. 31 00:01:46,630 --> 00:01:48,330 And I'm also a Nerdcore rapper. 32 00:01:48,330 --> 00:01:49,450 I go by OhMy. 33 00:01:49,450 --> 00:01:51,850 It's an I, not a one. 34 00:01:52,230 --> 00:01:54,810 You can find my music at MCOhMy.com. 35 00:01:54,810 --> 00:01:56,990 I'm actually on the DEF CON 28 tape. 36 00:01:56,990 --> 00:01:58,390 I got that hot track. 37 00:01:58,390 --> 00:01:59,470 I got that heat. 38 00:01:59,510 --> 00:02:00,790 Xs and Ram. 39 00:02:00,930 --> 00:02:04,370 If you want to listen to that, please do. 40 00:02:04,370 --> 00:02:07,370 And hopefully you enjoy all the music. 41 00:02:08,490 --> 00:02:09,850 So let's talk about the agenda. 42 00:02:09,850 --> 00:02:12,290 So first let's talk about why we're using Python 3. 43 00:02:12,290 --> 00:02:17,830 Then we'll talk about the two peps that have influenced audit hooks for Python 3.8. 44 00:02:17,990 --> 00:02:25,790 We'll talk about how to implement them, how to bypass them for Windows, how to bypass them for Linux, and how to bypass Python. 45 00:02:25,990 --> 00:02:30,050 And bypass Python is very... it's in quotes for a reason. 46 00:02:30,050 --> 00:02:34,190 But we'll talk about some interesting concepts that came up over the last year. 47 00:02:34,550 --> 00:02:36,170 So quick question. 48 00:02:37,410 --> 00:02:39,970 Why are you still using Python 2? 49 00:02:40,130 --> 00:02:49,270 I want you to look every single one of these people in the face and convince them that they have the time to listen to you explain why you're still using Python 2. 50 00:02:49,270 --> 00:02:50,130 I'll wait. 51 00:02:57,740 --> 00:02:58,420 All right. 52 00:02:58,420 --> 00:03:01,940 So you might have a legitimate reason, right? 53 00:03:01,940 --> 00:03:07,520 Sometimes there are tools out there that haven't been updated in the last 10 years or whatever. 54 00:03:07,600 --> 00:03:20,580 And if you are using something that is written in Python 2, I just suggest making a pull request, helping them move up to Python 3, and try not to break that backward compatibility if you really need it. 55 00:03:21,860 --> 00:03:26,780 So here's some Python 3 changes that have happened in the last couple of versions. 56 00:03:26,780 --> 00:03:31,480 Python 3.5, we had the async, await keywords that came out. 57 00:03:31,480 --> 00:03:33,620 Python 3.6, we had format strings. 58 00:03:33,620 --> 00:03:36,880 So you could say things like, hey, my name is Daddy Coco Man. 59 00:03:36,880 --> 00:03:43,920 And then you could print my name is in Curly Brace's name, and you would get my name is Daddy Coco Man. 60 00:03:44,320 --> 00:03:45,940 We also had ordered dictionaries. 61 00:03:45,940 --> 00:03:52,860 So now Python recognizes the insertion order for dictionaries, and you can always keep track of it. 62 00:03:52,860 --> 00:03:59,020 Previously, dictionaries would just be a random result every time you would try to print them or call them. 63 00:03:59,020 --> 00:04:00,700 But now they're ordered, which is great. 64 00:04:00,920 --> 00:04:05,060 In Python 3.7, async and await actually became reserved keywords. 65 00:04:05,580 --> 00:04:09,560 We had the breakpoint functions, so we could enter debuggers in the middle of our scripts. 66 00:04:09,680 --> 00:04:14,380 And we had the data class decorators, so we could make these struct-like classes. 67 00:04:14,380 --> 00:04:25,000 So you can say, here are these fields that a class is supposed to have, and here are some functions that it may want to implement, just like classes, but a little bit better, in my opinion. 68 00:04:25,540 --> 00:04:26,980 They serve different purposes. 69 00:04:27,360 --> 00:04:35,240 So for Python 3.8, we have this controversial thing called the walrus operator, where you can assign values while evaluating an expression. 70 00:04:35,420 --> 00:04:50,600 So on something like this, we can say, it reads, if the length of a is greater than 10, then we can assign it to n, and then be moving to the loop logic for print, list is too long, blah, blah, blah. 71 00:04:51,060 --> 00:04:56,980 But if length of a was not equal to 10, then we wouldn't assign it to n, and we would just move on. 72 00:04:56,980 --> 00:04:58,980 So it's nice, it saves you a line of code. 73 00:04:58,980 --> 00:05:02,560 A lot of Python purists really don't like it, and that's okay. 74 00:05:03,040 --> 00:05:08,560 People generally don't like changes to languages that they've been using for so long, but oh, well. 75 00:05:09,040 --> 00:05:20,800 So next, we have the async IO enabled REPL, so you can do Python dash M, async IO, and you can start using those await and async keywords inside of your REPL. 76 00:05:20,820 --> 00:05:24,700 And what we're here to talk about primarily are these audit hooks. 77 00:05:24,700 --> 00:05:32,380 So audit hooks are runtime events, are hooking those runtime events to perform some action, some arbitrary action, whatever you want to do. 78 00:05:33,160 --> 00:05:42,580 So the first pep we'll talk about explains how the audit hooks are supposed to be implemented, or how they're supposed to work, why you should have them. 79 00:05:42,740 --> 00:05:51,640 So the proposal was that sysadmins could use audit hooks in order to add some feature to their Python executable. 80 00:05:51,740 --> 00:06:04,480 So some suggestions that came in were integrating AMSI, script block for Windows, and then for Linux, you could send events to syslog or audit D. 81 00:06:04,880 --> 00:06:09,140 And if you... I meant to link it in here. 82 00:06:09,140 --> 00:06:09,860 I guess I didn't. 83 00:06:09,860 --> 00:06:11,340 I must have removed it by accident. 84 00:06:11,340 --> 00:06:24,600 There is a GitHub, GitHub.com slash ZOOBA, Z-O-O-B-A, from Steve Dower, who has a bunch of examples of what these audit hooks can look like in different implementations. 85 00:06:24,600 --> 00:06:26,160 So I would definitely check that out. 86 00:06:26,880 --> 00:06:29,700 Another thing you can do is restrict the importable modules. 87 00:06:29,700 --> 00:06:41,660 So you can say, well, maybe I need my people who are using Python in my environment to use the request module, but maybe for whatever reason, they don't need to use the mmap module. 88 00:06:41,740 --> 00:06:50,120 So you can say, well, I want them to use specific modules that are only for my environment, and I don't want them to use anything else. 89 00:06:50,240 --> 00:06:53,020 You can also do things like remove command line arguments. 90 00:06:53,020 --> 00:07:07,020 So before I mentioned with async IO or just a lot of other command line Python modules like HTTP server, you can actually remove that dash M or the dash C to prevent people from using those switches. 91 00:07:08,420 --> 00:07:16,340 But one thing to keep in mind is that the PEP actually reads that you should detect the code over preventing the code. 92 00:07:16,340 --> 00:07:27,400 And the reason for that is because sometimes you may use the Python dependency tree graph is out of control sometimes, right? 93 00:07:27,400 --> 00:07:33,000 You may try to use the NumPy module, and underneath it, it uses C types. 94 00:07:33,000 --> 00:07:38,920 So if you try to block C types completely because you didn't want someone importing it, you may actually mess up using NumPy. 95 00:07:38,920 --> 00:07:48,240 So it's important to just detect what's occurring rather than prevent what's occurring because you might mess up something that you had no idea was going to go wrong. 96 00:07:49,560 --> 00:07:55,800 Another thing that the PEP recommends you do is implement OS security level features to protect your modified Python. 97 00:07:55,800 --> 00:08:04,480 So that means device guard for Windows, maybe do some exe signing, use catalog files, that kind of stuff, and extended attributes for Unix. 98 00:08:04,480 --> 00:08:12,880 So maybe you want to add file attributes that have the hashes of the permitted Python files that are allowed to be used. 99 00:08:14,060 --> 00:08:20,780 PEP 578 says here's how we're going to implement or how you can implement these audit hooks. 100 00:08:20,780 --> 00:08:25,300 And they can be implemented inside the executable or inside of the script. 101 00:08:25,900 --> 00:08:37,980 So if you want to do it in C, you can actually recompile a new executable, a new Python.exe, and all the hooks that you apply to that code will be run by this new exe. 102 00:08:40,040 --> 00:08:59,460 Or if you want to just do it in Python inside of your script, you can, without compiling it, you can use the sys module, so import sys, and then sys.addAuditHook, and then the audit hook will be a function where you can handle all of your audit hook logic the same way that you can do it in C, 103 00:08:59,460 --> 00:09:03,440 except it will only apply to the script where it's implemented. 104 00:09:04,560 --> 00:09:06,060 So here's two examples. 105 00:09:06,060 --> 00:09:10,700 On the left side, we have the Python implementation of network prompt hook, right? 106 00:09:10,700 --> 00:09:26,340 We have, this is some bad Python, so ignore this, but we have an event that gets passed into the function, and then we're looking at this event and we're saying, does it equal socket.getAddressInfo? 107 00:09:26,340 --> 00:09:28,060 If so, I want to take some action. 108 00:09:28,060 --> 00:09:31,720 I want to warn the user that we're attempting to resolve some URL. 109 00:09:31,780 --> 00:09:36,300 If it's socket.connect, we're saying we're attempting to connect to this URL. 110 00:09:36,300 --> 00:09:37,520 Do you want to continue? 111 00:09:38,060 --> 00:09:43,880 On the right side, we have the C version, which you can use to compile a new Python executable. 112 00:09:43,880 --> 00:09:55,200 Notice it's considerably longer, and personally, I never really learned C and I don't really plan on writing C any time soon, so good luck to you if that's your thing. 113 00:09:55,900 --> 00:10:00,740 So here's a list of audit events that are currently implemented in Python 3.8. 114 00:10:00,740 --> 00:10:13,000 You can actually pull this from the documentation itself, but what I have highlighted are some of the things that you may really be interested in if you see these events occurring. 115 00:10:13,000 --> 00:10:33,780 Now, keep in mind that you have to modify this for your particular environment, but some things that you may find interesting are when if a script uses the compile function or the exec function or maybe some of these winreg functions on the right, things that you may not normally see in your environment, 116 00:10:33,780 --> 00:10:37,740 you have to be able to adjust your audit hooks appropriately. 117 00:10:38,400 --> 00:10:48,580 Pty.spawn, I don't know a single case of anyone using pty.spawn outside of trying to get a shell after they've already compromised your system. 118 00:10:48,640 --> 00:10:55,600 So things like that may be of interest to you, and you may want to capture those events as they occur. 119 00:10:55,600 --> 00:10:59,640 But again, the focus is on detection and not prevention. 120 00:10:59,680 --> 00:11:14,960 So we wanted to make sure that we are aware of these things that are happening, but we don't want to actually prevent them from happening unless you have a really good use case or use scenario to not let a particular event happen. 121 00:11:15,560 --> 00:11:16,620 I mean, you can totally do. 122 00:11:16,620 --> 00:11:18,160 You can block all events if you want to. 123 00:11:18,160 --> 00:11:21,620 It's not like a fake Python just sitting there. 124 00:11:21,620 --> 00:11:23,820 I don't know, honeypot Python or whatever. 125 00:11:24,620 --> 00:11:27,000 So bypassing audit hooks for Windows. 126 00:11:27,000 --> 00:11:28,920 So first we'll talk about how auditing works. 127 00:11:29,180 --> 00:11:38,120 So when an action is or when an audit hook is signaled, Python determines whether the action matches an audit rule. 128 00:11:38,120 --> 00:11:41,420 And that's kind of maybe not the best explanation. 129 00:11:41,420 --> 00:11:51,940 What happens is it will run through every single audit event or audit hook that you have, but you have to write your audit hooks to only care about the events that you want. 130 00:11:52,020 --> 00:11:56,440 So for example, in this network prompt hook, we only care about socket events. 131 00:11:56,440 --> 00:12:03,280 If it doesn't equal socket, then we're going to return because we don't want the rest of the hook to go through all that logic. 132 00:12:03,520 --> 00:12:11,180 So when you write your audit hooks, you have to make sure that you only process the events that you care about. 133 00:12:11,600 --> 00:12:19,320 Also, you probably shouldn't do anything like a while loop, some unbreakable condition, which is not a good idea. 134 00:12:19,820 --> 00:12:23,800 So the other thing to know is that all audit hooks are processed for all events. 135 00:12:23,800 --> 00:12:35,340 So even if I open a file , which is an audit hook event open, the open function, it's still going to get passed to this network prompt hook, but it won't do anything because I set a condition to only care about socket. 136 00:12:37,060 --> 00:12:41,860 So what's in the source code to figure out how these things work? 137 00:12:41,860 --> 00:12:45,940 So on the left side, we have the PySys add audit hook function. 138 00:12:46,480 --> 00:12:52,860 And what it does when it adds an audit hook, it literally just adds the hook to the end of a linked list. 139 00:12:53,860 --> 00:13:08,040 When the hook is called in the middle or the upper right, it will actually just go through each hook that has been defined and just goes through all those functions and determines whether or not it needs to do anything. 140 00:13:08,820 --> 00:13:16,380 Or based on... it will... you decide whether or not it needs to do anything, but it will just run all those functions at once. 141 00:13:18,340 --> 00:13:20,120 In order, I should say. 142 00:13:20,120 --> 00:13:23,480 On the right side, we also see that tracing is disallowed. 143 00:13:23,480 --> 00:13:30,020 Now, what I originally thought this meant was that I could not see into what an audit hook looks like. 144 00:13:30,020 --> 00:13:35,520 But it turns out that that tracing set to false is only for the Python debugger. 145 00:13:35,520 --> 00:13:46,060 So if you're using the Python debugger, for whatever reason, maybe in your IDE or maybe on the command line, you actually can't trace into the audit hooks. 146 00:13:46,060 --> 00:13:58,760 But if you're using an external debugger, so let's say maybe you grabbed a copy of a modified Python off of a system and you took it back to your station and you opened up a debugger, you can actually see what's going on. 147 00:13:58,840 --> 00:14:00,200 So here's an example, right? 148 00:14:00,200 --> 00:14:02,940 The same example where we had the socket.getaddress info. 149 00:14:02,940 --> 00:14:06,800 I can actually look into this audit hook to determine what it's doing. 150 00:14:06,860 --> 00:14:12,200 I'm not the greatest at assembly, and so I know what this says simply because I know what the code says. 151 00:14:12,200 --> 00:14:14,080 But you can still get a general idea. 152 00:14:14,080 --> 00:14:24,620 You can see the comparison strings, the compare, spython.string compare, and you can see that it's looking for certain audit hooks to occur before moving into some other logic. 153 00:14:25,180 --> 00:14:31,160 And you can also see the strings there attempt to resolve the URL that it tried to resolve. 154 00:14:32,960 --> 00:14:35,640 So here are some ideas for bypassing. 155 00:14:36,720 --> 00:14:40,400 By implementation, audit hooks can't be cleared once they're set. 156 00:14:40,400 --> 00:14:45,380 So there is a function, I think, to clear audit hooks, I think, but it doesn't actually work. 157 00:14:45,380 --> 00:14:46,480 You also can't overwrite. 158 00:14:46,480 --> 00:14:51,280 You can't, like, reload the sys module in order to clear all the audit hooks either. 159 00:14:51,880 --> 00:15:00,000 So one thing we could do is maybe overwrite the head of the linked list to point to null, but it might be possible. 160 00:15:01,080 --> 00:15:05,580 And attempting to locate the start of a linked list in memory might be a little bit difficult. 161 00:15:06,140 --> 00:15:09,700 But I'm actually really new to exploit development, so it might be possible. 162 00:15:09,700 --> 00:15:13,580 So if someone knows something I don't, please tell me, and maybe we'll figure that out. 163 00:15:13,700 --> 00:15:20,600 But in the meantime, maybe we can just overwrite the bytes in memory that calls the hook functions. 164 00:15:21,400 --> 00:15:24,800 And that smells like PowerShell, right? 165 00:15:24,800 --> 00:15:31,000 Everybody knows PowerShell and AMSI bypassing, and we can actually apply the same concept here. 166 00:15:31,040 --> 00:15:34,140 So there's plenty of resources to draw inspiration from. 167 00:15:34,440 --> 00:15:45,440 Adam Chester over at MDSec has plenty of articles about bypassing AMSI, so does RastaMouse, and there's a lot of inspiration just to draw just by reading their blogs. 168 00:15:46,820 --> 00:15:54,940 And it's also significantly easier to determine the address that you need to overwrite with these new bytes that you want to overwrite with. 169 00:15:55,400 --> 00:15:57,000 So let's go that way. 170 00:15:57,700 --> 00:16:14,440 So if I take the Python 3.8 DLL, and I open it up, or if I run Python, rather, this modified Python, and I go check out the Python 3.8 DLL, I can go find the address for PySysAudit, and we can see this is the 32-bit version. 171 00:16:14,680 --> 00:16:18,120 We can see the address and set a breakpoint on it. 172 00:16:19,360 --> 00:16:29,760 If I scroll further down, I'll get to a point where a function is called, and under call EAX, it has the name of the hook that is being called, right? 173 00:16:29,760 --> 00:16:35,880 So if I were to step into that call, I would actually see the logic that I showed you earlier. 174 00:16:36,260 --> 00:16:53,480 So the offending, I say offending, but the interesting call that we want to overwrite is where EIP is currently pointing, and that is call EAX for 32-bit, and call keyword pointer for 64-bit. 175 00:16:57,370 --> 00:16:59,410 So, again, it's very similar to PowerShell. 176 00:16:59,410 --> 00:17:09,510 We need to load the Python 3.8 DLL, we need to find the git address for PySysAudit, we need to change the memory permissions, and we need to just overwrite those bytes. 177 00:17:10,730 --> 00:17:20,590 And I learned this week, a couple of days ago, that I really need to go through all these versions of Python and grab their offsets. 178 00:17:20,590 --> 00:17:36,170 So I went through the 32 and 64-bit versions of Python, from 3.8 to 3.85, and even just for poops and laughter, I went to 3.9.0 Beta 5, just to see if it still worked, and it does. 179 00:17:36,170 --> 00:17:44,170 So we just have to set these offsets, and we can change the memory permissions and overwrite those offsets. 180 00:17:45,210 --> 00:17:47,050 Let's talk about Linux real quick. 181 00:17:48,470 --> 00:17:58,990 So I have never done any sort of, like, major Linux exploitation other than, like, a buffer overflow, right, that you find in, like, CTFs. 182 00:17:58,990 --> 00:18:09,370 I've never done any patching, so I didn't fully understand at the time how the Python packages worked on Linux. 183 00:18:10,170 --> 00:18:21,410 So there are no DLLs, right, because it's not Windows, and there was no sysmodule.so library that I thought was going to be there, and it just wasn't. 184 00:18:21,410 --> 00:18:24,550 So it's like the same, though, right? 185 00:18:24,550 --> 00:18:28,490 You can find the PySysAudit address by searching the Python executable itself. 186 00:18:28,490 --> 00:18:32,690 So I guess it's statically compiled into the binary, and you can just pull it out. 187 00:18:32,690 --> 00:18:49,630 So if you use the readelf, we can use readelf to figure out what the address of PySysAudit is, and we're going to cheat a bit, because I actually don't know how to parse ELF files. 188 00:18:49,630 --> 00:18:59,850 So in my code, I just subprocess readelf and grab the output, because I actually have no idea how to do it otherwise. 189 00:19:00,410 --> 00:19:03,090 And then we have to locate this address in memory. 190 00:19:03,650 --> 00:19:08,370 So once we grab that address here, it's 53007 foxtrot 0. 191 00:19:08,370 --> 00:19:12,950 We need to figure out where it's located and what region of memory that it's mapped to. 192 00:19:13,310 --> 00:19:21,910 And in this picture, we can see that it's under... it falls in the range of F230 and 6AE. 193 00:19:22,790 --> 00:19:25,530 So we need to modify those permissions. 194 00:19:26,070 --> 00:19:29,410 And we can do that, of course, with libc and mprotect. 195 00:19:29,430 --> 00:19:35,270 So we need the write execute permissions to change and to read write execute permissions. 196 00:19:35,710 --> 00:19:39,650 And sometimes, yes, sometimes it just be like that. 197 00:19:40,170 --> 00:19:42,370 I am watching the Discord, by the way. 198 00:19:42,370 --> 00:19:49,010 So what this looks like is we load the DLL for libc. 199 00:19:49,010 --> 00:19:50,790 Or we load libc, rather, I should say. 200 00:19:50,790 --> 00:19:52,550 And then we find the readelf. 201 00:19:52,550 --> 00:19:54,870 We find the address of pysis audit. 202 00:19:54,870 --> 00:19:59,910 We find the location of the memory mapped files. 203 00:19:59,910 --> 00:20:03,030 And then we determine in what region it falls under. 204 00:20:03,190 --> 00:20:05,630 And then we change the permissions. 205 00:20:05,750 --> 00:20:13,290 And then we just overwrite the the bytes in memory with these nops. 206 00:20:13,770 --> 00:20:22,370 So one thing I didn't go too far in depth in Linux is because every build for Python in Linux is different. 207 00:20:22,870 --> 00:20:27,330 So it's... I don't have the time of the day. 208 00:20:27,330 --> 00:20:28,590 Like, I'm stuck at home. 209 00:20:28,590 --> 00:20:31,250 I only go out to the supermarket if I need to. 210 00:20:31,250 --> 00:20:37,390 But I still don't have the time in the day to go through all these Linux versions and try to find these offsets. 211 00:20:37,390 --> 00:20:42,950 So later today I'll probably put this code up on GitHub. 212 00:20:42,950 --> 00:20:55,590 And if you have a version of Linux where you want to bypass audit hooks for 3.8, then you can send a pull request and I will happily accept once it's been tested. 213 00:20:58,400 --> 00:21:00,040 Let's talk about detection. 214 00:21:01,120 --> 00:21:13,120 While everything that I've mentioned works, everything up until how... up until you patch the binaries... the bytes, rather, can be detected. 215 00:21:13,120 --> 00:21:16,580 So importing C types can be detected, importing platform, importing sys. 216 00:21:16,580 --> 00:21:27,660 Every single function call that you make can be detected until you hit that men move and you overwrite the bytes. 217 00:21:28,320 --> 00:21:33,420 The way to defeat this is ironically better audit hook logging. 218 00:21:33,420 --> 00:21:50,540 Because if you can detect that these things are occurring and you send them off and you ship them to your seam or however you... your Windows event logs or however you collect your logs and you see that these are occurring, well, then you have a better chance of understanding what's going on with your Python executable on a random given station. 219 00:21:51,100 --> 00:22:00,740 So, yes, it's sort of like... it's like running AMSI bypass in PowerShell where you might get caught. 220 00:22:00,800 --> 00:22:02,280 It's the same thing. 221 00:22:02,520 --> 00:22:07,320 So that is something you should probably implement better audit hooks. 222 00:22:07,400 --> 00:22:10,940 And it might be harder for someone who doesn't program in C to implement, right? 223 00:22:10,940 --> 00:22:15,160 I don't think most sys admins have a lot of C knowledge. 224 00:22:15,160 --> 00:22:15,940 I could be wrong. 225 00:22:15,940 --> 00:22:17,060 I could be naive. 226 00:22:17,140 --> 00:22:18,440 I've never been a sys admin. 227 00:22:18,440 --> 00:22:19,460 I have no idea. 228 00:22:19,740 --> 00:22:30,140 But it's probably not something that you come across in the general resume where you need 10 years of Adobe XD that came out last year. 229 00:22:30,660 --> 00:22:41,860 So it would be great if somebody came up with a template of audit options and an action to help sys admins compile those custom Python executables. 230 00:22:41,860 --> 00:22:44,840 But as I mentioned, I don't have the time. 231 00:22:44,920 --> 00:22:46,320 So it's not me. 232 00:22:46,880 --> 00:22:50,600 So I want to give a demo real quick. 233 00:22:57,770 --> 00:23:01,850 Hopefully we can all still see the screen that I have up. 234 00:23:01,890 --> 00:23:04,790 I think it should be working. 235 00:23:06,210 --> 00:23:09,790 A Discord confirmation would be great if you can see this terminal. 236 00:23:11,590 --> 00:23:13,210 We can see it. 237 00:23:15,760 --> 00:23:17,700 Or a confirmation from Omar. 238 00:23:18,320 --> 00:23:20,300 Yeah, we can see it. 239 00:23:25,180 --> 00:23:27,620 Thank you. 240 00:23:27,740 --> 00:23:28,800 Great. 241 00:23:28,800 --> 00:23:33,420 So I have this executable here. 242 00:23:33,420 --> 00:23:36,080 This sPython that's been compiled. 243 00:23:36,680 --> 00:23:39,660 And I'm just going to enter it directly. 244 00:23:40,160 --> 00:23:46,940 And I'm going to try to run this code. 245 00:23:46,940 --> 00:23:55,560 And all I want to do is get the status code of the Red Team Village.io. 246 00:23:55,560 --> 00:23:56,600 I just want to make sure it's up. 247 00:23:56,600 --> 00:23:58,800 Because Red Team Village has been doing a great job. 248 00:23:58,820 --> 00:24:00,800 And I want to make sure that their website is up. 249 00:24:00,880 --> 00:24:06,580 But if I do that, I get this warning saying you're attempting to resolve Red Team Village.io. 250 00:24:06,580 --> 00:24:07,500 Do you want to continue? 251 00:24:07,540 --> 00:24:08,740 Yes, I do want to continue. 252 00:24:08,840 --> 00:24:10,580 Yes, I do want to handle the socket event. 253 00:24:10,580 --> 00:24:11,980 Yes, I do want to connect to this. 254 00:24:11,980 --> 00:24:15,380 And finally, I can get the HTTP status code. 255 00:24:15,580 --> 00:24:16,600 That's great. 256 00:24:17,220 --> 00:24:20,200 But I don't want to have to go through all that again. 257 00:24:20,260 --> 00:24:23,000 So maybe I'll just do this audit bypass. 258 00:24:24,100 --> 00:24:25,440 And we'll see. 259 00:24:25,440 --> 00:24:26,280 We're getting the handle. 260 00:24:26,280 --> 00:24:29,380 I'm printing out all this arbitrary stuff that doesn't really need to be printed out. 261 00:24:30,100 --> 00:24:31,680 We get the audit address. 262 00:24:31,680 --> 00:24:32,820 We find the protections. 263 00:24:32,820 --> 00:24:36,660 Using the offsets, we find the region of memory that we need to overwrite. 264 00:24:36,660 --> 00:24:38,660 And we successfully change the memory. 265 00:24:38,660 --> 00:24:44,900 And if you ever played NBA Jam, like the video game of your life, this is the point in time where I say, boom, shakalaka! 266 00:24:44,900 --> 00:24:46,920 And we bypass the audit hooks. 267 00:24:46,920 --> 00:24:52,720 So now when I run the same code, again, we just get status code 200. 268 00:24:53,060 --> 00:24:55,740 And yay! 269 00:24:55,740 --> 00:24:56,080 Right? 270 00:24:56,080 --> 00:24:56,860 We win. 271 00:24:56,860 --> 00:25:01,020 We don't have to worry about the audit hooks and going through all that stuff again. 272 00:25:01,020 --> 00:25:03,680 So that's great. 273 00:25:06,680 --> 00:25:08,440 Let's get back to these slides. 274 00:25:10,060 --> 00:25:11,100 Whoops. 275 00:25:12,900 --> 00:25:14,640 I lost my cursor. 276 00:25:15,220 --> 00:25:16,500 There we go. 277 00:25:18,920 --> 00:25:21,360 So let's talk about bypassing Python. 278 00:25:21,360 --> 00:25:30,760 Because as I mentioned, everything up until that point where you overwrite those bytes can be detected in memory. 279 00:25:30,760 --> 00:25:32,900 Or detected by an audit hook. 280 00:25:33,060 --> 00:25:34,240 And that could be bad. 281 00:25:34,240 --> 00:25:34,560 Right? 282 00:25:34,560 --> 00:25:36,420 So let's take this a step further. 283 00:25:36,760 --> 00:25:46,440 So since the bypass is easy to detect, are there any other methods that we can use to overwrite or to bypass Python in a sense? 284 00:25:46,540 --> 00:25:46,880 Right? 285 00:25:46,880 --> 00:25:49,240 So there are other ways to load and patch DLLs. 286 00:25:49,240 --> 00:25:53,960 I've had conversations with smart people about Python internals. 287 00:25:54,440 --> 00:25:56,980 And they've suggested other ways to do it. 288 00:25:57,180 --> 00:26:04,940 So, you know, just loading DLLs to kernel 32 and virtual protect and all that stuff is one way to do it. 289 00:26:04,940 --> 00:26:06,480 But there are other ways to do it. 290 00:26:07,720 --> 00:26:13,440 But everything that you do that way has to be run in the context of the Python interpreter. 291 00:26:13,680 --> 00:26:19,940 So no matter what I do, or no matter what you do, rather, you can be detected. 292 00:26:20,080 --> 00:26:21,580 No matter how you do it. 293 00:26:21,940 --> 00:26:26,920 So the question is, can we run Python without running the Python executable? 294 00:26:26,920 --> 00:26:28,520 And obviously, the answer is yes. 295 00:26:28,520 --> 00:26:32,580 Because I wouldn't be asking you this question and wasting your time with that bit of the slide. 296 00:26:32,600 --> 00:26:37,200 So there's a package called Python net, which is a legitimate package. 297 00:26:37,200 --> 00:26:38,060 It's great. 298 00:26:38,060 --> 00:26:43,960 And it uses it allows you to access the .NET CLR from normal Python. 299 00:26:44,060 --> 00:26:46,260 It's not Iron Python, right? 300 00:26:46,260 --> 00:26:47,440 It's not Iron Python at all. 301 00:26:47,440 --> 00:26:56,060 Iron Python is an implementation of Python that is, as far as I'm aware, not even MSIL compliant. 302 00:26:56,060 --> 00:27:00,700 But the syntax is still similar to Iron Python. 303 00:27:01,760 --> 00:27:06,740 And as of May 2020, it supports Python 3.8. 304 00:27:06,760 --> 00:27:07,760 That's great. 305 00:27:08,500 --> 00:27:10,020 And my camera just went out. 306 00:27:10,020 --> 00:27:12,460 So I'm going to turn my camera off, if that's okay. 307 00:27:14,900 --> 00:27:17,960 So we have the here's some syntax, right? 308 00:27:17,960 --> 00:27:23,500 We can import CLR from collections that we import counter. 309 00:27:23,500 --> 00:27:29,340 And then we can access the CLR the same way that we do it in sort of Iron Python-ish ways, right? 310 00:27:29,340 --> 00:27:36,920 Where from system.diagnostics, we can import process, and we can actually list all the processes in a CLR manner. 311 00:27:38,280 --> 00:27:39,920 CLR-like syntax. 312 00:27:39,920 --> 00:27:41,980 Or .NET-like syntax, I should say. 313 00:27:43,200 --> 00:27:47,940 You can actually also embed Python in your .NET executable. 314 00:27:48,140 --> 00:27:52,280 So Python.NET comes with a .NET assembly called Python.runtime.dll. 315 00:27:52,280 --> 00:27:55,600 And that can be used with .NET languages to run Python. 316 00:27:56,160 --> 00:28:03,640 The Python types for the basic primitive types are actually written in C sharp and compiled as part of this assembly. 317 00:28:04,160 --> 00:28:09,660 The only requirement is that your path variable must include a directory with the Python runtime installed. 318 00:28:09,660 --> 00:28:12,440 So in this case, we're looking for Python 3.8.dll. 319 00:28:13,700 --> 00:28:16,340 So here's an example of what it looks like in C sharp. 320 00:28:16,340 --> 00:28:25,620 We have this main function, and then we are using the Python gill, and we can import modules that Python knows about, such as numpy. 321 00:28:25,860 --> 00:28:34,940 And when we run this function, we get this output to the right, and it's pretty cool that we can actually run Python inside of .NET. 322 00:28:36,980 --> 00:28:40,020 And we can use C sharp to write this, but I like boolang. 323 00:28:40,160 --> 00:28:46,360 The reason I like boolang is because of Bytebleeder and the Silent Trinity project, that C2 framework that he has. 324 00:28:46,360 --> 00:28:47,340 It's pretty dope. 325 00:28:47,340 --> 00:28:53,480 I've contributed to it in the past, and I really like boolang because it's very Pythonic. 326 00:28:53,540 --> 00:28:58,540 If you look at the code that's on the screen right now, it almost seems like it's Python, but it's not. 327 00:28:58,540 --> 00:28:59,780 It's boolang. 328 00:29:00,080 --> 00:29:04,460 Also, support Bytebleeder in his open source sponsorship program that he has. 329 00:29:04,460 --> 00:29:13,300 It's definitely worth investing in people who have helped you in your corporate endeavors for pentesting and retting. 330 00:29:13,780 --> 00:29:23,440 So what we need to do, as I mentioned before, is point the path variable to a Python installation folder, which gives me an idea, right? 331 00:29:23,440 --> 00:29:31,540 Because the path variable can be set to any folder on the disk or on a network share. 332 00:29:31,760 --> 00:29:33,860 So you can already see where I'm going with this, hopefully. 333 00:29:35,080 --> 00:29:35,960 Hmm. 334 00:29:36,180 --> 00:29:39,320 I tried to find a black one, but there are no black emojis. 335 00:29:39,840 --> 00:29:46,300 So we can do Python across a network share by simply by pointing the Python path to it. 336 00:29:46,300 --> 00:29:50,880 So in this case, I had a share called winwork slash Python 3.7. 337 00:29:51,180 --> 00:29:55,440 And this is 3.7 because at the time I was testing it with 3.7 before 3.8 came out. 338 00:29:55,560 --> 00:29:58,060 It still works with 3.8, so ignore that. 339 00:29:58,600 --> 00:29:59,980 And that's what it looks like, right? 340 00:29:59,980 --> 00:30:08,620 So the path variable can be set to the server slash share, just as long as that share points to a Python 3x installation. 341 00:30:09,820 --> 00:30:11,600 And this is what it looks like. 342 00:30:11,640 --> 00:30:20,260 So you can see all this SMB traffic, and you can clearly see that I'm loading Python 3.7 across a share. 343 00:30:20,260 --> 00:30:23,800 Now, this isn't like an abuse of anything, right? 344 00:30:23,800 --> 00:30:29,320 You can actually install Python when you install Python normally on a network share and have everybody access it. 345 00:30:29,320 --> 00:30:33,880 It's slower because you have to load those resources remotely, but you can actually do that. 346 00:30:34,160 --> 00:30:40,000 But if we can use SMB, that probably means we can also use WebDAV. 347 00:30:41,380 --> 00:30:51,160 And sure enough, we can actually load Python across WebDAV by setting the server and the port. 348 00:30:51,680 --> 00:30:58,660 So whack whack server at port, and SSL is optional. 349 00:31:00,380 --> 00:31:12,040 There's a registry setting in Windows that determines how you can authenticate with WebDAV, and you may have to set that in some environments. 350 00:31:12,240 --> 00:31:19,140 I was having issues with this earlier today, or earlier, I guess it was today, or last night, whatever. 351 00:31:19,400 --> 00:31:22,900 And something broke, and I couldn't figure out what it was. 352 00:31:22,900 --> 00:31:27,260 So I can't show you what it looks like going across a WebDAV server. 353 00:31:27,700 --> 00:31:29,180 But it works. 354 00:31:29,180 --> 00:31:30,100 Here are the packets. 355 00:31:30,200 --> 00:31:31,600 It worked at some point in time. 356 00:31:31,600 --> 00:31:33,080 I just need to figure out what happened. 357 00:31:35,340 --> 00:31:39,300 So we can also host our own repo to remotely load packages, right? 358 00:31:39,300 --> 00:31:47,680 We don't need to pip install anything on the target box, assuming that we need extra modules that aren't already there on the Python 3.8 installation. 359 00:31:47,820 --> 00:31:54,340 Empire did this by implementing a custom import hook that allows you to modify the logic and how modules are loaded. 360 00:31:54,340 --> 00:32:10,200 So it allows you to load things remotely, and if you want more information on that, you can check out Chris Ross's blog on in-memory Python imports, or you can check out the Sulink's GitHub repo for remote importer, and you can check that out. 361 00:32:10,200 --> 00:32:11,280 It's really interesting stuff. 362 00:32:11,660 --> 00:32:18,880 There's also another module out there called HTTP importer, or something like that, that allows you to import modules over HTTP directly. 363 00:32:20,040 --> 00:32:23,480 And of course, Empire was written a long time ago. 364 00:32:23,480 --> 00:32:31,040 I know a new company is picking up Empire, I think it was BC Security, and I think they recently released a tool called Starkiller that seems really interesting. 365 00:32:31,040 --> 00:32:32,360 You should probably go check that out. 366 00:32:34,100 --> 00:32:38,860 If you want to load a package remotely, you need to just have it in the standard format. 367 00:32:38,860 --> 00:32:43,340 So here's the request module, and I want to just import this. 368 00:32:43,340 --> 00:32:44,420 This is what it needs to look like. 369 00:32:44,420 --> 00:32:46,200 It needs to be the zip file. 370 00:32:46,260 --> 00:32:54,900 Inside it, you need to have the name of the package, and then inside a folder with the name of the package, and inside of that, you need to have all the files that you need for the package. 371 00:32:56,340 --> 00:32:58,360 Let's do a demo of that real quick. 372 00:33:00,860 --> 00:33:02,340 All right. 373 00:33:08,880 --> 00:33:14,820 So in this folder, I have some code called scarysnake.boo. 374 00:33:14,820 --> 00:33:20,340 Everybody who writes C Sharp tools likes to call their tools Sharp something, but no, this is Boo. 375 00:33:20,340 --> 00:33:23,560 It's about ghosts, so we're doing scarysnake. 376 00:33:23,980 --> 00:33:26,420 And so we can actually compile this. 377 00:33:26,420 --> 00:33:34,420 I wrote a little thing using the compiler that comes with Boo, and we can just compile it real quick. 378 00:33:34,540 --> 00:33:42,820 We'll also use ILRepack to package the the additional assemblies that we need. 379 00:33:42,820 --> 00:33:47,620 That includes the Boo.lang assembly and some other additional things. 380 00:33:47,620 --> 00:33:51,620 Most importantly, the Python runtime.dll that you see here. 381 00:33:51,620 --> 00:33:53,820 That's the most important one, right? 382 00:33:54,120 --> 00:34:02,500 And now we have this tool called scarysnake, and it tells me that the Python path must be set. 383 00:34:02,900 --> 00:34:03,500 All right? 384 00:34:04,940 --> 00:34:07,160 So I'm going to set this Python path 385 00:34:14,620 --> 00:34:17,120 by just passing it as an argument. 386 00:34:17,320 --> 00:34:18,640 Sorry, I had to go find it. 387 00:34:20,320 --> 00:34:24,620 And then we can do something like maybe enter interactive mode. 388 00:34:25,960 --> 00:34:27,500 And this is the output that we get. 389 00:34:27,500 --> 00:34:33,740 It's a little, well, it's basically, I tried to imitate the same output that Python gives you when you start the interpreter. 390 00:34:34,220 --> 00:34:36,240 It shows us where the path is. 391 00:34:36,240 --> 00:34:40,480 And again, if you were doing this remotely, it would be over WebDAV. 392 00:34:40,480 --> 00:34:50,480 I think I had set up in the past, I had set up a server in Azure, and the load time was about 30 seconds just to run Python. 393 00:34:50,480 --> 00:34:58,340 But if it takes that much time to further escalate or do whatever you need to do with the Python and not get caught, I think it's worth the investment. 394 00:34:58,340 --> 00:35:05,540 So we can do things like, here we go again. 395 00:35:06,460 --> 00:35:11,460 And we're going to print out the status code, of course, from red team village.io. 396 00:35:11,780 --> 00:35:28,440 And without changing the normal Python executable, we basically brought our own interpreter to the system, and we were able to run Python, using the Python that's on the system, be able to run Python without worrying about all the hooks. 397 00:35:28,440 --> 00:35:32,520 Once again, boom, shakalaka, I'm on fire. 398 00:35:33,240 --> 00:35:35,140 I'm not literally on fire. 399 00:35:36,480 --> 00:35:38,180 Don't call the fire department. 400 00:35:39,120 --> 00:35:43,780 So some of the other options that are here include the local file. 401 00:35:43,780 --> 00:35:45,500 So you can run a local file. 402 00:35:45,500 --> 00:35:47,100 You can also run a remote file. 403 00:35:47,500 --> 00:35:51,180 I forgot to actually test this this morning, so I don't know if it works. 404 00:35:51,180 --> 00:35:53,060 So I'm not going to actually do it. 405 00:35:53,140 --> 00:35:55,320 Because that would be embarrassing. 406 00:35:55,540 --> 00:35:59,040 But it has worked in the past of trying to run a remote file. 407 00:35:59,040 --> 00:36:08,900 So you basically download the Python file, right, from GitHub or wherever you have it hosted, and you just sort of run it in memory. 408 00:36:11,000 --> 00:36:14,060 So let's try something real quick, right? 409 00:36:15,700 --> 00:36:17,860 I forget which system I'm on sometimes. 410 00:36:22,370 --> 00:36:23,590 There we go. 411 00:36:24,290 --> 00:36:25,330 Windows terminal. 412 00:36:25,510 --> 00:36:26,330 Split pane. 413 00:36:26,330 --> 00:36:27,190 Great. 414 00:36:27,630 --> 00:36:31,610 So I'm going to try doing this, right? 415 00:36:31,610 --> 00:36:34,330 I want to import the request module. 416 00:36:35,110 --> 00:36:37,630 But I don't have the request module. 417 00:36:38,770 --> 00:36:52,550 Just to show you that it works, that I'm actually using the actual Python that's hosted on the system, I'm going to Python-npip. 418 00:36:52,550 --> 00:37:00,350 Which, by the way, you should always do Python-npip because if you have several different Python versions, you don't want to confuse yourself. 419 00:37:00,630 --> 00:37:06,090 So you should always specify the version of Python that you're using when you run things like pip. 420 00:37:06,110 --> 00:37:07,590 It's just good practice. 421 00:37:08,730 --> 00:37:11,010 And we're going to just pip install request. 422 00:37:14,540 --> 00:37:20,200 And if I've installed request, and now I can import it with no problem. 423 00:37:20,200 --> 00:37:28,260 So, again, you can definitely use the Python libraries that are on the system to do it. 424 00:37:28,340 --> 00:37:35,240 Or you can host your own remotely and sort of just pull from a remote repo without actually touching anything on the disk. 425 00:37:35,240 --> 00:37:39,600 It's really up to you and what's in the best case scenario for your immediate situation. 426 00:37:39,720 --> 00:37:46,120 But I think it's great that we can actually just do this in the first place. 427 00:37:46,120 --> 00:38:01,620 So, so far, just to reiterate, we've talked about how to bypass autohooks for Windows, how to bypass autohooks for Linux, and how to not run Python on a system without running the Python executable whatsoever. 428 00:38:02,820 --> 00:38:06,600 For detections, it's a little bit difficult because everything is in .NET. 429 00:38:06,600 --> 00:38:14,140 So you have the same problem of .NET detections that you would find in any other .NET binary. 430 00:38:14,180 --> 00:38:19,740 If you're collecting ECW logs somehow and you have the space for that, wow, that's impressive. 431 00:38:19,920 --> 00:38:23,100 But if you have that space, you'll actually see that the Python runtime is loaded. 432 00:38:23,100 --> 00:38:27,000 So if you're in an environment where you don't normally see that, that could be something you detect. 433 00:38:27,000 --> 00:38:35,940 In this case, because I wrote it in Boo, it actually also detects the Boo.lang binary. 434 00:38:35,940 --> 00:38:38,980 But you could write this in C Sharp if you really wanted to. 435 00:38:38,980 --> 00:38:43,860 If you wanted to be a conformist and write all your tools in C Sharp, you could do that. 436 00:38:44,300 --> 00:38:47,800 And obviously, you wouldn't have the Boo.lang. 437 00:38:48,940 --> 00:38:55,120 So in conclusion, Python autohooks are great when they're implemented correctly. 438 00:38:56,000 --> 00:39:01,880 You should protect your custom Python executables with some OS-level protections. 439 00:39:02,260 --> 00:39:04,620 You need to do something, right? 440 00:39:05,320 --> 00:39:10,700 The same way you do any protection of any other binaries on your device. 441 00:39:10,740 --> 00:39:14,120 You need to make sure that your Python executables are protected. 442 00:39:14,120 --> 00:39:28,560 Because if I can pull your executable off of the system and you have autohooks enabled, I can debug it and get a pretty good idea of what autohooks you've implemented just by looking at the code or looking at the assembly. 443 00:39:29,760 --> 00:39:35,060 And of course, that ties in with don't rely solely on Python to protect your Python. 444 00:39:35,180 --> 00:39:45,840 So even if you had all these autohooks, I can pull your executable off and do some other nonsense with it and figure out how to bypass your autohooks. 445 00:39:46,560 --> 00:39:51,400 So thanks to everyone who has had some influence on this talk. 446 00:39:51,440 --> 00:39:59,640 Steve Dower, who works at Microsoft, he's a Python core dev, and he's actually the one who's done most of the implementation work for autohooks. 447 00:40:00,000 --> 00:40:11,600 Bytebleeder, who got me into Boo.lang and embedded scripting, Chris Ross over at SpecterOps, worked on Empire, and Adam Chester.