I thought it would be interesting to perform an automated code review on it using CheckMarx, to see how they did related to security. The source consisted mainly of C++ code (376,545 lines of code) as well as code written in assembler. The assembler code was not scanned because CheckMarx (or any other automated code scanners) does not support assembler. What came out of the tool was interesting.
CheckMarx indicated that the risk in the code is:
The distribution of risk from Informational to High:
You have to remember that this code is from the 1980s. Many people did not have a concept of secure code and the development tools did not address security at all.
The top five vulnerabilities are as follows:
From the code that I looked at, most of the issues come from the use of unsafe functions. For example:
The function strcpy has been replaced by a safe function strncpy. The function strncpy combats buffer overflow by requiring you to put a length in it. The function strncpy did not exist in the 1980s. The code also contains 123 instances of the goto statement. For example:
From the MSDN web site, Microsoft states, “It is good programming style to use the break, continue, and return statements instead of the goto statement whenever possible. However, because the break statement exits from only one level of a loop, you might have to use a goto statement to exit a deeply nested loop.” I am not sure of the C++ syntax back in the 1980s, but maybe break, continue, and return statements did not exist.
Below is the complete list of issues found in the code:
Vulnerability Type
Occurrences
Severity
Buffer Overflow unbounded
180
High
Buffer Overflow StrcpyStrcat
22
High
Format String Attack
18
High
Buffer Overflow OutOfBound
12
High
Buffer Overflow cpycat
3
High
Use of Uninitialized Pointer
135
Medium
Dangerous Functions
58
Medium
Use of Uninitialized Variable
41
Medium
Char Overflow
35
Medium
Stored Format String Attack
19
Medium
Stored Buffer Overflow cpycat
11
Medium
MemoryFree on StackVariable
4
Medium
Short Overflow
2
Medium
Integer Overflow
1
Medium
Memory Leak
1
Medium
NULL Pointer Dereference
341
Low
Potential Path Traversal
24
Low
Unchecked Array Index
18
Low
Unchecked Return Value
11
Low
Potential Off by One Error in Loops
6
Low
Use of Insufficiently Random Values
3
Low
Potential Precision Problem
2
Low
Size of Pointer Argument
1
Low
Methods Without ReturnType
500
Information
Unused Variable
310
Information
GOTO Statement
132
Information
Empty Methods
9
Information
Potential Off by One Error in Loops
6
Information
This code is a good example of what not to do.
Programming languages and tools have evolved to make your application much more secure, but only if you teach your developers the concepts of secure coding.
In an attempt to speed up our password cracking process, we have run a number of tests to better match our guesses with the passwords that are being used by our clients. This is by no means a definitive cracking methodology, as it will probably change next month, but here’s a look at what worked for us on a recent cracking test.
For a little background, these hashes were pulled from a domain controller in the last six months. The DC still had some hashes stored in the older LanManager (LM) format in addition to NTLM. The password cracking process is also helped by using any cleartext passwords, recovered during the penetration test, as a dictionary.
For this sample, there were:
1000 total hashes (159 LM/NTLM, 841 NTLM-Only)
828 unique hashes
172 accounts with duplicate* passwords (*shared with one or more accounts)
Since LM hashes are weaker, we cracked those first. Initial attacks cracked all of the LM/NTLM hashes, giving us a nice head start (130/828 unique hashes or 15.7% cracked) and a good list to feed back into our other attacks.
The General Methodology:
1. Use the dictionary and rules (Three minutes*) – Remaining Unique Hashes 698
Our dictionary file will typically catch the simple passwords. Our dictionary includes previously cracked passwords and most dictionary-word-based passwords will be in here. Add in a couple of simple rules (d3ad0ne, passwordspro, etc.) and this will catch a few of the “smarter” users with passwords like “1qaz2wsx%“. As for the starting rules, we’re currently using a mix of the default oclHashcat rules and some of the rules from KoreLogic’s 2010 rule list – https://contest-2010.korelogic.com/rules-hashcat.html For our sample set of data, the dictionary attack (with a couple of rules) caught 372 of the remaining 698 hashes (53%).
2. Start with the masking attacks (Fifteen minutes*) – Remaining Unique Hashes 326
Using mask attacks allows us to match common password patterns. Based on the passwords that we’ve cracked in the past, we identified the fifty most common password patterns (that our clients use). Here’s a handy perl script for identifying those patterns – https://pastebin.com/Sybzwf3K.
Due to the excessive time that some of these masks take, we’ve trimmed our list down to forty-three masks. The masks are based on the types of characters being used for the password They follow the following format:
?u?l?l?l?l?d?d?d
This is equivalent to (1 Uppercase Character) (4 Lowercase Characters) (3 Decimals). Or a more practical example would be “Netsp199”
Our top forty-three masks take about fifteen minutes to run through and caught (29/326) 8% of the remaining uncracked hashes from this sample.
3. Go back to the dictionary, this time with more ammunition (10 minutes*) – Remaining Unique Hashes 297
After we’ve cracked some of the passwords, we will want to funnel those results back into our mangling attacks. It’s not hard for us to guess “!123Acme123!” after we’ve already cracked “Acme123“. Just use the cracked passwords as your dictionary and repeat your rule-based attacks. This is also a good point to combine rules. oclHashcat allows you to combine rule sets to do a multi-vector mangle on your dictionary words. I’ve had pretty good luck with combining the best64 and the d3ad0neV3 rules, but your mileage may vary.
Using this technique, we were able to crack four of the remaining 297 (1.3%) uncracked hashes.
4. Double your dictionary, double your fun? (20-35 minutes)
At this point, we’re hitting our limits and need to start getting creative. Along with our hefty primary dictionary, we have a number of shorter dictionaries that are small enough that we can combine them to catch repeaters (e.g. “P@ssword P@ssword“). The attack is pretty simple: a word from the first dictionary will be appended with a word from the second.
This style of attack is known as the combinator attack and is run with the -a 1 mode of oclHashcat. Additional rules can be applied to each dictionary to catch some common word delineation patterns (“Super-Secret!“). The example here would append a dash to the first word and an exclamation mark to the second.
To be honest, this did not work for this sample set. Normally, we catch a few with this and it can be really handy in some scenarios, but that was not the case here.
At this point, we will (typically) be about an hour into our cracking process. From the uniqued sample set, we were able to crack 530 of the 828 hashes (64%) within one hour. From the complete set (including duplicates), we were able to crack 701 of the 1,000 total hashes (70.1%).
5. When all else fails, brute force
Take a look at the company password policy. Seven characters is the minimum? That will take us about forty minutes to go through. Eight characters? A little over two and a half days. What does that mean for our cracking? It means we can easily go after any remaining seven character passwords (where applicable). For those with eight character minimums (or higher), it doesn’t hurt for us to run a brute-force overnight on the hashes. Anything we get out of the brute-force can always be pulled back in to the wordlist for the rule-based attacks.
Given that a fair amount of our cracking happens during a well-defined project timeframe, we’ve found that it’s best for us to limit the cracking efforts to about twenty-four hours. This prevents us from wasting too much time on a single hash and it frees up our cracking rig for our other projects. If we really need to crack a hash, we’ll extend this time limit out, but a day is usually enough to catch most of the hashes we’re trying to crack.
With the overall efforts that we put in here (~24 hours), we ended up cracking 757 of the 1,000 hashes (75.7%).
As luck would have it, I wrote up all of the stats for this blog and immediately proceeded to crack two new sets of NTLM hashes. One was close to 800 hashes (90% cracked) and another had over 5000 hashes (84% cracked). So your results will vary based on the hashes you’re trying to crack.
*All times are based on our current setup (Four 7950 cards running OclHashcat 1.01)
One final item to note. This is not the definitive password cracking methodology. This will probably change for us in the next year, month, week… People are always changing up how they create passwords and that will continue to influence the way we attack their hashes. I’ll try to remember to come back to this next year and update with any changes. Did we miss any key cracking strategies? Let me know in the comments.
Extracting cleartext credentials from critical systems is always fun. While MSSQL server hashes local SQL credentials in the database, linked server credentials are stored encrypted. And if MSSQL can decrypt them, so can you using the PowerShell script released along with this blog. From the offensive point of view, this is pretty far into post exploitation as sysadmin privileges are needed on the SQL server and local administrator privileges are needed on the Windows server. From the defensive point of view, this is just another reminder that unnecessary database links, database links with excessive privileges, and the use of SQL server authentication rather than integrated authentication can result in unnecessary risk. This blog should be interesting to database hackers and admins interested in learning more.
Linked Servers
Microsoft SQL Server allows users to create links to external data sources, typically to other MSSQL servers. When these links are created, they can be configured to use the current security context or static SQL server credentials. If SQL server credentials are used, the user account and password are saved to the database encrypted and thus they are stored in a reversible format. A one-way hash cannot be used, because the SQL server has to be able to access the cleartext credentials to authenticate to other servers. So, if the credentials are encrypted and not hashed, there must be a way for the SQL server to decrypt them prior to use. The remainder of this blog will focus on how that happens.
Linked Server Password Storage
MSSQL stores link server information, including the encrypted password, in master.sys.syslnklgns table. Specifically, the encrypted password is stored in the “pwdhash” column (even though it’s not a hash). Below is an example:
The master.sys.syslnklgns table cannot be accessed using a normal SQL connection, but rather a Dedicated Administrative Connection (DAC) is needed (more information about DAC at https://technet.microsoft.com/en-us/library/ms178068%28v=sql.105%29.aspx). Sysadmin privileges are needed to start a DAC connection, but as local administrator privileges are needed anyways, that shouldn’t be a problem. If local administrators don’t have sysadmin privileges you’ll just have to impersonate the MSSQL server account or local SYSTEM account. More details on this can be found on Scott’s blog at https://www.netspi.com/blog/entryid/133/sql-server-local-authorization-bypass.
MSSQL Encryption
Time to introduce some MSSQL encryption basics. To move ahead, access to the Service Master Key (SMK) is required (more information about SMK at https://technet.microsoft.com/en-us/library/ms189060.aspx). According to microsoft.com “The Service Master Key is the root of the SQL Server encryption hierarchy. It is generated automatically the first time it is needed to encrypt another key.” SMK is stored in master.sys.key_encryptions table and it can be identified by the key_id 102. SMK is encrypted using Windows Data Protection API (DPAPI) and there are two versions of it in the database; one encrypted as LocalMachine and the other in the context of CurrentUser (meaning the SQL Server service account here). We’ll choose the former to extract the key as LocalMachine encryption uses the Machinekey for encryption and it can be decrypted without impersonating the service account. Below is an example of what that looks like:
Additional entropy is added to strengthen the encryption but the entropy bytes can be found in the registry at HKLM:SOFTWAREMicrosoftMicrosoft SQL Server[instancename]SecurityEntropy. Once again, local administrator privileges are needed to access the registry key. The entropy is stored in the registry for each MSSQL instance as shown below:
After that (and removing some padding / metadata from the encrypted value) we can decrypt the SMK using DPAPI.
Decrypting Linked Server Passwords
Based on the length of the SMK (or the MSSQL version) we can determine the encryption algorithm: MSSQL 2012 uses AES, earlier versions use 3DES. In additional, the pwdhash value has to be parsed a bit to find the encrypted password. The first answer referring Pro T-SQL Programmer’s guide at https://stackoverflow.com/questions/2822592/how-to-get-compatibility-between-c-sharp-and-sql2k8-aes-encryption got me on the right track; even though the byte format didn’t seem to match exactly like detailed on the page, it wasn’t too hard to find the right bytes to encrypt. So now, using the SMK, it is possible to extract all of the link credentials (when SQL Server account is used, not Windows authentication) in cleartext.
Decrypting Linked Server Passwords with PowerShell – Get-MSSQLLinkPasswords.psm1
The script must be run locally on the MSSQL server (as DPAPI requires access to the local machine key). The user executing the script must also have sysadmin access to all the database instances (for the DAC connection) and local admin privileges on the Windows server (to access the entropy bytes in registry). In addition, if UAC is enabled, the script must be ran as an administrator. Below is a summary of the process used by the script.
Identify all of the MSSQL instances on the server.
Attempt to create a DAC connection to each instance.
Select the encrypted linked server credentials from the “pwdhash” column of the “mas-ter.sys.syslnklgns” table for each instance.
Select the encrypted Service Master Key (SMK) from the “master.sys.key_encryptions” table of each instance where the “key_id” column is equal to 102. Select the version that has been encrypted as LocalMachine based on the “thumbprint” column.
Extract the entropy value from the registry location HKLM:SOFTWAREMicrosoftMicrosoft SQL Server[instancename]SecurityEntropy.
Use the information to decrypt the SMK.
The script determines the encryption algorithm (AES or 3DES) used to encrypt the SMK based on SQL Server version and SMK key length.
Use the SMK to decrypt the linked server credentials.
If successful, the script displays the cleartext linked server credentials. Below is an example of the end result:
I’ve tested the script with MSSQL 2005, 2008, 2012, 2008 Express, and 2012 Express. There might be some bugs, but it appears to work reliably. Please let me know if you notice any errors or if I did not account for certain situations etc.
IBM WebSphere Commerce or WebSphere Commerce Suite (WCS), developed by IBM, is a software platform framework for e-commerce and is actively being used by giant retailers.
While working on an engagement last year, I stumbled upon a crypto vulnerability in the IBM Websphere Commerce framework, originally found by VSR Security (CVE-2013-05230). Essentially the krypto parameter found in some URLs could be decrypted using a padding oracle attack. Since no public exploit has been published as time of this writing, I figured it might be an interesting exercise to write one.
if (decrypting) {
if (outputCapacity < paddedLen) {
cipher.save();
}
// create temporary output buffer so that only "real"
// data bytes are passed to user's output buffer.
byte[] outWithPadding = new byte[totalLen];
totalLen = finalNoPadding(finalBuf, finalOffset, outWithPadding,
0, totalLen);
if (padding != null) {
int padStart = padding.unpad(outWithPadding, 0, totalLen);
if (padStart < 0) {
throw new BadPaddingException("Given final block not "
+ "properly padded");
}
This crypto library will throw a BadPaddingException when invalid padding is encountered. It is then up to the code that is using this library to handle this exception. In this case, IBM WebSphere Commerce framework seems to leak the information about the padding byte by returning a different response when invalid padding occurs.
Detection
Once you have found a page with krypto URL parameter, differentiate between the following conditions:
Valid ciphertext decrypted
Valid padding – You will need this one to de-krypto 🙂
Invalid padding error
In real world scenario, I found condition (1) and (2) are the same for most of the time. Here is how to test it:
You can use Burp’s Comparer tool to compare those responses. If (3) is different from (2) and (1) then it may be possible to decrypt this parameter.
Exploitation
Padding oracle attack is not specific to any cipher, rather it is a generic attack against CBC ciphers. Therefore any padding oracle implementations will work in this scenario. I have looked at several padding oracle algorithms, and Ron Bowes’ algorithm by far is the fastest one. The reason is, most padding oracle algorithms iterate over the ciphertext bytes of [Block (n-1)] randomly (bytes ranging from 0-255) to find the plaintext of [Block n]. Ron’s algorithm also iterates over the ciphertext bytes of [Block (n-1)]; but instead of doing so randomly, it loops over a defined character set first (which is composed of ASCII characters, bytes ranging from 32-126), then the rest of the possible byte values (0-31 and 127-255). This greatly reduces number of requests sent, since most meaningful string is composed of ASCII characters.
In addition, I made the following adaptations to Ron’s poracle framework in order for the exploit to work with this particular case:
Encoding/decoding
Ron’s Bowes’ script assumes the payload is in hexadecimal format. So extra encoding/decoding of the krypto parameter is required. The decoding process is as follow:
URL decode key characters (including newline)
Base64 decode
ASCII encode
Encoding steps:
ASCII decode
Insert newline character at every 77th character (RFC 2045 or 4880)
Base64 encode
URL encode key characters
Format of payload
Ron’s original script is made on the assumption that the server will decrypt any two-block payload provided by the attacker. Let us assume that our valid ciphertext blocks is:
[Block 0] [Block 1] [Block 2] [Block 3]
To decrypt [Block 2], we need to send
[Block 1] [Block 2*]
and wait until the server returns padding error.
However, with IBM Web Commerce, the server does not decrypt any arbitrary two-block payload. It seems to require a valid header or expect certain values at the beginning of the block. Therefore, in order to decrypt [Block 3], we need to send:
[Block 0] [Block 1] [Block 2*] [Block 3]
This is an easy fix, I only needed to append the early blocks to the payload.
Parallelization
I added support for multi-threading. The script uses Meh’s Ruby thread pool library to reduce the overhead of creating and destroying threads.
I also added the ability to skip already-decrypted blocks and save temporary results to a session file. In situations where the connection ends abruptly, the script only needs to work off the remaining blocks.
Usage
Usage: Usage: Dekrypto.rb [options]
Specific options:
-s, --sort Sort temporary results
-v, --verbose Show debug messages
-t, --threads SIZE Set threadpool size
-f, --file FILE Save temporary results to file
-h, --help Show this message
Example
Running test server:
ruby KryptoTestServer.rb
Running Dekrypto script with verbose output, ten threads, saving temporary results to a text file (decrypted.txt)
When testing against real applications, I notice that some blocks may not be decrypted properly. My guess is that the server may catch earlier exception in the decoding phase (at either the URL decoding step or the UTF-8 decoding step) and return valid response. Currently, I do not know how to overcome this limitation so any suggestions will be much appreciated.
Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.
Name
Domain
Purpose
Expiry
Type
YSC
youtube.com
YouTube session cookie.
52 years
HTTP
Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.
Name
Domain
Purpose
Expiry
Type
VISITOR_INFO1_LIVE
youtube.com
YouTube cookie.
6 months
HTTP
Analytics cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.
We do not use cookies of this type.
Preference cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.
We do not use cookies of this type.
Unclassified cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.
We do not use cookies of this type.
Cookies are small text files that can be used by websites to make a user's experience more efficient. The law states that we can store cookies on your device if they are strictly necessary for the operation of this site. For all other types of cookies we need your permission. This site uses different types of cookies. Some cookies are placed by third party services that appear on our pages.