├── COPYING.GPL ├── COPYING.WINDOWS-PRIVESC-CHECK ├── README.md ├── README.original └── windows-privesc-check.py /COPYING.GPL: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /COPYING.WINDOWS-PRIVESC-CHECK: -------------------------------------------------------------------------------- 1 | This tool may be used for legal purposes only. Users take full responsibility 2 | for any actions performed using this tool. The author accepts no liability for 3 | damage caused by this tool. If these terms are not acceptable to you, then you 4 | may not use this tool. 5 | 6 | In all other respects the GPL version 2 applies. 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Windows Privesc Check 2 | ===================== 3 | 4 | This is a fork of Pentestmonkeys Windows Privesc Check. Here we add support for Windows versions with funny characters and try to clean up the code. 5 | 6 | Please see README.original for more detailed info. 7 | 8 | There is a branch for version 2.0 (wpc-2.0), every feature improvements should go there, this branch should only be used to preserve the original features. 9 | -------------------------------------------------------------------------------- /README.original: -------------------------------------------------------------------------------- 1 | Licence 2 | ======= 3 | 4 | windows-privesc-check - Security Auditing Tool For Windows 5 | Copyright (C) 2010 pentestmonkey@pentestmonkey.net 6 | http://pentestmonkey.net/windows-privesc-check 7 | 8 | This tool may be used for legal purposes only. Users take full responsibility 9 | for any actions performed using this tool. The author accepts no liability for 10 | damage caused by this tool. If these terms are not acceptable to you, then you 11 | may not use this tool. 12 | 13 | In all other respects the GPL version 2 applies. 14 | 15 | This program is free software; you can redistribute it and/or modify 16 | it under the terms of the GNU General Public License as published by 17 | the Free Software Foundation; either version 2 of the License, or 18 | (at your option) any later version. 19 | 20 | This program is distributed in the hope that it will be useful, 21 | but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | GNU General Public License for more details. 24 | 25 | You should have received a copy of the GNU General Public License along 26 | with this program; if not, write to the Free Software Foundation, Inc., 27 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 28 | 29 | TODO List 30 | ========= 31 | 32 | * TODO *Major code cleanup is needed...* 33 | * TODO review "read" perms. should probably ignore these. 34 | * TODO explaination of what various permissions mean 35 | * TODO Audit: Process listing, full paths and perms of all processes 36 | * TODO dlls used by programs (for all checks) 37 | * TODO find web roots and check for write access 38 | * TODO support remote server (remote file, reg keys, unc paths for file, remote resolving of sids) 39 | * TODO task scheduler 40 | * TODO alert if lanman hashes are being stored (for post-exploitation) 41 | * TODO alert if named local/domain service accounts are being used (for post-exploitation) 42 | * TODO Alert if really important patches are missing. These are metasploitable: 43 | * MS03_049 828749 Microsoft Workstation Service NetAddAlternateComputerName Overflow (netapi) 44 | * MS04_007 828028 Microsoft ASN.1 Library Bitstring Heap Overflow (killbill) 45 | * MS04_011 835732 Microsoft LSASS Service DsRolerUpgradeDownlevelServer Overflow (lsass) 46 | * MS04_031 841533 Microsoft NetDDE Service Overflow (netdde) 47 | * MS05_039 899588 Microsoft Plug and Play Service Overflow (pnp) 48 | * MS06_025 911280 Microsoft RRAS Service RASMAN Registry Overflow (rasmans_reg) 49 | * MS06_025 911280 Microsoft RRAS Service Overflow (rras) 50 | * MS06_040 921883 Microsoft Server Service NetpwPathCanonicalize Overflow (netapi) 51 | * MS06_066 923980 Microsoft Services MS06-066 nwapi32.dll (nwapi) 52 | * MS06_066 923980 Microsoft Services MS06-066 nwwks.dll (nwwks) 53 | * MS06_070 924270 Microsoft Workstation Service NetpManageIPCConnect Overflow (wkssvc) 54 | * MS07_029 935966 Microsoft DNS RPC Service extractQuotedChar() Overflow (SMB) (msdns_zonename) 55 | * MS08_067 958644 Microsoft Server Service Relative Path Stack Corruption (netapi) 56 | * MS09_050 975517 Microsoft SRV2.SYS SMB Negotiate ProcessID Function Table Dereference (smb2_negotiate_func_index) 57 | * MS03_026 823980 Microsoft RPC DCOM Interface Overflow 58 | * MS05_017 892944 Microsoft Message Queueing Service Path Overflow 59 | * MS07_065 937894 Microsoft Message Queueing Service DNS Name Path Overflow 60 | * TODO registry key checks for windows services 61 | * TODO per-user checks including perms on home dirs and startup folders + default user and all users 62 | * TODO need to be able to order issues more logically. Maybe group by section? 63 | 64 | Building an Executable 65 | ====================== 66 | 67 | This script should be converted to an exe before it is used for 68 | auditing - otherwise you'd have to install loads of dependencies 69 | on the target system. 70 | 71 | Download pyinstaller: http://www.pyinstaller.org/ 72 | 73 | Install the pywin32 dependency: http://pywin32.sourceforge.net/ 74 | 75 | Read the docs: http://www.pyinstaller.org/export/latest/tags/1.4/doc/Manual.html?format=raw 76 | 77 | Unzip to c:\pyinstaller (say) 78 | 79 | ``` 80 | cd c:\pyinstaller 81 | python Configure.py 82 | python Makespec.py --onefile c:\somepath\wpc.py 83 | python Build.py wpc\wpc.spec 84 | wpc\dist\wpc.exe 85 | ``` 86 | 87 | Alternative to pyinstaller is cxfreeze. This doesn't always work, though: 88 | \Python26\Scripts\cxfreeze wpc.py --target-dir dist 89 | zip -r wpc.zip dist 90 | 91 | You then need to copy wpc.zip to the target and unzip it. The exe therein 92 | should then run because all the dependencies are in the current (dist) directory. 93 | 94 | 64-bit vs 32-bit 95 | ================ 96 | 97 | If we run a 32-bit version of this script on a 64-bit box we have (at least) the 98 | following problems: 99 | * Registry: We CAN'T see the whole 64-bit registry. This seems insurmountable. 100 | * Files: It's harder to see in system32. This can be worked round. 101 | 102 | What types of object have permissions? 103 | ====================================== 104 | 105 | Files, directories and registry keys are the obvious candidates. There are several others too... 106 | 107 | This provides a good summary: http://msdn.microsoft.com/en-us/library/aa379557%28v=VS.85%29.aspx 108 | 109 | Files or directories on an NTFS file system GetNamedSecurityInfo, SetNamedSecurityInfo, GetSecurityInfo, SetSecurityInfo 110 | Named pipes 111 | Anonymous pipes 112 | Processes 113 | Threads 114 | File-mapping objects 115 | Access tokens 116 | Window-management objects (window stations and desktops) 117 | Registry keys 118 | Windows services 119 | Local or remote printers 120 | Network shares 121 | Interprocess synchronization objects 122 | Directory service objects 123 | 124 | This provides a good description of how Access Tokens interact with the Security Descriptors on Securable Objects: http://msdn.microsoft.com/en-us/library/aa374876%28v=VS.85%29.aspx 125 | 126 | http://msdn.microsoft.com/en-us/library/aa379593(VS.85).aspx 127 | win32security.SE_UNKNOWN_OBJECT_TYPE - n/a 128 | win32security.SE_FILE_OBJECT - poc working 129 | win32security.SE_SERVICE - poc working 130 | win32security.SE_PRINTER - TODO Indicates a printer. A printer object can be a local printer, such as PrinterName, or a remote printer, such as 131 | \\ComputerName\PrinterName. 132 | win32security.SE_REGISTRY_KEY - TODO 133 | Indicates a registry key. A registry key object can be in the local registry, such as CLASSES_ROOT\SomePath or in a remote registry, 134 | such as \\ComputerName\CLASSES_ROOT\SomePath. 135 | The names of registry keys must use the following literal strings to identify the predefined registry keys: 136 | "CLASSES_ROOT", "CURRENT_USER", "MACHINE", and "USERS". 137 | perms: http://msdn.microsoft.com/en-us/library/ms724878(VS.85).aspx 138 | win32security.SE_LMSHARE - TODO Indicates a network share. A share object can be local, such as ShareName, or remote, such as \\ComputerName\ShareName. 139 | win32security.SE_KERNEL_OBJECT - TODO 140 | Indicates a local kernel object. 141 | The GetSecurityInfo and SetSecurityInfo functions support all types of kernel objects. 142 | The GetNamedSecurityInfo and SetNamedSecurityInfo functions work only with the following kernel objects: 143 | semaphore, event, mutex, waitable timer, and file mapping. 144 | win32security.SE_WINDOW_OBJECT - TODO Indicates a window station or desktop object on the local computer. 145 | You cannot use GetNamedSecurityInfo and SetNamedSecurityInfo with these objects because the names of window stations or desktops are not unique. 146 | win32security.SE_DS_OBJECT - TODO - Active Directory object 147 | Indicates a directory service object or a property set or property of a directory service object. 148 | The name string for a directory service object must be in X.500 form, for example: 149 | CN=SomeObject,OU=ou2,OU=ou1,DC=DomainName,DC=CompanyName,DC=com,O=internet 150 | perm list: http://msdn.microsoft.com/en-us/library/aa772285(VS.85).aspx 151 | win32security.SE_DS_OBJECT_ALL - TODO 152 | Indicates a directory service object and all of its property sets and properties. 153 | win32security.SE_PROVIDER_DEFINED_OBJECT - TODO Indicates a provider-defined object. 154 | win32security.SE_WMIGUID_OBJECT - TODO Indicates a WMI object. 155 | win32security.SE_REGISTRY_WOW64_32KEY - TODO Indicates an object for a registry entry under WOW64. 156 | 157 | What sort of privileges can be granted on Windows? 158 | ================================================== 159 | 160 | These are bit like Capabilities on Linux. 161 | http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx 162 | http://msdn.microsoft.com/en-us/library/bb545671(VS.85).aspx 163 | 164 | These privs sound like they might allow the holder to gain admin rights: 165 | 166 | SE_ASSIGNPRIMARYTOKEN_NAME TEXT("SeAssignPrimaryTokenPrivilege") Required to assign the primary token of a process. User Right: Replace a process-level token. 167 | SE_BACKUP_NAME TEXT("SeBackupPrivilege") Required to perform backup operations. This privilege causes the system to grant all read access control to any file, regardless of the access control list (ACL) specified for the file. Any access request other than read is still evaluated with the ACL. This privilege is required by the RegSaveKey and RegSaveKeyExfunctions. The following access rights are granted if this privilege is held: READ_CONTROL ACCESS_SYSTEM_SECURITY FILE_GENERIC_READ FILE_TRAVERSE User Right: Back up files and directories. 168 | SE_CREATE_PAGEFILE_NAME TEXT("SeCreatePagefilePrivilege") Required to create a paging file. User Right: Create a pagefile. 169 | SE_CREATE_TOKEN_NAME TEXT("SeCreateTokenPrivilege") Required to create a primary token. User Right: Create a token object. 170 | SE_DEBUG_NAME TEXT("SeDebugPrivilege") Required to debug and adjust the memory of a process owned by another account. User Right: Debug programs. 171 | SE_ENABLE_DELEGATION_NAME TEXT("SeEnableDelegationPrivilege") Required to mark user and computer accounts as trusted for delegation. User Right: Enable computer and user accounts to be trusted for delegation. 172 | SE_LOAD_DRIVER_NAME TEXT("SeLoadDriverPrivilege") Required to load or unload a device driver. User Right: Load and unload device drivers. 173 | SE_MACHINE_ACCOUNT_NAME TEXT("SeMachineAccountPrivilege") Required to create a computer account. User Right: Add workstations to domain. 174 | SE_MANAGE_VOLUME_NAME TEXT("SeManageVolumePrivilege") Required to enable volume management privileges. User Right: Manage the files on a volume. 175 | SE_RELABEL_NAME TEXT("SeRelabelPrivilege") Required to modify the mandatory integrity level of an object. User Right: Modify an object label. 176 | SE_RESTORE_NAME TEXT("SeRestorePrivilege") Required to perform restore operations. This privilege causes the system to grant all write access control to any file, regardless of the ACL specified for the file. Any access request other than write is still evaluated with the ACL. Additionally, this privilege enables you to set any valid user or group SID as the owner of a file. This privilege is required by the RegLoadKey function. The following access rights are granted if this privilege is held: WRITE_DAC WRITE_OWNER ACCESS_SYSTEM_SECURITY FILE_GENERIC_WRITE FILE_ADD_FILE FILE_ADD_SUBDIRECTORY DELETE User Right: Restore files and directories. 177 | SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege") Required to shut down a local system. User Right: Shut down the system. 178 | SE_SYNC_AGENT_NAME TEXT("SeSyncAgentPrivilege") Required for a domain controller to use the LDAP directory synchronization services. This privilege enables the holder to read all objects and properties in the directory, regardless of the protection on the objects and properties. By default, it is assigned to the Administrator and LocalSystem accounts on domain controllers. User Right: Synchronize directory service data. 179 | SE_TAKE_OWNERSHIP_NAME TEXT("SeTakeOwnershipPrivilege") Required to take ownership of an object without being granted discretionary access. This privilege allows the owner value to be set only to those values that the holder may legitimately assign as the owner of an object. User Right: Take ownership of files or other objects. 180 | SE_TCB_NAME TEXT("SeTcbPrivilege") This privilege identifies its holder as part of the trusted computer base. Some trusted protected subsystems are granted this privilege. User Right: Act as part of the operating system. 181 | SE_TRUSTED_CREDMAN_ACCESS_NAME TEXT("SeTrustedCredManAccessPrivilege") Required to access Credential Manager as a trusted caller. User Right: Access Credential Manager as a trusted caller. 182 | 183 | These sound like they could be troublesome in the wrong hands: 184 | 185 | SE_SECURITY_NAME TEXT("SeSecurityPrivilege") Required to perform a number of security-related functions, such as controlling and viewing audit messages. This privilege identifies its holder as a security operator. User Right: Manage auditing and security log. 186 | SE_REMOTE_SHUTDOWN_NAME TEXT("SeRemoteShutdownPrivilege") Required to shut down a system using a network request. User Right: Force shutdown from a remote system. 187 | SE_PROF_SINGLE_PROCESS_NAME TEXT("SeProfileSingleProcessPrivilege") Required to gather profiling information for a single process. User Right: Profile single process. 188 | SE_AUDIT_NAME TEXT("SeAuditPrivilege") Required to generate audit-log entries. Give this privilege to secure servers.User Right: Generate security audits. 189 | SE_INC_BASE_PRIORITY_NAME TEXT("SeIncreaseBasePriorityPrivilege") Required to increase the base priority of a process. User Right: Increase scheduling priority. 190 | SE_INC_WORKING_SET_NAME TEXT("SeIncreaseWorkingSetPrivilege") Required to allocate more memory for applications that run in the context of users. User Right: Increase a process working set. 191 | SE_INCREASE_QUOTA_NAME TEXT("SeIncreaseQuotaPrivilege") Required to increase the quota assigned to a process. User Right: Adjust memory quotas for a process. 192 | SE_LOCK_MEMORY_NAME TEXT("SeLockMemoryPrivilege") Required to lock physical pages in memory. User Right: Lock pages in memory. 193 | SE_SYSTEM_ENVIRONMENT_NAME TEXT("SeSystemEnvironmentPrivilege") Required to modify the nonvolatile RAM of systems that use this type of memory to store configuration information. User Right: Modify firmware environment values. 194 | 195 | These sound less interesting: 196 | 197 | SE_CHANGE_NOTIFY_NAME TEXT("SeChangeNotifyPrivilege") Required to receive notifications of changes to files or directories. This privilege also causes the system to skip all traversal access checks. It is enabled by default for all users. User Right: Bypass traverse checking. 198 | SE_CREATE_GLOBAL_NAME TEXT("SeCreateGlobalPrivilege") Required to create named file mapping objects in the global namespace during Terminal Services sessions. This privilege is enabled by default for administrators, services, and the local system account.User Right: Create global objects. Windows XP/2000: This privilege is not supported. Note that this value is supported starting with Windows Server 2003, Windows XP with SP2, and Windows 2000 with SP4. 199 | SE_CREATE_PERMANENT_NAME TEXT("SeCreatePermanentPrivilege") Required to create a permanent object. User Right: Create permanent shared objects. 200 | SE_CREATE_SYMBOLIC_LINK_NAME TEXT("SeCreateSymbolicLinkPrivilege") Required to create a symbolic link. User Right: Create symbolic links. 201 | SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege") Required to impersonate. User Right: Impersonate a client after authentication. Windows XP/2000: This privilege is not supported. Note that this value is supported starting with Windows Server 2003, Windows XP with SP2, and Windows 2000 with SP4. 202 | SE_SYSTEM_PROFILE_NAME TEXT("SeSystemProfilePrivilege") Required to gather profiling information for the entire system. User Right: Profile system performance. 203 | SE_SYSTEMTIME_NAME TEXT("SeSystemtimePrivilege") Required to modify the system time. User Right: Change the system time. 204 | SE_TIME_ZONE_NAME TEXT("SeTimeZonePrivilege") Required to adjust the time zone associated with the computer's internal clock. User Right: Change the time zone. 205 | SE_UNDOCK_NAME TEXT("SeUndockPrivilege") Required to undock a laptop. User Right: Remove computer from docking station. 206 | SE_UNSOLICITED_INPUT_NAME TEXT("SeUnsolicitedInputPrivilege") Required to read unsolicited input from a terminal device. User Right: Not applicable. 207 | 208 | These are from ntsecapi.h: 209 | 210 | SE_BATCH_LOGON_NAME TEXT("SeBatchLogonRight") Required for an account to log on using the batch logon type. 211 | SE_DENY_BATCH_LOGON_NAME TEXT("SeDenyBatchLogonRight") Explicitly denies an account the right to log on using the batch logon type. 212 | SE_DENY_INTERACTIVE_LOGON_NAME TEXT("SeDenyInteractiveLogonRight") Explicitly denies an account the right to log on using the interactive logon type. 213 | SE_DENY_NETWORK_LOGON_NAME TEXT("SeDenyNetworkLogonRight") Explicitly denies an account the right to log on using the network logon type. 214 | SE_DENY_REMOTE_INTERACTIVE_LOGON_NAME TEXT("SeDenyRemoteInteractiveLogonRight") Explicitly denies an account the right to log on remotely using the interactive logon type. 215 | SE_DENY_SERVICE_LOGON_NAME TEXT("SeDenyServiceLogonRight") Explicitly denies an account the right to log on using the service logon type. 216 | SE_INTERACTIVE_LOGON_NAME TEXT("SeInteractiveLogonRight") Required for an account to log on using the interactive logon type. 217 | SE_NETWORK_LOGON_NAME TEXT("SeNetworkLogonRight") Required for an account to log on using the network logon type. 218 | SE_REMOTE_INTERACTIVE_LOGON_NAME TEXT("SeRemoteInteractiveLogonRight") Required for an account to log on remotely using the interactive logon type. 219 | SE_SERVICE_LOGON_NAME TEXT("SeServiceLogonRight") Required for an account to log on using the service logon type. 220 | -------------------------------------------------------------------------------- /windows-privesc-check.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import glob 3 | import datetime 4 | import socket 5 | import os, sys 6 | import win32process 7 | import re 8 | import win32security, ntsecuritycon, win32api, win32con, win32file 9 | import win32service 10 | import pywintypes # doesn't play well with molebox pro - why did we need this anyway? 11 | import win32net 12 | import ctypes 13 | import getopt 14 | import _winreg 15 | import win32netcon 16 | from subprocess import Popen, PIPE, STDOUT 17 | from ntsecuritycon import TokenSessionId, TokenSandBoxInert, TokenType, TokenImpersonationLevel, TokenVirtualizationEnabled, TokenVirtualizationAllowed, TokenHasRestrictions, TokenElevationType, TokenUIAccess, TokenUser, TokenOwner, TokenGroups, TokenRestrictedSids, TokenPrivileges, TokenPrimaryGroup, TokenSource, TokenDefaultDacl, TokenStatistics, TokenOrigin, TokenLinkedToken, TokenLogonSid, TokenElevation, TokenIntegrityLevel, TokenMandatoryPolicy, SE_ASSIGNPRIMARYTOKEN_NAME, SE_BACKUP_NAME, SE_CREATE_PAGEFILE_NAME, SE_CREATE_TOKEN_NAME, SE_DEBUG_NAME, SE_LOAD_DRIVER_NAME, SE_MACHINE_ACCOUNT_NAME, SE_RESTORE_NAME, SE_SHUTDOWN_NAME, SE_TAKE_OWNERSHIP_NAME, SE_TCB_NAME 18 | import unicodedata 19 | 20 | k32 = ctypes.windll.kernel32 21 | wow64 = ctypes.c_long( 0 ) 22 | on64bitwindows = 1 23 | remote_server = None 24 | remote_username = None 25 | remote_password = None 26 | remote_domain = None 27 | local_ips = socket.gethostbyname_ex(socket.gethostname())[2] # have to do this before Wow64DisableWow64FsRedirection 28 | 29 | version = "1.0" 30 | svnversion="$Revision$" # Don't change this line. Auto-updated. 31 | svnnum=re.sub('[^0-9]', '', svnversion) 32 | if svnnum: 33 | version = version + "svn" + svnnum 34 | 35 | all_checks = 0 36 | registry_checks = 0 37 | path_checks = 0 38 | service_checks = 0 39 | service_audit = 0 40 | drive_checks = 0 41 | eventlog_checks = 0 42 | progfiles_checks = 0 43 | process_checks = 0 44 | share_checks = 0 45 | passpol_audit = 0 46 | user_group_audit = 0 47 | logged_in_audit = 0 48 | process_audit = 0 49 | admin_users_audit= 0 50 | host_info_audit = 0 51 | ignore_trusted = 0 52 | owner_info = 0 53 | weak_perms_only = 0 54 | host_info_audit = 0 55 | patch_checks = 0 56 | verbose = 0 57 | report_file_name = None 58 | 59 | kb_nos = { 60 | '977165': 'MS10_015 Vulnerabilities in Windows Kernel Could Allow Elevation of Privilege (kitrap0d - meterpreter "getsystem")', 61 | '828749': 'MS03_049 Microsoft Workstation Service NetAddAlternateComputerName Overflow (netapi) ', 62 | '828028': 'MS04_007 Microsoft ASN.1 Library Bitstring Heap Overflow (killbill) ', 63 | '835732': 'MS04_011 Microsoft LSASS Service DsRolerUpgradeDownlevelServer Overflow (lsass) ', 64 | '841533': 'MS04_031 Microsoft NetDDE Service Overflow (netdde)', 65 | '899588': 'MS05_039 Microsoft Plug and Play Service Overflow (pnp)', 66 | '911280': 'MS06_025 Microsoft RRAS Service RASMAN Registry Overflow (rasmans_reg)', 67 | '911280': 'MS06_025 Microsoft RRAS Service Overflow (rras)', 68 | '921883': 'MS06_040 Microsoft Server Service NetpwPathCanonicalize Overflow (netapi)', 69 | '923980': 'MS06_066 Microsoft Services MS06-066 nwapi32.dll (nwapi)', 70 | '923980': 'MS06_066 Microsoft Services MS06-066 nwwks.dll (nwwks)', 71 | '924270': 'MS06_070 Microsoft Workstation Service NetpManageIPCConnect Overflow (wkssvc)', 72 | '935966': 'MS07_029 Microsoft DNS RPC Service extractQuotedChar() Overflow (SMB) (msdns_zonename)', 73 | '958644': 'MS08_067 Microsoft Server Service Relative Path Stack Corruption (netapi)', 74 | '975517': 'MS09_050 Microsoft SRV2.SYS SMB Negotiate ProcessID Function Table Dereference (smb2_negotiate_func_index)', 75 | '823980': 'MS03_026 Microsoft RPC DCOM Interface Overflow', 76 | '892944': 'MS05_017 Microsoft Message Queueing Service Path Overflow', 77 | '937894': 'MS07_065 Microsoft Message Queueing Service DNS Name Path Overflow', 78 | '2592799': 'MS11-080: Vulnerability in ancillary function driver could allow elevation of privilege', 79 | '2305420': 'MS10-092: Vulnerability in Task Scheduler could allow for elevation of privilege' 80 | } 81 | 82 | reg_paths = ( 83 | 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services', 84 | 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run', 85 | 'HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run', 86 | 'HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce', 87 | 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run', 88 | 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell', 89 | "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Userinit", 90 | 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce', 91 | 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce', 92 | 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices', 93 | 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce', 94 | 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServices', 95 | 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce', 96 | 'HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows', 97 | ) 98 | 99 | # We don't care if some users / groups hold dangerous permission because they're trusted 100 | # These have fully qualified names: 101 | trusted_principles_fq = ( 102 | "BUILTIN\\Administrators", 103 | u"BUILTIN\\Rendszergazd\xe1k", # Hungarian name for Administrators 104 | "NT SERVICE\\TrustedInstaller", 105 | "NT AUTHORITY\\SYSTEM" 106 | ) 107 | 108 | # We may temporarily regard a user as trusted (e.g. if we're looking for writable 109 | # files in a user's path, we do not care that he can write to his own path) 110 | tmp_trusted_principles_fq = ( 111 | ) 112 | 113 | eventlog_key_hklm = 'SYSTEM\CurrentControlSet\Services\Eventlog' 114 | 115 | # We don't care if members of these groups hold dangerous permission because they're trusted 116 | # These have names without a domain: 117 | trusted_principles = ( 118 | "Administrators", 119 | u"Rendszergazd\xe1k", # Hungarian name for Administrators 120 | "Domain Admins", 121 | "Enterprise Admins", 122 | ) 123 | 124 | # Windows privileges from 125 | windows_privileges = ( 126 | "SeAssignPrimaryTokenPrivilege", 127 | "SeBackupPrivilege", 128 | "SeCreatePagefilePrivilege", 129 | "SeCreateTokenPrivilege", 130 | "SeDebugPrivilege", 131 | "SeEnableDelegationPrivilege", 132 | "SeLoadDriverPrivilege", 133 | "SeMachineAccountPrivilege", 134 | "SeManageVolumePrivilege", 135 | "SeRelabelPrivilege", 136 | "SeRestorePrivilege", 137 | "SeShutdownPrivilege", 138 | "SeSyncAgentPrivilege", 139 | "SeTakeOwnershipPrivilege", 140 | "SeTcbPrivilege", 141 | "SeTrustedCredManAccessPrivilege", 142 | "SeSecurityPrivilege", 143 | "SeRemoteShutdownPrivilege", 144 | "SeProfileSingleProcessPrivilege", 145 | "SeAuditPrivilege", 146 | "SeIncreaseBasePriorityPrivilege", 147 | "SeIncreaseWorkingSetPrivilege", 148 | "SeIncreaseQuotaPrivilege", 149 | "SeLockMemoryPrivilege", 150 | "SeSystemEnvironmentPrivilege", 151 | "SeChangeNotifyPrivilege", 152 | "SeCreateGlobalPrivilege", 153 | "SeCreatePermanentPrivilege", 154 | "SeCreateSymbolicLinkPrivilege", 155 | "SeImpersonatePrivilege", 156 | "SeSystemProfilePrivilege", 157 | "SeSystemtimePrivilege", 158 | "SeTimeZonePrivilege", 159 | "SeUndockPrivilege", 160 | "SeUnsolicitedInputPrivilege", 161 | "SeBatchLogonRight", 162 | "SeDenyBatchLogonRight", 163 | "SeDenyInteractiveLogonRight", 164 | "SeDenyNetworkLogonRight", 165 | "SeDenyRemoteInteractiveLogonRight", 166 | "SeDenyServiceLogonRight", 167 | "SeInteractiveLogonRight", 168 | "SeNetworkLogonRight", 169 | "SeRemoteInteractiveLogonRight", 170 | "SeServiceLogonRight" 171 | ) 172 | 173 | share_types = ( 174 | "STYPE_IPC", 175 | "STYPE_DISKTREE", 176 | "STYPE_PRINTQ", 177 | "STYPE_DEVICE", 178 | ) 179 | 180 | sv_types = ( 181 | "SV_TYPE_WORKSTATION", 182 | "SV_TYPE_SERVER", 183 | "SV_TYPE_SQLSERVER", 184 | "SV_TYPE_DOMAIN_CTRL", 185 | "SV_TYPE_DOMAIN_BAKCTRL", 186 | "SV_TYPE_TIME_SOURCE", 187 | "SV_TYPE_AFP", 188 | "SV_TYPE_NOVELL", 189 | "SV_TYPE_DOMAIN_MEMBER", 190 | "SV_TYPE_PRINTQ_SERVER", 191 | "SV_TYPE_DIALIN_SERVER", 192 | "SV_TYPE_XENIX_SERVER", 193 | "SV_TYPE_NT", 194 | "SV_TYPE_WFW", 195 | "SV_TYPE_SERVER_MFPN", 196 | "SV_TYPE_SERVER_NT", 197 | "SV_TYPE_POTENTIAL_BROWSER", 198 | "SV_TYPE_BACKUP_BROWSER", 199 | "SV_TYPE_MASTER_BROWSER", 200 | "SV_TYPE_DOMAIN_MASTER", 201 | "SV_TYPE_SERVER_OSF", 202 | "SV_TYPE_SERVER_VMS", 203 | "SV_TYPE_WINDOWS", 204 | "SV_TYPE_DFS", 205 | "SV_TYPE_CLUSTER_NT", 206 | "SV_TYPE_TERMINALSERVER", # missing from win32netcon.py 207 | #"SV_TYPE_CLUSTER_VS_NT", # missing from win32netcon.py 208 | "SV_TYPE_DCE", 209 | "SV_TYPE_ALTERNATE_XPORT", 210 | "SV_TYPE_LOCAL_LIST_ONLY", 211 | "SV_TYPE_DOMAIN_ENUM" 212 | ) 213 | 214 | win32netcon.SV_TYPE_TERMINALSERVER = 0x2000000 215 | 216 | dangerous_perms_write = { 217 | # http://www.tek-tips.com/faqs.cfm?fid 218 | 'share': { 219 | ntsecuritycon: ( 220 | "FILE_READ_DATA", # 221 | "FILE_WRITE_DATA", 222 | "FILE_APPEND_DATA", 223 | "FILE_READ_EA", # 224 | "FILE_WRITE_EA", 225 | "FILE_EXECUTE", # 226 | "FILE_READ_ATTRIBUTES", # 227 | "FILE_WRITE_ATTRIBUTES", 228 | "DELETE", 229 | "READ_CONTROL", # 230 | "WRITE_DAC", 231 | "WRITE_OWNER", 232 | "SYNCHRONIZE", # 233 | ) 234 | }, 235 | 'file': { 236 | ntsecuritycon: ( 237 | #"FILE_READ_DATA", 238 | "FILE_WRITE_DATA", 239 | "FILE_APPEND_DATA", 240 | #"FILE_READ_EA", 241 | "FILE_WRITE_EA", 242 | #"FILE_EXECUTE", 243 | #"FILE_READ_ATTRIBUTES", 244 | "FILE_WRITE_ATTRIBUTES", 245 | "DELETE", 246 | #"READ_CONTROL", 247 | "WRITE_DAC", 248 | "WRITE_OWNER", 249 | #"SYNCHRONIZE", 250 | ) 251 | }, 252 | # http://msdn.microsoft.com/en-us/library/ms724878(VS.85).aspx 253 | # KEY_ALL_ACCESS: STANDARD_RIGHTS_REQUIRED KEY_QUERY_VALUE KEY_SET_VALUE KEY_CREATE_SUB_KEY KEY_ENUMERATE_SUB_KEYS KEY_NOTIFY KEY_CREATE_LINK 254 | # KEY_CREATE_LINK (0x0020) Reserved for system use. 255 | # KEY_CREATE_SUB_KEY (0x0004) Required to create a subkey of a registry key. 256 | # KEY_ENUMERATE_SUB_KEYS (0x0008) Required to enumerate the subkeys of a registry key. 257 | # KEY_EXECUTE (0x20019) Equivalent to KEY_READ. 258 | # KEY_NOTIFY (0x0010) Required to request change notifications for a registry key or for subkeys of a registry key. 259 | # KEY_QUERY_VALUE (0x0001) Required to query the values of a registry key. 260 | # KEY_READ (0x20019) Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values. 261 | # KEY_SET_VALUE (0x0002) Required to create, delete, or set a registry value. 262 | # KEY_WOW64_32KEY (0x0200) Indicates that an application on 64-bit Windows should operate on the 32-bit registry view. For more information, see Accessing an Alternate Registry View. This flag must be combined using the OR operator with the other flags in this table that either query or access registry values. 263 | # Windows 2000: This flag is not supported. 264 | # KEY_WOW64_64KEY (0x0100) Indicates that an application on 64-bit Windows should operate on the 64-bit registry view. For more information, see Accessing an Alternate Registry View. 265 | # This flag must be combined using the OR operator with the other flags in this table that either query or access registry values. 266 | # Windows 2000: This flag is not supported. 267 | # KEY_WRITE (0x20006) Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights. 268 | # "STANDARD_RIGHTS_REQUIRED", 269 | # "STANDARD_RIGHTS_WRITE", 270 | # "STANDARD_RIGHTS_READ", 271 | # "DELETE", 272 | # "READ_CONTROL", 273 | # "WRITE_DAC", 274 | #"WRITE_OWNER", 275 | 'reg': { 276 | _winreg: ( 277 | #"KEY_ALL_ACCESS", # Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK access rights. 278 | #"KEY_QUERY_VALUE", # GUI "Query Value" 279 | "KEY_SET_VALUE", # GUI "Set Value". Required to create, delete, or set a registry value. 280 | "KEY_CREATE_LINK", # GUI "Create Link". Reserved for system use. 281 | "KEY_CREATE_SUB_KEY", # GUI "Create subkey" 282 | # "KEY_ENUMERATE_SUB_KEYS", # GUI "Create subkeys" 283 | # "KEY_NOTIFY", # GUI "Notify" 284 | #"KEY_EXECUTE", # same as KEY_READ 285 | #"KEY_READ", 286 | #"KEY_WOW64_32KEY", 287 | #"KEY_WOW64_64KEY", 288 | # "KEY_WRITE", # Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights. 289 | ), 290 | ntsecuritycon: ( 291 | "DELETE", # GUI "Delete" 292 | # "READ_CONTROL", # GUI "Read Control" - read security descriptor 293 | "WRITE_DAC", # GUI "Write DAC" 294 | "WRITE_OWNER", # GUI "Write Owner" 295 | #"STANDARD_RIGHTS_REQUIRED", 296 | #"STANDARD_RIGHTS_WRITE", 297 | #"STANDARD_RIGHTS_READ", 298 | ) 299 | }, 300 | 'directory': { 301 | ntsecuritycon: ( 302 | #"FILE_LIST_DIRECTORY", 303 | "FILE_ADD_FILE", 304 | "FILE_ADD_SUBDIRECTORY", 305 | #"FILE_READ_EA", 306 | "FILE_WRITE_EA", 307 | #"FILE_TRAVERSE", 308 | "FILE_DELETE_CHILD", 309 | #"FILE_READ_ATTRIBUTES", 310 | "FILE_WRITE_ATTRIBUTES", 311 | "DELETE", 312 | #"READ_CONTROL", 313 | "WRITE_DAC", 314 | "WRITE_OWNER", 315 | #"SYNCHRONIZE", 316 | ) 317 | }, 318 | 'service_manager': { 319 | # For service manager 320 | # http://msdn.microsoft.com/en-us/library/ms685981(VS.85).aspx 321 | # SC_MANAGER_ALL_ACCESS (0xF003F) Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table. 322 | # SC_MANAGER_CREATE_SERVICE (0x0002) Required to call the CreateService function to create a service object and add it to the database. 323 | # SC_MANAGER_CONNECT (0x0001) Required to connect to the service control manager. 324 | # SC_MANAGER_ENUMERATE_SERVICE (0x0004) Required to call the EnumServicesStatusEx function to list the services that are in the database. 325 | # SC_MANAGER_LOCK (0x0008) Required to call the LockServiceDatabase function to acquire a lock on the database. 326 | # SC_MANAGER_MODIFY_BOOT_CONFIG (0x0020) Required to call the NotifyBootConfigStatus function. 327 | # SC_MANAGER_QUERY_LOCK_STATUS (0x0010)Required to call the QueryServiceLockStatus function to retrieve the lock status information for the database. 328 | win32service: ( 329 | "SC_MANAGER_ALL_ACCESS", 330 | "SC_MANAGER_CREATE_SERVICE", 331 | "SC_MANAGER_CONNECT", 332 | "SC_MANAGER_ENUMERATE_SERVICE", 333 | "SC_MANAGER_LOCK", 334 | "SC_MANAGER_MODIFY_BOOT_CONFIG", 335 | "SC_MANAGER_QUERY_LOCK_STATUS", 336 | ) 337 | }, 338 | 'service': { 339 | # For services: 340 | # http://msdn.microsoft.com/en-us/library/ms685981(VS.85).aspx 341 | # SERVICE_ALL_ACCESS (0xF01FF) Includes STANDARD_RIGHTS_REQUIRED in addition to all access rights in this table. 342 | # SERVICE_CHANGE_CONFIG (0x0002) Required to call the ChangeServiceConfig or ChangeServiceConfig2 function to change the service configuration. Because this grants the caller the right to change the executable file that the system runs, it should be granted only to administrators. 343 | # SERVICE_ENUMERATE_DEPENDENTS (0x0008) Required to call the EnumDependentServices function to enumerate all the services dependent on the service. 344 | # SERVICE_INTERROGATE (0x0080) Required to call the ControlService function to ask the service to report its status immediately. 345 | # SERVICE_PAUSE_CONTINUE (0x0040) Required to call the ControlService function to pause or continue the service. 346 | # SERVICE_QUERY_CONFIG (0x0001) Required to call the QueryServiceConfig and QueryServiceConfig2 functions to query the service configuration. 347 | # SERVICE_QUERY_STATUS (0x0004) Required to call the QueryServiceStatusEx function to ask the service control manager about the status of the service. 348 | # SERVICE_START (0x0010) Required to call the StartService function to start the service. 349 | # SERVICE_STOP (0x0020) Required to call the ControlService function to stop the service. 350 | # SERVICE_USER_DEFINED_CONTROL(0x0100) Required to call the ControlService function to specify a user-defined control code. 351 | win32service: ( 352 | # "SERVICE_INTERROGATE", 353 | # "SERVICE_QUERY_STATUS", 354 | # "SERVICE_ENUMERATE_DEPENDENTS", 355 | "SERVICE_ALL_ACCESS", 356 | "SERVICE_CHANGE_CONFIG", 357 | "SERVICE_PAUSE_CONTINUE", 358 | # "SERVICE_QUERY_CONFIG", 359 | "SERVICE_START", 360 | "SERVICE_STOP", 361 | # "SERVICE_USER_DEFINED_CONTROL", # TODO this is granted most of the time. Double check that's not a bad thing. 362 | ) 363 | }, 364 | } 365 | 366 | all_perms = { 367 | 'share': { 368 | ntsecuritycon: ( 369 | "FILE_READ_DATA", # 370 | "FILE_WRITE_DATA", 371 | "FILE_APPEND_DATA", 372 | "FILE_READ_EA", # 373 | "FILE_WRITE_EA", 374 | "FILE_EXECUTE", # 375 | "FILE_READ_ATTRIBUTES", # 376 | "FILE_WRITE_ATTRIBUTES", 377 | "DELETE", 378 | "READ_CONTROL", # 379 | "WRITE_DAC", 380 | "WRITE_OWNER", 381 | "SYNCHRONIZE", # 382 | ) 383 | }, 384 | 'file': { 385 | ntsecuritycon: ( 386 | "FILE_READ_DATA", 387 | "FILE_WRITE_DATA", 388 | "FILE_APPEND_DATA", 389 | "FILE_READ_EA", 390 | "FILE_WRITE_EA", 391 | "FILE_EXECUTE", 392 | "FILE_READ_ATTRIBUTES", 393 | "FILE_WRITE_ATTRIBUTES", 394 | "DELETE", 395 | "READ_CONTROL", 396 | "WRITE_DAC", 397 | "WRITE_OWNER", 398 | "SYNCHRONIZE", 399 | ) 400 | }, 401 | 'reg': { 402 | _winreg: ( 403 | "KEY_ALL_ACCESS", 404 | "KEY_CREATE_LINK", 405 | "KEY_CREATE_SUB_KEY", 406 | "KEY_ENUMERATE_SUB_KEYS", 407 | "KEY_EXECUTE", 408 | "KEY_NOTIFY", 409 | "KEY_QUERY_VALUE", 410 | "KEY_READ", 411 | "KEY_SET_VALUE", 412 | "KEY_WOW64_32KEY", 413 | "KEY_WOW64_64KEY", 414 | "KEY_WRITE", 415 | ), 416 | ntsecuritycon: ( 417 | "DELETE", 418 | "READ_CONTROL", 419 | "WRITE_DAC", 420 | "WRITE_OWNER", 421 | "STANDARD_RIGHTS_REQUIRED", 422 | "STANDARD_RIGHTS_WRITE", 423 | "STANDARD_RIGHTS_READ", 424 | "SYNCHRONIZE", 425 | ) 426 | }, 427 | 'directory': { 428 | ntsecuritycon: ( 429 | "FILE_LIST_DIRECTORY", 430 | "FILE_ADD_FILE", 431 | "FILE_ADD_SUBDIRECTORY", 432 | "FILE_READ_EA", 433 | "FILE_WRITE_EA", 434 | "FILE_TRAVERSE", 435 | "FILE_DELETE_CHILD", 436 | "FILE_READ_ATTRIBUTES", 437 | "FILE_WRITE_ATTRIBUTES", 438 | "DELETE", 439 | "READ_CONTROL", 440 | "WRITE_DAC", 441 | "WRITE_OWNER", 442 | "SYNCHRONIZE", 443 | ) 444 | }, 445 | 'service_manager': { 446 | win32service: ( 447 | "SC_MANAGER_ALL_ACCESS", 448 | "SC_MANAGER_CREATE_SERVICE", 449 | "SC_MANAGER_CONNECT", 450 | "SC_MANAGER_ENUMERATE_SERVICE", 451 | "SC_MANAGER_LOCK", 452 | "SC_MANAGER_MODIFY_BOOT_CONFIG", 453 | "SC_MANAGER_QUERY_LOCK_STATUS", 454 | ) 455 | }, 456 | 'service': { 457 | win32service: ( 458 | "SERVICE_INTERROGATE", 459 | "SERVICE_QUERY_STATUS", 460 | "SERVICE_ENUMERATE_DEPENDENTS", 461 | "SERVICE_ALL_ACCESS", 462 | "SERVICE_CHANGE_CONFIG", 463 | "SERVICE_PAUSE_CONTINUE", 464 | "SERVICE_QUERY_CONFIG", 465 | "SERVICE_START", 466 | "SERVICE_STOP", 467 | "SERVICE_USER_DEFINED_CONTROL", # TODO this is granted most of the time. Double check that's not a bad thing. 468 | ) 469 | }, 470 | 'process': { 471 | win32con: ( 472 | "PROCESS_TERMINATE", 473 | "PROCESS_CREATE_THREAD", 474 | "PROCESS_VM_OPERATION", 475 | "PROCESS_VM_READ", 476 | "PROCESS_VM_WRITE", 477 | "PROCESS_DUP_HANDLE", 478 | "PROCESS_CREATE_PROCESS", 479 | "PROCESS_SET_QUOTA", 480 | "PROCESS_SET_INFORMATION", 481 | "PROCESS_QUERY_INFORMATION", 482 | "PROCESS_ALL_ACCESS" 483 | ), 484 | ntsecuritycon: ( 485 | "DELETE", 486 | "READ_CONTROL", 487 | "WRITE_DAC", 488 | "WRITE_OWNER", 489 | "SYNCHRONIZE", 490 | "STANDARD_RIGHTS_REQUIRED", 491 | "STANDARD_RIGHTS_READ", 492 | "STANDARD_RIGHTS_WRITE", 493 | "STANDARD_RIGHTS_EXECUTE", 494 | "STANDARD_RIGHTS_ALL", 495 | "SPECIFIC_RIGHTS_ALL", 496 | "ACCESS_SYSTEM_SECURITY", 497 | "MAXIMUM_ALLOWED", 498 | "GENERIC_READ", 499 | "GENERIC_WRITE", 500 | "GENERIC_EXECUTE", 501 | "GENERIC_ALL" 502 | ) 503 | }, 504 | 'thread': { 505 | win32con: ( 506 | "THREAD_TERMINATE", 507 | "THREAD_SUSPEND_RESUME", 508 | "THREAD_GET_CONTEXT", 509 | "THREAD_SET_CONTEXT", 510 | "THREAD_SET_INFORMATION", 511 | "THREAD_QUERY_INFORMATION", 512 | "THREAD_SET_THREAD_TOKEN", 513 | "THREAD_IMPERSONATE", 514 | "THREAD_DIRECT_IMPERSONATION", 515 | "THREAD_ALL_ACCESS", 516 | "THREAD_QUERY_LIMITED_INFORMATION", 517 | "THREAD_SET_LIMITED_INFORMATION" 518 | ), 519 | ntsecuritycon: ( 520 | "DELETE", 521 | "READ_CONTROL", 522 | "WRITE_DAC", 523 | "WRITE_OWNER", 524 | "SYNCHRONIZE", 525 | ) 526 | }, 527 | } 528 | 529 | # Used to store a data structure representing the issues we've found 530 | # We use this to generate the report 531 | issues = {} 532 | 533 | issue_template = { 534 | 'WPC001': { 535 | 'title': "Insecure Permissions on Program Files", 536 | 'description': '''Some of the programs in %ProgramFiles% and/or %ProgramFiles(x86)% could be changed by non-administrative users. 537 | 538 | This could allow certain users on the system to place malicious code into certain key directories, or to replace programs with malicious ones. A malicious local user could use this technique to hijack the privileges of other local users, running commands with their privileges. 539 | ''', 540 | 'recommendation': '''Programs run by multiple users should only be changable only by administrative users. The directories containing these programs should only be changable only by administrators too. Revoke write privileges for non-administrative users from the above programs and directories.''', 541 | 'supporting_data': { 542 | 'writable_progs': { 543 | 'section': "description", 544 | 'preamble': "The programs below can be modified by non-administrative users:", 545 | }, 546 | 'writable_dirs': { 547 | 'section': "description", 548 | 'preamble': "The directories below can be changed by non-administrative users:", 549 | }, 550 | } 551 | }, 552 | 553 | 'WPC002': { 554 | 'title': "Insecure Permissions on Files and Directories in Path (OBSELETE ISSUE)", 555 | 'description': '''Some of the programs and directories in the %PATH% variable could be changed by non-administrative users. 556 | 557 | This could allow certain users on the system to place malicious code into certain key directories, or to replace programs with malicious ones. A malicious local user could use this technique to hijack the privileges of other local users, running commands with their privileges. 558 | ''', 559 | 'recommendation': '''Programs run by multiple users should only be changable only by administrative users. The directories containing these programs should only be changable only by administrators too. Revoke write privileges for non-administrative users from the above programs and directories.''', 560 | 'supporting_data': { 561 | 'writable_progs': { 562 | 'section': "description", 563 | 'preamble': "The programs below are in the path of the user used to carry out this audit. Each one can be changed by non-administrative users:", 564 | }, 565 | 'writable_dirs': { 566 | 'section': "description", 567 | 'preamble': "The directories below are in the path of the user used to carry out this audit. Each one can be changed by non-administrative users:", 568 | } 569 | } 570 | }, 571 | 572 | 'WPC003': { 573 | 'title': "Insecure Permissions In Windows Registry", 574 | 'description': '''Some registry keys that hold the names of programs run by other users were checked and found to have insecure permissions. It would be possible for non-administrative users to modify the registry to cause a different programs to be run. This weakness could be abused by low-privileged users to run commands of their choosing with higher privileges.''', 575 | 'recommendation': '''Modify the permissions on the above registry keys to allow only administrators write access. Revoke write access from low-privileged users.''', 576 | 'supporting_data': { 577 | 'writable_reg_paths': { 578 | 'section': "description", 579 | 'preamble': "The registry keys below could be changed by non-administrative users:", 580 | }, 581 | } 582 | }, 583 | 584 | 'WPC004': { 585 | 'title': "Insecure Permissions On Windows Service Executables", 586 | 'description': '''Some of the programs that are run when Windows Services start were found to have weak file permissions. It is possible for non-administrative local users to replace some of the Windows Service executables with malicious programs.''', 587 | 'recommendation': '''Modify the permissions on the above programs to allow only administrators write access. Revoke write access from low-privileged users.''', 588 | 'supporting_data': { 589 | 'writable_progs': { 590 | 'section': "description", 591 | 'preamble': "The programs below could be changed by non-administrative users:", 592 | }, 593 | } 594 | }, 595 | 596 | 'WPC005': { 597 | 'title': "Insecure Permissions On Windows Service Registry Keys (NOT IMPLEMENTED YET)", 598 | 'description': '''Some registry keys that hold the names of programs that are run when Windows Services start were found to have weak file permissions. They could be changed by non-administrative users to cause malicious programs to be run instead of the intended Windows Service Executable.''', 599 | 'recommendation': '''Modify the permissions on the above programs to allow only administrators write access. Revoke write access from low-privileged users.''', 600 | 'supporting_data': { 601 | 'writable_reg_paths': { 602 | 'section': "description", 603 | 'preamble': "The registry keys below could be changed by non-administrative users:", 604 | }, 605 | } 606 | }, 607 | 608 | 'WPC007': { 609 | 'title': "Insecure Permissions On Event Log File", 610 | 'description': '''Some of the Event Log files could be changed by non-administrative users. This may allow attackers to cover their tracks.''', 611 | 'recommendation': '''Modify the permissions on the above files to allow only administrators write access. Revoke write access from low-privileged users.''', 612 | 'supporting_data': { 613 | 'writable_eventlog_file': { 614 | 'section': "description", 615 | 'preamble': "The files below could be changed by non-administrative users:", 616 | }, 617 | } 618 | }, 619 | 620 | 'WPC008': { 621 | 'title': "Insecure Permissions On Event Log DLL", 622 | 'description': '''Some DLL files used by Event Viewer to display logs could be changed by non-administrative users. It may be possible to replace these with a view to having code run when an administrative user next views log files.''', 623 | 'recommendation': '''Modify the permissions on the above DLLs to allow only administrators write access. Revoke write access from low-privileged users.''', 624 | 'supporting_data': { 625 | 'writable_eventlog_dll': { 626 | 'section': "description", 627 | 'preamble': "The DLL files below could be changed by non-administrative users:", 628 | }, 629 | } 630 | }, 631 | 632 | 'WPC009': { 633 | 'title': "Insecure Permissions On Event Log Registry Key (NOT IMPLMENTED YET)", 634 | 'description': '''Some registry keys that hold the names of DLLs used by Event Viewer and the location of Log Files are writable by non-administrative users. It may be possible to maliciouly alter the registry to change the location of log files or run malicious code.''', 635 | 'recommendation': '''Modify the permissions on the above programs to allow only administrators write access. Revoke write access from low-privileged users.''', 636 | 'supporting_data': { 637 | 'writable_eventlog_key': { 638 | 'section': "description", 639 | 'preamble': "The registry keys below could be changed by non-administrative users:", 640 | }, 641 | } 642 | }, 643 | 644 | 'WPC010': { 645 | 'title': "Insecure Permissions On Drive Root", 646 | 'description': '''Some of the local drive roots allow non-administrative users to create files and folders. This could allow malicious files to be placed in on the server in the hope that they'll allow a local user to escalate privileges (e.g. create program.exe which might get accidentally launched by another user).''', 647 | 'recommendation': '''Modify the permissions on the drive roots to only allow administrators write access. Revoke write access from low-privileged users.''', 648 | 'supporting_data': { 649 | 'writable_drive_root': { 650 | 'section': "description", 651 | 'preamble': "The following drives allow non-administrative users to write to their root directory:", 652 | }, 653 | } 654 | }, 655 | 656 | 'WPC011': { 657 | 'title': "Insecure (Non-NTFS) File System Used", 658 | 'description': '''Some local drives use Non-NTFS file systems. These drive therefore don't allow secure file permissions to be used. Any local user can change any data on these drives.''', 659 | 'recommendation': '''Use NTFS filesystems instead of FAT. Ensure that strong file permissions are set - NTFS file permissions are insecure by default after FAT file systems are converted.''', 660 | 'supporting_data': { 661 | 'fat_fs_drives': { 662 | 'section': "description", 663 | 'preamble': "The following drives use Non-NTFS file systems:", 664 | }, 665 | } 666 | }, 667 | 668 | 'WPC012': { 669 | 'title': "Insecure Permissions On Windows Services", 670 | 'description': '''Some of the Windows Services installed have weak permissions. This could allow non-administrators to manipulate services to their own advantage. The impact depends on the permissions granted, but can include starting services, stopping service or even reconfiguring them to run a different program. This can lead to denial of service or even privilege escalation if the service is running as a user with more privilege than a malicious local user.''', 671 | 'recommendation': '''Review the permissions that have been granted to non-administrative users and revoke access where possible.''', 672 | 'supporting_data': { 673 | 'weak_service_perms': { 674 | 'section': "description", 675 | 'preamble': "Some Windows Services can be manipulated by non-administrator users:", 676 | }, 677 | } 678 | }, 679 | 'WPC013': { 680 | 'title': "Insecure Permissions On Files / Directories In System PATH", 681 | 'description': '''Some programs/directories in the system path have weak permissions. TODO which user are affected by this issue?''', 682 | 'recommendation': '''Review the permissions that have been granted to non-administrative users and revoke access where possible.''', 683 | 'supporting_data': { 684 | 'weak_perms_exe': { 685 | 'section': "description", 686 | 'preamble': "The following programs/DLLs in the system PATH can be manipulated by non-administrator users:", 687 | }, 688 | 'weak_perms_dir': { 689 | 'section': "description", 690 | 'preamble': "The following directories in the system PATH can be manipulated by non-administrator users:", 691 | }, 692 | } 693 | }, 694 | 'WPC014': { 695 | 'title': "Insecure Permissions On Files / Directories In Current User's PATH", 696 | 'description': '''Some programs/directories in the path of the user used to perform this audit have weak permissions. TODO which user was used to perform this audit?''', 697 | 'recommendation': '''Review the permissions that have been granted to non-administrative users and revoke access where possible.''', 698 | 'supporting_data': { 699 | 'weak_perms_exe': { 700 | 'section': "description", 701 | 'preamble': "The following programs/DLLs in current user's PATH can be manipulated by non-administrator users:", 702 | }, 703 | 'weak_perms_dir': { 704 | 'section': "description", 705 | 'preamble': "The following directories in the current user's PATH can be manipulated by non-administrator users:", 706 | }, 707 | } 708 | }, 709 | 710 | 'WPC015': { 711 | 'title': "Insecure Permissions On Files / Directories In Users' PATHs (NEED TO CHECK THIS WORKS)", 712 | 'description': '''Some programs/directories in the paths of users on this system have weak permissions.''', 713 | 'recommendation': '''Review the permissions that have been granted to non-administrative users and revoke access where possible.''', 714 | 'supporting_data': { 715 | 'weak_perms_exe': { 716 | 'section': "description", 717 | 'preamble': "The following programs/DLLs in users' PATHs can be manipulated by non-administrator users:", 718 | }, 719 | 'weak_perms_dir': { 720 | 'section': "description", 721 | 'preamble': "The following directories in users' PATHs can be manipulated by non-administrator users:", 722 | }, 723 | } 724 | }, 725 | 'WPC016': { 726 | 'title': "Insecure Permissions On Running Programs", 727 | 'description': '''Some programs running at the time of the audit have weak file permissions. The corresponding programs could be altered by non-administrator users.''', 728 | 'recommendation': '''Review the permissions that have been granted to non-administrative users and revoke access where possible.''', 729 | 'supporting_data': { 730 | 'weak_perms_exes': { 731 | 'section': "description", 732 | 'preamble': "The following programs were running at the time of the audit, but could be changed on-disk by non-administrator users:", 733 | }, 734 | 'weak_perms_dlls': { 735 | 'section': "description", 736 | 'preamble': "The following DLLs are used by program which were running at the time of the audit. These DLLs can be changed on-disk by non-administrator users:", 737 | }, 738 | } 739 | }, 740 | 'WPC017': { 741 | 'title': "Shares Accessible By Non-Admin Users", 742 | 'description': '''The share-level permissions on some Windows file shares allows access by non-administrative users. This can often be desirable, in which case this issue can be ignored. However, sometimes it can allow data to be stolen or programs to be malciously modified. NB: Setting strong NTFS permissions can sometimes mean that data which seems to be exposed on a share actually isn't accessible.''', 743 | 'recommendation': '''Review the share-level permissions that have been granted to non-administrative users and revoke access where possible. Share-level permissions can be viewed in Windows Explorer: Right-click folder | Sharing and Security | "Sharing" tab | "Permissions" button (for XP - other OSs may vary slightly).''', 744 | 'supporting_data': { 745 | 'non_admin_shares': { 746 | 'section': "description", 747 | 'preamble': "The following shares are accessible by non-administrative users:", 748 | }, 749 | } 750 | }, 751 | } 752 | 753 | issue_template_html = ''' 754 |

REPLACE_TITLE

755 | 756 | 757 | 758 | 761 | 765 | 766 | 767 | 768 | 771 | 775 | 776 |
759 | Description 760 | 762 | REPLACE_DESCRIPTION 763 | REPLACE_DESCRIPTION_DATA 764 |
769 | Recommendation 770 | 772 | REPLACE_RECOMMENDATION 773 | REPLACE_RECOMMENDATION_DATA 774 |
777 | ''' 778 | 779 | issue_list_html =''' 780 | REPLACE_PREAMBLE 781 |
    782 | REPLACE_ITEM 783 |
784 | ''' 785 | 786 | # TODO nice looking css, internal links, risk ratings 787 | # TODO record group members for audit user, separate date and time; os and sp 788 | overview_template_html = ''' 789 | 790 | 791 | 831 | 832 |
833 |

windows-privesc-check

Audit of Host:

REPLACE_HOSTNAME

834 |
835 | 836 |

Contents

837 | REPLACE_CONTENTS 838 |

Information about this Audit

839 |

This report was generated on REPLACE_DATETIME by vREPLACE_VERSION of windows-privesc-check.

840 | 841 |

The audit was run as the user REPLACE_AUDIT_USER.

842 | 843 |

The following table provides information about this audit:

844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 |
HostnameREPLACE_HOSTNAME
Domain/WorkgroupREPLACE_DOMWKG
Operating SystemREPLACE_OS
IP Addresses
    REPLACE_IPS
867 |

Escalation Vectors

868 | REPLACE_ISSUES 869 | 870 |

Scan Parameters

871 | For the purposes of the audit the following users were considered to be trusted. Any privileges assigned to them have not been considered as potential attack vectors: 872 |
    873 | REPLACE_TRUSTED_USERS 874 |
875 | 876 | Additionally members of the following groups were considered trusted: 877 |
    878 | REPLACE_TRUSTED_GROUPS 879 |
880 | 881 | The following file/directory/registry permissions were considered to be potentially dangerous. This audit exclusively searched for instances of these permissions: 882 |
    883 | REPLACE_DANGEROUS_PERMS 884 |
885 | 886 | ''' 887 | 888 | def handle_unicode(u): 889 | try: 890 | return u.encode('ascii','replace') 891 | except: 892 | return u.decode("ascii",'ignore') 893 | 894 | def usage(): 895 | print "Usage: windows-privesc-check [options] checks" 896 | print "" 897 | print "checks must be at least one of:" 898 | print " -a|--all_checks Run all security checks (see below)" 899 | print " -r|--registry_checks Check RunOnce and other critical keys" 900 | print " -t|--path_checks Check %PATH% for insecure permissions" 901 | print " -S|--service_checks Check Windows services for insecure permissions" 902 | print " -d|--drive_checks Check for FAT filesystems and weak perms in root dir" 903 | print " -E|--eventlog_checks Check Event Logs for insecure permissions" 904 | print " -F|--progfiles_checks Check Program Files directories for insecure perms" 905 | print " -R|--process_checks Check Running Processes for insecure permissions" 906 | print " -H|--share_checks Check shares for insecure permissions" 907 | #print " -T|--patch_checks Check some important patches" 908 | print " -U|--user_groups Dump users, groups and privileges (no HTML yet)" 909 | print " -A|--admin_users Dump admin users / high priv users (no HTML yet)" 910 | print " -O|--processes Dump process info (no HTML yet)" 911 | print " -P|--passpol Dump password policy (no HTML yet)" 912 | print " -i|--host_info Dump host info - OS, domain controller, ... (no HTML yet)" 913 | print " -e|--services Dump service info (no HTML yet)" 914 | # TODO options to flag a user/group as trusted 915 | print "" 916 | print "options are:" 917 | print " -h|--help This help message" 918 | print " -w|--write_perms_only Only list write perms (dump opts only)" 919 | print " -I|--ignore_trusted Ignore trusted users, empty groups (dump opts only)" 920 | print " -W|--owner_info Owner, Group info (dump opts only)" 921 | print " -v|--verbose More detail output (use with -U)" 922 | print " -o|--report_file file Report filename. Default privesc-report-[host].html" 923 | print " -s|--server host Remote server name. Only works with -u!" 924 | print " -u|--username arg Remote username. Only works with -u!" 925 | print " -p|--password arg Remote password. Only works with -u!" 926 | print " -d|--domain arg Remote domain. Only works with -u!" 927 | print "" 928 | sys.exit(0) 929 | # 930 | # Reporting functions 931 | # 932 | 933 | def format_issues(format, issue_template, issue_data): 934 | report = "" 935 | toc = "" 936 | overview = overview_template_html 937 | overview = overview.replace('REPLACE_HOSTNAME', audit_data['hostname']) 938 | overview = overview.replace('REPLACE_DOMWKG', audit_data['domwkg']) 939 | # overview = overview.replace('REPLACE_IPS', "
  • " + "
  • ".join(audit_data['ips']) + "
  • ") 940 | for item in audit_data['ips']: 941 | overview = overview.replace('REPLACE_IPS', list_item("REPLACE_IPS", item)) 942 | overview = overview.replace('REPLACE_IPS', '') 943 | 944 | overview = overview.replace('REPLACE_OS', audit_data['os_name'] + " (" + audit_data['os_version'] + ")") 945 | 946 | overview = overview.replace('REPLACE_VERSION', audit_data['version']) 947 | overview = overview.replace('REPLACE_DATETIME', audit_data['datetime']) 948 | overview = overview.replace('REPLACE_AUDIT_USER', audit_data['audit_user']) 949 | 950 | for item in audit_data['trusted_users']: 951 | overview = overview.replace('REPLACE_TRUSTED_USERS', list_item("REPLACE_TRUSTED_USERS", item)) 952 | overview = overview.replace('REPLACE_TRUSTED_USERS', '') 953 | 954 | for item in audit_data['trusted_groups']: 955 | overview = overview.replace('REPLACE_TRUSTED_GROUPS', list_item("REPLACE_TRUSTED_GROUPS", item)) 956 | overview = overview.replace('REPLACE_TRUSTED_GROUPS', '') 957 | 958 | permlist = '' 959 | for permtype in dangerous_perms_write.keys(): 960 | permlist += "Permission type '" + permtype + "'

    " 961 | permlist += "

      " 962 | for location in dangerous_perms_write[permtype].keys(): 963 | for item in dangerous_perms_write[permtype][location]: 964 | permlist += "\t
    • " + item + "
    • " 965 | permlist += "
    " 966 | 967 | #for item in audit_data['dangerous_privs']: 968 | # overview = overview.replace('REPLACE_DANGEROUS_PERM', list_item("REPLACE_DANGEROUS_PERM", item)) 969 | #overview = overview.replace('REPLACE_DANGEROUS_PERM', '') 970 | overview = overview.replace('REPLACE_DANGEROUS_PERMS', permlist) 971 | 972 | for item in audit_data['ips']: 973 | overview = overview.replace('REPLACE_IP', list_item("REPLACE_IPS", item)) 974 | overview = overview.replace('REPLACE_IP', '') 975 | 976 | for issue_no in issue_data: 977 | # print "[V] Processing issue issue_no\n" 978 | report = report + format_issue(format, issue_no, issue_data, issue_template) 979 | toc = toc + '' + issue_template[issue_no]['title'] + "

    " 980 | 981 | if report: 982 | overview = overview.replace('REPLACE_ISSUES', report) 983 | overview = overview.replace('REPLACE_CONTENTS', toc) 984 | else: 985 | overview = overview.replace('REPLACE_ISSUES', "No issues found") 986 | overview = overview.replace('REPLACE_CONTENTS', "No issues found") 987 | 988 | return overview 989 | 990 | def list_item(tag, item): 991 | return "

  • " + item + "
  • \n" + tag 992 | 993 | def format_issue(format, issue_no, issue_data, issue_template): # $format is xml, html, or text 994 | if not issue_no in issue_template: 995 | print "[E] Can't find an issue template for issue number issue_no. Bug!" 996 | sys.exit(1) 997 | 998 | issue = issue_template_html 999 | issue = issue.replace('REPLACE_TITLE', '' + issue_template[issue_no]['title'] + '') 1000 | description = issue_template[issue_no]['description'] 1001 | description = description.replace('\n\n+', "

    \n") 1002 | for key in issue_data[issue_no]: 1003 | #print "[D] Processing data for %s" % key 1004 | 1005 | # print "[D] $key has type issue_data[issue_no]['$key']['type']\n" 1006 | #if issue_data[issue_no][key]['type'] == "list": 1007 | # TODO alter data structre to include type 1008 | #section = issue_template[issue_no]['supporting_data'][key]['section'] 1009 | # print "[D] Data belongs to section section\n" 1010 | #if (section == "description"): 1011 | preamble = issue_template[issue_no]['supporting_data'][key]['preamble'] 1012 | data = issue_list_html 1013 | data = data.replace('REPLACE_PREAMBLE', preamble) 1014 | for item in issue_data[issue_no][key]: 1015 | # TODO alter data structure to include data 1016 | # print "Processing item " + item 1017 | perm_string = " ".join(issue_data[issue_no][key][item]) 1018 | data = data.replace('REPLACE_ITEM', list_item("REPLACE_ITEM", item + ": " + perm_string)) 1019 | 1020 | data = data.replace('REPLACE_ITEM', '') 1021 | issue = issue.replace('REPLACE_DESCRIPTION_DATA', data + "\nREPLACE_DESCRIPTION_DATA") 1022 | #elif section == "recommendation": 1023 | # pass 1024 | #issue = issue.replace('REPLACE_RECOMMENDATION_DATA', "data\nREPLACE_DESCRIPTION_DATA', 1025 | 1026 | issue = issue.replace('REPLACE_RECOMMENDATION_DATA', '') 1027 | issue = issue.replace('REPLACE_DESCRIPTION_DATA', '') 1028 | issue = issue.replace('REPLACE_DESCRIPTION', description + "

    \n") 1029 | recommendation = issue_template[issue_no]['recommendation'] 1030 | issue = issue.replace('REPLACE_RECOMMENDATION', recommendation + "

    \n") 1031 | recommendation = recommendation.replace('\n\n+', '

    \n') 1032 | return issue 1033 | 1034 | def format_audit_data(format, audit_data): # $format is xml, html, or text 1035 | print "format_audit_data not implemented yet" 1036 | 1037 | # Inputs: 1038 | # string: issue_name 1039 | # array: weak_perms 1040 | def save_issue(issue_name, data_type, weak_perms): 1041 | #print weak_perms 1042 | global issues 1043 | if not issue_name in issues: 1044 | issues[issue_name] = {} 1045 | #if not 'supporting_data' in issues[issue_name]: 1046 | # issues[issue_name]['supporting_data'] = {} 1047 | for weak_perm in weak_perms: 1048 | object = weak_perm[0] 1049 | domain = weak_perm[1] 1050 | name = weak_perm[2] 1051 | permission = weak_perm[3] 1052 | #print repr((object,domain,name)) 1053 | key = u"%s has the following permissions granted for %s\\%s" % (handle_unicode(object),handle_unicode(domain),handle_unicode(name)) 1054 | #handle_unicode(object) + u" has the following permissions granted for " + handle_unicode(domain) + u"\\" + handle_unicode(name) 1055 | if not data_type in issues[issue_name]: 1056 | issues[issue_name][data_type]= {} 1057 | if not key in issues[issue_name][data_type]: 1058 | issues[issue_name][data_type][key] = [] 1059 | issues[issue_name][data_type][key].append(permission) 1060 | issues[issue_name][data_type][key] = list(set(issues[issue_name][data_type][key])) # dedup 1061 | 1062 | def save_issue_string(issue_name, data_type, issue_string): 1063 | #print weak_perms 1064 | global issues 1065 | if not issue_name in issues: 1066 | issues[issue_name] = {} 1067 | if not data_type in issues[issue_name]: 1068 | issues[issue_name][data_type]= {} 1069 | if not issue_string in issues[issue_name][data_type]: 1070 | issues[issue_name][data_type][issue_string] = [] 1071 | 1072 | # args: string, string 1073 | # Returns 1 if the principle provided is trusted (admin / system / user-definted trusted principle) 1074 | # Returns 0 otherwise 1075 | def principle_is_trusted(principle, domain): 1076 | #print "is_trusted principle: "+repr(principle) 1077 | #print "is_trusted: "+repr(trusted_principles_fq) 1078 | if domain + "\\" + principle in trusted_principles_fq: 1079 | return 1 1080 | 1081 | if principle in trusted_principles: 1082 | return 1 1083 | 1084 | global tmp_trusted_principles_fq 1085 | if domain + "\\" + principle in tmp_trusted_principles_fq: 1086 | return 1 1087 | 1088 | # Consider groups with zero members to be trusted too 1089 | try: 1090 | memberdict, total, rh = win32net.NetLocalGroupGetMembers(remote_server, principle , 1 , 0 , 100000 ) 1091 | if len(memberdict) == 0: 1092 | return 1 1093 | except: 1094 | # If a user is a member of a trusted group (like administrators), then they are trusted 1095 | try: 1096 | group_attrs = win32net.NetUserGetLocalGroups(remote_server, principle) 1097 | if set(group_attrs).intersection(set(trusted_principles)): 1098 | return 1 1099 | except: 1100 | pass 1101 | 1102 | return 0 1103 | 1104 | # for memberinfo in memberdict: 1105 | # print "\t" + memberinfo['name'] + " (" + win32security.ConvertSidToStringSid(memberinfo['sid']) + ")" 1106 | # TODO ignore groups that only contain administrators 1107 | 1108 | # There are all possible objects. SE_OBJECT_TYPE (http://msdn.microsoft.com/en-us/library/aa379593(VS.85).aspx): 1109 | # win32security.SE_UNKNOWN_OBJECT_TYPE 1110 | # win32security.SE_FILE_OBJECT 1111 | # win32security.SE_SERVICE 1112 | # win32security.SE_PRINTER 1113 | # win32security.SE_REGISTRY_KEY 1114 | # win32security.SE_LMSHARE 1115 | # win32security.SE_KERNEL_OBJECT 1116 | # win32security.SE_WINDOW_OBJECT 1117 | # win32security.SE_DS_OBJECT 1118 | # win32security.SE_DS_OBJECT_ALL 1119 | # win32security.SE_PROVIDER_DEFINED_OBJECT 1120 | # win32security.SE_WMIGUID_OBJECT 1121 | # win32security.SE_REGISTRY_WOW64_32KEY 1122 | # object_type_s is one of 1123 | # service 1124 | # file 1125 | # dir 1126 | def check_weak_perms(object_name, object_type_s, perms): 1127 | object_type = None 1128 | if object_type_s == 'file': 1129 | object_type = win32security.SE_FILE_OBJECT 1130 | if object_type_s == 'directory': 1131 | object_type = win32security.SE_FILE_OBJECT 1132 | if object_type_s == 'service': 1133 | object_type = win32security.SE_SERVICE 1134 | 1135 | if object_type == win32security.SE_FILE_OBJECT: 1136 | # if not os.path.exists(object_name): 1137 | # print "WARNING: %s doesn't exist" % object_name 1138 | 1139 | if os.path.isfile(object_name): 1140 | object_type_s = 'file' 1141 | else: 1142 | object_type_s = 'directory' 1143 | 1144 | if object_type == None: 1145 | print "ERROR: Unknown object type %s" % object_type_s 1146 | exit(1) 1147 | 1148 | try: 1149 | sd = win32security.GetNamedSecurityInfo ( 1150 | object_name, 1151 | object_type, 1152 | win32security.OWNER_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION 1153 | ) 1154 | except: 1155 | # print "WARNING: Can't get security descriptor for " + object_name + ". skipping. (" + details[2] + ")" 1156 | return [] 1157 | 1158 | return check_weak_perms_sd(object_name, object_type_s, sd, perms) 1159 | 1160 | def check_weak_write_perms_by_sd(object_name, object_type_s, sd): 1161 | return check_weak_perms_sd(object_name, object_type_s, sd, dangerous_perms_write) 1162 | 1163 | def check_weak_perms_sd(object_name, object_type_s, sd, perms): 1164 | dacl= sd.GetSecurityDescriptorDacl() 1165 | if dacl == None: 1166 | print "No Discretionary ACL" 1167 | return [] 1168 | 1169 | owner_sid = sd.GetSecurityDescriptorOwner() 1170 | try: 1171 | owner_name, owner_domain, type = win32security.LookupAccountSid(remote_server, owner_sid) 1172 | owner_fq = owner_domain + "\\" + owner_name 1173 | except: 1174 | try: 1175 | owner_fq = owner_name = win32security.ConvertSidToStringSid(owner_sid) 1176 | owner_domain = "" 1177 | except: 1178 | owner_domain = "" 1179 | owner_fq = owner_name = "INVALIDSID!" 1180 | 1181 | weak_perms = [] 1182 | for ace_no in range(0, dacl.GetAceCount()): 1183 | #print "[D] ACE #%d" % ace_no 1184 | ace = dacl.GetAce(ace_no) 1185 | flags = ace[0][1] 1186 | 1187 | try: 1188 | principle, domain, type = win32security.LookupAccountSid(remote_server, ace[2]) 1189 | except: 1190 | principle = win32security.ConvertSidToStringSid(ace[2]) 1191 | domain = "" 1192 | 1193 | #print "[D] ACE is for %s\\%s" % (principle, domain) 1194 | #print "[D] ACE Perm mask: " + int2bin(ace[1]) 1195 | #print "[D] ace_type: " + str(ace[0][0]) 1196 | #print "[D] DACL: " + win32security.ConvertSecurityDescriptorToStringSecurityDescriptor(sd, win32security.SDDL_REVISION_1, win32security.DACL_SECURITY_INFORMATION) 1197 | if principle_is_trusted(principle, domain): 1198 | #print "[D] Ignoring trusted principle %s\\%s" % (principle, domain) 1199 | continue 1200 | 1201 | if principle == "CREATOR OWNER": 1202 | if principle_is_trusted(owner_name, owner_domain): 1203 | continue 1204 | else: 1205 | principle = "CREATOR OWNER [%s]" % owner_fq 1206 | 1207 | for i in ("ACCESS_ALLOWED_ACE_TYPE", "ACCESS_DENIED_ACE_TYPE", "SYSTEM_AUDIT_ACE_TYPE", "SYSTEM_ALARM_ACE_TYPE"): 1208 | if getattr(ntsecuritycon, i) == ace[0][0]: 1209 | ace_type_s = i 1210 | 1211 | if not ace_type_s == "ACCESS_ALLOWED_ACE_TYPE": 1212 | vprint("WARNING: Unimplmented ACE type encountered: " + ace_type_s + ". skipping.") 1213 | continue 1214 | 1215 | for mod, perms_tuple in perms[object_type_s].iteritems(): 1216 | for perm in perms_tuple: 1217 | if getattr(mod, perm) & ace[1] == getattr(mod, perm): 1218 | weak_perms.append([object_name, domain, principle, perm]) 1219 | return weak_perms 1220 | 1221 | def dump_perms(object_name, object_type_s, options={}): 1222 | object_type = None 1223 | if object_type_s == 'file': 1224 | object_type = win32security.SE_FILE_OBJECT 1225 | if object_type_s == 'directory': 1226 | object_type = win32security.SE_FILE_OBJECT 1227 | if object_type_s == 'service': 1228 | object_type = win32security.SE_SERVICE 1229 | 1230 | if object_type == win32security.SE_FILE_OBJECT: 1231 | # if not os.path.exists(object_name): 1232 | # print "WARNING: %s doesn't exist" % object_name 1233 | 1234 | if os.path.isfile(object_name): 1235 | object_type_s = 'file' 1236 | else: 1237 | object_type_s = 'directory' 1238 | 1239 | if object_type == None: 1240 | print "ERROR: Unknown object type %s" % object_type_s 1241 | exit(1) 1242 | 1243 | try: 1244 | sd = win32security.GetNamedSecurityInfo ( 1245 | object_name, 1246 | object_type, 1247 | win32security.OWNER_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION 1248 | ) 1249 | except: 1250 | # print "WARNING: Can't get security descriptor for " + object_name + ". skipping. (" + details[2] + ")" 1251 | return [] 1252 | 1253 | return dump_sd(object_name, object_type_s, sd, options) 1254 | 1255 | def dump_sd(object_name, object_type_s, sd, options={}): 1256 | perms = all_perms 1257 | if not sd: 1258 | return 1259 | dacl = sd.GetSecurityDescriptorDacl() 1260 | if dacl == None: 1261 | print "No Discretionary ACL" 1262 | return [] 1263 | 1264 | owner_sid = sd.GetSecurityDescriptorOwner() 1265 | 1266 | try: 1267 | owner_name, owner_domain, type = win32security.LookupAccountSid(remote_server, owner_sid) 1268 | owner_fq = owner_domain + "\\" + owner_name 1269 | except: 1270 | try: 1271 | owner_fq = owner_name = win32security.ConvertSidToStringSid(owner_sid) 1272 | owner_domain = "" 1273 | except: 1274 | owner_domain = "" 1275 | owner_fq = owner_name = None 1276 | 1277 | group_sid = sd.GetSecurityDescriptorGroup() 1278 | try: 1279 | group_name, group_domain, type = win32security.LookupAccountSid(remote_server, group_sid) 1280 | group_fq = group_domain + "\\" + group_name 1281 | except: 1282 | try: 1283 | group_fq = group_name = win32security.ConvertSidToStringSid(group_sid) 1284 | group_domain = "" 1285 | except: 1286 | group_domain = "" 1287 | group_fq = group_name = "[none]" 1288 | 1289 | if owner_info: 1290 | print "\tOwner: " + str(owner_fq) 1291 | print "\tGroup: " + str(group_fq) 1292 | 1293 | weak_perms = [] 1294 | dump_acl(object_name, object_type_s, dacl, options) 1295 | return 1296 | 1297 | def dump_acl(object_name, object_type_s, sd, options={}): 1298 | dacl = sd 1299 | if dacl == None: 1300 | print "No Discretionary ACL" 1301 | return [] 1302 | 1303 | weak_perms = [] 1304 | for ace_no in range(0, dacl.GetAceCount()): 1305 | # print "[D] ACE #%d" % ace_no 1306 | ace = dacl.GetAce(ace_no) 1307 | flags = ace[0][1] 1308 | 1309 | try: 1310 | principle, domain, type = win32security.LookupAccountSid(remote_server, ace[2]) 1311 | except: 1312 | principle = win32security.ConvertSidToStringSid(ace[2]) 1313 | domain = "" 1314 | 1315 | mask = ace[1] 1316 | if ace[1] < 0: 1317 | mask = ace[1] + 2**32 1318 | 1319 | if ignore_trusted and principle_is_trusted(principle, domain): 1320 | # print "[D] Ignoring trusted principle %s\\%s" % (principle, domain) 1321 | continue 1322 | 1323 | if principle == "CREATOR OWNER": 1324 | if ignore_trusted and principle_is_trusted(owner_name, owner_domain): 1325 | #print "[D] Ignoring trusted principle (creator owner) %s\\%s" % (principle, domain) 1326 | continue 1327 | else: 1328 | principle = "CREATOR OWNER [%s\%s]" % (domain, principle) 1329 | 1330 | for i in ("ACCESS_ALLOWED_ACE_TYPE", "ACCESS_DENIED_ACE_TYPE", "SYSTEM_AUDIT_ACE_TYPE", "SYSTEM_ALARM_ACE_TYPE"): 1331 | if getattr(ntsecuritycon, i) == ace[0][0]: 1332 | ace_type_s = i 1333 | 1334 | ace_type_short = ace_type_s 1335 | 1336 | if ace_type_s == "ACCESS_DENIED_ACE_TYPE": 1337 | ace_type_short = "DENY" 1338 | 1339 | if ace_type_s == "ACCESS_ALLOWED_ACE_TYPE": 1340 | ace_type_short = "ALLOW" 1341 | 1342 | if weak_perms_only: 1343 | perms = dangerous_perms_write 1344 | else: 1345 | perms = all_perms 1346 | 1347 | for mod, perms_tuple in perms[object_type_s].iteritems(): 1348 | for perm in perms_tuple: 1349 | #print "Checking for perm %s in ACE %s" % (perm, mask) 1350 | if getattr(mod, perm) & mask == getattr(mod, perm): 1351 | weak_perms.append([object_name, domain, principle, perm, ace_type_short]) 1352 | print_weak_perms(object_type_s, weak_perms, options) 1353 | 1354 | def check_weak_write_perms(object_name, object_type_s): 1355 | return check_weak_perms(object_name, object_type_s, dangerous_perms_write) 1356 | 1357 | def check_registry(): 1358 | for key_string in reg_paths: 1359 | parts = key_string.split("\\") 1360 | hive = parts[0] 1361 | key_string = "\\".join(parts[1:]) 1362 | try: 1363 | keyh = win32api.RegOpenKeyEx(getattr(win32con, hive), key_string, 0, win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE | win32con.KEY_READ) 1364 | except: 1365 | #print "Can't open: " + hive + "\\" + key_string 1366 | continue 1367 | 1368 | sd = win32api.RegGetKeySecurity(keyh, win32security.DACL_SECURITY_INFORMATION | win32security.OWNER_SECURITY_INFORMATION) 1369 | weak_perms = check_weak_write_perms_by_sd(hive + "\\" + key_string, 'reg', sd) 1370 | if weak_perms: 1371 | vprint(hive + "\\" + key_string) 1372 | #print weak_perms 1373 | if verbose == 0: 1374 | sys.stdout.write(".") 1375 | save_issue("WPC003", "writable_reg_paths", weak_perms) 1376 | # print_weak_perms("x", weak_perms) 1377 | print 1378 | 1379 | # TODO save_issue("WPC009", "writable_eventlog_key", weak_perms) # weak perms on event log reg key 1380 | def check_event_logs(): 1381 | key_string = "HKEY_LOCAL_MACHINE\\" + eventlog_key_hklm 1382 | try: 1383 | keyh = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, eventlog_key_hklm , 0, win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE | win32con.KEY_READ) 1384 | except: 1385 | print "Can't open: " + key_string 1386 | return 0 1387 | 1388 | subkeys = win32api.RegEnumKeyEx(keyh) 1389 | for subkey in subkeys: 1390 | # print key_string + "\\" + subkey[0] 1391 | sys.stdout.write(".") 1392 | try: 1393 | subkeyh = win32api.RegOpenKeyEx(keyh, subkey[0] , 0, win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE | win32con.KEY_READ) 1394 | except: 1395 | print "Can't open: " + key_string 1396 | else: 1397 | subkey_count, value_count, mod_time = win32api.RegQueryInfoKey(subkeyh) 1398 | # print "\tChild Nodes: %s subkeys, %s values" % (subkey_count, value_count) 1399 | 1400 | try: 1401 | filename, type = win32api.RegQueryValueEx(subkeyh, "DisplayNameFile") 1402 | except: 1403 | pass 1404 | else: 1405 | weak_perms = check_weak_write_perms(os.path.expandvars(filename), 'file') 1406 | if weak_perms: 1407 | # print "------------------------------------------------" 1408 | # print "Weak permissions found on event log display DLL:" 1409 | # print_weak_perms("File", weak_perms) 1410 | sys.stdout.write("!") 1411 | save_issue("WPC008", "writable_eventlog_dll", weak_perms) 1412 | 1413 | try: 1414 | filename, type = win32api.RegQueryValueEx(subkeyh, "File") 1415 | except: 1416 | pass 1417 | else: 1418 | weak_perms = check_weak_write_perms(os.path.expandvars(filename), 'file') 1419 | if weak_perms: 1420 | # print "------------------------------------------------" 1421 | # print "Weak permissions found on event log file:" 1422 | # print_weak_perms("File", weak_perms) 1423 | sys.stdout.write("!") 1424 | save_issue("WPC007", "writable_eventlog_file", weak_perms) 1425 | print 1426 | #sd = win32api.RegGetKeySecurity(subkeyh, win32security.DACL_SECURITY_INFORMATION) # TODO: get owner too? 1427 | #print "\tDACL: " + win32security.ConvertSecurityDescriptorToStringSecurityDescriptor(sd, win32security.SDDL_REVISION_1, win32security.DACL_SECURITY_INFORMATION) 1428 | 1429 | def get_extra_privs(): 1430 | # Try to give ourselves some extra privs (only works if we're admin): 1431 | # SeBackupPrivilege - so we can read anything 1432 | # SeDebugPrivilege - so we can find out about other processes (otherwise OpenProcess will fail for some) 1433 | # SeSecurityPrivilege - ??? what does this do? 1434 | 1435 | # Problem: Vista+ support "Protected" processes, e.g. audiodg.exe. We can't see info about these. 1436 | # Interesting post on why Protected Process aren't really secure anyway: http://www.alex-ionescu.com/?p=34 1437 | 1438 | th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32con.TOKEN_ADJUST_PRIVILEGES | win32con.TOKEN_QUERY) 1439 | privs = win32security.GetTokenInformation(th, TokenPrivileges) 1440 | newprivs = [] 1441 | for privtuple in privs: 1442 | if privtuple[0] == win32security.LookupPrivilegeValue(remote_server, "SeBackupPrivilege") or privtuple[0] == win32security.LookupPrivilegeValue(remote_server, "SeDebugPrivilege") or privtuple[0] == win32security.LookupPrivilegeValue(remote_server, "SeSecurityPrivilege"): 1443 | print "Added privilege " + str(privtuple[0]) 1444 | # privtuple[1] = 2 # tuples are immutable. WHY?! 1445 | newprivs.append((privtuple[0], 2)) # SE_PRIVILEGE_ENABLED 1446 | else: 1447 | newprivs.append((privtuple[0], privtuple[1])) 1448 | 1449 | # Adjust privs 1450 | privs = tuple(newprivs) 1451 | str(win32security.AdjustTokenPrivileges(th, False , privs)) 1452 | 1453 | def audit_processes(): 1454 | get_extra_privs() 1455 | # Things we might want to know about a process: 1456 | # TCP/UDP/Local sockets 1457 | # Treads - and the tokens of each (API doesn't support getting a thread handle!) 1458 | # Shared memory 1459 | 1460 | pids = win32process.EnumProcesses() 1461 | for pid in sorted(pids): 1462 | print "---------------------------------------------------------" 1463 | print "PID: %s" % pid 1464 | # TODO there's a security descriptor for each process accessible via GetSecurityInfo according to http://msdn.microsoft.com/en-us/library/ms684880%28VS.85%29.aspx 1465 | 1466 | ph = 0 1467 | gotph = 0 1468 | try: 1469 | # PROCESS_VM_READ is required to list modules (DLLs, EXE) 1470 | ph = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, pid) 1471 | gotph = 1 1472 | vprint("OpenProcess with VM_READ and PROCESS_QUERY_INFORMATION: Success") 1473 | except: 1474 | print("OpenProcess with VM_READ and PROCESS_QUERY_INFORMATION: Failed") 1475 | try: 1476 | # We can still get some info without PROCESS_VM_READ 1477 | ph = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION , False, pid) 1478 | gotph = 1 1479 | vprint("OpenProcess with PROCESS_QUERY_INFORMATION: Success") 1480 | except: 1481 | print "OpenProcess with PROCESS_QUERY_INFORMATION: Failed" 1482 | try: 1483 | # If we have to resort to using PROCESS_QUERY_LIMITED_INFORMATION, the process is protected. 1484 | # There's no point trying PROCESS_VM_READ 1485 | ph = win32api.OpenProcess(win32con.PROCESS_QUERY_LIMITED_INFORMATION , False, pid) 1486 | gotph = 1 1487 | vprint("OpenProcess with PROCESS_QUERY_LIMITED_INFORMATION: Success") 1488 | except: 1489 | print "OpenProcess with PROCESS_QUERY_LIMITED_INFORMATION: Failed" 1490 | # Move onto the next process. We don't have a process handle! 1491 | 1492 | exe = "[unknown]" 1493 | gotexe = 0 1494 | mhs = 0 1495 | try: 1496 | mhs = win32process.EnumProcessModules(ph) 1497 | mhs = list(mhs) 1498 | exe = win32process.GetModuleFileNameEx(ph, mhs.pop(0)) 1499 | gotexe = 1 1500 | except: 1501 | pass 1502 | print "Filename: %s" % exe 1503 | 1504 | gottokenh = 0 1505 | 1506 | try: 1507 | tokenh = win32security.OpenProcessToken(ph, win32con.TOKEN_QUERY) 1508 | gottokenh = 1 1509 | 1510 | sidObj, intVal = win32security.GetTokenInformation(tokenh, TokenUser) 1511 | if sidObj: 1512 | accountName, domainName, accountTypeInt = win32security.LookupAccountSid(remote_server, sidObj) 1513 | print "TokenUser: %s\%s (type %s)" % (domainName, accountName, accountTypeInt) 1514 | 1515 | sidObj = win32security.GetTokenInformation(tokenh, TokenOwner) 1516 | if sidObj: 1517 | accountName, domainName, accountTypeInt = win32security.LookupAccountSid(remote_server, sidObj) 1518 | print "TokenOwner: %s\%s (type %s)" % (domainName, accountName, accountTypeInt) 1519 | 1520 | sidObj = win32security.GetTokenInformation(tokenh, TokenPrimaryGroup) 1521 | if sidObj: 1522 | accountName, domainName, accountTypeInt = win32security.LookupAccountSid(remote_server, sidObj) 1523 | print "TokenPrimaryGroup: %s\%s (type %s)" % (domainName, accountName, accountTypeInt) 1524 | except: 1525 | print "OpenProcessToken with TOKEN_QUERY: Failed" 1526 | print "TokenUser: Unknown" 1527 | print "TokenOwner: Unknown" 1528 | print "TokenPrimaryGroup: Unknown" 1529 | pass 1530 | 1531 | user = "unknown\\unknown" 1532 | 1533 | # TODO I'm not sure how to interogate threads. 1534 | # There's no OpenThread() in win32api. I need a thread handle before I can get Thread Tokens. 1535 | # The code below lists threadid's, be we can't use the handle (it's not a PyHandle) 1536 | # 1537 | # hThreadSnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, pid) 1538 | # if hThreadSnap == INVALID_HANDLE_VALUE: 1539 | # print "Failed to get Thread snapshot" 1540 | # else: 1541 | # te32 = Thread32First (hThreadSnap) 1542 | # if te32: 1543 | # while True: 1544 | # if te32.th32OwnerProcessID == pid: 1545 | # hThread = OpenThread (win32con.THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID) 1546 | # print "PID %s, ThreadID %s" % (pid, te32.th32ThreadID) 1547 | # print "Priority: " + str(win32process.GetThreadPriority(hThread)) 1548 | # CloseHandle (hThread) 1549 | # te32 = Thread32Next (hThreadSnap) 1550 | # if not te32: 1551 | # break 1552 | # CloseHandle (hThreadSnap) 1553 | 1554 | # except: 1555 | # print "EnumProcessModules: Failed" 1556 | # continue 1557 | # print "EnumProcessModules: Success" 1558 | 1559 | if ph: 1560 | print "IsWow64 Process: %s" % win32process.IsWow64Process(ph) 1561 | 1562 | if gottokenh: 1563 | vprint("OpenProcessToken with TOKEN_QUERY: Success") 1564 | imp_levels = { 1565 | "SecurityAnonymous": 0, 1566 | "SecurityIdentification": 1, 1567 | "SecurityImpersonation": 2, 1568 | "SecurityDelegation": 3 1569 | } 1570 | #for ilevel in imp_levels.keys(): 1571 | #sys.stdout.write("Trying DuplicateToken with " + ilevel) 1572 | #try: 1573 | #win32security.DuplicateToken(tokenh, imp_levels[ilevel]) 1574 | #print "success" 1575 | #except: 1576 | #print "failed" 1577 | tokentype = win32security.GetTokenInformation(tokenh, TokenType) 1578 | tokentype_str = "TokenImpersonation" 1579 | if tokentype == 1: 1580 | tokentype_str = "TokenPrimary" 1581 | print "Token Type: " + tokentype_str 1582 | print "Logon Session ID: " + str(win32security.GetTokenInformation(tokenh, TokenOrigin)) 1583 | try: 1584 | source = win32security.GetTokenInformation(tokenh, TokenSource) 1585 | print "Token Source: " + source 1586 | except: 1587 | print "Token Source: Unknown (Access Denied)" 1588 | 1589 | try: 1590 | print "TokenImpersonationLevel: %s" % win32security.GetTokenInformation(tokenh, TokenImpersonationLevel) # doesn't work on xp 1591 | except: 1592 | pass 1593 | 1594 | try: 1595 | r = win32security.GetTokenInformation(tokenh, TokenHasRestrictions) # doesn't work on xp 1596 | if r == 0: 1597 | print "TokenHasRestrictions: 0 (not filtered)" 1598 | else: 1599 | print "TokenHasRestrictions: %s (token has been filtered)" % r 1600 | except: 1601 | pass 1602 | 1603 | try: 1604 | e = win32security.GetTokenInformation(tokenh, TokenElevationType) # vista 1605 | if e == 1: 1606 | print "TokenElevationType: TokenElevationTypeDefault" 1607 | elif e == 2: 1608 | print "TokenElevationType: TokenElevationTypeFull" 1609 | elif e == 3: 1610 | print "TokenElevationType: TokenElevationTypeLimited" 1611 | else: 1612 | print "TokenElevationType: Unknown (%s)" % e 1613 | except: 1614 | pass 1615 | 1616 | try: 1617 | print "TokenUIAccess: %s" % win32security.GetTokenInformation(tokenh, TokenUIAccess) # doesn't work on xp 1618 | except: 1619 | pass 1620 | 1621 | try: 1622 | print "TokenLinkedToken: %s" % win32security.GetTokenInformation(tokenh, TokenLinkedToken) # vista 1623 | except: 1624 | pass 1625 | 1626 | try: 1627 | print "TokenLogonSid: %s" % win32security.GetTokenInformation(tokenh, TokenLogonSid) # doesn't work on xp 1628 | print "TokenElevation: %s" % win32security.GetTokenInformation(tokenh, TokenElevation) # vista 1629 | except: 1630 | pass 1631 | 1632 | try: 1633 | sid, i = win32security.GetTokenInformation(tokenh, TokenIntegrityLevel) # vista 1634 | try: 1635 | accountName, domainName, accountTypeInt = win32security.LookupAccountSid(None, sid) 1636 | user = domainName + "\\" + accountName + " (" + win32security.ConvertSidToStringSid(sid) + ")" 1637 | except: 1638 | user = win32security.ConvertSidToStringSid(sid) 1639 | print "TokenIntegrityLevel: %s %s" % (user, i) 1640 | except: 1641 | pass 1642 | 1643 | try: 1644 | m = win32security.GetTokenInformation(tokenh, TokenMandatoryPolicy) # vista 1645 | if m == 0: 1646 | print "TokenMandatoryPolicy: OFF" 1647 | elif m == 1: 1648 | print "TokenMandatoryPolicy: NO_WRITE_UP" 1649 | elif m == 2: 1650 | print "TokenMandatoryPolicy: NEW_PROCESS_MIN" 1651 | elif m == 3: 1652 | print "TokenMandatoryPolicy: POLICY_VALID_MASK" 1653 | else: 1654 | print "TokenMandatoryPolicy: %s" % m 1655 | except: 1656 | pass 1657 | 1658 | print "Token Resitrcted Sids: " + str(win32security.GetTokenInformation(tokenh, TokenRestrictedSids)) 1659 | print "IsTokenRestricted: " + str(win32security.IsTokenRestricted(tokenh)) 1660 | print "\nToken Groups: " 1661 | for tup in win32security.GetTokenInformation(tokenh, TokenGroups): 1662 | sid = tup[0] 1663 | attr = tup[1] 1664 | attr_str = attr 1665 | if attr < 0: 1666 | attr = 2**32 + attr 1667 | attr_str_a = [] 1668 | if attr & 1: 1669 | # attr_str_a.append("SE_GROUP_MANDATORY") 1670 | attr_str_a.append("MANDATORY") 1671 | if attr & 2: 1672 | # attr_str_a.append("SE_GROUP_ENABLED_BY_DEFAULT") 1673 | attr_str_a.append("ENABLED_BY_DEFAULT") 1674 | if attr & 4: 1675 | # attr_str_a.append("SE_GROUP_ENABLED") 1676 | attr_str_a.append("ENABLED") 1677 | if attr & 8: 1678 | # attr_str_a.append("SE_GROUP_OWNER") 1679 | attr_str_a.append("OWNER") 1680 | if attr & 0x40000000: 1681 | # attr_str_a.append("SE_GROUP_LOGON_ID") 1682 | attr_str_a.append("LOGON_ID") 1683 | attr_str = ("|".join(attr_str_a)) 1684 | try: 1685 | accountName, domainName, accountTypeInt = win32security.LookupAccountSid(remote_server, sid) 1686 | user = domainName + "\\" + accountName + " (" + win32security.ConvertSidToStringSid(sid) + ")" 1687 | except: 1688 | user = win32security.ConvertSidToStringSid(sid) 1689 | print "\t%s: %s" % (user, attr_str) 1690 | # Link that explains how privs are added / removed from tokens: 1691 | # http://support.microsoft.com/kb/326256 1692 | print "\nToken Privileges:" 1693 | privs = win32security.GetTokenInformation(tokenh, TokenPrivileges) 1694 | for priv_tuple in privs: 1695 | priv_val = priv_tuple[0] 1696 | attr = priv_tuple[1] 1697 | attr_str = "unknown_attr(" + str(attr) + ")" 1698 | attr_str_a = [] 1699 | if attr == 0: 1700 | attr_str_a.append("[disabled but not removed]") 1701 | if attr & 1: 1702 | # attr_str_a.append("SE_PRIVILEGE_ENABLED_BY_DEFAULT") 1703 | attr_str_a.append("ENABLED_BY_DEFAULT") 1704 | if attr & 2: 1705 | # attr_str_a.append("SE_PRIVILEGE_ENABLED") 1706 | attr_str_a.append("ENABLED") 1707 | if attr & 0x80000000: 1708 | # attr_str_a.append("SE_PRIVILEGE_USED_FOR_ACCESS") 1709 | attr_str_a.append("USED_FOR_ACCESS") 1710 | if attr & 4: 1711 | # attr_str_a.append("SE_PRIVILEGE_REMOVED") 1712 | attr_str_a.append("REMOVED") 1713 | if attr_str_a: 1714 | attr_str = ("|").join(attr_str_a) 1715 | print "\t%s: %s" % (win32security.LookupPrivilegeName(remote_server, priv_val), attr_str) 1716 | 1717 | 1718 | #print "\nProcess ACL (buggy - probably wrong):" 1719 | #dump_acl(pid, 'process', win32security.GetTokenInformation(tokenh, TokenDefaultDacl), {'brief': 1}) # TODO can't understand ACL 1720 | # sidObj = win32security.GetTokenInformation(tokenh, TokenOwner) # Owner returns "Administrators" instead of SYSTEM. It's not what we want. 1721 | # if sidObj: 1722 | # accountName, domainName, accountTypeInt = win32security.LookupAccountSid(remote_server, sidObj) 1723 | # print "User: %s\%s (type %s)" % (domainName, accountName, accountTypeInt) 1724 | if gotexe: 1725 | print "\nFile permissions on %s:" % exe 1726 | dump_perms(exe, 'file', {'brief': 1}) 1727 | print 1728 | 1729 | if mhs and ph: 1730 | for mh in mhs: 1731 | dll = win32process.GetModuleFileNameEx(ph, mh) 1732 | print "Loaded module: %s" % dll 1733 | dump_perms(dll, 'file', {'brief': 1}) 1734 | 1735 | print 1736 | 1737 | 1738 | def check_processes(): 1739 | pids = win32process.EnumProcesses() 1740 | # TODO also check out WMI. It might not be running, but it could help if it is: 1741 | # http://groups.google.com/group/comp.lang.python/browse_thread/thread/1f50065064173ccb 1742 | # TODO process explorer can find quite a lot more information than this script. This script has several problems: 1743 | # TODO I can't open 64-bit processes for a 32-bit app. I get this error: 1744 | # ERROR: can't open 6100: 299 EnumProcessModules, Only part of a ReadProcessMemory 1745 | # or WriteProcessMemory request was completed. 1746 | # TODO I can't seem to get the name of elevated processes (user running as me, but with admin privs) 1747 | # TODO I can't get details of certain processes runnign as SYSTEM on xp (e.g. pid 4 "system", csrss.exe) 1748 | # TODO should be able to find name (and threads?) for all processes. Not necessarily path. 1749 | 1750 | for pid in sorted(pids): 1751 | # TODO there's a security descriptor for each process accessible via GetSecurityInfo according to http://msdn.microsoft.com/en-us/library/ms684880%28VS.85%29.aspx 1752 | # TODO could we connect with PROCESS_QUERY_LIMITED_INFORMATION instead on Vista+ 1753 | try: 1754 | ph = win32api.OpenProcess(win32con.PROCESS_VM_READ | win32con.PROCESS_QUERY_INFORMATION , False, pid) 1755 | except: 1756 | # print "ERROR: can't connected to PID " + str(pid) 1757 | sys.stdout.write("?") 1758 | continue 1759 | else: 1760 | user = "unknown\\unknown" 1761 | try: 1762 | tokenh = win32security.OpenProcessToken(ph, win32con.TOKEN_QUERY) 1763 | except: 1764 | pass 1765 | else: 1766 | sidObj, intVal = win32security.GetTokenInformation(tokenh, TokenUser) 1767 | #source = win32security.GetTokenInformation(tokenh, TokenSource) 1768 | if sidObj: 1769 | accountName, domainName, accountTypeInt = win32security.LookupAccountSid(remote_server, sidObj) 1770 | # print "pid=%d accountname=%s domainname=%s wow64=%s" % (pid, accountName, domainName, win32process.IsWow64Process(ph)) 1771 | user = domainName + "\\" + accountName 1772 | 1773 | # print "PID %d is running as %s" % (pid, user) 1774 | sys.stdout.write(".") 1775 | try: 1776 | mhs = win32process.EnumProcessModules(ph) 1777 | # print mhs 1778 | except: 1779 | continue 1780 | 1781 | mhs = list(mhs) 1782 | exe = win32process.GetModuleFileNameEx(ph, mhs.pop(0)) 1783 | weak_perms = check_weak_write_perms(exe, 'file') 1784 | # print_weak_perms("PID " + str(pid) + " running as " + user + ":", weak_perms) 1785 | if weak_perms: 1786 | save_issue("WPC016", "weak_perms_exes", weak_perms) 1787 | sys.stdout.write("!") 1788 | 1789 | for mh in mhs: 1790 | # print "PID %d (%s) has loaded module: %s" % (pid, exe, win32process.GetModuleFileNameEx(ph, mh)) 1791 | dll = win32process.GetModuleFileNameEx(ph, mh) 1792 | weak_perms = check_weak_write_perms(dll, 'file') 1793 | # print_weak_perms("DLL used by PID " + str(pid) + " running as " + user + " (" + exe + "):", weak_perms) 1794 | if weak_perms: 1795 | save_issue("WPC016", "weak_perms_dlls", weak_perms) 1796 | sys.stdout.write("!") 1797 | print 1798 | 1799 | def check_services(): 1800 | sch = win32service.OpenSCManager(remote_server, None, win32service.SC_MANAGER_ENUMERATE_SERVICE ) 1801 | try: 1802 | # TODO Haven't seen this work - even when running as SYSTEM 1803 | sd = win32service.QueryServiceObjectSecurity(sch, win32security.OWNER_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION) 1804 | print check_weak_write_perms_by_sd("Service Manager", 'service_manager', sd) 1805 | except: 1806 | pass 1807 | 1808 | # Need to connect to service (OpenService) with minimum privs to read DACL. Here are our options: 1809 | # 1810 | # http://www.pinvoke.net/default.aspx/advapi32/OpenSCManager.html?diff=y 1811 | # SC_MANAGER_ALL_ACCESS (0xF003F) Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table. 1812 | # SC_MANAGER_CREATE_SERVICE (0x0002) Required to call the CreateService function to create a service object and add it to the database. 1813 | # SC_MANAGER_CONNECT (0x0001) Required to connect to the service control manager. 1814 | # SC_MANAGER_ENUMERATE_SERVICE (0x0004) Required to call the EnumServicesStatusEx function to list the services that are in the database. 1815 | # SC_MANAGER_LOCK (0x0008) Required to call the LockServiceDatabase function to acquire a lock on the database. 1816 | # SC_MANAGER_MODIFY_BOOT_CONFIG (0x0020) Required to call the NotifyBootConfigStatus function. 1817 | # SC_MANAGER_QUERY_LOCK_STATUS (0x0010)Required to call the QueryServiceLockStatus function to retrieve the lock status information for the database. 1818 | # GENERIC_READ 1819 | # GENERIC_WRITE 1820 | # GENERIC_EXECUTE 1821 | # GENERIC_ALL 1822 | 1823 | services = win32service.EnumServicesStatus(sch, win32service.SERVICE_WIN32, win32service.SERVICE_STATE_ALL ) 1824 | for service in services: 1825 | try: 1826 | sh = win32service.OpenService(sch, service[0] , win32service.SC_MANAGER_CONNECT ) 1827 | service_info = win32service.QueryServiceConfig(sh) 1828 | except: 1829 | print "WARNING: Can't open service " + service[0] 1830 | continue 1831 | 1832 | try: 1833 | sh = win32service.OpenService(sch, service[0] , win32con.GENERIC_READ ) 1834 | sd = win32service.QueryServiceObjectSecurity(sh, win32security.OWNER_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION) 1835 | except: 1836 | # print "Service Perms: Unknown (Access Denied)" 1837 | continue 1838 | 1839 | weak_perms = check_weak_write_perms_by_sd("Service \"" + service[1] + "\" (" + service[0] + ") which runs as user \"" + service_info[7] + "\"", 'service', sd) 1840 | binary = None 1841 | weak_perms_binary = [] 1842 | if not remote_server: 1843 | binary = get_binary(service_info[3]) 1844 | if binary: 1845 | weak_perms_binary = check_weak_write_perms(binary, 'file') 1846 | 1847 | 1848 | if weak_perms or weak_perms_binary: 1849 | vprint("-"*40) 1850 | vprint("Service: " + service[0]) 1851 | vprint("Description: " + service[1]) 1852 | vprint("Binary: " + service_info[3]) 1853 | if binary: 1854 | vprint("Binary (clean): " + binary) 1855 | else: 1856 | vprint("Binary (clean): [Missing Binary]") 1857 | vprint("Run as: " + service_info[7]) 1858 | vprint("Weak Perms: ") 1859 | # service_info = win32service.QueryServiceConfig2(sh, win32service.SERVICE_CONFIG_DESCRIPTION) # long description of service. not interesting. 1860 | # print "Service Perms: " + win32security.ConvertSecurityDescriptorToStringSecurityDescriptor(sd, win32security.SDDL_REVISION_1, win32security.DACL_SECURITY_INFORMATION) 1861 | print_weak_perms("file", weak_perms_binary) 1862 | if weak_perms_binary: 1863 | save_issue("WPC004", "writable_progs", weak_perms_binary) 1864 | 1865 | print_weak_perms("service", weak_perms) 1866 | if weak_perms: 1867 | save_issue("WPC012", "weak_service_perms", weak_perms) 1868 | if verbose == 0: 1869 | sys.stdout.write("!") 1870 | else: 1871 | if verbose == 0: 1872 | sys.stdout.write(".") 1873 | print 1874 | 1875 | def audit_services(): 1876 | print 1877 | sch = win32service.OpenSCManager(remote_server, None, win32service.SC_MANAGER_ENUMERATE_SERVICE ) 1878 | try: 1879 | # TODO Haven't seen this work - even when running as SYSTEM 1880 | sd = win32service.QueryServiceObjectSecurity(sch, win32security.OWNER_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION) 1881 | print check_weak_write_perms_by_sd("Service Manager", 'service_manager', sd) 1882 | except: 1883 | #print "ERROR: Can't get security descriptor for service manager" 1884 | pass 1885 | 1886 | # Need to connect to service (OpenService) with minimum privs to read DACL. Here are our options: 1887 | # 1888 | # http://www.pinvoke.net/default.aspx/advapi32/OpenSCManager.html?diff=y 1889 | # SC_MANAGER_ALL_ACCESS (0xF003F) Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table. 1890 | # SC_MANAGER_CREATE_SERVICE (0x0002) Required to call the CreateService function to create a service object and add it to the database. 1891 | # SC_MANAGER_CONNECT (0x0001) Required to connect to the service control manager. 1892 | # SC_MANAGER_ENUMERATE_SERVICE (0x0004) Required to call the EnumServicesStatusEx function to list the services that are in the database. 1893 | # SC_MANAGER_LOCK (0x0008) Required to call the LockServiceDatabase function to acquire a lock on the database. 1894 | # SC_MANAGER_MODIFY_BOOT_CONFIG (0x0020) Required to call the NotifyBootConfigStatus function. 1895 | # SC_MANAGER_QUERY_LOCK_STATUS (0x0010)Required to call the QueryServiceLockStatus function to retrieve the lock status information for the database. 1896 | # GENERIC_READ 1897 | # GENERIC_WRITE 1898 | # GENERIC_EXECUTE 1899 | # GENERIC_ALL 1900 | 1901 | services = win32service.EnumServicesStatus(sch, win32service.SERVICE_WIN32, win32service.SERVICE_STATE_ALL ) 1902 | for service in services: 1903 | sh = win32service.OpenService(sch, service[0] , win32service.SC_MANAGER_CONNECT ) 1904 | service_info = win32service.QueryServiceConfig(sh) 1905 | binary = None 1906 | if remote_server: 1907 | print "WARNING: Running agianst remote server. Checking perms of .exe not implemented." 1908 | else: 1909 | binary = get_binary(service_info[3]) 1910 | print "-"*64 1911 | print("Service: " + handle_unicode(service[0])) 1912 | print("Description: " + handle_unicode(service[1])) 1913 | print("Binary: " + handle_unicode(service_info[3])) 1914 | if binary: 1915 | print("Binary (clean): " + binary) 1916 | else: 1917 | if remote_server: 1918 | print("Binary (clean): [N/A Running remotely]") 1919 | else: 1920 | print("Binary (clean): [Missing Binary/Remote]") 1921 | print("Run as: " + service_info[7]) 1922 | 1923 | print "\nFile Permissions on executable %s:" % binary 1924 | if binary: 1925 | dump_perms(binary, 'file', {'brief': 1}) 1926 | else: 1927 | print "WARNING: Can't get full path of binary. Skipping." 1928 | 1929 | print "\nPermissions on service:" 1930 | 1931 | try: 1932 | sh = win32service.OpenService(sch, service[0] , win32con.GENERIC_READ ) 1933 | except: 1934 | print "ERROR: OpenService failed" 1935 | 1936 | try: 1937 | sd = win32service.QueryServiceObjectSecurity(sh, win32security.OWNER_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION) 1938 | except: 1939 | print "ERROR: QueryServiceObjectSecurity didn't get security descriptor for service" 1940 | 1941 | dump_sd("Service \"" + service[1] + "\" (" + service[0] + ") which runs as user \"" + service_info[7] + "\"", 'service', sd, {'brief': 1}) 1942 | 1943 | print "\nPermissions on registry data:" 1944 | print "WARNING: Not implmented yet" 1945 | # service_info = win32service.QueryServiceConfig2(sh, win32service.SERVICE_CONFIG_DESCRIPTION) # long description of service. not interesting. 1946 | # print "Service Perms: " + win32security.ConvertSecurityDescriptorToStringSecurityDescriptor(sd, win32security.SDDL_REVISION_1, win32security.DACL_SECURITY_INFORMATION) 1947 | print 1948 | 1949 | def vprint(string): 1950 | if (verbose): 1951 | print string 1952 | 1953 | def get_binary(binary_dirty): 1954 | m = re.search('^[\s]*?"([^"]+)"', binary_dirty) 1955 | 1956 | if m and os.path.exists(m.group(1)): 1957 | return m.group(1) 1958 | else: 1959 | if m: 1960 | binary_dirty = m.group(1) 1961 | 1962 | chunks = binary_dirty.split(" ") 1963 | candidate = "" 1964 | for chunk in chunks: 1965 | if candidate: 1966 | candidate = candidate + " " 1967 | candidate = candidate + chunk 1968 | 1969 | if os.path.exists(candidate) and os.path.isfile(candidate): 1970 | return candidate 1971 | if os.path.exists(candidate + ".exe") and os.path.isfile(candidate + ".exe"): 1972 | return candidate + ".exe" 1973 | global on64bitwindows 1974 | if on64bitwindows: 1975 | candidate2 = candidate.replace("system32", "syswow64") 1976 | if os.path.exists(candidate2) and os.path.isfile(candidate2): 1977 | return candidate2 1978 | if os.path.exists(candidate2 + ".exe") and os.path.isfile(candidate2 + ".exe"): 1979 | return candidate2 + ".exe" 1980 | 1981 | return None 1982 | 1983 | def print_weak_perms(type, weak_perms, options={}): 1984 | brief = 0 1985 | if options: 1986 | if options['brief']: 1987 | brief = 1 1988 | for perms in weak_perms: 1989 | object_name = perms[0] 1990 | domain = perms[1] 1991 | principle = perms[2] 1992 | perm = perms[3] 1993 | if len(perms) == 5: 1994 | acl_type = perms[4] 1995 | if acl_type == "ALLOW": 1996 | acl_type = "" 1997 | else: 1998 | acl_type = acl_type + " " 1999 | else: 2000 | acl_type = "" 2001 | slash = "\\" 2002 | if domain == "": 2003 | slash = "" 2004 | 2005 | if brief: 2006 | print "\t%s%s%s%s: %s" % (acl_type, domain, slash, principle, perm) 2007 | else: 2008 | #print repr((acl_type, domain, slash, principle, perm, type, object_name)) 2009 | print u"\t%s%s%s%s has permission %s on %s %s" % (handle_unicode(acl_type), handle_unicode(domain), handle_unicode(slash), handle_unicode(principle), handle_unicode(perm), handle_unicode(type), handle_unicode(object_name)) 2010 | 2011 | def check_path(path, issue_no): 2012 | dirs = set(path.split(';')) 2013 | exts = ('exe', 'com', 'bat', 'dll') # TODO pl, rb, py, php, inc, asp, aspx, ocx, vbs, more? 2014 | for dir in dirs: 2015 | weak_flag = 0 2016 | weak_perms = check_weak_write_perms(dir, 'directory') 2017 | if weak_perms: 2018 | save_issue(issue_no, "weak_perms_dir", weak_perms) 2019 | print_weak_perms("Directory", weak_perms) 2020 | weak_flag = 1 2021 | for ext in exts: 2022 | for file in glob.glob(dir + '\*.' + ext): 2023 | #print "Processing " + file 2024 | weak_perms = check_weak_write_perms(file, 'file') 2025 | if weak_perms: 2026 | save_issue(issue_no, "weak_perms_exe", weak_perms) 2027 | print_weak_perms("File", weak_perms) 2028 | weak_flag = 1 2029 | if weak_flag == 1: 2030 | sys.stdout.write("!") 2031 | else: 2032 | sys.stdout.write(".") 2033 | 2034 | def get_user_paths(): 2035 | try: 2036 | keyh = win32api.RegOpenKeyEx(win32con.HKEY_USERS, None , 0, win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE | win32con.KEY_READ) 2037 | except: 2038 | return 0 2039 | paths = [] 2040 | subkeys = win32api.RegEnumKeyEx(keyh) 2041 | for subkey in subkeys: 2042 | try: 2043 | subkeyh = win32api.RegOpenKeyEx(keyh, subkey[0] + "\\Environment" , 0, win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE | win32con.KEY_READ) 2044 | except: 2045 | pass 2046 | else: 2047 | subkey_count, value_count, mod_time = win32api.RegQueryInfoKey(subkeyh) 2048 | 2049 | try: 2050 | path, type = win32api.RegQueryValueEx(subkeyh, "PATH") 2051 | paths.append((subkey[0], path)) 2052 | except: 2053 | pass 2054 | return paths 2055 | 2056 | def get_system_path(): 2057 | # HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment 2058 | key_string = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' 2059 | try: 2060 | keyh = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, key_string , 0, win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE | win32con.KEY_READ) 2061 | except: 2062 | return None 2063 | 2064 | try: 2065 | path, type = win32api.RegQueryValueEx(keyh, "PATH") 2066 | return path 2067 | except: 2068 | return None 2069 | 2070 | #name=sys.argv[1] 2071 | #if not os.path.exists(name): 2072 | #print name, "does not exist!" 2073 | #sys.exit() 2074 | 2075 | 2076 | def check_user_paths(): 2077 | for user_path in get_user_paths(): 2078 | user_sid_s = user_path[0] 2079 | try: 2080 | user_sid = win32security.ConvertStringSidToSid(user_sid_s) 2081 | principle, domain, type = win32security.LookupAccountSid(remote_server, user_sid) 2082 | user_fq = domain + "\\" + principle 2083 | except: 2084 | print "WARNING: Can't convert sid %s to name. Skipping." % user_sid_s 2085 | continue 2086 | 2087 | path = user_path[1] 2088 | vprint("Checking path of %s" % user_fq) 2089 | global tmp_trusted_principles_fq 2090 | tmp_trusted_principles_fq = (user_fq) 2091 | check_path(path, "WPC015") 2092 | tmp_trusted_principles_fq = () 2093 | 2094 | def check_current_path(): 2095 | vprint("Checking current user's PATH") 2096 | global tmp_trusted_principles_fq 2097 | tmp_trusted_principles_fq = (os.environ['userdomain'] + "\\" + os.environ['username']) 2098 | check_path(os.environ['path'], "WPC014") 2099 | tmp_trusted_principles_fq = () 2100 | 2101 | def check_system_path(): 2102 | vprint("Checking system PATH") 2103 | check_path(get_system_path(), "WPC013") 2104 | 2105 | def check_paths(): 2106 | check_system_path() 2107 | check_current_path() 2108 | check_user_paths() 2109 | print 2110 | 2111 | def check_drives(): 2112 | for drive in win32api.GetLogicalDriveStrings().split("\x00"): 2113 | sys.stdout.write(".") 2114 | type = win32file.GetDriveType(drive) 2115 | if type == win32con.DRIVE_FIXED: 2116 | fs = win32api.GetVolumeInformation(drive)[4] 2117 | if fs == 'NTFS': 2118 | warning = "" 2119 | weak_perms = check_weak_write_perms(drive, 'directory') 2120 | if weak_perms: 2121 | # print "Weak permissions on drive root %s:" % drive 2122 | # print_weak_perms('directory', weak_perms) 2123 | sys.stdout.write(".") 2124 | save_issue("WPC010", "writable_drive_root", weak_perms) 2125 | elif fs == 'FAT': 2126 | save_issue_string("WPC011", "fat_fs_drives", "Fixed drive " + drive + ": has " + fs + " filesystem (FAT does not support file permissions)" ) 2127 | sys.stdout.write("!") 2128 | elif fs == 'FAT32': 2129 | save_issue_string("WPC011", "fat_fs_drives", "Fixed drive " + drive + ": has " + fs + " filesystem (FAT32 does not support file permissions)" ) 2130 | sys.stdout.write("!") 2131 | else: 2132 | warning = " (not NTFS - might be insecure)" 2133 | save_issue_string("WPC011", "fat_fs_drives", "Fixed drive " + drive + ": has " + fs + " filesystem (Not NTFS - might not be secure)" ) 2134 | sys.stdout.write("!") 2135 | # print "Fixed drive %s has %s filesystem%s" % (drive, fs, warning) 2136 | print 2137 | 2138 | def check_shares(): 2139 | resume = 0; 2140 | try: 2141 | (sharelist, total, resume) = win32net.NetShareEnum(None, 502, resume, 9999) 2142 | for share in sharelist: 2143 | sys.stdout.write(".") 2144 | sd = share['security_descriptor'] 2145 | # print "%s (%s) %s type=%s" % (share['netname'], share['path'], share['remark'], share['type']) 2146 | if sd: 2147 | weak_perms = check_weak_write_perms_by_sd("Share \"" + share['netname'] + "\" (" + share['path'] + ") ", 'share', sd) 2148 | if weak_perms: 2149 | save_issue("WPC017", "non_admin_shares", weak_perms) 2150 | sys.stdout.write("!") 2151 | except: 2152 | print "[E] Can't check shares - not enough privs?" 2153 | 2154 | # TODO not option to call this yet 2155 | def audit_shares(): 2156 | print "\n[+] Shares\n" 2157 | resume = 0; 2158 | try: 2159 | (sharelist, total, resume) = win32net.NetShareEnum(remote_server, 502, resume, 999999) 2160 | #print win32net.NetShareGetInfo(remote_server, ?, 0) # do we need this? 2161 | 2162 | for share in sharelist: 2163 | # Determine type of share 2164 | types = [] 2165 | if share['type'] & getattr(win32netcon, "STYPE_SPECIAL"): 2166 | # print "Share type: " 2167 | types.append("STYPE_SPECIAL") 2168 | share['type'] = share['type'] & 3 # mask off "special" 2169 | #print share['type'] 2170 | for stype in share_types: 2171 | if share['type'] == getattr(win32netcon, stype): 2172 | types.append(stype) 2173 | #print "Share type: " + stype 2174 | break 2175 | print "---------------" 2176 | print "Share: " + share['netname'] 2177 | print "Path: " + share['path'] 2178 | print "Remark: " + share['remark'] 2179 | print "Type(s): " + "|".join(types) 2180 | print "Reserved: %s" % share['reserved'] 2181 | print "Passwd: %s" % share['passwd'] 2182 | print "Current Uses: %s" % share['current_uses'] 2183 | print "Max Uses: %s" % share['max_uses'] 2184 | print "Permissions: %s" % share['permissions'] 2185 | print "Sec. Desc.: " 2186 | dump_sd(share['netname'], 'share', share['security_descriptor']) 2187 | except: 2188 | print "[E] Couldn't get share information" 2189 | 2190 | print "\n[+] Server Info (NetServerGetInfo 102)\n" 2191 | 2192 | 2193 | def check_progfiles(): 2194 | # %ProgramFiles% 2195 | # %ProgramFiles(x86)% 2196 | prog_dirs = [] 2197 | # re_exe = re.compile('\.exe$|\.com$|\.bat$|\.dll$', re.IGNORECASE) 2198 | exts = ('exe', 'com', 'bat', 'dll') # TODO pl, rb, py, php, inc, asp, aspx, ocx, vbs, more? 2199 | 2200 | if os.getenv('ProgramFiles'): 2201 | prog_dirs.append(os.environ['ProgramFiles']) 2202 | 2203 | if os.getenv('ProgramFiles(x86)'): 2204 | prog_dirs.append(os.environ['ProgramFiles(x86)']) 2205 | 2206 | dot_count = 0 2207 | weak_flag = 0 2208 | for prog_dir in prog_dirs: 2209 | # print "Looking for programs under %s..." % prog_dir 2210 | for root, dirs, files in os.walk(prog_dir): 2211 | #print "root=%s, dirs=%s, files=%s" % (root, dirs, files) 2212 | # for file in files: 2213 | # m = re_exe.search(file) 2214 | # if m is None: 2215 | # continue 2216 | # #print "Checking file %s" % os.path.join(root, file) 2217 | # weak_perms = check_weak_write_perms(os.path.join(root, file), 'file') 2218 | # if weak_perms: 2219 | # print_weak_perms("File", weak_perms) 2220 | for file in dirs: 2221 | #print "Checking dir %s" % os.path.join(root, file) 2222 | weak_perms = check_weak_write_perms(os.path.join(root, file), 'file') 2223 | if weak_perms: 2224 | #print_weak_perms("Directory", weak_perms) 2225 | save_issue("WPC001", "writable_dirs", weak_perms) 2226 | weak_flag = 1 2227 | dir = file 2228 | for ext in exts: 2229 | for f in glob.glob(u"%s\\%s\\*.%s" % (handle_unicode(root),handle_unicode(dir),handle_unicode(ext))): #root + "\\" + dir + '\*.' + ext): 2230 | #print "Processing " + f 2231 | weak_perms = check_weak_write_perms(f, 'file') 2232 | if weak_perms: 2233 | print_weak_perms("File", weak_perms) 2234 | save_issue("WPC001", "writable_progs", weak_perms) 2235 | weak_flag = 1 2236 | dot_count = dot_count + 1; 2237 | # Don't print out all the dots. There are too many! 2238 | if dot_count > 10: 2239 | if weak_flag == 1: 2240 | sys.stdout.write("!") 2241 | else: 2242 | sys.stdout.write(".") 2243 | dot_count = 0; 2244 | weak_flag = 0; 2245 | print 2246 | 2247 | def check_patches(): 2248 | # TODO: This is more difficult than I'd hoped. You can't just search for the KB number: XP will appear to be vulnerable to dcom. Need to search for KB number or SP2 in this case. 2249 | # from subprocess import Popen, PIPE 2250 | patchlist = Popen(["systeminfo"], stdout=PIPE).communicate()[0] 2251 | for kb_no in kb_nos: 2252 | print "Searching for " + kb_no 2253 | if re.search(kb_no, patchlist): 2254 | print "found" 2255 | 2256 | 2257 | def print_section(title): 2258 | if (verbose != 0): 2259 | print "%s\n%s\n%s\n" % ("="*32,title,"="*32) 2260 | else: 2261 | sys.stdout.write(title + ": ") 2262 | 2263 | # http://www.daniweb.com/code/snippet216539.html 2264 | def int2bin(n): 2265 | bStr = '' 2266 | if n < 0: n = n + 2^32 2267 | if n == 0: return '0' 2268 | while n > 0: 2269 | bStr = str(n % 2) + bStr 2270 | n = n >> 1 2271 | return bStr 2272 | 2273 | def impersonate(username, password, domain): 2274 | if username: 2275 | print "Using alternative credentials:" 2276 | print "Username: " + str(username) 2277 | print "Password: " + str(password) 2278 | print "Domain: " + str(domain) 2279 | handle = win32security.LogonUser( username, domain, password, win32security.LOGON32_LOGON_NEW_CREDENTIALS, win32security.LOGON32_PROVIDER_WINNT50 ) 2280 | win32security.ImpersonateLoggedOnUser( handle ) 2281 | else: 2282 | print "Running as current user. No logon creds supplied (-u, -d, -p)." 2283 | print 2284 | 2285 | def audit_passpol(): 2286 | print 2287 | print "[+] NetUserModalsGet 0,1,2,3" 2288 | print 2289 | 2290 | try: 2291 | data = win32net.NetUserModalsGet(remote_server, 0) 2292 | for key in data.keys(): 2293 | print "%s: %s" % (key, data[key]) 2294 | data = win32net.NetUserModalsGet(remote_server, 1) 2295 | for key in data.keys(): 2296 | print "%s: %s" % (key, data[key]) 2297 | data = win32net.NetUserModalsGet(remote_server, 2) 2298 | for key in data.keys(): 2299 | if key == 'domain_id': 2300 | print "%s: %s" % (key, win32security.ConvertSidToStringSid(data[key])) 2301 | elif key == 'lockout_threshold' and data[key] == '0': 2302 | print "%s: %s (accounts aren't locked out)" % (key, data[key]) 2303 | else: 2304 | print "%s: %s" % (key, data[key]) 2305 | data = win32net.NetUserModalsGet(remote_server, 3) 2306 | for key in data.keys(): 2307 | if key == 'lockout_threshold' and data[key] == 0: 2308 | print "%s: %s (accounts aren't locked out)" % (key, data[key]) 2309 | else: 2310 | print "%s: %s" % (key, data[key]) 2311 | except: 2312 | print "[E] Couldn't get NetUserModals data" 2313 | 2314 | # Recursive function to find group members (and the member of any groups in those groups...) 2315 | def get_group_members(server, group, depth): 2316 | resume = 0 2317 | indent = "\t" * depth 2318 | members = [] 2319 | while True: 2320 | try: 2321 | m, total, resume = win32net.NetLocalGroupGetMembers(server, group, 2, resume, 999999) 2322 | except: 2323 | break 2324 | for member in m: 2325 | if member['sidusage'] == 4: 2326 | type = "local group" 2327 | g = member['domainandname'].split("\\") 2328 | print indent + member['domainandname'] + " (" + str(type) + ")" 2329 | get_group_members(server, g[1], depth + 1) 2330 | elif member['sidusage'] == 2: 2331 | type = "domain group" 2332 | print indent + member['domainandname'] + " (" + str(type) + ")" 2333 | elif member['sidusage'] == 1: 2334 | type = "user" 2335 | print indent + member['domainandname'] + " (" + str(type) + ")" 2336 | else: 2337 | type = "type " + str(member['sidusage']) 2338 | print indent + member['domainandname'] + " (" + str(type) + ")" 2339 | if resume == 0: 2340 | break 2341 | 2342 | def audit_admin_users(): 2343 | print 2344 | for group in ("administrators", "domain admins", "enterprise admins"): 2345 | print "\n[+] Members of " + group + ":" 2346 | get_group_members(remote_server, group, 0) 2347 | print 2348 | 2349 | # It might be interesting to look up who has powerful privs, but LsaEnumerateAccountsWithUserRight doesn't seem to work as a low priv user 2350 | # SE_ASSIGNPRIMARYTOKEN_NAME TEXT("SeAssignPrimaryTokenPrivilege") Required to assign the primary token of a process. User Right: Replace a process-level token. 2351 | # SE_BACKUP_NAME TEXT("SeBackupPrivilege") Required to perform backup operations. This privilege causes the system to grant all read access control to any file, regardless of the access control list (ACL) specified for the file. Any access request other than read is still evaluated with the ACL. This privilege is required by the RegSaveKey and RegSaveKeyExfunctions. The following access rights are granted if this privilege is held: READ_CONTROL ACCESS_SYSTEM_SECURITY FILE_GENERIC_READ FILE_TRAVERSE User Right: Back up files and directories. 2352 | # SE_CREATE_PAGEFILE_NAME TEXT("SeCreatePagefilePrivilege") Required to create a paging file. User Right: Create a pagefile. 2353 | # SE_CREATE_TOKEN_NAME TEXT("SeCreateTokenPrivilege") Required to create a primary token. User Right: Create a token object. 2354 | # SE_DEBUG_NAME TEXT("SeDebugPrivilege") Required to debug and adjust the memory of a process owned by another account. User Right: Debug programs. 2355 | # SE_ENABLE_DELEGATION_NAME TEXT("SeEnableDelegationPrivilege") Required to mark user and computer accounts as trusted for delegation. User Right: Enable computer and user accounts to be trusted for delegation. 2356 | # SE_LOAD_DRIVER_NAME TEXT("SeLoadDriverPrivilege") Required to load or unload a device driver. User Right: Load and unload device drivers. 2357 | # SE_MACHINE_ACCOUNT_NAME TEXT("SeMachineAccountPrivilege") Required to create a computer account. User Right: Add workstations to domain. 2358 | # SE_MANAGE_VOLUME_NAME TEXT("SeManageVolumePrivilege") Required to enable volume management privileges. User Right: Manage the files on a volume. 2359 | # SE_RELABEL_NAME TEXT("SeRelabelPrivilege") Required to modify the mandatory integrity level of an object. User Right: Modify an object label. 2360 | # SE_RESTORE_NAME TEXT("SeRestorePrivilege") Required to perform restore operations. This privilege causes the system to grant all write access control to any file, regardless of the ACL specified for the file. Any access request other than write is still evaluated with the ACL. Additionally, this privilege enables you to set any valid user or group SID as the owner of a file. This privilege is required by the RegLoadKey function. The following access rights are granted if this privilege is held: WRITE_DAC WRITE_OWNER ACCESS_SYSTEM_SECURITY FILE_GENERIC_WRITE FILE_ADD_FILE FILE_ADD_SUBDIRECTORY DELETE User Right: Restore files and directories. 2361 | # SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege") Required to shut down a local system. User Right: Shut down the system. 2362 | # SE_SYNC_AGENT_NAME TEXT("SeSyncAgentPrivilege") Required for a domain controller to use the LDAP directory synchronization services. This privilege enables the holder to read all objects and properties in the directory, regardless of the protection on the objects and properties. By default, it is assigned to the Administrator and LocalSystem accounts on domain controllers. User Right: Synchronize directory service data. 2363 | # SE_TAKE_OWNERSHIP_NAME TEXT("SeTakeOwnershipPrivilege") Required to take ownership of an object without being granted discretionary access. This privilege allows the owner value to be set only to those values that the holder may legitimately assign as the owner of an object. User Right: Take ownership of files or other objects. 2364 | # SE_TCB_NAME TEXT("SeTcbPrivilege") This privilege identifies its holder as part of the trusted computer base. Some trusted protected subsystems are granted this privilege. User Right: Act as part of the operating system. 2365 | # SE_TRUSTED_CREDMAN_ACCESS_NAME TEXT("SeTrustedCredManAccessPrivilege") Required to access Credential Manager as a trusted caller. User Right: Access Credential Manager as a trusted caller. 2366 | 2367 | # Need: SE_ENABLE_DELEGATION_NAME, SE_MANAGE_VOLUME_NAME, SE_RELABEL_NAME, SE_SYNC_AGENT_NAME, SE_TRUSTED_CREDMAN_ACCESS_NAME 2368 | # ph = win32security.LsaOpenPolicy(remote_server, win32security.POLICY_VIEW_LOCAL_INFORMATION | win32security.POLICY_LOOKUP_NAMES) 2369 | # for priv in (SE_ASSIGNPRIMARYTOKEN_NAME, SE_BACKUP_NAME, SE_CREATE_PAGEFILE_NAME, SE_CREATE_TOKEN_NAME, SE_DEBUG_NAME, SE_LOAD_DRIVER_NAME, SE_MACHINE_ACCOUNT_NAME, SE_RESTORE_NAME, SE_SHUTDOWN_NAME, SE_TAKE_OWNERSHIP_NAME, SE_TCB_NAME): 2370 | # print "Looking up who has " + priv + "priv" 2371 | # try: 2372 | # sids = win32security.LsaEnumerateAccountsWithUserRight(ph, priv) 2373 | # print sids 2374 | # except: 2375 | # print "[E] Lookup failed" 2376 | 2377 | def audit_logged_in(): 2378 | resume = 0 2379 | print "\n[+] Logged in users:" 2380 | try: 2381 | while True: 2382 | users, total, resume = win32net.NetWkstaUserEnum(remote_server, 1 , resume , 999999 ) 2383 | for user in users: 2384 | print "User logged in: Logon Server=\"%s\" Logon Domain=\"%s\" Username=\"%s\"" % (user['logon_server'], user['logon_domain'], user['username']) 2385 | if resume == 0: 2386 | break 2387 | except: 2388 | print "[E] Failed" 2389 | 2390 | def audit_host_info(): 2391 | print "\n" 2392 | if remote_server: 2393 | print "Querying remote server: " + remote_server 2394 | 2395 | # Only works on local host 2396 | #win32net.NetGetJoinInformation() 2397 | 2398 | # This looks interesting, but doesn't seem to work. Maybe unsupported legacy api. 2399 | #pywintypes.error: (50, 'NetUseEnum', 'The request is not supported.') 2400 | #print 2401 | #print "[+] Getting Net Use info" 2402 | #print 2403 | 2404 | #resume = 0 2405 | #use, total, resume = win32net.NetUseEnum(remote_server, 2, resume , 999999 ) 2406 | #print use 2407 | 2408 | print 2409 | print "[+] Workstation Info (NetWkstaGetInfo 102)" 2410 | print 2411 | 2412 | try: 2413 | #print win32net.NetWkstaGetInfo(remote_server, 100) 2414 | #print win32net.NetWkstaGetInfo(remote_server, 101) 2415 | serverinfo = win32net.NetWkstaGetInfo(remote_server, 102) 2416 | print "Computer Name: %s" % serverinfo['computername'] 2417 | print "Langroup: %s" % serverinfo['langroup'] 2418 | print "OS: %s.%s" % (serverinfo['ver_major'], serverinfo['ver_minor']) 2419 | print "Logged On Users: %s" % serverinfo['logged_on_users'] 2420 | print "Lanroot: %s" % serverinfo['lanroot'] 2421 | 2422 | if serverinfo['platform_id'] & win32netcon.PLATFORM_ID_NT: 2423 | print "Platform: PLATFORM_ID_NT (means NT family, not NT4)" 2424 | if serverinfo['platform_id'] == win32netcon.PLATFORM_ID_OS2: 2425 | print "Platform: PLATFORM_ID_OS2" 2426 | if serverinfo['platform_id'] == win32netcon.PLATFORM_ID_DOS: 2427 | print "Platform: PLATFORM_ID_DOS" 2428 | if serverinfo['platform_id'] == win32netcon.PLATFORM_ID_OSF: 2429 | print "Platform: PLATFORM_ID_OSF" 2430 | if serverinfo['platform_id'] == win32netcon.PLATFORM_ID_VMS: 2431 | print "Platform: PLATFORM_ID_VMS" 2432 | except: 2433 | print "[E] Couldn't get Workstation Info" 2434 | 2435 | print 2436 | print "[+] Server Info (NetServerGetInfo 102)" 2437 | print 2438 | 2439 | try: 2440 | #print "NetServerGetInfo 100" + str(win32net.NetServerGetInfo(remote_server, 100)) 2441 | #print "NetServerGetInfo 101" + str(win32net.NetServerGetInfo(remote_server, 101)) 2442 | serverinfo = win32net.NetServerGetInfo(remote_server, 102) 2443 | print "Name: %s" % serverinfo['name'] 2444 | print "Comment: %s" % serverinfo['comment'] 2445 | print "OS: %s.%s" % (serverinfo['version_major'], serverinfo['version_minor']) 2446 | print "Userpath: %s" % serverinfo['userpath'] 2447 | print "Hidden: %s" % serverinfo['hidden'] 2448 | 2449 | if serverinfo['platform_id'] & win32netcon.PLATFORM_ID_NT: 2450 | print "Platform: PLATFORM_ID_NT (means NT family, not NT4)" 2451 | if serverinfo['platform_id'] == win32netcon.PLATFORM_ID_OS2: 2452 | print "Platform: PLATFORM_ID_OS2" 2453 | if serverinfo['platform_id'] == win32netcon.PLATFORM_ID_DOS: 2454 | print "Platform: PLATFORM_ID_DOS" 2455 | if serverinfo['platform_id'] == win32netcon.PLATFORM_ID_OSF: 2456 | print "Platform: PLATFORM_ID_OSF" 2457 | if serverinfo['platform_id'] == win32netcon.PLATFORM_ID_VMS: 2458 | print "Platform: PLATFORM_ID_VMS" 2459 | for sv_type in sv_types: 2460 | if serverinfo['type'] & getattr(win32netcon, sv_type): 2461 | print "Type: " + sv_type 2462 | except: 2463 | print "[E] Couldn't get Server Info" 2464 | 2465 | 2466 | print 2467 | print "[+] LsaQueryInformationPolicy" 2468 | print 2469 | 2470 | try: 2471 | ph = win32security.LsaOpenPolicy(remote_server, win32security.POLICY_VIEW_LOCAL_INFORMATION | win32security.POLICY_LOOKUP_NAMES) 2472 | print "PolicyDnsDomainInformation:" 2473 | print win32security.LsaQueryInformationPolicy(ph, win32security.PolicyDnsDomainInformation) 2474 | print "PolicyDnsDomainInformation:" 2475 | print win32security.LsaQueryInformationPolicy(ph, win32security.PolicyPrimaryDomainInformation) 2476 | print "PolicyPrimaryDomainInformation:" 2477 | print win32security.LsaQueryInformationPolicy(ph, win32security.PolicyAccountDomainInformation) 2478 | print "PolicyLsaServerRoleInformation:" 2479 | print win32security.LsaQueryInformationPolicy(ph, win32security.PolicyLsaServerRoleInformation) 2480 | except: 2481 | print "[E] Couldn't LsaOpenPolicy" 2482 | 2483 | # DsBindWithCred isn't available from python! 2484 | 2485 | # IADsComputer looks useful, but also isn't implemented: 2486 | # http://msdn.microsoft.com/en-us/library/aa705980%28v=VS.85%29.aspx 2487 | 2488 | # The following always seems to fail: 2489 | # need a dc hostname as remote_server 2490 | # and domain 2491 | #try: 2492 | # hds = win32security.DsBind(remote_server, remote_domain) 2493 | # print "hds: " + hds 2494 | # print "DsListDomainsInSite: "+ str(win32security.DsListDomainsInSite(hds)) 2495 | #except: 2496 | # pass 2497 | 2498 | # domain can be null. i think domainguid can be null. sitename null. flags = 0. 2499 | 2500 | # lists roles recognised by the server (fsmo roles?) 2501 | # win32security.DsListRoles(hds) 2502 | 2503 | # list misc info for a server 2504 | # win32security.DsListInfoForServer(hds, server) 2505 | 2506 | # but how to get a list of sites? 2507 | # win32security.DsListServersInSite(hds, site ) 2508 | 2509 | # win32security.DsCrackNames(hds, flags , formatOffered , formatDesired , names ) 2510 | # ...For example, user objects can be identified by SAM account names (Domain\UserName), user principal name (UserName@Domain.com), or distinguished name. 2511 | 2512 | print 2513 | print "[+] Getting domain controller info" 2514 | print 2515 | 2516 | try: 2517 | domain = None # TODO: could call of each domain if we had a list 2518 | print "PDC: " + win32net.NetGetDCName(remote_server, domain) 2519 | # Try to list some domain controllers for the remote host 2520 | # There are better ways of doing this, but they don't seem to be available via python! 2521 | dc_seen = {} 2522 | for filter in (0, 0x00004000, 0x00000080, 0x00001000, 0x00000400, 0x00000040, 0x00000010): 2523 | dc_info = win32security.DsGetDcName(remote_server, None, None, None, filter) 2524 | if not dc_info['DomainControllerAddress'] in dc_seen: 2525 | print "\n[+] Found DC\n" 2526 | for k in dc_info: 2527 | print k + ": " + str(dc_info[k]) 2528 | dc_seen[dc_info['DomainControllerAddress']] = 1 2529 | print "\nWARNING: Above is not necessarily a complete list of DCs\n" 2530 | #print "Domain controller: " + str(win32security.DsGetDcName(remote_server, None, None, None, 0)) # any dc 2531 | #print "Domain controller: " + str(win32security.DsGetDcName(remote_server, None, None, None, 0x00004000)) # not the system we connect to 2532 | #print "Domain controller: " + str(win32security.DsGetDcName(remote_server, None, None, None, 0x00000080)) # pdc 2533 | #print "Domain controller: " + str(win32security.DsGetDcName(remote_server, None, None, None, 0x00001000)) # writeable 2534 | #print "Domain controller: " + str(win32security.DsGetDcName(remote_server, None, None, None, 0x00000400)) # kerberos 2535 | #print "Domain controller: " + str(win32security.DsGetDcName(remote_server, None, None, None, 0x00000040)) # gc 2536 | #print "Domain controller: " + str(win32security.DsGetDcName(remote_server, None, None, None, 0x00000010)) # directory service 2537 | except: 2538 | print "[E] Couldn't get DC info" 2539 | 2540 | # This function sounds very much like what lservers.exe does, but the server name must be None 2541 | # according to http://msdn.microsoft.com/en-us/library/aa370623%28VS.85%29.aspx. No use to us. 2542 | # print win32net.NetServerEnum(remote_server, 100 or 101, win32netcon.SV_TYPE_ALL, "SOMEDOMAIN.COM", 0, 999999) 2543 | 2544 | def audit_user_group(): 2545 | try: 2546 | ph = win32security.LsaOpenPolicy(remote_server, win32security.POLICY_VIEW_LOCAL_INFORMATION | win32security.POLICY_LOOKUP_NAMES) 2547 | except: 2548 | pass 2549 | 2550 | print 2551 | print "[+] Local Groups" 2552 | print 2553 | resume = 0 2554 | groups = [] 2555 | while True: 2556 | try: 2557 | g, total, resume = win32net.NetLocalGroupEnum(remote_server, 0, resume, 999999) 2558 | groups = groups + g 2559 | if resume == 0: 2560 | break 2561 | except: 2562 | print "[E] NetLocalGroupEnum failed" 2563 | break 2564 | for group in groups: 2565 | members = [] 2566 | while True: 2567 | m, total, resume = win32net.NetLocalGroupGetMembers(remote_server, group['name'], 1, resume, 999999) 2568 | for member in m: 2569 | members.append(member['name']) 2570 | if resume == 0: 2571 | break 2572 | sid, s, i = win32security.LookupAccountName(remote_server, group['name']) 2573 | sid_string = win32security.ConvertSidToStringSid(sid) 2574 | print "Group %s has sid %s" % (group['name'], sid_string) 2575 | for m in members: 2576 | print "Group %s has member: %s" % (group['name'], m) 2577 | if verbose: 2578 | try: 2579 | privs = win32security.LsaEnumerateAccountRights(ph, sid) 2580 | for priv in privs: 2581 | print "Group %s has privilege: %s" % (group['name'], priv) 2582 | except: 2583 | print "Group %s: privilege lookup failed " % (group['name']) 2584 | 2585 | print 2586 | print "[+] Non-local Groups" 2587 | print 2588 | resume = 0 2589 | groups = [] 2590 | while True: 2591 | try: 2592 | g, total, resume = win32net.NetGroupEnum(remote_server, 0, resume, 999999) 2593 | groups = groups + g 2594 | if resume == 0: 2595 | break 2596 | except: 2597 | print "[E] NetGroupEnum failed" 2598 | break 2599 | 2600 | for group in groups: 2601 | members = [] 2602 | while True: 2603 | try: 2604 | m, total, resume = win32net.NetGroupGetUsers(remote_server, group['name'], 0, resume, 999999) 2605 | for member in m: 2606 | members.append(member['name']) 2607 | if resume == 0: 2608 | break 2609 | except: 2610 | print "[E] NetGroupEnum failed" 2611 | break 2612 | sid, s, i = win32security.LookupAccountName(remote_server, group['name']) 2613 | sid_string = win32security.ConvertSidToStringSid(sid) 2614 | print "Group %s has sid %s" % (group['name'], sid_string) 2615 | for m in members: 2616 | print "Group %s has member: %s" % (group['name'], m) 2617 | if verbose: 2618 | try: 2619 | privs = win32security.LsaEnumerateAccountRights(ph, sid) 2620 | for priv in privs: 2621 | print "Group %s has privilege: %s" % (group['name'], priv) 2622 | except: 2623 | print "Group %s has no privileges" % (group['name']) 2624 | 2625 | print "\n[+] Users\n" 2626 | 2627 | resume = 0 2628 | users = [] 2629 | if verbose: 2630 | level = 11 2631 | else: 2632 | level = 0 2633 | while True: 2634 | try: 2635 | # u, total, resume = win32net.NetUserEnum(remote_server, 11, 0, resume, 999999) # lots of user detail 2636 | # u, total, resume = win32net.NetUserEnum(remote_server, 0, 0, resume, 999999) # just the username 2637 | u, total, resume = win32net.NetUserEnum(remote_server, level, 0, resume, 999999) 2638 | for user in u: 2639 | if verbose: 2640 | for k in user: 2641 | if k != 'parms': 2642 | print k + "\t: " + str(user[k]) 2643 | print 2644 | users.append(user['name']) 2645 | if resume == 0: 2646 | break 2647 | except: 2648 | print "[E] NetUserEnum failed" 2649 | break 2650 | 2651 | for user in users: 2652 | gprivs = [] 2653 | sid, s, i = win32security.LookupAccountName(remote_server, user) 2654 | sid_string = win32security.ConvertSidToStringSid(sid) 2655 | print "User %s has sid %s" % (user, sid_string) 2656 | groups = win32net.NetUserGetLocalGroups(remote_server, user, 0) 2657 | for group in groups: 2658 | gsid, s, i = win32security.LookupAccountName(remote_server, group) 2659 | try: 2660 | privs = win32security.LsaEnumerateAccountRights(ph, gsid) 2661 | gprivs = list(list(gprivs) + list(privs)) 2662 | except: 2663 | pass 2664 | print "User %s is in this local group: %s" % (user, group) 2665 | group_list = win32net.NetUserGetGroups(remote_server, user) 2666 | groups = [] 2667 | for g in group_list: 2668 | groups.append(g[0]) 2669 | for group in groups: 2670 | print "User %s is in this non-local group: %s" % (user, group) 2671 | if verbose: 2672 | privs = [] 2673 | try: 2674 | privs = win32security.LsaEnumerateAccountRights(ph, sid) 2675 | except: 2676 | pass 2677 | for priv in list(set(list(gprivs) + list(privs))): 2678 | print "User %s has privilege %s" % (user, priv) 2679 | 2680 | if verbose: 2681 | print 2682 | print "[+] Privileges" 2683 | print 2684 | 2685 | for priv in windows_privileges: 2686 | try: 2687 | for s in win32security.LsaEnumerateAccountsWithUserRight(ph, priv): 2688 | priv_desc = "NoDesc!" 2689 | try: 2690 | priv_desc = win32security.LookupPrivilegeDisplayName(remote_server, priv) 2691 | except: 2692 | pass 2693 | 2694 | name, domain, type = win32security.LookupAccountSid(remote_server, s) 2695 | type_string = "unknown_type" 2696 | if type == 4: 2697 | type_string = "group" 2698 | if type == 5: 2699 | type_string = "user" 2700 | print "Privilege %s (%s) is held by %s\%s (%s)" % (priv, priv_desc, domain, name, type_string) 2701 | # print "Privilege %s is held by %s\%s (%s)" % (priv, domain, name, type_string) 2702 | except: 2703 | #print "Skipping %s - doesn't exist for this platform" % priv 2704 | pass 2705 | 2706 | # Main 2707 | 2708 | print "windows-privesc-check v%s (http://pentestmonkey.net/windows-privesc-check)\n" % version 2709 | 2710 | # Process Command Line Options 2711 | try: 2712 | opts, args = getopt.getopt(sys.argv[1:], "artSDEPRHUOMAFILIehwiWvo:s:u:p:d:", ["help", "verbose", "all_checks", "registry_checks", "path_checks", "service_checks", "services", "drive_checks", "eventlog_checks", "progfiles_checks", "passpol", "process_checks", "share_checks", "user_groups", "processes", "ignore_trusted", "owner_info", "write_perms_only", "domain", "patch_checks", "admin_users", "host_info", "logged_in", "report_file=", "username=", "password=", "domain=", "server="]) 2713 | except getopt.GetoptError, err: 2714 | # print help information and exit: 2715 | print str(err) # will print something like "option -a not recognized" 2716 | usage() 2717 | sys.exit(2) 2718 | output = None 2719 | for o, a in opts: 2720 | if o in ("-a", "--all_checks"): 2721 | all_checks = 1 2722 | elif o in ("-r", "--registry_checks"): 2723 | registry_checks = 1 2724 | elif o in ("-t", "--path_checks"): 2725 | path_checks = 1 2726 | elif o in ("-S", "--service_checks"): 2727 | service_checks = 1 2728 | elif o in ("-D", "--drive_checks"): 2729 | drive_checks = 1 2730 | elif o in ("-E", "--eventlog_checks"): 2731 | eventlog_checks = 1 2732 | elif o in ("-F", "--progfiles_checks"): 2733 | progfiles_checks = 1 2734 | elif o in ("-R", "--process_checks"): 2735 | process_checks = 1 2736 | elif o in ("-H", "--share_checks"): 2737 | share_checks = 1 2738 | # elif o in ("-T", "--patch_checks"): 2739 | # patch_checks = 1 2740 | elif o in ("-L", "--logged_in_audit"): 2741 | logged_in_audit = 1 2742 | elif o in ("-U", "--user_group_audit"): 2743 | user_group_audit = 1 2744 | elif o in ("-P", "--passpol"): 2745 | passpol_audit = 1 2746 | elif o in ("-A", "--admin_users_audit"): 2747 | admin_users_audit = 1 2748 | elif o in ("-O", "--process_audit"): 2749 | process_audit = 1 2750 | elif o in ("-i", "--host_info"): 2751 | host_info_audit = 1 2752 | elif o in ("-e", "--services"): 2753 | service_audit = 1 2754 | elif o in ("-h", "--help"): 2755 | usage() 2756 | sys.exit() 2757 | elif o in ("-w", "--write_perms_only"): 2758 | weak_perms_only = 1 2759 | elif o in ("-I", "--ignore_trusted"): 2760 | ignore_trusted = 1 2761 | elif o in ("-W", "--owner_info"): 2762 | owner_info = 1 2763 | elif o in ("-v", "--verbose"): 2764 | verbose = verbose + 1 2765 | elif o in ("-o", "--report_file"): 2766 | report_file_name = a 2767 | elif o in ("-s", "--server"): 2768 | remote_server = a 2769 | print "Remote server selected: " + a 2770 | elif o in ("-u", "--username"): 2771 | remote_username = a 2772 | elif o in ("-p", "--password"): 2773 | remote_password = a 2774 | elif o in ("-d", "--domain"): 2775 | remote_domain = a 2776 | else: 2777 | assert False, "unhandled option" 2778 | 2779 | if all_checks: 2780 | registry_checks = 1 2781 | path_checks = 1 2782 | service_checks = 1 2783 | service_audit = 1 2784 | drive_checks = 1 2785 | eventlog_checks = 1 2786 | progfiles_checks = 1 2787 | process_checks = 1 2788 | share_checks = 1 2789 | user_group_audit = 1 2790 | passpol_audit = 1 2791 | logged_in_audit = 1 2792 | admin_users_audit= 1 2793 | host_info_audit = 1 2794 | patch_checks = 1 2795 | process_audit = 1 2796 | 2797 | # Print usage message unless at least on type of check is selected 2798 | if not ( 2799 | registry_checks or 2800 | path_checks or 2801 | service_checks or 2802 | service_audit or 2803 | drive_checks or 2804 | eventlog_checks or 2805 | progfiles_checks or 2806 | process_checks or 2807 | share_checks or 2808 | logged_in_audit or 2809 | user_group_audit or 2810 | passpol_audit or 2811 | admin_users_audit or 2812 | host_info_audit or 2813 | process_audit or 2814 | patch_checks 2815 | ): 2816 | usage() 2817 | 2818 | if report_file_name == None: 2819 | report_file_name = "privesc-report-" + socket.gethostname() + ".html" 2820 | 2821 | # Better open the report file now in case there's a permissions problem 2822 | REPORT = open(report_file_name,"w") 2823 | 2824 | # Print out scan parameters 2825 | print "Audit parameters:" 2826 | print "Registry Checks: ....... " + str(registry_checks) 2827 | print "PATH Checks: ........... " + str(path_checks) 2828 | print "Service Checks: ........ " + str(service_checks) 2829 | print "Eventlog Checks: ....... " + str(drive_checks) 2830 | print "Program Files Checks: .. " + str(eventlog_checks) 2831 | print "Process Checks: ........ " + str(progfiles_checks) 2832 | print "Patch Checks: ..........." + str(patch_checks) 2833 | print "User/Group Audit: ...... " + str(user_group_audit) 2834 | print "Password Policy Audit .. " + str(passpol_audit) 2835 | print "Logged-in User Audit ... " + str(logged_in_audit) 2836 | print "Admin Users Audit: ..... " + str(admin_users_audit) 2837 | print "Host Info Audit: ....... " + str(host_info_audit) 2838 | print "Process Audit: ......... " + str(process_audit) 2839 | print "Service Audit .......... " + str(service_audit) 2840 | print "Ignore Trusted ......... " + str(ignore_trusted) 2841 | print "Owner Info ............. " + str(owner_info) 2842 | print "Weak Perms Only ........ " + str(weak_perms_only) 2843 | print "Verbosity .............. " + str(verbose) 2844 | print "Output File: ........... " + report_file_name 2845 | print 2846 | 2847 | impersonate(remote_username, remote_password, remote_domain) 2848 | 2849 | # Load win32security 2850 | # 2851 | # Try to open file and ingore the result. This gets win32security loaded and working. 2852 | # We can then turn off WOW64 and call repeatedly. If we turn off WOW64 first, 2853 | # win32security will fail to work properly. 2854 | 2855 | try: 2856 | sd = win32security.GetNamedSecurityInfo ( 2857 | ".", 2858 | win32security.SE_FILE_OBJECT, 2859 | win32security.OWNER_SECURITY_INFORMATION | win32security.DACL_SECURITY_INFORMATION 2860 | ) 2861 | except: 2862 | # nothing 2863 | pass 2864 | 2865 | # Load win32net 2866 | # 2867 | # NetLocalGroupEnum fails with like under Windows 7 64-bit, but not XP 32-bit: 2868 | # pywintypes.error: (127, 'NetLocalGroupEnum', 'The specified procedure could not be found.') 2869 | dummy = win32net.NetLocalGroupEnum(None, 0, 0, 1000) 2870 | 2871 | # Disable WOW64 - we WANT to see 32-bit areas of the filesystem 2872 | # 2873 | # Need to wrap in a try because the following call will error on 32-bit windows 2874 | try: 2875 | k32.Wow64DisableWow64FsRedirection( ctypes.byref(wow64) ) 2876 | except: 2877 | on64bitwindows = 0 2878 | # WOW64 is now disabled, so we can read file permissions without Windows redirecting us from system32 to syswow64 2879 | 2880 | # Run checks 2881 | 2882 | if registry_checks: 2883 | print_section("Registry Checks") 2884 | check_registry() 2885 | 2886 | if path_checks: 2887 | print_section("PATH Checks") 2888 | check_paths() 2889 | 2890 | if service_checks: 2891 | print_section("Service Checks") 2892 | check_services() 2893 | 2894 | if service_audit: 2895 | print_section("Service Audit") 2896 | audit_services() 2897 | 2898 | if drive_checks: 2899 | print_section("Drive Checks") 2900 | check_drives() 2901 | 2902 | if eventlog_checks: 2903 | print_section("Event Log Checks") 2904 | check_event_logs() 2905 | 2906 | if progfiles_checks: 2907 | print_section("Program Files Checks") 2908 | check_progfiles() 2909 | 2910 | if process_checks: 2911 | print_section("Process Checks") 2912 | check_processes() 2913 | 2914 | if share_checks: 2915 | print_section("Share Checks") 2916 | check_shares() 2917 | 2918 | if logged_in_audit: 2919 | print_section("Logged-in User Audit") 2920 | audit_logged_in() 2921 | 2922 | if user_group_audit: 2923 | print_section("User/Group Audit") 2924 | audit_user_group() 2925 | 2926 | if passpol_audit: 2927 | print_section("Password Policy") 2928 | audit_passpol() 2929 | 2930 | if admin_users_audit: 2931 | print_section("Admin Users Audit") 2932 | audit_admin_users() 2933 | 2934 | if host_info_audit: 2935 | print_section("Host Info Audit") 2936 | audit_host_info() 2937 | 2938 | if process_audit: 2939 | print_section("Process Audit") 2940 | audit_processes() 2941 | 2942 | if patch_checks: 2943 | print_section("Patch Checks") 2944 | check_patches() 2945 | 2946 | # task_name='test_addtask.job' 2947 | # ts=pythoncom.CoCreateInstance(taskscheduler.CLSID_CTaskScheduler,None,pythoncom.CLSCTX_INPROC_SERVER,taskscheduler.IID_ITaskScheduler) 2948 | # tasks=ts.Enum() 2949 | # for task in tasks: 2950 | # print task 2951 | # print issues 2952 | 2953 | # Generate report 2954 | 2955 | audit_data = {} 2956 | 2957 | audit_data['hostname'] = socket.gethostname() 2958 | ver_list = win32api.GetVersionEx(1) 2959 | os_ver = str(ver_list[0]) + "." + str(ver_list[1]) 2960 | # version numbers from http://msdn.microsoft.com/en-us/library/ms724832(VS.85).aspx 2961 | if os_ver == "4.0": 2962 | os_str = "Windows NT" 2963 | if os_ver == "5.0": 2964 | os_str = "Windows 2000" 2965 | if os_ver == "5.1": 2966 | os_str = "Windows XP" 2967 | if os_ver == "5.2": 2968 | os_str = "Windows 2003" 2969 | if os_ver == "6.0": 2970 | os_str = "Windows Vista" 2971 | if os_ver == "6.0": 2972 | os_str = "Windows 2008" 2973 | if os_ver == "6.1": 2974 | os_str = "Windows 2008 R2" 2975 | if os_ver == "6.1": 2976 | os_str = "Windows 7" 2977 | 2978 | audit_data['os_name'] = os_str 2979 | # print ver_list 2980 | # audit_data['os_version'] = str(ver_list[0]) + "." + str(ver_list[1]) + "." + str(ver_list[2]) + " SP" + str(ver_list[5])+ "." + str(ver_list[6]) 2981 | audit_data['os_version'] = str(ver_list[0]) + "." + str(ver_list[1]) + "." + str(ver_list[2]) + " SP" + str(ver_list[5]) 2982 | # http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx 2983 | audit_data['ips'] = local_ips 2984 | audit_data['domwkg'] = win32api.GetDomainName() 2985 | audit_data['version'] = version 2986 | audit_data['datetime'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") 2987 | audit_data['audit_user'] = os.environ['USERDOMAIN'] + "\\" + os.environ['USERNAME'] 2988 | audit_data['trusted_users'] = (handle_unicode(p) for p in trusted_principles_fq) 2989 | audit_data['trusted_groups'] = (handle_unicode(p) for p in trusted_principles) 2990 | audit_data['dangerous_privs'] = 'somedangerous_privs' 2991 | 2992 | REPORT.write(format_issues("html", issue_template, issues)) 2993 | REPORT.close 2994 | 2995 | print "\n\nReport saved to %s \n" % report_file_name 2996 | --------------------------------------------------------------------------------